Remove build artifacts, dev tool configs, unused dependencies, and third-party source dumps. Add proper README, update .gitignore, clean up Makefile.
333 lines
12 KiB
Java
333 lines
12 KiB
Java
package com.tiedup.remake.cells;
|
|
|
|
import com.tiedup.remake.core.TiedUpMod;
|
|
import com.tiedup.remake.items.base.ILockable;
|
|
import com.tiedup.remake.items.base.ItemCollar;
|
|
import com.tiedup.remake.prison.PrisonerManager;
|
|
import com.tiedup.remake.state.IRestrainable;
|
|
import com.tiedup.remake.util.KidnappedHelper;
|
|
import com.tiedup.remake.v2.BodyRegionV2;
|
|
import java.util.List;
|
|
import java.util.Set;
|
|
import java.util.UUID;
|
|
import net.minecraft.ChatFormatting;
|
|
import net.minecraft.nbt.CompoundTag;
|
|
import net.minecraft.network.chat.Component;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
import net.minecraft.server.level.ServerPlayer;
|
|
import net.minecraft.world.entity.player.Player;
|
|
import net.minecraft.world.item.ItemStack;
|
|
|
|
/**
|
|
* Handles camp lifecycle events: camp death, prisoner freeing, and camp defense alerts.
|
|
*
|
|
* <p>This is a stateless utility class. All state lives in {@link CampOwnership}
|
|
* (the SavedData singleton). Methods here orchestrate multi-system side effects
|
|
* that don't belong in the data layer.
|
|
*/
|
|
public final class CampLifecycleManager {
|
|
|
|
private CampLifecycleManager() {} // utility class
|
|
|
|
/**
|
|
* Mark a camp as dead (trader killed) and perform full cleanup.
|
|
* This:
|
|
* - Cancels all ransoms for the camp's prisoners
|
|
* - Frees all prisoners (untie, unlock collars)
|
|
* - Clears all labor states
|
|
* - Removes all cells belonging to the camp
|
|
* - Makes the camp inactive
|
|
*
|
|
* @param campId The camp UUID
|
|
* @param level The server level for entity lookups
|
|
*/
|
|
public static void markCampDead(UUID campId, ServerLevel level) {
|
|
CampOwnership ownership = CampOwnership.get(level);
|
|
CampOwnership.CampData data = ownership.getCamp(campId);
|
|
if (data == null) {
|
|
return;
|
|
}
|
|
|
|
UUID traderUUID = data.getTraderUUID();
|
|
|
|
TiedUpMod.LOGGER.info(
|
|
"[CampLifecycleManager] Camp {} dying - freeing all prisoners",
|
|
campId.toString().substring(0, 8)
|
|
);
|
|
|
|
// PERFORMANCE FIX: Use PrisonerManager's index instead of CellRegistry
|
|
// This is O(1) lookup instead of iterating all cells
|
|
PrisonerManager manager = PrisonerManager.get(level);
|
|
Set<UUID> prisonerIds = manager.getPrisonersInCamp(campId);
|
|
|
|
TiedUpMod.LOGGER.debug(
|
|
"[CampLifecycleManager] Found {} prisoners in camp {} via index",
|
|
prisonerIds.size(),
|
|
campId.toString().substring(0, 8)
|
|
);
|
|
|
|
// Cancel ransoms and free each prisoner
|
|
for (UUID prisonerId : prisonerIds) {
|
|
// Cancel ransom by clearing it
|
|
if (manager.getRansomRecord(prisonerId) != null) {
|
|
manager.setRansomRecord(prisonerId, null);
|
|
TiedUpMod.LOGGER.debug(
|
|
"[CampLifecycleManager] Cancelled ransom for prisoner {}",
|
|
prisonerId.toString().substring(0, 8)
|
|
);
|
|
}
|
|
|
|
// Free the prisoner
|
|
ServerPlayer prisoner = level
|
|
.getServer()
|
|
.getPlayerList()
|
|
.getPlayer(prisonerId);
|
|
if (prisoner != null) {
|
|
// Online: untie, unlock collar, release with 5-min grace period
|
|
removeLaborTools(prisoner);
|
|
freePrisonerOnCampDeath(prisoner, traderUUID, level);
|
|
// Cell cleanup only -- freePrisonerOnCampDeath already called release()
|
|
// which transitions to PROTECTED with grace period.
|
|
// Calling escape() here would override PROTECTED->FREE, losing the grace.
|
|
CellRegistryV2.get(level).releasePrisonerFromAllCells(
|
|
prisonerId
|
|
);
|
|
} else {
|
|
// Offline: full escape via PrisonerService (no grace period needed)
|
|
com.tiedup.remake.prison.service.PrisonerService.get().escape(
|
|
level,
|
|
prisonerId,
|
|
"camp death"
|
|
);
|
|
}
|
|
ownership.unmarkPrisonerProcessed(prisonerId);
|
|
}
|
|
|
|
// HIGH FIX: Remove all cells belonging to this camp from CellRegistryV2
|
|
// Prevents memory leak and stale data in indices
|
|
CellRegistryV2 cellRegistry = CellRegistryV2.get(level);
|
|
List<CellDataV2> campCells = cellRegistry.getCellsByCamp(campId);
|
|
for (CellDataV2 cell : campCells) {
|
|
cellRegistry.removeCell(cell.getId());
|
|
TiedUpMod.LOGGER.debug(
|
|
"[CampLifecycleManager] Removed cell {} from registry",
|
|
cell.getId().toString().substring(0, 8)
|
|
);
|
|
}
|
|
TiedUpMod.LOGGER.info(
|
|
"[CampLifecycleManager] Removed {} cells for dead camp {}",
|
|
campCells.size(),
|
|
campId.toString().substring(0, 8)
|
|
);
|
|
|
|
// Mark camp as dead
|
|
data.setAlive(false);
|
|
ownership.setDirty();
|
|
|
|
TiedUpMod.LOGGER.info(
|
|
"[CampLifecycleManager] Camp {} is now dead, {} prisoners freed",
|
|
campId.toString().substring(0, 8),
|
|
prisonerIds.size()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Alert all NPCs in a camp to defend against an attacker.
|
|
* Called when someone tries to restrain the trader or maid.
|
|
*
|
|
* @param campId The camp UUID
|
|
* @param attacker The player attacking
|
|
* @param level The server level
|
|
*/
|
|
public static void alertCampToDefend(
|
|
UUID campId,
|
|
Player attacker,
|
|
ServerLevel level
|
|
) {
|
|
CampOwnership ownership = CampOwnership.get(level);
|
|
CampOwnership.CampData camp = ownership.getCamp(campId);
|
|
if (camp == null) return;
|
|
|
|
int alertedCount = 0;
|
|
|
|
// 1. Alert the trader
|
|
UUID traderUUID = camp.getTraderUUID();
|
|
if (traderUUID != null) {
|
|
net.minecraft.world.entity.Entity traderEntity = level.getEntity(
|
|
traderUUID
|
|
);
|
|
if (
|
|
traderEntity instanceof
|
|
com.tiedup.remake.entities.EntitySlaveTrader trader
|
|
) {
|
|
if (trader.isAlive() && !trader.isTiedUp()) {
|
|
trader.setTarget(attacker);
|
|
trader.setLastAttacker(attacker);
|
|
alertedCount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 2. Alert the maid
|
|
UUID maidUUID = camp.getMaidUUID();
|
|
if (maidUUID != null) {
|
|
net.minecraft.world.entity.Entity maidEntity = level.getEntity(
|
|
maidUUID
|
|
);
|
|
if (
|
|
maidEntity instanceof com.tiedup.remake.entities.EntityMaid maid
|
|
) {
|
|
if (maid.isAlive() && !maid.isTiedUp()) {
|
|
maid.setTarget(attacker);
|
|
maid.setMaidState(
|
|
com.tiedup.remake.entities.ai.maid.MaidState.DEFENDING
|
|
);
|
|
alertedCount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 3. Alert all kidnappers
|
|
Set<UUID> kidnapperUUIDs = camp.getLinkedKidnappers();
|
|
for (UUID kidnapperUUID : kidnapperUUIDs) {
|
|
net.minecraft.world.entity.Entity entity = level.getEntity(
|
|
kidnapperUUID
|
|
);
|
|
if (
|
|
entity instanceof
|
|
com.tiedup.remake.entities.EntityKidnapper kidnapper
|
|
) {
|
|
if (kidnapper.isAlive() && !kidnapper.isTiedUp()) {
|
|
kidnapper.setTarget(attacker);
|
|
kidnapper.setLastAttacker(attacker);
|
|
alertedCount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
TiedUpMod.LOGGER.info(
|
|
"[CampLifecycleManager] Camp {} alerted {} NPCs to defend against {}",
|
|
campId.toString().substring(0, 8),
|
|
alertedCount,
|
|
attacker.getName().getString()
|
|
);
|
|
}
|
|
|
|
// ==================== PRIVATE HELPERS ====================
|
|
|
|
/**
|
|
* Free a prisoner when their camp dies.
|
|
* Untie, unlock collar, cancel sale, notify.
|
|
* Uses legitimate removal flag to prevent alerting kidnappers.
|
|
*/
|
|
private static void freePrisonerOnCampDeath(
|
|
ServerPlayer prisoner,
|
|
UUID traderUUID,
|
|
ServerLevel level
|
|
) {
|
|
IRestrainable state = KidnappedHelper.getKidnappedState(prisoner);
|
|
if (state == null) {
|
|
return;
|
|
}
|
|
|
|
// Suppress collar removal alerts - this is a legitimate release (camp death)
|
|
ItemCollar.runWithSuppressedAlert(() -> {
|
|
// Unlock collar if owned by the dead camp/trader
|
|
unlockCollarIfOwnedBy(prisoner, state, traderUUID);
|
|
|
|
// Remove all restraints (including collar if any)
|
|
state.untie(true);
|
|
|
|
// Cancel sale
|
|
state.cancelSale();
|
|
});
|
|
|
|
// Clear client HUD
|
|
com.tiedup.remake.network.ModNetwork.sendToPlayer(
|
|
new com.tiedup.remake.network.labor.PacketSyncLaborProgress(),
|
|
prisoner
|
|
);
|
|
|
|
// Notify prisoner
|
|
prisoner.sendSystemMessage(
|
|
Component.literal("Your captor has died. You are FREE!").withStyle(
|
|
ChatFormatting.GREEN,
|
|
ChatFormatting.BOLD
|
|
)
|
|
);
|
|
|
|
// Grant grace period (5 minutes = 6000 ticks)
|
|
PrisonerManager manager = PrisonerManager.get(level);
|
|
manager.release(prisoner.getUUID(), level.getGameTime(), 6000);
|
|
|
|
prisoner.sendSystemMessage(
|
|
Component.literal(
|
|
"You have 5 minutes of protection from kidnappers."
|
|
).withStyle(ChatFormatting.AQUA)
|
|
);
|
|
|
|
TiedUpMod.LOGGER.info(
|
|
"[CampLifecycleManager] Freed prisoner {} on camp death (no alert)",
|
|
prisoner.getName().getString()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Unlock a prisoner's collar if it's owned by the specified owner (trader/kidnapper).
|
|
*/
|
|
private static void unlockCollarIfOwnedBy(
|
|
ServerPlayer prisoner,
|
|
IRestrainable state,
|
|
UUID ownerUUID
|
|
) {
|
|
ItemStack collar = state.getEquipment(BodyRegionV2.NECK);
|
|
if (collar.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
if (collar.getItem() instanceof ItemCollar collarItem) {
|
|
List<UUID> owners = collarItem.getOwners(collar);
|
|
|
|
// If the dead trader/camp is an owner, unlock the collar
|
|
if (owners.contains(ownerUUID)) {
|
|
if (collar.getItem() instanceof ILockable lockable) {
|
|
lockable.setLockedByKeyUUID(collar, null); // Unlock and clear
|
|
}
|
|
TiedUpMod.LOGGER.debug(
|
|
"[CampLifecycleManager] Unlocked collar for {} (owner {} died)",
|
|
prisoner.getName().getString(),
|
|
ownerUUID.toString().substring(0, 8)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* SECURITY: Remove all labor tools from player inventory.
|
|
* Prevents prisoners from keeping unbreakable tools when freed/released.
|
|
*/
|
|
private static void removeLaborTools(ServerPlayer player) {
|
|
var inventory = player.getInventory();
|
|
int removedCount = 0;
|
|
|
|
for (int i = 0; i < inventory.getContainerSize(); i++) {
|
|
ItemStack stack = inventory.getItem(i);
|
|
if (!stack.isEmpty() && stack.hasTag()) {
|
|
CompoundTag tag = stack.getTag();
|
|
if (tag != null && tag.getBoolean("LaborTool")) {
|
|
inventory.setItem(i, ItemStack.EMPTY);
|
|
removedCount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (removedCount > 0) {
|
|
TiedUpMod.LOGGER.debug(
|
|
"[CampLifecycleManager] Removed {} labor tools from {} on camp death",
|
|
removedCount,
|
|
player.getName().getString()
|
|
);
|
|
}
|
|
}
|
|
}
|