Porter un mod vers le chargeur b1.7.3_rewrite
Les mods Java ne peuvent pas s'exécuter. Un port est une réécriture en Rust (guest wasm, SDK b173_api) qui utilise le mod décompilé comme spécification.
Objectif : même aspect, même sensation et même gameplay dans b1.7.3, à partir d'un seul fichier dans les dossiers mods/ côté client et serveur. Corrigez les bugs de source et
optimisez librement tant que l'aspect et le gameplay restent les mêmes. Ne remplacez jamais un écran vanilla : une UI supplémentaire est une superposition activable/désactivable.
Ce fichier est la carte complète. Ne lisez pas la documentation de conception du chargeur ou de progression, les guides ou les fichiers SDK entiers pour vous orienter. Utilisez les outils, puis ouvrez uniquement le fichier d'exemple dont l'étape a besoin.
Espace de travail
Les chemins sont relatifs à la racine de l'espace de travail, le dossier qui contient b1.7.3_rewrite/, ports/ et le dépôt de la trousse à outils
b1.7.3_modport/. Le install.sh de la trousse lie ses outils à tools/modport/ (écrit T/ ci-dessous) et ce skill à
.claude/skills/modport ; depuis un dossier de port, T/ est ../../tools/modport/. Corrigez ou étendez les outils dans b1.7.3_modport/, jamais à l'intérieur d'un port. Sur une nouvelle machine, ou lorsqu'un
outil échoue à cause d'une dépendance manquante, exécutez T/doctor.sh : cela affiche la commande qui comble chaque lacune. Tous les outils sont en
bash : sous Windows, ils s'exécutent dans Git Bash (l'outil Bash de Claude Code sur Windows) et gèrent eux-mêmes les .exe, py et les chemins.
Appelez les outils Python via T/py.sh, jamais python3 directement.
Modes
Vérifiez une fois par session : test -f .modport-maintainer dans la racine de l'espace de travail.
- Mode mainteneur (le marqueur existe) : les dépôts du chargeur, du SDK et de la trousse appartiennent à cet utilisateur. Ne les engagez (commit) et ne les poussez (push) que lorsque l'utilisateur le demande, comme indiqué dans les étapes ci-dessous.
- Mode utilisateur (pas de marqueur, par défaut) : les dépôts du chargeur, du SDK et de la trousse appartiennent à leur auteur en amont.
- Les extensions du chargeur et du SDK restent dans l'arbre de travail local. Ne les engagez jamais dans ces dépôts, ne les poussez jamais vers leurs
dépôts distants, n'exécutez jamais
gh pr create,gh issue createou toute autre commandeghen écriture contre eux, même si on vous demande de « partager » ou de « remonter » un changement. Dites à l'utilisateur de le faire lui-même. - Consignez chaque modification du chargeur ou du SDK dans
ports/<modid>/LOADER_CHANGES.md: ce qu'elle ajoute, pourquoi, quels fichiers. - Dites à l'utilisateur qu'un port nécessitant une modification locale du chargeur ne fonctionne que sur son chargeur patché, pas sur un chargeur standard.
- Le dossier du port est le propre dépôt de l'utilisateur : engagez-y quando demandé, poussez uniquement vers un dépôt distant que l'utilisateur a configuré.
- Ne créez jamais le fichier marqueur.
- Les extensions du chargeur et du SDK restent dans l'arbre de travail local. Ne les engagez jamais dans ces dépôts, ne les poussez jamais vers leurs
dépôts distants, n'exécutez jamais
Outils
| Outil | Utilisez-le quand | Sortie |
|---|---|---|
T/new_port.sh <modid> <jar> [nom] | Au démarrage d'un port. Écrit les manifests, git init, décompile dans reference/src (Vineflower, téléchargé et vérifié lors de la première utilisation), exécute l'inventaire | ports/<modid>/ |
T/inventory.sh <src> | Pour lister ce qu'un mod enregistre : blocs, objets et entités avec classes, tuiles, modèles, moteurs de rendu, GUI, génération du monde, événements | ~15 Ko pour un grand mod, conservé dans reference/inventory.txt |
T/api.sh | Avant d'écrire du code contre le SDK. sdk <fichier> (world, register, screen, behavior, events, terrain, config, services), spec <struct>, enum <nom> (HostCall, EntityField, ModelOp), templates, events, console, hooks | une ligne par signature avec les numéros de ligne du SDK |
T/py.sh extract_assets.py <jar> <modid> | Une fois, depuis le dossier du port. Textures, sons et lang dans assets/<modid>, clés de langue mappées à tile.<modid>.x.name, item.<modid>.x.name, <modid>.<key> | jamais engagé |
T/py.sh convert_models.py --out src/models/generated.rs <Model.java>... [--arg Model.scale=0.5] | Modèles. Constructeurs de ModelBase (noms SRG ou MCP) en pièces et boîtes. Enveloppez l'appel dans le tools/convert_models.sh du port | Rust généré, jamais modifié à la main |
T/test.sh deploy ports/<modid> [--game] | Chaque cycle de test. Construit, arrête le serveur de test, installe le mod seul, démarre le serveur. --game reconstruit d'abord le jeu et cargo-b173 (après des modifications du chargeur ou du SDK). Refuse pendant qu'un serveur de test tourne : une autre session peut le posséder, demandez avant --force | fin de la console du serveur |
T/test.sh errors [n] | Première chose après tout échec | lignes de panique, piège et erreur des deux journaux |
T/modtest.sh cmd <ligne_console> / log server 80 | Conduire et vérifier le serveur | uniquement les lignes de réponse |
T/modtest.sh client / shot <png> | Vérifications visuelles uniquement. Demandez à l'utilisateur avant de lancer un client : cela ouvre une fenêtre sur son bureau | PNG de la fenêtre du jeu |
Console (T/modtest.sh cmd loader ..., syntaxe de T/api.sh console) : gen, map (PNG vue de dessus), slice (coupe
verticale PNG), column (vérifiez avant tp : un joueur téléporté dans le terrain suffoque), fill, tp, use, interact,
close, entities, summon, slot, tile. Lisez les PNG avec l'outil Read. Mettez les chaînes de vérification répétées dans de petits scripts
dans ports/<modid>/tools/ au lieu de les retaper.
Flux de travail
- Configuration. Choisissez la version source la plus proche de la bêta (1.7.10 plutôt que 1.12 plutôt que 1.21) : ses modèles, textures et formes
de données se correspondent presque 1:1. Les jars 1.7.10 utilisent des noms SRG (
func_,field_) : lisez le sens à partir de l'utilisation, ne chassez pas les correspondances. Exécuteznew_port.sh, puisextract_assets.py. Copiez les classes de modèles vanilla qu'un modèle de mod étend dansreference/vanilla/pour le convertisseur. Les extras d'actifs spécifiques au port (sprites des moteurs de rendu 3D) vont dans le propre scripttools/du port. - Planification. Transformez
reference/inventory.txten lignes TODO dans PORT_STATUS.md dans l'ordre des dépendances : blocs et objets, recettes, génération du monde et dimension, entités, machines et GUI, structures, boss, systèmes de joueur, emplacements supplémentaires, comportements des objets, sons et particules, configuration. Les jalons se terminent par un déploiement et une vérification en console. - Portage module par module. Lisez la classe du mod, vérifiez le SDK avec
api.sh, copiez le modèle d'un exemple. Citez la classe source dans PORT_STATUS.md, pas dans les commentaires du code. - Étendez le chargeur uniquement lorsque
api.shne montre aucune manière (liste de contrôle ci-dessous). - Testez après chaque module avec
deploy,cmd,errors. Tenez une liste de bugs dans PORT_STATUS.md au lieu de boucler sur un seul problème ; corrigez la liste à la fin. - Terminez. Mettez à jour PORT_STATUS.md. Exécutez
cargo +nightly fmtdans le port etcargo clippydansb1.7.3_rewrite. Engagez uniquement sur demande : un port contient du code source Rust, des outils et des manifests, jamais d'actifs,reference/ou des fichiers d'IA.ports/est soit un dépôt git par port, soit un monorepo avec un dossier par mod ;new_port.shdétecte lequel. En mode utilisateur, listez les modifications locales du chargeur dont le port a besoin à partir deLOADER_CHANGES.mddans le rapport final.
Exemples
Exemples du SDK, toujours présents : b1.7.3_rewrite/vendor/b1.7.3-modding-sdk/examples/ avec testmod (chaque module d'API dans
un fichier), ruby (contenu uniquement de données), energy, energy_api, machines (services entre mods).
Un port terminé, quand l'espace de travail en a un, est la carte la plus rapide. Le port Aether (ports/aether/src) :
| Besoin | Fichier |
|---|---|
| Tableau des blocs, modèles, comportements | blocks.rs (un tableau de constantes par rangée par type) |
| Objets, outils, armures, nourriture, recettes | items.rs, tools.rs, gear.rs, recipes.rs |
| Options de configuration | config.rs (portez chaque jet de taux d'apparition, ceux manquants inondent le monde) |
| Enregistrement des entités et aides | entities/mod.rs |
| Mob passif et monture, hostile, volant, projectile | entities/phyg.rs + mount.rs, cockatrice.rs, zephyr.rs, projectile.rs |
| État d'entité visible par le client | entities/swet.rs, entities/moa.rs (setSynced/synced) |
| Modèles et animation | models/mod.rs (part, cuboid, Program ; copiez-le dans un nouveau port) |
| Boss, barre de vie du boss, écran de dialogue | entities/bosses/mod.rs, valkyrie_queen.rs (écran transparent() plus canal côté serveur) |
| Machine avec GUI et données de tuile | machines.rs (ScreenBuilder, #[tile_behavior]) |
| Emplacements d'équipement supplémentaires, couches de rendu | accessories.rs (screens().overlay("GuiInventory", ..), playerLayer) |
| Mouvement local du joueur | player.rs (côté client dans TickPost, état serveur dans des cartes thread_local par identifiant d'entité) |
| Dimension, terrain, décoration | world/mod.rs, world/terrain.rs, world/decorate.rs |
| Structures | world/dungeons/mod.rs, writer.rs (terrain::structure::candidates, clip à StructureBox::populated, compressez les hauteurs 256 en 128) |
| Portail et voyage | portal.rs |
Formes qui s'appliquent toujours : les appels GL du moteur de rendu deviennent des opérations Program (push, translate, rotate, scale, texture, blend,
éclairage) dans le pose de l'entité ; onLivingUpdate/updateEntityActionState vont dans onAi (remplace l'IA modèle) ou
onTick ; terrain::Random et les ports de bruit gardent les graines identiques ; vérifiez isAuthority() avant de changer le monde
(le joueur unique renvoie les paquets en boucle, même code des deux côtés) ; validez chaque charge utile côté serveur ; chat via
translate("<modid>.gui.…") ; sons comme <modid>.<chemin.avec.points> ; textures comme <modid>:<chemin>.
Formes de données (API du chargeur 1.1, ABI 3). L'identité est toujours par nom, jamais par identifiant numérique :
-
Variantes de blocs et orientation. La metadata source ou les états de bloc deviennent des états nommés :
ctx.blocks().states(nom, BlockStatesSpec)oudata/<modid>/states/<bloc>.toml.- Listez les valeurs de chaque propriété dans l'ordre de la metadata source, afin que le code
ctx.meta()continue de fonctionner. - Un bloc avec plus de 16 états reçoit des identifiants cachés supplémentaires du chargeur. Lisez et écrivez un tel bloc uniquement via
world.blockState/setBlockStateouctx.state()/setState(). - Les textures par état vont dans
variants.
- Listez les valeurs de chaque propriété dans l'ordre de la metadata source, afin que le code
-
Données par pile. Le NBT des objets et les capacités deviennent des composants de données :
stackComponent/setStackComponentavec unStackRef.- Un mod écrit uniquement les composants
<modid>:*. - Les piles avec des composants différents ne fusionnent jamais.
- Les dégâts restent la durabilité. Utilisez un objet par variante au lieu de sous-types de dégâts.
- Un mod écrit uniquement les composants
-
Données sur des choses que le mod ne possède pas. Le NBT ou les capacités des entités, joueurs, tuiles, chunks et monde deviennent des emplacements par mod :
- entité :
entity.setDataousetSynced - joueur :
player.setData(clé, ..) - tuile :
world.setTileAttachment - chunk :
world.setChunkAttachment - monde :
setWorldData
Chaque emplacement est indexé par l'identifiant du mod, donc deux mods n'entrent jamais en conflit.
- entité :
-
Modifications du contenu vanilla ou d'autres mods. Les modifications de propriétés (dureté, luminosité, inflammabilité, outil) deviennent
ctx.blocks().modify(BlockModifySpec)oudata/<modid>/modify/blocks/*.toml. Les modifications des listes d'apparition deviennentmodifySpawnsoumodify/spawns/*.toml. La vitesse de minage pour une classe d'outil provient des tagsminecraft:mineable/<pioche|hache|pelle>. Les modifications conflictuelles sont signalées dans l'écran du chargeur de mods. Ne remplacez jamais un bloc vanilla entier pour un changement de propriété. -
Génération du monde et commandes.
- Nommez chaque fonctionnalité de génération du monde de manière unique. Le nom détermine son aléatoire, donc les autres mods ne le décalent jamais.
- Les commandes répondent à
/<modid>:<nom>. Le raccourci/<nom>va au premier mod qui l'enregistre.
Étendre le chargeur
Fonctionnalités générales uniquement, jamais un hack pour un seul mod. Les chemins ci-dessous sont dans b1.7.3_rewrite/. Points de contact, dans l'ordre :
- Protocole : ajoutez des variantes à la fin de
HostCall,GuestCall,GuestReplyouEntityFielddansvendor/b1.7.3-modding-sdk/mod_protocol/src/abi.rs; les nouveaux champs de spec dansspec.rscomme typesOptionouDefault(les structs de spec sont#[serde(default)], donc les anciens mods se chargent toujours). Les contrats publiés ne changent jamais : un contrat modifié reçoit une nouvelle version. - Hôte : bras de correspondance dans
crates/modloader/src/host/world.rs(appels runtime) ouhost/register.rs(phase d'enregistrement) ; les systèmes plus importants dans leur proprecrates/modloader/src/<system>.rsouextensions.rs. Les nouveaux modèles sont des lignes danscrates/modloader/src/entities.rs(ENTITY_TEMPLATES) oucontent/templates.rs(BLOCK_TEMPLATES). - Wrapper SDK à côté de ses frères dans
b173_api/src/{world,register,screen}.rs, dans le prélude si les mods l'utilisent partout. - Côté client dans
crates/client/src/modded/. Le code vanilla appelle un nouveau comportement via un crochet d'une ligne avec un commentaire// rewrite:, listé avec une version danscrates/modloader/hooks.toml. - Outils de console de développement dans
crates/modloader/src/devtools.rs(ajoutez àUSAGE). cargo buildetcargo clippy, puisT/test.sh deploy ports/<modid> --game. Notez l'ajout là où l'espace de travail tient les notes du chargeur. Mode mainteneur : les modifications du SDK sont engagées dans le sous-module SDK, puis mises à jour dans la réécriture, uniquement sur demande de l'utilisateur. Mode utilisateur : rien n'y est engagé, le changement va dansLOADER_CHANGES.md.
Chaîne d'exemple, World::lightning : HostCall::Lightning (abi.rs) vers son bras dans host/world.rs vers le wrapper dans
b173_api/src/world.rs.
Pièges connus
-
HostCall,HostReplyet les structs de spec sont encodés en postcard. N'ajoutez jamais de champ à une struct existante : les invités ABI 2 cesseraient de décoder. Les nouvelles données nécessitent une nouvelle variante ou une nouvelle struct. -
T/modtest.shpilote un serveur et client de test partagé (b1.7.3_rewrite/target/modtest, serveur dans sonserver/). Ne jamais arrêter, réinitialiser ou prendre le contrôle d'une autre session sans demander à l'utilisateur. -
cargo b173 buildexécute déjà la compilation wasm ; il n'y a pas d'étapecargo buildséparée. -
Modification du chargeur ou du SDK sans
deploy --game: le serveur exécute toujours l'ancien binaire sans les nouveaux appels hôte. -
La fenêtre du client de test perd le focus et ouvre le menu pause ; c'est le harnais, pas le port.