MetaKey¶
MetaKey<T> es un identificador único y tipado que representa un tipo específico de metadato que puede almacenarse en un IMetaStore.
Clase MetaKey¶
Ubicación: com.hypixel.hytale.server.core.meta.MetaKey.java
public class MetaKey<T> {
private final int id;
MetaKey(int id) {
this.id = id;
}
public int getId() {
return this.id;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MetaKey<?> metaKey = (MetaKey) o;
return id == metaKey.id;
}
@Override
public int hashCode() {
return this.id;
}
@Override
public String toString() {
return "MetaKey{id=" + this.id + "}";
}
}
Características¶
Type-Safe¶
Las MetaKeys son genéricas, garantizando seguridad de tipos:
MetaKey<String> NAME = registry.registerMetaObject(e -> "");
MetaKey<Integer> LEVEL = registry.registerMetaObject(e -> 1);
// Compilación correcta
String name = entity.getMetaObject(NAME);
int level = entity.getMetaObject(LEVEL);
// Error de compilación
int nameInt = entity.getMetaObject(NAME); // ❌ Error de tipo
Identificador Único¶
Cada MetaKey tiene un ID único asignado secuencialmente:
MetaKey<String> KEY1 = registry.registerMetaObject(e -> ""); // id=0
MetaKey<Integer> KEY2 = registry.registerMetaObject(e -> 0); // id=1
MetaKey<Boolean> KEY3 = registry.registerMetaObject(e -> false); // id=2
System.out.println(KEY1.getId()); // 0
System.out.println(KEY2.getId()); // 1
System.out.println(KEY3.getId()); // 2
Creación de MetaKeys¶
Las MetaKeys solo pueden crearse mediante un MetaRegistry:
MetaRegistry<Entity> registry = new MetaRegistry<>();
// Crear MetaKey no persistente
MetaKey<String> DISPLAY_NAME = registry.registerMetaObject(
entity -> entity.getName() + " (Custom)"
);
// Crear MetaKey persistente
MetaKey<Integer> SCORE = registry.registerMetaObject(
entity -> 0, // Valor por defecto
true, // Persistente
"player_score", // Nombre de serialización
Codec.INT // Codec para serializar
);
Tipos de MetaKeys¶
MetaKey Normal¶
Características: - No persiste entre sesiones - Más ligera en memoria - Para datos temporales
Ejemplo:
// Contador de ticks desde el spawn
public static final MetaKey<Integer> TICKS_ALIVE =
registry.registerMetaObject(e -> 0);
// Lista de entidades cercanas (se recalcula cada tick)
public static final MetaKey<List<Entity>> NEARBY_ENTITIES =
registry.registerMetaObject(e -> new ArrayList<>());
PersistentMetaKey¶
Ubicación: com.hypixel.hytale.server.core.meta.PersistentMetaKey.java
Características: - Persiste entre sesiones - Requiere codec para serialización - Tiene un nombre de clave único - Para datos importantes
Ejemplo:
// Puntos de experiencia del jugador
public static final MetaKey<Integer> XP =
registry.registerMetaObject(
player -> 0,
true, // Persistente
"player_xp", // Nombre único
Codec.INT // Codec
);
// Configuración de jugador
public static final MetaKey<PlayerConfig> CONFIG =
registry.registerMetaObject(
player -> new PlayerConfig(),
true,
"player_config",
PlayerConfig.CODEC
);
Uso de MetaKeys¶
Almacenar Datos¶
public class PlayerManager {
private static final MetaKey<Integer> KILL_COUNT =
registry.registerMetaObject(p -> 0);
public void onPlayerKill(Player player) {
int kills = player.getMetaObject(KILL_COUNT);
player.putMetaObject(KILL_COUNT, kills + 1);
}
}
Recuperar Datos¶
public int getKillCount(Player player) {
// getMetaObject() nunca retorna null
return player.getMetaObject(KILL_COUNT);
}
public Integer getKillCountIfExists(Player player) {
// getIfPresentMetaObject() puede retornar null
return player.getIfPresentMetaObject(KILL_COUNT);
}
Verificar Existencia¶
Eliminar Datos¶
public void clearCustomData(Entity entity) {
Integer oldValue = entity.removeMetaObject(CUSTOM_DATA);
if (oldValue != null) {
System.out.println("Eliminado: " + oldValue);
}
}
Patrones de Uso¶
Lazy Initialization¶
private static final MetaKey<ExpensiveObject> EXPENSIVE =
registry.registerMetaObject(e -> {
System.out.println("Inicializando objeto costoso...");
return new ExpensiveObject();
});
// Solo se inicializa la primera vez que se accede
ExpensiveObject obj = entity.getMetaObject(EXPENSIVE);
Factory Pattern¶
private static final MetaKey<ConnectionPool> POOL =
registry.registerMetaObject(server -> {
return new ConnectionPool(
server.getConfig().getMaxConnections()
);
});
Default Values from Config¶
private static final MetaKey<Integer> MAX_HEALTH =
registry.registerMetaObject(entity -> {
EntityType type = entity.getType();
return ConfigManager.getDefaultHealth(type);
});
MetaKeys Predefinidas¶
Interaction Context¶
// Interaction.java:568-591
public static final MetaKey<Ref<EntityStore>> TARGET_ENTITY =
CONTEXT_META_REGISTRY.registerMetaObject(data -> null);
public static final MetaKey<Vector4d> HIT_LOCATION =
CONTEXT_META_REGISTRY.registerMetaObject(data -> null);
public static final MetaKey<String> HIT_DETAIL =
CONTEXT_META_REGISTRY.registerMetaObject(data -> null);
public static final MetaKey<BlockPosition> TARGET_BLOCK =
CONTEXT_META_REGISTRY.registerMetaObject(data -> null);
public static final MetaKey<Integer> TARGET_SLOT =
CONTEXT_META_REGISTRY.registerMetaObject(data -> 0);
Uso:
InteractionContext ctx = ...;
// Obtener entidad objetivo
Ref<EntityStore> target = ctx.getMetaObject(Interaction.TARGET_ENTITY);
if (target != null && target.isValid()) {
Entity entity = commandBuffer.getEntity(target);
// Procesar entidad
}
// Obtener posición del impacto
Vector4d hitPos = ctx.getMetaObject(Interaction.HIT_LOCATION);
System.out.println("Hit: " + hitPos);
Interaction Instance¶
// Interaction.java:586-590
public static final MetaKey<Float> TIME_SHIFT =
META_REGISTRY.registerMetaObject(data -> null);
public static final MetaKey<Damage> DAMAGE =
CONTEXT_META_REGISTRY.registerMetaObject(data -> null);
Mejores Prácticas¶
✅ Hacer¶
-
Declarar como constantes estáticas
-
Usar nombres descriptivos
-
Agrupar MetaKeys relacionadas
-
Documentar el propósito
❌ No Hacer¶
-
No crear MetaKeys dinámicamente
-
No duplicar MetaKeys
-
No usar tipos mutables sin cuidado
Comparación e Identidad¶
Igualdad¶
Las MetaKeys son iguales si tienen el mismo ID:
MetaKey<String> key1 = registry.registerMetaObject(e -> "");
MetaKey<String> key2 = registry.registerMetaObject(e -> "");
System.out.println(key1.equals(key2)); // false (IDs diferentes)
System.out.println(key1.getId()); // 0
System.out.println(key2.getId()); // 1
// La misma instancia
System.out.println(key1.equals(key1)); // true
Uso en Colecciones¶
// Como key en Map
Map<MetaKey<?>, Object> metaMap = new HashMap<>();
metaMap.put(PLAYER_NAME, "Steve");
// En Set
Set<MetaKey<?>> requiredMeta = new HashSet<>();
requiredMeta.add(PLAYER_LEVEL);
requiredMeta.add(PLAYER_XP);
Rendimiento¶
Acceso Rápido¶
El ID numérico permite acceso O(1):
// Internamente en IMetaStoreImpl
private Object[] metaObjects;
public <T> T getMetaObject(MetaKey<T> key) {
return (T) metaObjects[key.getId()]; // O(1)
}
Memoria¶
Cada MetaKey ocupa: - 16 bytes (header del objeto) - 4 bytes (int id) - Total: ~20 bytes por MetaKey
Debugging¶
Imprimir Información¶
public void debugMetaKey(MetaKey<?> key) {
System.out.println("MetaKey Info:");
System.out.println(" ID: " + key.getId());
System.out.println(" Class: " + key.getClass().getName());
System.out.println(" Hash: " + key.hashCode());
if (key instanceof PersistentMetaKey) {
PersistentMetaKey<?> pkey = (PersistentMetaKey<?>) key;
System.out.println(" Name: " + pkey.getKeyName());
System.out.println(" Persistent: true");
}
}
Rastrear Uso¶
private static final Map<MetaKey<?>, Integer> accessCount =
new ConcurrentHashMap<>();
public <T> T getMetaObjectWithTracking(Entity entity, MetaKey<T> key) {
accessCount.merge(key, 1, Integer::sum);
return entity.getMetaObject(key);
}
public void printStats() {
accessCount.forEach((key, count) -> {
System.out.println("Key " + key.getId() + " accessed " + count + " times");
});
}
Siguiente¶
- IMetaStore: Interfaz de almacenamiento
- MetaRegistry: Registro de MetaKeys
- Overview: Visión general del sistema
¿Preguntas? Consulta FAQ o Troubleshooting