package com.tiedup.remake.state.components; import com.tiedup.remake.cells.CampOwnership; import com.tiedup.remake.cells.CellRegistryV2; import com.tiedup.remake.core.TiedUpMod; import com.tiedup.remake.items.base.ItemBind; import com.tiedup.remake.state.hosts.IPlayerBindStateHost; import com.tiedup.remake.v2.BodyRegionV2; import java.util.UUID; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; /** * Component responsible for player lifecycle management. * Handles connection, death, respawn scenarios. * * Single Responsibility: Lifecycle events and registry coordination * Complexity: HIGH (coordinates 4+ registries on death) * Risk: HIGH (critical path, must maintain registry cleanup order) * * Registry Cleanup Order (on death): * 1. CellRegistryV2 - remove from cells * 2. PrisonerManager - release prisoner (via PrisonerService) */ public class PlayerLifecycle { private final IPlayerBindStateHost host; public PlayerLifecycle(IPlayerBindStateHost host) { this.host = host; } // ========== Lifecycle Methods ========== /** * Resets the player instance upon reconnection or respawn. * * IMPORTANT: Handle transport restoration for pole binding. * With proxy-based leash system, leashes are not persisted through disconnection. * The leash proxy is ephemeral and will be recreated if needed. * * Leg Binding: Speed reduction based on hasLegsBound(), not isTiedUp(). */ public void resetNewConnection(Player player) { // Update player reference host.setOnline(true); host.setCaptor(null); // Reset struggle animation state (prevent stuck animations) host.setStrugglingClient(false); // Leash proxy doesn't persist through disconnection // If player was leashed, they are now freed // H6 fix: V1 speed reduction re-application is no longer needed for players. // MovementStyleManager (V2 tick-based system) re-resolves the active movement // style on the first tick after login (clearMovementState() resets activeMovementStyle // to null, triggering a fresh activation). The V1 RestraintEffectUtils call here would // cause double stacking with the V2 MULTIPLY_BASE modifier. } /** * Called when the kidnapped player dies. * Comprehensive cleanup: unlock items, drop items, free captivity, cleanup registries. * * @param world The world/level where death occurred * @return true if death was handled */ public boolean onDeathKidnapped(Level world) { Player player = host.getPlayer(); // Mark player as offline host.setOnline(false); // Clean up all registries on death (server-side only) if (world instanceof ServerLevel serverLevel) { UUID playerId = player.getUUID(); cleanupRegistries(serverLevel, playerId); } TiedUpMod.LOGGER.debug( "[PlayerLifecycle] {} died while kidnapped", player.getName().getString() ); return true; } /** * Coordinates cleanup of all registries when player dies. * Order matters: Cell → Camp → Ransom → Prisoner * * @param serverLevel The server level * @param playerId The player's UUID */ private void cleanupRegistries(ServerLevel serverLevel, UUID playerId) { String playerName = host.getPlayer().getName().getString(); // 1. Clean up CellRegistryV2 - remove from any cells CellRegistryV2 cellRegistry = CellRegistryV2.get(serverLevel); int cellsRemoved = cellRegistry.releasePrisonerFromAllCells(playerId); if (cellsRemoved > 0) { TiedUpMod.LOGGER.debug( "[PlayerLifecycle] Removed {} from {} cells on death", playerName, cellsRemoved ); } // 2. Clean up prisoner state - release from imprisonment com.tiedup.remake.prison.PrisonerManager manager = com.tiedup.remake.prison.PrisonerManager.get(serverLevel); com.tiedup.remake.prison.PrisonerState state = manager.getState( playerId ); // Release if imprisoned or working (player died) if ( state == com.tiedup.remake.prison.PrisonerState.IMPRISONED || state == com.tiedup.remake.prison.PrisonerState.WORKING ) { // Use centralized escape service for complete cleanup com.tiedup.remake.prison.service.PrisonerService.get().escape( serverLevel, playerId, "player_death" ); } } // ========== Helper Methods ========== /** * Get the current bind ItemStack. * Epic 5F: Migrated to V2EquipmentHelper. */ private ItemStack getCurrentBind() { return com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper.getInRegion( host.getPlayer(), com.tiedup.remake.v2.BodyRegionV2.ARMS ); } /** * Check if player has legs bound. */ private boolean hasLegsBound(ItemStack bind) { if (bind.isEmpty()) return false; if (!(bind.getItem() instanceof ItemBind)) return false; return ItemBind.hasLegsBound(bind); } }