Manipuladores de Datos

Advertencia

These docs were written for SpongeAPI 7 and are likely out of date. If you feel like you can help update them, please submit a PR!

Accediendo y modificando datos

A data manipulator represents a certain component and all of its data. It stores a representation of that data and can be offered to or created from data holders which possess a matching component. Again, let’s use an example. Once more we try to heal someone (or something).

** 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 void 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);
    }
}

First, we need to check if our target has health data. We do so by first asking it to provide us with its health data by passing its class to the get() method. We get an Optional which we can use for our check. This Optional will be absent if either our target does not support HealthData or if it supports it but at the present moment does not hold any health data.

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);
    }
}

First, we check if both targets support HealthData. If they do, we save the health of both in one variable each. We don’t need to bother with Optional this time since we verified that HealthData is supported and the getOrCreate() method ensures that even if no data is present, default values are generated.

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 () ``.

A possible use case for this would be a custom event fired when someone is healed. It should provide copies of the health data before and after, but event listeners should not be able to change them. Therefore, we can write our event to only provide ImmutableHealthData instances. That way, even if third party code gets to interact with our data, we can rest assured that it will not be changed.

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.