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:
332
src/main/java/com/tiedup/remake/cells/CampLifecycleManager.java
Normal file
332
src/main/java/com/tiedup/remake/cells/CampLifecycleManager.java
Normal file
@@ -0,0 +1,332 @@
|
||||
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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user