@startuml moderation-class-diagram title CR-Core — Moderation feature (class diagram, skeleton) skinparam classAttributeIconSize 0 hide empty members package "fr.luc.crcore.features.moderation" { class ModerationState <> { - playerId: UUID - enteredAt: Instant - inventoryContents: ItemStack[] - armorContents: ItemStack[] - offhandItem: ItemStack - xpLevel: int - xpProgress: float - health: double - foodLevel: int - saturation: float - location: Location - gameMode: GameMode - allowFlight / flying / walkSpeed / flySpeed -- + ModerationState(player) + restoreTo(player): void } interface ModerationRepository { + findByPlayer(uuid): Optional + exists(uuid): boolean + save(state): void + delete(uuid): boolean + findAll(): Collection } interface ModeratorTool { + getKey(): String + getSlot(): int ' 0..8 + buildIcon(): ItemStack + onLeftClick(player): void + onRightClick(player): void + onInteractEntity(player, target): void } class ModeratorToolRegistry { - bySlot: Map - byKey: Map + register(tool): void + unregister(key): boolean + get(key): Optional + getBySlot(slot): Optional + all(): Collection } interface ModerationService { + enter(player): void + exit(player): void + isInModeration(uuid): boolean + getState(uuid): Optional + getActiveModerators(): Set -- + vanish(player) / unvanish / isVanished / getVanishedPlayers + freeze(uuid) / unfreeze / isFrozen / getFrozenPlayers + getToolRegistry(): ModeratorToolRegistry } package "fr.luc.crcore.features.moderation.impl" { class InMemoryModerationRepository InMemoryModerationRepository ..|> ModerationRepository class ModerationServiceImpl { # plugin: Plugin # repository: ModerationRepository # toolRegistry: ModeratorToolRegistry # vanished: Set # frozen: Set -- # onAfterEnter(player) ' hook # onAfterExit(player) } ModerationServiceImpl ..|> ModerationService class BukkitEventFiringModerationServiceImpl BukkitEventFiringModerationServiceImpl --|> ModerationServiceImpl class ModerationListener { + registerOn(plugin): void -- @ onInteract ' route → tool.onLeftClick / onRightClick @ onInteractEntity ' route → tool.onInteractEntity @ onDrop / onSwap ' lock hotbar @ onInventoryClick ' cancel sur self-inv @ onJoin ' re-hide vanished players @ onQuit ' unfreeze @ onMove ' cancel si frozen } ModerationListener ..|> "org.bukkit.event.Listener" ModerationListener --> ModerationService } package "fr.luc.crcore.features.moderation.tool" { class ExitTool ' slot 8 class VanishToggleTool ' slot 7 class FreezeTool ' slot 2 (entity right-click) class InventorySpyTool ' slot 1 (entity right-click) class TeleportRandomPlayerTool ' slot 0 ExitTool ..|> ModeratorTool VanishToggleTool ..|> ModeratorTool FreezeTool ..|> ModeratorTool InventorySpyTool ..|> ModeratorTool TeleportRandomPlayerTool ..|> ModeratorTool } package "fr.luc.crcore.features.moderation.event" { abstract class ModerationEvent { - moderator: Player + getModerator(): Player } ModerationEvent --|> "org.bukkit.event.Event" class ModerationEnterEvent class ModerationExitEvent ModerationEnterEvent --|> ModerationEvent ModerationExitEvent --|> ModerationEvent } package "fr.luc.crcore.features.moderation.exception" { class ModerationException class ModerationAlreadyActiveException class ModerationNotActiveException ModerationException --|> RuntimeException ModerationAlreadyActiveException --|> ModerationException ModerationNotActiveException --|> ModerationException } package "fr.luc.crcore.features.moderation.command" { class AdminToggleSubCommand { + execute(ctx): CommandResult } AdminToggleSubCommand ..> ModerationService } ModerationService ..> ModerationState : reads/writes ModerationService o--> ModerationRepository ModerationService o--> ModeratorToolRegistry ModeratorToolRegistry o--> "*" ModeratorTool } package "fr.luc.crcore" { class CRCore { + moderation(): ModerationService # buildModerationService(repo, registry): ModerationService # registerDefaultModeratorTools(registry, mod): void } CRCore "1" *-- "0..1" ModerationService : owns (if setupModeration) CRCore ..> ModerationListener : registers } note bottom of ModerationServiceImpl enter(player) : 1. ModerationState snapshot → repo.save 2. clear inventory, set tools in hotbar 3. gamemode CREATIVE + allowFlight 4. vanish(player) 5. onAfterEnter() → event fired exit(player) : 1. state.restoreTo(player) ' inv + xp + loc + gm + flight 2. unvanish(player) 3. unfreeze + repo.delete 4. onAfterExit() → event fired Skeleton : in-memory repository only. Persistance SQLite à venir. end note @enduml