Files
TiedUp-/docs/AUDIT.md
NotEvil 449178f57b feat(D-01/C): consumer migration — 85 files migrated to V2 helpers
Phase 1 (state): PlayerBindState, PlayerCaptorManager, PlayerEquipment,
  PlayerDataRetrieval, PlayerLifecycle, PlayerShockCollar, StruggleAccessory
Phase 2 (client): AnimationTickHandler, NpcAnimationTickHandler, 5 render
  handlers, DamselModel, 3 client mixins, SelfBondageInputHandler,
  SlaveManagementScreen, ActionPanel, SlaveEntryWidget, ModKeybindings
Phase 3 (entities): 28 entity/AI files migrated to CollarHelper,
  BindModeHelper, PoseTypeHelper, createStack()
Phase 4 (network): PacketSlaveAction, PacketMasterEquip,
  PacketAssignCellToCollar, PacketNpcCommand, PacketFurnitureForcemount
Phase 5 (events): RestraintTaskTickHandler, PetPlayRestrictionHandler,
  PlayerEnslavementHandler, ChatEventHandler, LaborAttackPunishmentHandler
Phase 6 (commands): BondageSubCommand, CollarCommand, NPCCommand,
  KidnapSetCommand
Phase 7 (compat): MCAKidnappedAdapter, MCA mixins
Phase 8 (misc): GagTalkManager, PetRequestManager, HangingCagePiece,
  BondageItemBlockEntity, TrappedChestBlockEntity, DispenserBehaviors,
  BondageItemLoaderUtility, RestraintApplicator, StruggleSessionManager,
  MovementStyleResolver, CampLifecycleManager

Some files retain dual V1/V2 checks (instanceof V1 || V2Helper) for
coexistence — V1-only branches removed in Branch D.
2026-04-15 00:16:50 +02:00

36 KiB
Raw Blame History

TiedUp! — Codebase Audit

Audit complet du mod, systeme par systeme, pour consolider la base et la coherence du code.


Objectif

Passer en revue chaque systeme du mod en profondeur pour :

  1. Consolider — identifier et corriger les incohérences, le code mort, la duplication
  2. Améliorer — proposer des refactors ciblés là où l'architecture freine le développement
  3. Documenter — laisser une trace des décisions et de l'état de chaque système

Ce n'est pas un rewrite. On stabilise ce qui existe.


Règles de l'audit

  • Système par système, dans l'ordre de dépendance (fondations d'abord)
  • Deep dive — on remonte les sources, les dépendants, les call chains via le MCP
  • Pas de changement sans discussion — on constate, on discute, puis on corrige
  • Pas de sur-ingénierie — on fixe les vrais problèmes, on ne refactor pas pour le plaisir
  • Reindex MCP après chaque batch de corrections significatives

Processus par système

Chaque audit suit ce cycle :

1. EXPLORATION   — Lire le code, tracer les dépendances (MCP)
2. CONSTATS      — Documenter les problèmes trouvés (dans ce fichier)
3. VÉRIFICATION  — Relire, confirmer, pas de faux positifs
4. PROPOSITIONS  — Pour chaque constat :
                     → Fix direct (bug, incohérence simple)
                     → Amélioration architecturale (refactor ciblé, avec justification)
5. DISCUSSION    — Valider avec l'utilisateur avant d'implémenter
6. CORRECTION    — Appliquer les changements validés
7. REINDEX       — MCP reindex après corrections

On ne passe au système suivant qu'une fois le cycle terminé.


Ordre d'audit

# Système Packages Status
1 Core + Registries core, ModItems, ModEntities, ModNetwork, ModGameRules Done
2 State state, state/components, state/hosts, state/struggle Done
3 Items (v1) + V2 Bondage items/**, v2/bondage/** Done
4 Network network/** Done
5 Client Animation + GLTF client/animation/**, client/gltf Done
6 Furniture v2/furniture/** Done
7 Entities + AI entities/** Done
8 Events events/** Done
9 Dialogue + Personality dialogue/**, personality Done
10 Cells / Prison / Blocks cells, prison/**, blocks/** Done
11 Compat compat/mca/**, compat/wildfire/** Done
12 Util + Commands + Worldgen + Resources util/**, commands/**, worldgen, resources Done

Échelle de sévérité

Niveau Signification Action
Haute Bug actif, bloquant, ou dette qui empêche une feature majeure Corriger avant de continuer
Moyenne Incohérence ou fragilité qui va poser problème à terme Planifier la correction
Basse Code smell, naming, organisation — pas urgent Corriger si on touche le fichier
Cosmétique Style, formatting, commentaires Optionnel

Constats

#1 — Core + Registries

Positif :

  • DeferredRegister correct partout
  • Factory pattern propre pour les variant items
  • SettingsAccessor bridge solide (safeGet, BUG-003 fix)
  • Séparation client/server correcte
  • Data-driven reload listeners bien câblés

Problèmes : (tous vérifiés ✓)

ID Sévérité Constat Fichier(s) Proposition
C-01 Haute SystemMessageManager : 80+ messages hardcodés en anglais, zero Component.translatable() core/SystemMessageManager.java Fix : Remplacer Component.literal() par Component.translatable() avec clés dans en_us.json. Garder l'enum + les couleurs, changer uniquement le transport du texte.
C-02 Moyenne 25 settings triplés entre ModConfig + ModGameRules + SettingsAccessor. Historique de bug (BUG-001 : defaults désyncés) core/ModConfig.java, core/ModGameRules.java, core/SettingsAccessor.java Archi : Évaluer si les GameRules sont vraiment utiles (ils dupliquent la config serveur). Si oui, centraliser les defaults dans une seule source. Si non, supprimer les GameRules doublons et garder ModConfig + SettingsAccessor.
C-03 Moyenne ModNetwork : 74 IDs séquentiels. Pattern standard Forge mais fragile à l'insertion. PROTOCOL_VERSION protège partiellement. network/ModNetwork.java Pas d'action immédiate : pattern idiomatique Forge 1.20.1. Bumper PROTOCOL_VERSION à chaque ajout/suppression de packet. Documenter cette règle.
C-04 Basse ChokeEffect importe EntityMaster (core → entities) pour check "non-lethal when master-owned" core/ChokeEffect.java Fix : Extraire le check via un tag NBT ou capability sur le Player, consultable sans dépendre d'EntityMaster.
C-05 Basse 10 types d'entités NPC utilisent DamselRenderer — nom spécifique pour un usage générique core/TiedUpMod.java, client/renderer/DamselRenderer.java Fix : Renommer DamselRendererNpcRenderer ou HumanoidNpcRenderer.
C-06 Cosmétique 47+ FQCNs dans le corps de TiedUpMod au lieu d'imports core/TiedUpMod.java Fix : Remplacer par des imports. Faire quand on touche le fichier.
C-07 Basse ModConfig.ServerConfig : 127 valeurs configurables, 628 lignes, 20+ catégories dans une classe core/ModConfig.java Archi : Découper en sous-classes par domaine (StruggleConfig, NpcConfig, EconomyConfig, etc.) au prochain refactor config. Pas urgent.

#2 — State

Positif :

  • Hiérarchie d'interfaces bien conçue (ISP) : IRestrainableEntityICapturableIBondageStateIRestrainable (union)
  • Décomposition en 11 components (PlayerEquipment, PlayerStateQuery, etc.) — bonne intention
  • IBondageState n'expose que l'API V2 region-based — l'interface publique est propre
  • CollarRegistry et SocialData : implémentations SavedData propres avec persistence correcte
  • IPlayerLeashAccess : séparation mixin clean pour le système de laisse
  • PlayerCaptorManager : thread-safe avec CopyOnWriteArrayList et synchronized

Problèmes : (tous vérifiés ✓)

ID Sévérité Constat Fichier(s) Proposition
S-01 Moyenne PlayerBindState : god class de façade, 1237 lignes, ~80 méthodes de pure délégation vers 11 components state/PlayerBindState.java Archi : Évaluer si les consommateurs peuvent utiliser les components directement via des accesseurs typés (state.equipment().putBindOn()) au lieu de passer par la façade. Réduirait la surface de ~80 méthodes de boilerplate. Risque : gros refactor, beaucoup de call sites.
S-02 Moyenne 8 champs de mouvement publics (hopCooldown, lastX, etc.) directement mutés par MovementStyleManager (33 accès directs) state/PlayerBindState.java, v2/bondage/movement/MovementStyleManager.java Fix : Extraire dans un MovementState component avec getters/setters. MovementStyleManager opère via ce component.
S-03 Basse API V1 slot-based (putBindOn, takeGagOff) coexiste avec V2 region-based sur la classe concrète PlayerBindState. L'interface IBondageState est propre (V2 only). state/PlayerBindState.java Archi : Marquer les méthodes V1 @Deprecated pour guider la migration. Les call sites (commands, etc.) devraient migrer vers equip(BodyRegionV2). Pas urgent car l'interface est déjà propre.
S-04 Basse hasLegsBound() lit le slot ARMS (pas LEGS) — design V1 intentionnel : un seul item "bind" couvre bras+jambes via NBT mode. Pas un bug. state/IBondageState.java Pas d'action immédiate : cohérent avec le système actuel. Documenter le design dans un commentaire. Deviendra un vrai problème quand des items LEGS dédiés seront ajoutés en V2.
S-05 Moyenne Thread safety incohérente : volatile (3 champs), synchronized (5 méthodes), rien (le reste). La paire isStruggling/struggleStartTick peut être observée dans un état inconsistant. state/PlayerBindState.java Fix : Définir une stratégie claire. Les champs accédés cross-thread (mouvement, struggle, captor) doivent être soit volatile soit synchronized. Auditer chaque champ.
S-06 Basse HumanChairHelper dans state/ mais c'est un utilitaire pur sans lien avec le state. Utilisé par AI, animation, mixins. state/HumanChairHelper.java Fix : Déplacer dans items/base/ (à côté de PoseType dont il dépend) ou util/. Faire quand on touche le fichier.

#3 — Items (V1) + V2 Bondage

Positif :

  • IV2BondageItem bien conçu : multi-region, stack-aware, pose priority, blocked regions
  • V2EquipmentManager : conflict resolution solide (swap single, supersede global)
  • V2EquipmentHelper : facade propre pour read/write/sync
  • DataDrivenBondageItem : singleton + NBT registry pattern intelligent pour items data-driven
  • ILockable : système lock/jam/key complet et cohérent
  • IHasResistance : résistance NBT avec migration legacy, bien documentée
  • BodyRegionV2 enum complet (15 régions, global flag)
  • Variant enums + factory pattern (BindVariant, GagVariant, etc.) propres

Problèmes : (tous vérifiés ✓)

ID Sévérité Constat Fichier(s) Proposition
I-01 Haute Deux hiérarchies d'interfaces parallèles RÉSOLU : suppression V1 (voir Décision D-01)
I-02 Haute V1 items bypassent la conflict resolution V2 RÉSOLU : suppression V1 (voir Décision D-01)
I-03 Moyenne DataDrivenBondageItem.getBaseResistance() scanne tous items équipés et retourne MAX difficulty car IHasResistance n'a pas de paramètre ItemStack. Workaround documenté mais approximatif — peut surestimer la résistance. v2/bondage/datadriven/DataDrivenBondageItem.java Archi : Ajouter getBaseResistance(ItemStack, LivingEntity) à IHasResistance avec default qui délègue à l'ancienne méthode. DataDrivenBondageItem override la version stack-aware.
I-04 Basse IBondageItem.getBodyRegion() single-region RÉSOLU : suppression V1 (voir Décision D-01)
I-05 Moyenne V1 items pas @Deprecated RÉSOLU : suppression V1 (voir Décision D-01)

Décision D-01 — Suppression totale du système V1 + Composants data-driven

Décision : Le système V1 items est supprimé entièrement. Tous les items deviennent data-driven V2. La logique complexe (shock, GPS, lock, gagging, blinding, resistance, etc.) est extraite en composants réutilisables déclarables dans le JSON.

Périmètre de suppression :

  • IBondageItem (interface)
  • ItemBind, ItemGag, ItemBlindfold, ItemCollar, ItemEarplugs, ItemMittens (abstracts)
  • GenericBind, GenericGag, GenericBlindfold, GenericEarplugs, GenericMittens (concrets)
  • BindVariant, GagVariant, BlindfoldVariant, EarplugsVariant, MittensVariant (enums)
  • ItemClassicCollar, ItemShockCollar, ItemShockCollarAuto, ItemGpsCollar, ItemChokeCollar (collars)
  • ItemHood, ItemMedicalGag, ItemBallGag3D (combos/special)
  • Registrations V1 dans ModItems
  • PlayerEquipment.equipInRegion() → remplacé par V2EquipmentManager.tryEquip()

Interfaces à conserver / migrer :

  • ILockable — conservé, utilisé par V2 items
  • IHasResistance — conservé, refactoré avec paramètre ItemStack (I-03)
  • IKnife — conservé (outils, pas des bondage items)
  • IAdjustable — à évaluer (potentiellement composant)
  • IHasBlindingEffect, IHasGaggingEffect — deviennent des composants

Système de composants envisagé :

Chaque composant est une logique serveur réutilisable qu'un item data-driven peut déclarer :

{
  "type": "tiedup:bondage_item",
  "display_name": "Shock Collar",
  "model": "tiedup:models/gltf/shock_collar.glb",
  "regions": ["NECK"],
  "components": {
    "lockable": true,
    "shock": { "auto_interval": 200, "damage": 2.0 },
    "gps": { "safe_zone_radius": 50 },
    "gagging": { "comprehension": 0.2, "range": 10.0 },
    "blinding": { "overlay": "tiedup:textures/overlay/blindfold.png" },
    "resistance": { "base": 150 }
  },
  "escape_difficulty": 5,
  "pose_priority": 10
}

Exemples de composants à extraire de la logique V1 existante :

Composant Source V1 Comportement
lockable ILockable Lock/unlock, padlock, key matching, jam, lock resistance
resistance IHasResistance Struggle resistance, configurable base value
shock ItemShockCollar Auto-shock intervals, manual shock, damage
gps ItemGpsCollar Safe zone, zone violation detection, owner alerts
gagging IHasGaggingEffect Muffled speech, comprehension %, range limit
blinding IHasBlindingEffect Blindfold overlay, hardcore mode
choking ItemChokeCollar Air drain, darkness, slowness, non-lethal master mode
adjustable IAdjustable Tightness level, visual adjustment

Ce refactor est le plus gros chantier identifié par l'audit. Il fera l'objet d'un plan d'implémentation dédié après la fin de l'audit.

#4 — Network

Positif :

  • AbstractClientPacket / AbstractPlayerSyncPacket — bon pattern de base, handle enqueue sur main thread, retry queue pour les players pas encore loaded
  • PacketRateLimiter — token bucket complet avec catégories (struggle, minigame, action, selfbondage, ui). Thread-safe. Bon anti-spam.
  • SyncManager — facade centralisée pour sync inventory/state/enslavement/struggle/clothes. Pattern sendSync() générique propre.
  • NetworkEventHandler — gère correctement login sync, start-tracking sync, furniture reconnection, et fix MC-262715 (stale riding state)
  • PacketSlaveAction — bonnes validations serveur : dimension check, distance check, collar ownership check, GPS permission check
  • PacketSelfBondage — rate limited, route correctement V2 via handleV2SelfBondage() avec conflict check (isRegionOccupied + isRegionBlocked)

Problèmes :

ID Sévérité Constat Fichier(s) Proposition
N-01 Moyenne PacketSelfBondage.handle() contient 5 branches V1 (instanceof ItemBind/ItemGag/ItemBlindfold/ItemMittens/ItemEarplugs) qui devront être supprimées avec D-01. La branche V2 (instanceof IV2BondageItem) restera seule. network/selfbondage/PacketSelfBondage.java Fix D-01 : Supprimer les branches V1, ne garder que la route V2. Simplifie massivement le fichier.
N-02 Moyenne 4 packets dépendent de ItemCollar (V1 class) : PacketSlaveAction, PacketMasterEquip, PacketAssignCellToCollar, PacketNpcCommand. La logique collar (ownership, canShock, hasGPS) est couplée à la classe Java. network/slave/PacketSlaveAction.java, network/slave/PacketMasterEquip.java, network/cell/PacketAssignCellToCollar.java, network/personality/PacketNpcCommand.java Fix D-01 : Quand ItemCollar migre vers le système composants, ces packets devront checker les composants (ex: hasComponent("shock")) au lieu de instanceof ItemCollar.
N-03 Basse PacketSyncBindState sync des flags d'état V1 (isTiedUp, isGagged, isBlindfolded, etc.) séparément de PacketSyncV2Equipment qui sync le V2 capability. Potentiellement redondant post-suppression V1 — l'état peut être dérivé du V2 equipment. network/sync/PacketSyncBindState.java, v2/bondage/network/PacketSyncV2Equipment.java Archi post-D-01 : Évaluer si PacketSyncBindState peut être supprimé et ses flags dérivés côté client depuis V2 equipment. Réduirait le nombre de packets sync.
N-04 Basse SyncManager.syncAllPlayersTo() envoie 4 packets distincts par joueur (V2Equipment, BindState, Enslavement, Struggle, + Clothes si applicable). Pour un serveur avec N joueurs, un login génère ~4N packets. network/sync/SyncManager.java Archi : Considérer un packet bulk PacketSyncFullState qui combine tout en un seul envoi. Pas urgent — 4N packets est acceptable pour les tailles de serveur visées.
N-05 Cosmétique Pas de MCABondageManager dans le package network, mais PacketSyncMCABondage existe — la logique MCA bondage sync est split entre network/sync/ et compat/mca/. network/sync/PacketSyncMCABondage.java, compat/mca/ Pas d'action : Acceptable pour un module de compatibilité.

#5 — Client Animation + GLTF

Positif :

  • Architecture 3 couches propre : Context layer (pri 40) → Item layer (pri 42) → Furniture layer (pri 43). Priorités claires, bon découplage.
  • BondageAnimationManager : API unifiée playAnimation/playDirect/playContext/playFurniture pour players et NPCs. Gestion des remote players (fallback stack), pending queue pour retry, furniture grace ticks pour éviter les stuck poses.
  • GlbAnimationResolver : Fallback chain fidèle au ARTIST_GUIDE (FullSitStruggle → SitStruggle → FullStruggle → Struggle → FullIdle → Idle). Support variants (.1, .2) avec random selection.
  • GltfAnimationApplier : Multi-item composite animation propre. Cache par state key, skip si unchanged. applyMultiItemV2Animation() merge les bones de plusieurs items dans un seul AnimationBuilder.
  • ContextGlbRegistry : Hot-reload des GLB de contexte depuis resource packs. Atomic swap pour thread safety render.
  • AnimationContextResolver : Résolution claire de contexte (sitting → struggling → movement style → sneaking → walking → idle). Version NPC séparée.
  • GLTF pipeline (12 fichiers) : Zéro dépendance V1. Parser, cache, skinning engine, mesh renderer, bone mapper, pose converter — tout est V2 natif.

Problèmes :

ID Sévérité Constat Fichier(s) Proposition
A-01 Moyenne 6 fichiers animation dépendent de ItemBind et PoseType (V1) pour déterminer le type de pose (STANDARD, DOG, HUMAN_CHAIR) et le bind mode (arms/legs/full). tick/AnimationTickHandler.java, tick/NpcAnimationTickHandler.java, render/PlayerArmHideEventHandler.java, render/PetBedRenderHandler.java, render/DogPoseRenderHandler.java, util/AnimationIdBuilder.java Fix D-01 : Quand les items V1 sont supprimés, la pose type et le bind mode doivent venir du système V2 (data-driven definition ou composant). PoseType peut être conservé comme enum mais lu depuis DataDrivenItemDefinition au lieu de ItemBind.getPoseType().
A-02 Basse StaticPoseApplier dépend de PoseType — applique des rotations hardcodées par pose type (V1 fallback pour quand le GLTF n'est pas disponible). animation/StaticPoseApplier.java Évaluer D-01 : Si tous les items ont un GLB, le static pose applier devient un fallback pur. Peut être conservé comme sécurité ou supprimé.
A-03 Basse GltfAnimationApplier a un toggle debug F9 hardcodé qui charge un GLB spécifique (cuffs_prototype.glb). client/gltf/GltfAnimationApplier.java (l.~350) Fix : Supprimer ou mettre derrière un flag dev. Mineur.
A-04 Cosmétique Le fallback animation dans BondageAnimationManager.tryFallbackAnimation() contient des patterns V1 spécifiques (_arms_, sit_dog_, kneel_dog_). Post-D-01, ces patterns n'existeront plus. animation/BondageAnimationManager.java Fix D-01 : Nettoyer les fallbacks V1 obsolètes. Le système GLB a sa propre fallback chain (GlbAnimationResolver).

#6 — Furniture

Positif :

  • Architecture data-driven exemplaire : FurnitureDefinition (record immuable) + FurnitureRegistry (volatile atomic swap) + FurnitureParser + FurnitureServerReloadListener. Exactement le même pattern que les bondage items V2 data-driven.
  • ISeatProvider : interface propre et générique — conçue pour être implémentée par des monstres aussi (ARTIST_GUIDE: "monster seat system"). Bonne anticipation.
  • SeatDefinition : record immuable avec tous les champs du guide artiste (blocked regions, lockable, locked difficulty, item difficulty bonus).
  • EntityFurniture : Entity simple (pas LivingEntity), synced via IEntityAdditionalSpawnData. Dimensions variables depuis la definition. Animation state machine (IDLE → OCCUPIED → LOCKING → STRUGGLE → UNLOCKING). Seat assignments persistés en NBT.
  • FurniturePlacerItem : singleton item avec NBT ID, snap-to-wall, floor-only. Même pattern que DataDrivenBondageItem.
  • FurnitureAnimationContext : Conversion GLB → KeyframeAnimation avec bones sélectifs (blocked regions only). S'intègre proprement avec la furniture layer (pri 43) de BondageAnimationManager.
  • FurnitureGltfData : Parsing dédié qui sépare furniture armature des Player_* seat skeletons dans un seul GLB. Fidèle à l'ARTIST_GUIDE.
  • Packets : Rate limited, distance checks, permission checks (collar ownership pour forcemount).
  • Reconnection robuste : NetworkEventHandler.handleFurnitureReconnection() restaure les joueurs locked dans un seat après déconnexion, avec teleport si le meuble n'existe plus.

Problèmes :

ID Sévérité Constat Fichier(s) Proposition
F-01 Moyenne EntityFurniture et PacketFurnitureForcemount dépendent de ItemCollar (V1) pour vérifier collar ownership avant forcemount. v2/furniture/EntityFurniture.java, v2/furniture/network/PacketFurnitureForcemount.java Fix D-01 : Quand ItemCollar migre vers composants, le check ownership doit utiliser le composant lockable ou collar au lieu de instanceof ItemCollar.
F-02 Basse FurnitureAnimationContext.create() log "V1: skeleton parsing not yet implemented" quand seatSkeleton est null. Si le GLB n'a pas de skeleton data parsé, l'animation silencieusement ne se joue pas. v2/furniture/client/FurnitureAnimationContext.java Évaluer : Vérifier que le parser GLB furniture extrait toujours le skeleton. Si oui, le fallback est juste un safety net. Sinon, c'est un bug silencieux.

Verdict : Le système furniture est le plus propre du mod. Zéro dette architecturale, fidèle au guide artiste, extensible (monster seats prêts). Les deux constats sont mineurs — un couplage V1 qui part avec D-01 et un fallback debug à vérifier.

#7 — Entities + AI

Hiérarchie d'héritage :

PathfinderMob
  └─ AbstractTiedUpNpc (1281 lignes, ~100 méthodes) — implements IRestrainable, IAnimatedPlayer, IV2EquipmentHolder
       ├─ EntityDamsel (834 lignes) — capturable NPC, personality, dialogue, inventory
       │    ├─ EntityDamselShiny — variante rare
       │    └─ EntityLaborGuard — garde de prison
       └─ EntityKidnapper (2039 lignes, ~170 méthodes) — implements ICaptor, IDialogueSpeaker
            └─ EntityKidnapperElite
                 ├─ EntityKidnapperMerchant — marchand neutre/hostile
                 ├─ EntityKidnapperArcher — attaque à distance
                 ├─ EntitySlaveTrader — boss de camp
                 ├─ EntityMaid — servante du trader
                 └─ EntityMaster (1192 lignes) — pet play system

Positif :

  • Composant-based decomposition pour Damsel : DamselBondageManager, DamselPersonalitySystem, DamselInventoryManager, DamselAIController, DamselAnimationController, DamselAppearance, NpcEquipmentManager, NpcCaptivityManager — 8 components avec interfaces host (IBondageHost, IAIHost, IAnimationHost, etc.). Bonne intention.
  • Composant-based pour Kidnapper : KidnapperAggressionSystem, KidnapperAlertManager, KidnapperAppearance, KidnapperCaptiveManager, KidnapperCellManager, KidnapperCampManager, KidnapperStateManager, KidnapperSaleManager, KidnapperTargetSelector, KidnapperDataSerializer — 10 components avec interfaces host. Très granulaire.
  • AI goals bien séparés : 80+ goals dédiés par type de NPC. Chaque goal est une classe autonome avec une seule responsabilité (KidnapperCaptureGoal, MasterDogwalkGoal, NpcFarmCommandGoal, etc.).
  • V2 equipment intégré : AbstractTiedUpNpc implémente IV2EquipmentHolder, utilise V2BondageEquipment directement. Les NPCs sont déjà sur le système V2.
  • State machines Kidnapper : KidnapperState enum avec états clairs (IDLE, HUNTING, CAPTURING, FLEEING, etc.).
  • Master NPC complet : pet play system avec task manager, state machine, punishment, dogwalk, furniture interaction — complexe mais fonctionnel.

Problèmes :

ID Sévérité Constat Fichier(s) Proposition
E-01 Haute EntityKidnapper = 2039 lignes, la plus grosse classe du mod. Malgré la décomposition en 10 components, la classe reste un god class. Elle mélange : ICaptor impl, targeting, capture equipment, sale system, job system, camp system, cell integration, alert system, NBT serialization, display name, dialogue, et des dizaines de getters/helpers. entities/EntityKidnapper.java Archi : Continuer la décomposition. Candidates : extraire le système de vente (startSale/completeSale/cancelSale/abandonCaptive) dans un component dédié, extraire le dialogue, extraire le ciblage. Objectif : ramener la classe sous 800 lignes.
E-02 Haute AbstractTiedUpNpc = 1281 lignes avec ~100 méthodes. Même pattern que PlayerBindState (S-01) — c'est une façade de délégation vers les components, mais doit aussi implémenter IRestrainable (30+ méthodes) directement. entities/AbstractTiedUpNpc.java Archi : La taille vient surtout de l'implémentation de IRestrainable. Évaluer si les méthodes bondage peuvent être déléguées à DamselBondageManager via un pattern default sur IRestrainable (mais IRestrainable est une interface, pas une classe — limité). Ou accepter la taille comme coût de l'implémentation multi-interface.
E-03 Moyenne 24 fichiers entities dépendent de V1 item classes (ItemBind, ItemCollar, PoseType, BindVariant, etc.). C'est le package le plus impacté par D-01. 24 fichiers (voir liste grep) Fix D-01 : Migration bulk. Les instanceof ItemBind deviennent instanceof IV2BondageItem, les ItemCollar checks deviennent des component checks. PoseType et BindVariant sont remplacés par des propriétés data-driven.
E-04 Moyenne Héritage profond : EntityMaid → EntityKidnapperElite → EntityKidnapper → AbstractTiedUpNpc → PathfinderMob. 5 niveaux. EntityMaid et EntitySlaveTrader héritent de toute la logique kidnapper (capture, targeting, sale) alors qu'ils n'utilisent pas tout. entities/EntityMaid.java, entities/EntitySlaveTrader.java Archi : Envisager une refactorisation vers composition plutôt qu'héritage. La Maid n'est PAS un kidnapper — elle ne devrait pas hériter de canCapture(), getCaptureBindTime(), etc. Long terme : AbstractTiedUpNpc → EntityDamsel (passive) / EntityKidnapper (hostile), et les autres types composent leurs comportements. Pas urgent mais dette croissante.
E-05 Basse EntityDamsel et EntityKidnapper ont des hiérarchies de host interfaces parallèles : damsel/components/IBondageHost, damsel/components/IAIHost vs kidnapper/components/IAIHost, kidnapper/components/ICaptiveHost, etc. Certaines pourraient être unifiées. entities/damsel/components/*.java, entities/kidnapper/components/*.java Pas d'action immédiate : Les interfaces host sont des contrats internes de chaque sous-arbre. Les unifier créerait un couplage horizontal. Acceptable tel quel.
E-06 Basse EntityMaster (1192 lignes) contient le pet play system complet. Components MasterPetManager, MasterTaskManager, MasterStateManager existent mais la classe orchestre encore beaucoup de logique. entities/EntityMaster.java Archi : Même recommandation que E-01 — continuer la décomposition. Moins urgent car le système est plus cohérent (une seule responsabilité : pet play).

#8 — Events

27 handlers, 5722 lignes, 8 domaines (camp, captivity, combat, interaction, lifecycle, restriction, system).

Positif : Bonne séparation par domaine. La plupart des handlers sont focalisés (85-200 lignes).

ID Sévérité Constat Proposition
EV-01 Moyenne RestraintTaskTickHandler (675 lignes, 12 @SubscribeEvent) — consolide tous les ticks restraint. Découper par type de tâche (tying, untying, force-feeding, shock checks).
EV-02 Moyenne BondageItemRestrictionHandler (544 lignes, 12 @SubscribeEvent) — consolide toutes les restrictions. Découper par type de restriction (legs, arms, gags, etc.).
EV-03 Moyenne 7/27 handlers importent des classes V1 (ItemBind, ItemCollar, ItemGag, IKnife, ILockable). Fix D-01 : Migrer vers V2 checks (composants).

#9 — Dialogue + Personality

31 fichiers, 7625 lignes. Dialogue 100% data-driven (JSON par personality × speaker type). Personality enum-based (11 types) avec state machine (needs, mood, commands).

Positif : Pipeline de chargement JSON propre (default → personality override → speaker-type). 18 catégories de dialogue. Dépendance unidirectionnelle (dialogue → personality, pas l'inverse). Seulement 3 fichiers importent du V1.

ID Sévérité Constat Proposition
DI-01 Moyenne 6 god classes dans dialogue/ (EntityDialogueManager 622l, ConversationManager 564l, GagTalkManager 557l, DialogueLoader 469l, DialogueBridge 463l, DialogueManager 428l). Acceptable pour la complexité du système. DialogueBridge (mapping legacy → new) peut être supprimé après D-01.
DI-02 Basse PersonalityState (709 lignes) — god class conteneur d'état NPC (needs, mood, commands, jobs, home). Continuer la décomposition si ça grossit. OK pour l'instant.
DI-03 Basse 3 fichiers importent V1 (GagTalkManager → ItemGag, ToolMode → ItemBind, PetRequestManager → BindVariant). Fix D-01 : Migrer.

#10 — Cells / Prison / Blocks

41 fichiers, 13329 lignes. Système SavedData massif (CellRegistryV2, PrisonerManager, CampOwnership, ConfiscatedInventoryRegistry).

Positif : Architecture SavedData correcte. Spatial indexing dans CellRegistryV2. Séparation services (PrisonerService, ItemService, BondageService).

ID Sévérité Constat Proposition
CP-01 Haute PrisonerService (1058 lignes) — plus grosse classe du package, gère tout le lifecycle prisonnier. Décomposer : labor, ransom, confiscation pourraient être des services séparés.
CP-02 Moyenne MarkerBlockEntity (1146 lignes) — god class block entity, gère spawning + teleportation + cell deletion. Extraire la logique spawning et teleportation dans des helpers.
CP-03 Moyenne BondageItemBlockEntity utilise 6 imports V1 (ItemBind, ItemGag, ItemBlindfold, ItemCollar, ItemEarplugs, GenericClothes) pour valider les items dans les trap blocks. Fix D-01 : Remplacer par instanceof IV2BondageItem + component checks.
CP-04 Basse CellRegistryV2 (903 lignes) — gros mais justifié par les index spatiaux. Acceptable.

#11 — Compat (MCA + Wildfire)

24 fichiers, 6536 lignes. MCA très couplé (21 fichiers, 5 sous-systèmes). Wildfire léger (3 fichiers, rendu only).

ID Sévérité Constat Proposition
CO-01 Haute MCAKidnappedAdapter (907 lignes) — implémente IRestrainable complet pour les villagers MCA. God class + dépend de V1 items. Fix D-01 : Migrer V1 → V2. Décomposer en components comme AbstractTiedUpNpc.
CO-02 Moyenne WildfireDamselLayer (988 lignes) — rendu physique très complexe. Acceptable pour un système de rendu physique. Pas urgent.
CO-03 Basse MCA compat utilise WeakHashMap et reflection pour détection — bon pattern de découplage. Pas d'action.

#12 — Util + Commands + Worldgen

55 fichiers, 15475 lignes.

ID Sévérité Constat Proposition
UC-01 Haute BondageSubCommand (1232 lignes) — monolithique, contient 16 sous-commandes (tie, untie, gag, collar, etc.) dans un seul fichier. Découper : TieCommands, GagCommands, CollarCommands, etc.
UC-02 Haute RoomTheme (1368 lignes) — config hardcodée de palettes de blocs pour worldgen. Archi : Externaliser en data-driven (JSON). C'est exactement le type de contenu qui devrait être configurable.
UC-03 Moyenne 7 fichiers importent V1 items (commands + RestraintApplicator + MCA adapter + HangingCagePiece). Fix D-01 : Migrer.
UC-04 Basse NPCCommand (764 lignes) — gros mais focalisé sur le spawning/state NPC. Acceptable, pourrait split par entity type.

Bilan Final

Statistiques

  • 12/12 systèmes audités
  • 744 classes, 233k lignes analysées
  • 38 constats documentés (+ 4 résolus par D-01)
  • 1 décision architecturale majeure (D-01 : suppression V1 + composants data-driven)

Classement par santé

Rang Système Verdict Problèmes
1 Furniture Exemplaire 2 mineurs
2 Animation + GLTF Excellent Résidus V1 seulement
3 Network Solide Résidus V1 seulement
4 Dialogue + Personality Bon God classes acceptables
5 Events Bon 2 handlers trop gros
6 Core Dette technique i18n, config triple
7 State Croissance organique God class, thread safety
8 Cells / Prison Fonctionnel mais lourd 11 god classes
9 Compat Fonctionnel mais couplé MCA adapter 907l
10 Util / Commands / Worldgen Fonctionnel BondageSubCmd 1232l, RoomTheme 1368l
11 Entities + AI Riche mais massif 2039l god class, héritage 5 niveaux
12 Items V1/V2 Point critique D-01 : suppression totale V1

Priorités de correction

Priorité Chantier Impact
P0 D-01 : Suppression V1 + composants data-driven Élimine la dette #1 du mod. Impacte ~60 fichiers. Requiert un plan dédié.
P1 C-01 : i18n SystemMessageManager Requis pour toute traduction du mod.
P1 UC-02 : RoomTheme → data-driven 1368 lignes hardcodées de config worldgen.
P2 E-01/E-02 : Décomposition EntityKidnapper/AbstractTiedUpNpc 2039 + 1281 lignes. Améliore la maintenabilité entities.
P2 CP-01 : Décomposition PrisonerService 1058 lignes.
P2 UC-01 : Split BondageSubCommand 1232 lignes en 1 fichier.
P3 S-02 : Encapsuler MovementState 8 champs publics mutés directement.
P3 S-05 : Thread safety cohérente 3 stratégies sans cohérence dans PlayerBindState.
P3 C-02 : Unifier Config/GameRules 25 settings triplés.
P4 Renommages (C-05 DamselRenderer, C-06 FQCNs) et cleanups cosmétiques.

D-01 Phase 1 — Suivi implémentation

Branche : feature/d01-component-system (17 commits, build clean)

Review adversariale : 3 critiques + 5 hauts trouvés et corrigés.

Problèmes notés (non bloquants, à traiter lors de la migration Phase 2) :

ID Constat Action
SMELL-002 GaggingComponent n'a aucun consommateur — GagTalkManager lit GagMaterial.getComprehension(), pas le composant. Lors de la migration Phase 2, faire pointer GagTalkManager vers le composant pour les items data-driven.
SMELL-003 Duplication sémantique entre le champ top-level lockable (boolean) et le composant LockableComponent. Un item doit configurer les deux pour un lock complet. Lors de la migration Phase 2, le champ lockable devrait être dérivé de la présence du composant lockable.
NOTE-003 test_component_gag.json est dans les resources de production — visible par les joueurs. Supprimer ou déplacer avant release. OK pour le dev.