D1: ThreadLocal alert suppression moved from ItemCollar to CollarHelper.
onCollarRemoved() logic (kidnapper alert) moved to CollarHelper.
D2+D3: Deleted 17 V1 item classes + 4 V1-only interfaces:
ItemBind, ItemGag, ItemBlindfold, ItemCollar, ItemEarplugs, ItemMittens,
ItemColor, ItemClassicCollar, ItemShockCollar, ItemShockCollarAuto,
ItemGpsCollar, ItemChokeCollar, ItemHood, ItemMedicalGag,
IBondageItem, IHasGaggingEffect, IHasBlindingEffect, IAdjustable
D4: KidnapperTheme/KidnapperItemSelector/DispenserBehaviors migrated
from variant enums to string-based DataDrivenItemRegistry IDs.
D5: Deleted 11 variant enums + Generic* factories + ItemBallGag3D:
BindVariant, GagVariant, BlindfoldVariant, EarplugsVariant, MittensVariant,
GenericBind, GenericGag, GenericBlindfold, GenericEarplugs, GenericMittens
D6: ModItems cleaned — all V1 bondage registrations removed.
D7: ModCreativeTabs rewritten — iterates DataDrivenItemRegistry.
D8+D9: All V2 helpers cleaned (V1 fallbacks removed), orphan imports removed.
Zero V1 bondage code references remain (only Javadoc comments).
All bondage items are now data-driven via 47 JSON definitions.
36 KiB
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 :
- Consolider — identifier et corriger les incohérences, le code mort, la duplication
- Améliorer — proposer des refactors ciblés là où l'architecture freine le développement
- 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 DamselRenderer → NpcRenderer 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) :
IRestrainableEntity→ICapturable→IBondageState→IRestrainable(union) - Décomposition en 11 components (PlayerEquipment, PlayerStateQuery, etc.) — bonne intention
IBondageStaten'expose que l'API V2 region-based — l'interface publique est propreCollarRegistryetSocialData: implémentationsSavedDatapropres avec persistence correcteIPlayerLeashAccess: séparation mixin clean pour le système de laissePlayerCaptorManager: thread-safe avecCopyOnWriteArrayListetsynchronized
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 :
IV2BondageItembien conçu : multi-region, stack-aware, pose priority, blocked regionsV2EquipmentManager: conflict resolution solide (swap single, supersede global)V2EquipmentHelper: facade propre pour read/write/syncDataDrivenBondageItem: singleton + NBT registry pattern intelligent pour items data-drivenILockable: système lock/jam/key complet et cohérentIHasResistance: résistance NBT avec migration legacy, bien documentéeBodyRegionV2enum 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 | RÉSOLU : suppression V1 (voir Décision D-01) | |||
| I-02 | 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 | RÉSOLU : suppression V1 (voir Décision D-01) | |||
| I-05 | 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é parV2EquipmentManager.tryEquip()
Interfaces à conserver / migrer :
ILockable— conservé, utilisé par V2 itemsIHasResistance— 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 loadedPacketRateLimiter— 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. PatternsendSync()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 checkPacketSelfBondage— rate limited, route correctement V2 viahandleV2SelfBondage()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/playFurniturepour 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 viaIEntityAdditionalSpawnData. 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 queDataDrivenBondageItem.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é :
AbstractTiedUpNpcimplémenteIV2EquipmentHolder, utiliseV2BondageEquipmentdirectement. Les NPCs sont déjà sur le système V2. - State machines Kidnapper :
KidnapperStateenum 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. |