Clean repo for open source release
Remove build artifacts, dev tool configs, unused dependencies, and third-party source dumps. Add proper README, update .gitignore, clean up Makefile.
This commit is contained in:
386
src/main/java/com/tiedup/remake/state/CollarRegistry.java
Normal file
386
src/main/java/com/tiedup/remake/state/CollarRegistry.java
Normal file
@@ -0,0 +1,386 @@
|
||||
package com.tiedup.remake.state;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.level.saveddata.SavedData;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Global registry for collar ownership relationships.
|
||||
*
|
||||
* This registry tracks which entities are wearing collars and who owns them.
|
||||
* It persists across server restarts and provides efficient lookups in both directions:
|
||||
* - Owner UUID → Set of collar-wearer UUIDs (slaves)
|
||||
* - Wearer UUID → Set of owner UUIDs (masters)
|
||||
*
|
||||
* Terminology:
|
||||
* - "Slave" = Entity wearing a collar owned by a player (passive ownership)
|
||||
* - "Captive" = Entity attached by leash (active physical control) - managed by PlayerCaptiveManager
|
||||
*
|
||||
* Phase 17: Terminology Refactoring
|
||||
*/
|
||||
public class CollarRegistry extends SavedData {
|
||||
|
||||
private static final String DATA_NAME = "tiedup_collar_registry";
|
||||
|
||||
// Owner UUID → Set of wearer UUIDs (a master can own multiple slaves)
|
||||
private final Map<UUID, Set<UUID>> ownerToWearers =
|
||||
new ConcurrentHashMap<>();
|
||||
|
||||
// Wearer UUID → Set of owner UUIDs (a collar can have multiple owners)
|
||||
private final Map<UUID, Set<UUID>> wearerToOwners =
|
||||
new ConcurrentHashMap<>();
|
||||
|
||||
// ==================== STATIC ACCESS ====================
|
||||
|
||||
/**
|
||||
* Get the CollarRegistry for a server level.
|
||||
* Creates a new registry if one doesn't exist.
|
||||
*/
|
||||
public static CollarRegistry get(ServerLevel level) {
|
||||
return level
|
||||
.getDataStorage()
|
||||
.computeIfAbsent(
|
||||
CollarRegistry::load,
|
||||
CollarRegistry::new,
|
||||
DATA_NAME
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the CollarRegistry from a MinecraftServer.
|
||||
* Uses the overworld as the storage dimension.
|
||||
*/
|
||||
public static CollarRegistry get(MinecraftServer server) {
|
||||
ServerLevel overworld = server.overworld();
|
||||
return get(overworld);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to get registry from a ServerPlayer.
|
||||
*/
|
||||
@Nullable
|
||||
public static CollarRegistry get(ServerPlayer player) {
|
||||
if (player.getServer() == null) return null;
|
||||
return get(player.getServer());
|
||||
}
|
||||
|
||||
// ==================== REGISTRATION ====================
|
||||
|
||||
/**
|
||||
* Register a collar relationship: owner now owns the collar on wearer.
|
||||
*
|
||||
* @param wearerUUID UUID of the entity wearing the collar
|
||||
* @param ownerUUID UUID of the collar's owner
|
||||
*/
|
||||
public void registerCollar(UUID wearerUUID, UUID ownerUUID) {
|
||||
// Add to owner → wearers map
|
||||
ownerToWearers
|
||||
.computeIfAbsent(ownerUUID, k -> ConcurrentHashMap.newKeySet())
|
||||
.add(wearerUUID);
|
||||
|
||||
// Add to wearer → owners map
|
||||
wearerToOwners
|
||||
.computeIfAbsent(wearerUUID, k -> ConcurrentHashMap.newKeySet())
|
||||
.add(ownerUUID);
|
||||
|
||||
setDirty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a collar with multiple owners at once.
|
||||
*
|
||||
* @param wearerUUID UUID of the entity wearing the collar
|
||||
* @param ownerUUIDs Set of owner UUIDs
|
||||
*/
|
||||
public void registerCollar(UUID wearerUUID, Set<UUID> ownerUUIDs) {
|
||||
for (UUID ownerUUID : ownerUUIDs) {
|
||||
registerCollar(wearerUUID, ownerUUID);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a specific owner from a collar wearer.
|
||||
*
|
||||
* @param wearerUUID UUID of the entity wearing the collar
|
||||
* @param ownerUUID UUID of the owner to remove
|
||||
*/
|
||||
private void unregisterOwner(UUID wearerUUID, UUID ownerUUID) {
|
||||
// Remove from owner → wearers map using atomic operation
|
||||
ownerToWearers.computeIfPresent(ownerUUID, (key, wearers) -> {
|
||||
wearers.remove(wearerUUID);
|
||||
return wearers.isEmpty() ? null : wearers;
|
||||
});
|
||||
|
||||
// Remove from wearer → owners map using atomic operation
|
||||
wearerToOwners.computeIfPresent(wearerUUID, (key, owners) -> {
|
||||
owners.remove(ownerUUID);
|
||||
return owners.isEmpty() ? null : owners;
|
||||
});
|
||||
|
||||
setDirty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Completely unregister a collar wearer (removes all owner relationships).
|
||||
* Called when a collar is removed from an entity.
|
||||
*
|
||||
* @param wearerUUID UUID of the entity whose collar was removed
|
||||
*/
|
||||
public void unregisterWearer(UUID wearerUUID) {
|
||||
Set<UUID> owners = wearerToOwners.remove(wearerUUID);
|
||||
if (owners != null) {
|
||||
for (UUID ownerUUID : owners) {
|
||||
// Use atomic operation for thread-safe removal
|
||||
ownerToWearers.computeIfPresent(ownerUUID, (key, wearers) -> {
|
||||
wearers.remove(wearerUUID);
|
||||
return wearers.isEmpty() ? null : wearers;
|
||||
});
|
||||
}
|
||||
setDirty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a wearer's owners completely (replaces all existing owners).
|
||||
* Useful when collar NBT is the source of truth.
|
||||
*
|
||||
* @param wearerUUID UUID of the entity wearing the collar
|
||||
* @param newOwnerUUIDs New set of owner UUIDs
|
||||
*/
|
||||
public void updateWearerOwners(UUID wearerUUID, Set<UUID> newOwnerUUIDs) {
|
||||
// Get current owners
|
||||
Set<UUID> currentOwners = wearerToOwners.get(wearerUUID);
|
||||
if (currentOwners == null) {
|
||||
currentOwners = Collections.emptySet();
|
||||
}
|
||||
|
||||
// Find owners to remove
|
||||
Set<UUID> toRemove = new HashSet<>(currentOwners);
|
||||
toRemove.removeAll(newOwnerUUIDs);
|
||||
|
||||
// Find owners to add
|
||||
Set<UUID> toAdd = new HashSet<>(newOwnerUUIDs);
|
||||
toAdd.removeAll(currentOwners);
|
||||
|
||||
// Apply changes
|
||||
for (UUID ownerUUID : toRemove) {
|
||||
unregisterOwner(wearerUUID, ownerUUID);
|
||||
}
|
||||
for (UUID ownerUUID : toAdd) {
|
||||
registerCollar(wearerUUID, ownerUUID);
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== QUERIES ====================
|
||||
|
||||
/**
|
||||
* Get all slaves (collar wearers) owned by a specific owner.
|
||||
*
|
||||
* @param ownerUUID UUID of the owner
|
||||
* @return Unmodifiable set of wearer UUIDs (never null)
|
||||
*/
|
||||
public Set<UUID> getSlaves(UUID ownerUUID) {
|
||||
Set<UUID> wearers = ownerToWearers.get(ownerUUID);
|
||||
if (wearers == null) return Collections.emptySet();
|
||||
return Collections.unmodifiableSet(new HashSet<>(wearers));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all owners of a specific collar wearer.
|
||||
*
|
||||
* @param wearerUUID UUID of the wearer
|
||||
* @return Unmodifiable set of owner UUIDs (never null)
|
||||
*/
|
||||
public Set<UUID> getOwners(UUID wearerUUID) {
|
||||
Set<UUID> owners = wearerToOwners.get(wearerUUID);
|
||||
if (owners == null) return Collections.emptySet();
|
||||
return Collections.unmodifiableSet(new HashSet<>(owners));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an owner has any slaves.
|
||||
*/
|
||||
public boolean hasSlaves(UUID ownerUUID) {
|
||||
Set<UUID> wearers = ownerToWearers.get(ownerUUID);
|
||||
return wearers != null && !wearers.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a wearer has any owners.
|
||||
*/
|
||||
public boolean hasOwners(UUID wearerUUID) {
|
||||
Set<UUID> owners = wearerToOwners.get(wearerUUID);
|
||||
return owners != null && !owners.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a specific owner owns a specific wearer.
|
||||
*/
|
||||
public boolean isOwner(UUID ownerUUID, UUID wearerUUID) {
|
||||
Set<UUID> wearers = ownerToWearers.get(ownerUUID);
|
||||
return wearers != null && wearers.contains(wearerUUID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the count of slaves for an owner.
|
||||
*/
|
||||
public int getSlaveCount(UUID ownerUUID) {
|
||||
Set<UUID> wearers = ownerToWearers.get(ownerUUID);
|
||||
return wearers == null ? 0 : wearers.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all registered wearers (for admin/debug purposes).
|
||||
*/
|
||||
public Set<UUID> getAllWearers() {
|
||||
return Collections.unmodifiableSet(
|
||||
new HashSet<>(wearerToOwners.keySet())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all registered owners (for admin/debug purposes).
|
||||
*/
|
||||
public Set<UUID> getAllOwners() {
|
||||
return Collections.unmodifiableSet(
|
||||
new HashSet<>(ownerToWearers.keySet())
|
||||
);
|
||||
}
|
||||
|
||||
// ==================== ENTITY RESOLUTION ====================
|
||||
|
||||
/**
|
||||
* Find all slave entities for an owner that are currently loaded.
|
||||
* This is useful for GUI and proximity-based actions.
|
||||
*
|
||||
* @param owner The owner player
|
||||
* @return List of currently loaded slave entities
|
||||
*/
|
||||
public List<LivingEntity> findLoadedSlaves(ServerPlayer owner) {
|
||||
List<LivingEntity> loadedSlaves = new ArrayList<>();
|
||||
Set<UUID> slaveUUIDs = getSlaves(owner.getUUID());
|
||||
|
||||
MinecraftServer server = owner.getServer();
|
||||
if (server == null) return loadedSlaves;
|
||||
|
||||
for (UUID slaveUUID : slaveUUIDs) {
|
||||
Entity entity = findEntityByUUID(server, slaveUUID);
|
||||
if (entity instanceof LivingEntity living) {
|
||||
loadedSlaves.add(living);
|
||||
}
|
||||
}
|
||||
|
||||
return loadedSlaves;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an entity by UUID across all dimensions.
|
||||
* Optimized: checks player list first (O(1)) before searching dimensions.
|
||||
*/
|
||||
@Nullable
|
||||
private Entity findEntityByUUID(MinecraftServer server, UUID uuid) {
|
||||
// Check player first - O(1) lookup
|
||||
net.minecraft.server.level.ServerPlayer player = server
|
||||
.getPlayerList()
|
||||
.getPlayer(uuid);
|
||||
if (player != null) {
|
||||
return player;
|
||||
}
|
||||
|
||||
// Fallback: search dimensions for NPCs
|
||||
for (ServerLevel level : server.getAllLevels()) {
|
||||
Entity entity = level.getEntity(uuid);
|
||||
if (entity != null) {
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// ==================== PERSISTENCE ====================
|
||||
|
||||
@Override
|
||||
public @NotNull CompoundTag save(@NotNull CompoundTag tag) {
|
||||
ListTag registryList = new ListTag();
|
||||
|
||||
for (Map.Entry<UUID, Set<UUID>> entry : wearerToOwners.entrySet()) {
|
||||
CompoundTag wearerTag = new CompoundTag();
|
||||
wearerTag.putUUID("wearer", entry.getKey());
|
||||
|
||||
ListTag ownersTag = new ListTag();
|
||||
for (UUID ownerUUID : entry.getValue()) {
|
||||
CompoundTag ownerTag = new CompoundTag();
|
||||
ownerTag.putUUID("uuid", ownerUUID);
|
||||
ownersTag.add(ownerTag);
|
||||
}
|
||||
wearerTag.put("owners", ownersTag);
|
||||
registryList.add(wearerTag);
|
||||
}
|
||||
|
||||
tag.put("collar_registry", registryList);
|
||||
return tag;
|
||||
}
|
||||
|
||||
public static CollarRegistry load(CompoundTag tag) {
|
||||
CollarRegistry registry = new CollarRegistry();
|
||||
|
||||
ListTag registryList = tag.getList("collar_registry", Tag.TAG_COMPOUND);
|
||||
for (int i = 0; i < registryList.size(); i++) {
|
||||
CompoundTag wearerTag = registryList.getCompound(i);
|
||||
UUID wearerUUID = wearerTag.getUUID("wearer");
|
||||
|
||||
ListTag ownersTag = wearerTag.getList("owners", Tag.TAG_COMPOUND);
|
||||
for (int j = 0; j < ownersTag.size(); j++) {
|
||||
CompoundTag ownerTag = ownersTag.getCompound(j);
|
||||
UUID ownerUUID = ownerTag.getUUID("uuid");
|
||||
|
||||
// Register relationship (without marking dirty - we're loading)
|
||||
registry.ownerToWearers
|
||||
.computeIfAbsent(ownerUUID, k ->
|
||||
ConcurrentHashMap.newKeySet()
|
||||
)
|
||||
.add(wearerUUID);
|
||||
registry.wearerToOwners
|
||||
.computeIfAbsent(wearerUUID, k ->
|
||||
ConcurrentHashMap.newKeySet()
|
||||
)
|
||||
.add(ownerUUID);
|
||||
}
|
||||
}
|
||||
|
||||
return registry;
|
||||
}
|
||||
|
||||
// ==================== DEBUG ====================
|
||||
|
||||
/**
|
||||
* Get a debug string representation of the registry.
|
||||
*/
|
||||
public String toDebugString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("CollarRegistry:\n");
|
||||
sb.append(" Owners: ").append(ownerToWearers.size()).append("\n");
|
||||
sb.append(" Wearers: ").append(wearerToOwners.size()).append("\n");
|
||||
|
||||
for (Map.Entry<UUID, Set<UUID>> entry : ownerToWearers.entrySet()) {
|
||||
sb
|
||||
.append(" Owner ")
|
||||
.append(entry.getKey().toString().substring(0, 8))
|
||||
.append("... → ")
|
||||
.append(entry.getValue().size())
|
||||
.append(" slaves\n");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user