Manipuladores de Datos

Accediendo y modificando datos

Un manipulador de datos representa un determinado componente y todos sus datos. Este almacena una representación de esos datos y puede ser ofrecido o creado a partir de titulares de datos que poseen un componente coincidente. De nuevo, usemos un ejemplo. Y nuevamente trata de sanar a alguien (o algo).

** Ejemplo de código: Curación con manipuladores de datos **

import org.spongepowered.api.data.DataHolder;
import org.spongepowered.api.data.DataTransactionResult;
import org.spongepowered.api.data.manipulator.mutable.entity.HealthData;
import org.spongepowered.api.data.value.mutable.MutableBoundedValue;

import java.util.Optional;

public static DataTransactionResult heal(DataHolder target) {
    Optional<HealthData> healthOptional = target.get(HealthData.class);
    if (healthOptional.isPresent()) {
        HealthData healthData = healthOptional.get();

        double maxHealth = healthData.maxHealth().get();
        MutableBoundedValue<Double> currentHealth = healthData.health();
        currentHealth.set(maxHealth);
        healthData.set(currentHealth);

        target.offer(healthData);
    }
}

Primero, debemos verificar si nuestro objetivo tiene datos de salud. Lo hacemos pidiéndole primero que nos proporcione sus datos de salud pasando su clase al método `` get () . Obtenemos un `` Opcional que podemos usar para nuestro control. Este `` Optional`` estará ausente si nuestro objetivo no es compatible con :javadoc: HealthData o si lo admite, pero en este momento no contiene ningún dato de salud.

Si los datos de salud están presentes, ahora contiene una copia mutable de los datos presentes en el titular de los datos. Hacemos nuestras modificaciones y finalmente volvemos a ofrecer los datos modificados a nuestro objetivo, donde es aceptado (nuevamente, `` offer () `` devolverá a :javadoc: DataTransactionResult que no tomaremos en cuenta en este ejemplo y volveremos a :doc: en un punto posterior <transactions>).

Como puede ver, los resultados para `` health () `` y `` maxHealth () `` son nuevamente valores con clave que obtenemos de :javadoc: DataHolder. Como el :javadoc: MutableBoundedValue que recibimos al llamar` health () nuevamente solo contiene una copia de los datos, primero debemos aplicar nuestros cambios a :javadoc: DataManipulator` antes de poder ofrecer el ` healthData` de vuelta hacia nuestro objetivo.

Truco

Regla n. ° 1 de la API de datos: todo lo que recibe es una copia. Por lo tanto, cada vez que cambie algo, asegúrese de que su cambio se propague de nuevo hacia donde vino el valor original.

Otra posible modificación es eliminar completamente un `` DataManipulator``. Esto se hace a través del método `` remove () `` que acepta una referencia de clase para el tipo de `` DataManipulator`` para eliminar. Algunos datos no pueden eliminarse e intentar hacerlo siempre devolverá un DataTransactionResult que indica una falla. El siguiente código intenta eliminar un nombre personalizado de un `` DataHolder`` dado. De nuevo, el resultado de la transacción se descarta.

** Ejemplo de código: eliminar un nombre de visualización personalizado **

import org.spongepowered.api.data.manipulator.mutable.DisplayNameData;

public void removeName(DataHolder target) {
    target.remove(DisplayNameData.class);
}

DataManipulator vs llaves

Si comparas nuestros dos ejemplos de curación, te preguntarás “¿Por qué molestarse con los manipuladores de datos de todos modos? Las claves son mucho más fáciles” y tienen razón, para obtener y establecer valores únicos. Pero el verdadero mérito de un manipulador de datos es que contiene * todos * los datos pertenecientes a un determinado componente. Echemos un vistazo a otro ejemplo.

** Ejemplo de código: intercambio de la salud de dos titulares de datos **

public void swapHealth(DataHolder targetA, DataHolder targetB) {
    if (targetA.supports(HealthData.class) && targetB.supports(HealthData.class)) {
        HealthData healthA = targetA.getOrCreate(HealthData.class).get();
        HealthData healthB = targetB.getOrCreate(HealthData.class).get();
        targetA.offer(healthB);
        targetB.offer(healthA);
    }
}

Primero verificamos si ambos objetivos son compatibles con HealthData. Si lo hacen, guardamos el estado de ambos en una variable cada uno. No necesitamos molestarnos con `` Optional`` esta vez ya que verificamos que `` HealthData`` sea compatible y el método `` getOrCreate () `` asegura que incluso si no hay datos, se generan valores predeterminados.

Luego, solo ofrecemos los datos de salud guardados al objetivo * other *, cambiando así su estado de salud entre sí.

Este ejemplo hecho con `` Llaves`` sería un poco más largo y más complicado ya que tendríamos que cuidar cada clave individual por nosotros mismos. Y si, en lugar de salud cambiamos otro manipulador de datos que contiene aún más datos (tal vez :javadoc: InvisibilityData que incluso contiene una lista), tendríamos mucho más trabajo por hacer. Pero dado que el propio titular de los datos se ocupa de todos los datos que le pertenecen, incluso podríamos modificar la función anterior para intercambiar datos arbitrarios entre dos titulares.

** Ejemplo de código: Intercambio de cualquier manipulador de datos **

import org.spongepowered.api.data.manipulator.DataManipulator;

public  <T extends DataManipulator<?,?>> void swapData(DataHolder targetA, DataHolder targetB, Class<T> dataClass) {
   if (targetA.supports(dataClass) && targetB.supports(dataClass)) {
       T dataA = targetA.getOrCreate(dataClass).get();
       T dataB = targetB.getOrCreate(dataClass).get();
       targetA.offer(dataB);
       targetB.offer(dataA);
   }
}

La capacidad de escribir una función que puede simplemente intercambiar datos en un titular de datos con los mismos datos en otro titular de datos demuestra el objetivo de diseño central de la API de datos: Máxima compatibilidad en toda la API.

Manipuladores de datos mutables vs. inmutables

Para cada manipulador de datos, hay una coincidencia :javadoc: ImmutableDataManipulator. Por ejemplo, tanto `` HealthData`` como :javadoc: ImmutableHealthData contienen los mismos datos, solo el último devuelve nuevas instancias al solicitar datos modificados.

La conversión entre manipuladores de datos mutables e inmutables es realizada a través de los métodos ``asImmutable () `` y `` asMutable () ``, cada uno de los cuales devolverá una copia de los datos. La única forma de obtener un manipulador de datos inmutables desde un soporte de datos es obtener uno mutable y luego usar `` asImmutable () ``.

Un posible caso de uso para esto sería un evento personalizado que se presente cuando alguien es sanado. Debe proporcionar copias de los datos de salud antes y después, pero los oyentes del evento no deberían poder cambiarlos. Por lo tanto, podemos escribir nuestro evento para proporcionar solo instancias de ImmutableHealthData. De esta forma, incluso si el código de un tercero interactúa con nuestros datos, podemos estar seguros de que no cambiará.

Ausencia de datos

Como se mencionó anteriormente, el método `` get () `` puede devolver un `` Optional`` vacío si uno de los siguientes es verdadero:

  • El `` DataHolder`` no es compatible con el `` DataManipulator``

  • El `` DataHolder`` es compatible con el `` DataManipulator``, pero actualmente no contiene datos de ese tipo

Existe una gran diferencia semántica entre los datos que no están presentes y los datos que consisten en valores predeterminados. Si bien esto último siempre es posible, hay casos en los que es imposible que un DataHolder admita un tipo de datos y luego no los guarde. Ejemplos de esos incluyen:

  • `` HealthData`` siempre está presente en cada (vainilla) `` DataHolder`` que lo soporta

  • DisplayNameData is always present on a Player, but may be absent on other entities.