Ir para o conteúdo

IMetaStore

IMetaStore<K> es la interfaz principal para almacenar y recuperar metadatos asociados a objetos del juego.

Interfaz

Ubicación: com.hypixel.hytale.server.core.meta.IMetaStore.java

public interface IMetaStore<K> {
    IMetaStoreImpl<K> getMetaStore();

    // Obtener valor (con inicialización automática si no existe)
    default <T> T getMetaObject(MetaKey<T> key);

    // Obtener valor sin inicializar (puede retornar null)
    @Nullable
    default <T> T getIfPresentMetaObject(MetaKey<T> key);

    // Establecer valor (retorna el valor anterior o null)
    @Nullable
    default <T> T putMetaObject(MetaKey<T> key, T obj);

    // Eliminar valor
    @Nullable
    default <T> T removeMetaObject(MetaKey<T> key);

    // Eliminar valor serializado
    @Nullable
    default <T> T removeSerializedMetaObject(MetaKey<T> key);

    // Verificar si existe un valor
    default boolean hasMetaObject(MetaKey<?> key);

    // Iterar sobre todos los metadatos
    default void forEachMetaObject(MetaEntryConsumer consumer);

    // Marcar como modificado (para persistencia)
    default void markMetaStoreDirty();

    // Consumir flag de modificación
    default boolean consumeMetaStoreDirty();

    @FunctionalInterface
    interface MetaEntryConsumer {
        <T> void accept(int id, T value);
    }
}

Métodos Principales

getMetaObject

Obtiene un valor, inicializándolo automáticamente si no existe.

// Declarar MetaKey con valor por defecto
MetaKey<Integer> LEVEL = registry.registerMetaObject(e -> 1);

// Primera vez: inicializa con valor por defecto
int level = entity.getMetaObject(LEVEL); // Retorna 1

// Segunda vez: retorna valor almacenado
entity.putMetaObject(LEVEL, 5);
level = entity.getMetaObject(LEVEL); // Retorna 5

getIfPresentMetaObject

Obtiene un valor sin inicializar. Retorna null si no existe.

MetaKey<String> NICKNAME = registry.registerMetaObject(e -> "");

// Primera vez: retorna null (no inicializa)
String nick = entity.getIfPresentMetaObject(NICKNAME); // null

// Después de establecer un valor
entity.putMetaObject(NICKNAME, "Steve");
nick = entity.getIfPresentMetaObject(NICKNAME); // "Steve"

Uso típico:

// Verificar si tiene datos opcionales
String customData = entity.getIfPresentMetaObject(CUSTOM_DATA);
if (customData != null) {
    // Procesar datos personalizados
    processCustomData(customData);
}

putMetaObject

Establece un valor y retorna el valor anterior.

MetaKey<Integer> SCORE = registry.registerMetaObject(e -> 0);

// Primera vez: retorna null (no había valor)
Integer oldScore = entity.putMetaObject(SCORE, 100); // null

// Segunda vez: retorna el valor anterior
oldScore = entity.putMetaObject(SCORE, 200); // 100

Patrón de incremento:

public void addScore(Entity entity, int amount) {
    int current = entity.getMetaObject(SCORE);
    entity.putMetaObject(SCORE, current + amount);
}

removeMetaObject

Elimina un metadato y retorna su valor.

MetaKey<String> TEMP_DATA = registry.registerMetaObject(e -> "");

entity.putMetaObject(TEMP_DATA, "temporary");

// Eliminar y obtener valor
String removed = entity.removeMetaObject(TEMP_DATA); // "temporary"

// Ya no existe
boolean exists = entity.hasMetaObject(TEMP_DATA); // false

hasMetaObject

Verifica si existe un metadato sin inicializarlo.

if (entity.hasMetaObject(CUSTOM_FLAG)) {
    // El metadato existe
    boolean flag = entity.getMetaObject(CUSTOM_FLAG);
} else {
    // El metadato no existe (no se ha establecido)
}

forEachMetaObject

Itera sobre todos los metadatos almacenados.

entity.forEachMetaObject((id, value) -> {
    System.out.println("Meta[" + id + "] = " + value);
});

Ejemplo con filtrado:

entity.forEachMetaObject((id, value) -> {
    if (value instanceof String) {
        System.out.println("String meta: " + value);
    }
});

Implementación Interna

IMetaStoreImpl

La implementación real usa un array para almacenamiento eficiente:

public class IMetaStoreImpl<K> {
    private Object[] metaObjects;
    private MetaRegistry<K> registry;
    private K parent;
    private boolean dirty;

    public <T> T getMetaObject(MetaKey<T> key) {
        int id = key.getId();

        // Expandir array si es necesario
        if (id >= metaObjects.length) {
            metaObjects = Arrays.copyOf(metaObjects, id + 1);
        }

        // Inicializar si es null
        if (metaObjects[id] == null) {
            T value = registry.newMetaObject(key, parent);
            metaObjects[id] = value;
            markDirty();
        }

        return (T) metaObjects[id];
    }

    public <T> T putMetaObject(MetaKey<T> key, T value) {
        int id = key.getId();

        if (id >= metaObjects.length) {
            metaObjects = Arrays.copyOf(metaObjects, id + 1);
        }

        T oldValue = (T) metaObjects[id];
        metaObjects[id] = value;
        markDirty();

        return oldValue;
    }
}

Persistencia

Marcar como Modificado

// Cuando se modifica un valor persistente
entity.putMetaObject(PERSISTENT_KEY, newValue);
// Automáticamente marca como dirty

// Verificar si hay cambios
if (entity.consumeMetaStoreDirty()) {
    // Guardar cambios a disco
    saveEntityData(entity);
}

Sistema de Dirty Flag

private boolean dirty = false;

public void markMetaStoreDirty() {
    this.dirty = true;
}

public boolean consumeMetaStoreDirty() {
    boolean wasDirty = this.dirty;
    this.dirty = false;
    return wasDirty;
}

Ejemplos de Uso

Sistema de Estadísticas

public class StatsSystem {
    private static final MetaKey<Integer> KILLS =
        registry.registerMetaObject(p -> 0, true, "kills", Codec.INT);

    private static final MetaKey<Integer> DEATHS =
        registry.registerMetaObject(p -> 0, true, "deaths", Codec.INT);

    public void onPlayerKill(Player player, Entity victim) {
        int kills = player.getMetaObject(KILLS);
        player.putMetaObject(KILLS, kills + 1);

        sendMessage(player, "Kills: " + (kills + 1));
    }

    public void onPlayerDeath(Player player) {
        int deaths = player.getMetaObject(DEATHS);
        player.putMetaObject(DEATHS, deaths + 1);
    }

    public double getKDRatio(Player player) {
        int kills = player.getMetaObject(KILLS);
        int deaths = player.getMetaObject(DEATHS);
        return deaths > 0 ? (double) kills / deaths : kills;
    }
}

Sistema de Flags

public class FlagSystem {
    private static final MetaKey<Set<String>> FLAGS =
        registry.registerMetaObject(e -> new HashSet<>());

    public void addFlag(Entity entity, String flag) {
        Set<String> flags = entity.getMetaObject(FLAGS);
        flags.add(flag);
    }

    public boolean hasFlag(Entity entity, String flag) {
        Set<String> flags = entity.getIfPresentMetaObject(FLAGS);
        return flags != null && flags.contains(flag);
    }

    public void removeFlag(Entity entity, String flag) {
        Set<String> flags = entity.getIfPresentMetaObject(FLAGS);
        if (flags != null) {
            flags.remove(flag);
        }
    }
}

Cache de Datos Costosos

public class CacheSystem {
    private static final MetaKey<PathfindingCache> PATH_CACHE =
        registry.registerMetaObject(e -> new PathfindingCache());

    public Path findPath(Entity entity, Vector3d target) {
        PathfindingCache cache = entity.getMetaObject(PATH_CACHE);

        // Verificar cache
        Path cached = cache.get(target);
        if (cached != null && !cached.isStale()) {
            return cached;
        }

        // Calcular nueva ruta
        Path path = calculatePath(entity.getPosition(), target);
        cache.put(target, path);

        return path;
    }
}

Mejores Prácticas

✅ Hacer

  1. Usar getIfPresentMetaObject para datos opcionales

    String optional = entity.getIfPresentMetaObject(OPTIONAL_DATA);
    if (optional != null) {
        process(optional);
    }
    

  2. Verificar hasMetaObject antes de acciones costosas

    if (entity.hasMetaObject(CACHE_KEY)) {
        return entity.getMetaObject(CACHE_KEY);
    }
    return computeExpensiveValue();
    

  3. Limpiar metadatos temporales

    entity.removeMetaObject(TEMP_CALCULATION);
    

❌ No Hacer

  1. No confiar en getMetaObject para valores opcionales
  2. No almacenar referencias circulares
  3. No modificar valores sin putMetaObject

Siguiente


¿Preguntas? Consulta FAQ