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.
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¶
-
Usar getIfPresentMetaObject para datos opcionales
-
Verificar hasMetaObject antes de acciones costosas
-
Limpiar metadatos temporales
❌ No Hacer¶
- No confiar en getMetaObject para valores opcionales
- No almacenar referencias circulares
- No modificar valores sin putMetaObject
Siguiente¶
- MetaRegistry: Registro de MetaKeys
- MetaKey: Identificadores de metadatos
- Overview: Visión general del sistema
¿Preguntas? Consulta FAQ