Volver al índice
src/main/java/ar/com/companeros/identity/AgentRegistry.java
package ar.com.companeros.identity;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import net.minecraft.core.BlockPos;
import net.minecraft.core.GlobalPos;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.saveddata.SavedData;
import net.minecraftforge.common.util.FakePlayer;
/** Registro global guardado en el overworld, sin depender de chunks cargados. */
public final class AgentRegistry extends SavedData {
// Identidad global y formato versionado.
public static final int SCHEMA_VERSION = 1;
private static final int MAX_IDENTITIES = 256;
private final UUID worldId;
private final Map<UUID, AgentIdentity> identities = new LinkedHashMap<>();
public AgentRegistry() { this(UUID.randomUUID()); setDirty(); }
private AgentRegistry(UUID worldId) { this.worldId = worldId; }
public static AgentRegistry get(MinecraftServer server) {
if (!server.isSameThread())
throw new IllegalStateException("El registro se usa en el hilo del servidor");
return server.overworld().getDataStorage().computeIfAbsent(
AgentRegistry::load, AgentRegistry::new, "companeros_agentes");
}
public UUID worldId() { return worldId; }
public boolean isAgent(UUID id) { return identities.containsKey(id); }
public boolean isHuman(Player player) {
return !(player instanceof FakePlayer) && !isAgent(player.getUUID());
}
public Optional<AgentIdentity> find(UUID id) { return Optional.ofNullable(identities.get(id)); }
public List<AgentIdentity> entries() {
return identities.values().stream().sorted(Comparator.comparing(AgentIdentity::name)
.thenComparing(identity -> identity.id().toString())).toList();
}
// Cambios explícitos: registrar de nuevo no reinicia estado ni fecha de creación.
public AgentIdentity register(UUID id, String name, UUID legacyOwner, GlobalPos camp, String mode) {
AgentIdentity previous = identities.get(id);
if (previous == null && identities.size() >= MAX_IDENTITIES)
throw new IllegalStateException("Se alcanzó el límite del registro de agentes");
if (identities.values().stream().anyMatch(other -> !other.id().equals(id)
&& other.state() != AgentState.RETIRED && other.name().equalsIgnoreCase(name)))
throw new IllegalArgumentException("Ya existe un agente con ese nombre");
long now = System.currentTimeMillis();
AgentIdentity next = new AgentIdentity(id, name,
previous == null ? AgentState.LEGACY : previous.state(), legacyOwner, camp, mode,
previous == null ? now : previous.createdAtUtc(),
previous == null ? now : Math.max(now, previous.updatedAtUtc()));
identities.put(id, next); setDirty();
return next;
}
public AgentIdentity updateSettings(UUID id, UUID legacyOwner, GlobalPos camp, String mode) {
AgentIdentity current = require(id);
return register(id, current.name(), legacyOwner, camp, mode);
}
public AgentIdentity updateSettings(UUID id, GlobalPos camp, String mode) {
return updateSettings(id, require(id).legacyOwner(), camp, mode);
}
public AgentIdentity markState(UUID id, AgentState state) {
AgentIdentity current = require(id);
AgentIdentity next = new AgentIdentity(current.id(), current.name(), state,
current.legacyOwner(), current.camp(), current.mode(), current.createdAtUtc(),
Math.max(System.currentTimeMillis(), current.updatedAtUtc()));
identities.put(id, next); setDirty();
return next;
}
private AgentIdentity require(UUID id) {
AgentIdentity identity = identities.get(id);
if (identity == null) throw new IllegalArgumentException("Agente no registrado");
return identity;
}
// Guardado y carga: un formato futuro o identidad inválida nunca borra datos en silencio.
@Override public CompoundTag save(CompoundTag tag) {
tag.putInt("SchemaVersion", SCHEMA_VERSION);
tag.putUUID("WorldId", worldId);
ListTag list = new ListTag();
for (AgentIdentity identity : entries()) {
CompoundTag entry = new CompoundTag();
entry.putUUID("Id", identity.id()); entry.putString("Name", identity.name());
entry.putString("State", identity.state().name()); entry.putString("Mode", identity.mode());
entry.putLong("CreatedAtUtc", identity.createdAtUtc());
entry.putLong("UpdatedAtUtc", identity.updatedAtUtc());
if (identity.legacyOwner() != null) entry.putUUID("LegacyOwner", identity.legacyOwner());
if (identity.camp() != null) {
entry.putString("Dimension", identity.camp().dimension().location().toString());
entry.putLong("Camp", identity.camp().pos().asLong());
}
list.add(entry);
}
tag.put("Agents", list);
return tag;
}
public static AgentRegistry load(CompoundTag tag) {
if (tag.getInt("SchemaVersion") > SCHEMA_VERSION)
throw new IllegalStateException("Formato de agentes más nuevo que el mod");
if (tag.getInt("SchemaVersion") == SCHEMA_VERSION
&& (!tag.hasUUID("WorldId") || !tag.contains("Agents", 9)))
throw new IllegalStateException("Registro de agentes incompleto");
AgentRegistry data = new AgentRegistry(tag.hasUUID("WorldId") ? tag.getUUID("WorldId") : UUID.randomUUID());
ListTag list = tag.getList("Agents", 10);
if (list.size() > MAX_IDENTITIES)
throw new IllegalStateException("Registro de agentes fuera de límite");
java.util.Set<String> activeNames = new java.util.HashSet<>();
for (int i = 0; i < list.size(); i++) {
CompoundTag entry = list.getCompound(i);
GlobalPos camp = entry.contains("Camp") ? GlobalPos.of(ResourceKey.create(Registries.DIMENSION,
new ResourceLocation(entry.getString("Dimension"))), BlockPos.of(entry.getLong("Camp"))) : null;
UUID id = entry.getUUID("Id");
AgentIdentity identity = new AgentIdentity(id, entry.getString("Name"),
AgentState.valueOf(entry.getString("State")),
entry.hasUUID("LegacyOwner") ? entry.getUUID("LegacyOwner") : null,
camp, entry.getString("Mode"), entry.getLong("CreatedAtUtc"), entry.getLong("UpdatedAtUtc"));
if (identity.state() != AgentState.RETIRED
&& !activeNames.add(identity.name().toLowerCase(java.util.Locale.ROOT)))
throw new IllegalStateException("Nombre duplicado en registro de agentes");
if (data.identities.putIfAbsent(id, identity) != null)
throw new IllegalStateException("UUID duplicado en registro de agentes");
}
if (!tag.hasUUID("WorldId")) data.setDirty();
return data;
}
}