Saltar a contenido

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

public boolean hasCustomData(Entity entity) {
    return entity.hasMetaObject(CUSTOM_DATA);
}

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

  1. Declarar como constantes estáticas

    public static final MetaKey<String> PLAYER_NAME =
        registry.registerMetaObject(p -> "Unknown");
    

  2. Usar nombres descriptivos

    public static final MetaKey<Long> LAST_INTERACTION_TIME = ...;
    // Mejor que: KEY1, DATA, TEMP, etc.
    

  3. Agrupar MetaKeys relacionadas

    public class CombatMeta {
        public static final MetaKey<Integer> DAMAGE_DEALT = ...;
        public static final MetaKey<Integer> DAMAGE_TAKEN = ...;
        public static final MetaKey<Entity> LAST_ATTACKER = ...;
    }
    

  4. Documentar el propósito

    /**
     * Almacena el tiempo en milisegundos desde el último ataque.
     * Usado para calcular cooldowns de combate.
     */
    public static final MetaKey<Long> LAST_ATTACK_TIME = ...;
    

❌ No Hacer

  1. No crear MetaKeys dinámicamente

    // ❌ MAL
    for (String name : names) {
        MetaKey<String> key = registry.registerMetaObject(e -> "");
    }
    
    // ✅ BIEN
    public static final MetaKey<Map<String, String>> NAMES =
        registry.registerMetaObject(e -> new HashMap<>());
    

  2. No duplicar MetaKeys

    // ❌ MAL - Dos keys para lo mismo
    MetaKey<String> NAME1 = registry.registerMetaObject(e -> "");
    MetaKey<String> NAME2 = registry.registerMetaObject(e -> "");
    
    // ✅ BIEN - Una key compartida
    public static final MetaKey<String> NAME =
        registry.registerMetaObject(e -> "");
    

  3. No usar tipos mutables sin cuidado

    // ❌ Riesgo de modificación compartida
    MetaKey<List<String>> SHARED_LIST =
        registry.registerMetaObject(e -> singletonList);
    
    // ✅ Nueva instancia para cada entidad
    MetaKey<List<String>> INSTANCE_LIST =
        registry.registerMetaObject(e -> new ArrayList<>());
    

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


¿Preguntas? Consulta FAQ o Troubleshooting