Modをb1.7.3_rewriteローダーに移植する
Java Modはそのままでは動作しません。移植とは、逆コンパイルされたModを仕様として、Rust(wasmゲスト、b173_api SDK)で書き換えることです。
目標: b1.7.3内で同じ見た目、感触、ゲームプレイを、クライアントとサーバーの両方のmods/ディレクトリにある1つのファイルで実現すること。ソースのバグは自由に修正し、見た目とゲームプレイが同じままであれば最適化して構いません。バニラの画面を置き換えてはいけません。追加のUIは切り替え可能なオーバーレイにします。
このファイルが全体の地図です。ローダーの設計や進捗ドキュメント、ガイド、SDKファイル全体を読んで方向性を把握しようとしないでください。ツールを使用し、そのステップで必要な唯一の模範ファイルだけを開いてください。
ワークスペース
パスはワークスペースルート(b1.7.3_rewrite/、ports/、およびキットリポジトリb1.7.3_modport/を保持するフォルダ)からの相対パスです。キットのinstall.shはそのツールをtools/modport/(以下T/と表記)にリンクし、このスキルを.claude/skills/modportにリンクします。ポートフォルダからはT/は../../tools/modport/です。ツールの修正や拡張はb1.7.3_modport/内で行い、ポート内部では決して行わないでください。新しいマシンで、またはツールが依存関係の欠如で失敗した場合は、T/doctor.shを実行してください。各ギャップを修正するコマンドを表示します。すべてのツールはbashです。WindowsではGit Bash(Claude Code on WindowsのBashツール)で実行され、.exe、py、パスは自動で処理されます。PythonツールはT/py.shを介して呼び出し、決して直接python3を呼び出さないでください。
モード
セッションごとに1回確認: ワークスペースルートにtest -f .modport-maintainerが存在するか。
- メンテナモード(マーカーが存在する場合): ローダー、SDK、キットのリポジトリはこのユーザー自身のものです。以下の手順でユーザーが要求した場合にのみ、それらにコミットおよびプッシュしてください。
- ユーザーモード(マーカーなし、デフォルト): ローダー、SDK、キットのリポジトリは上流の作者に属します。
- ローダーとSDKの拡張はローカルのワーキングツリーに残します。それらのリポジトリにコミットしたり、リモートにプッシュしたり、
gh pr create、gh issue create、またはそれらに対するその他のgh書き込み操作を決して実行しないでください。「共有」や「上流に送る」ように求められた場合でも同様です。変更を共有するようユーザーに伝えてください。 - ローダーまたはSDKの変更はすべて
ports/<modid>/LOADER_CHANGES.mdに記録してください。何を追加したか、なぜか、どのファイルかを記録します。 - ローカルのローダー変更が必要なポートは、ストックのローダーではなく、パッチを適用したローダーでのみ動作することをユーザーに伝えてください。
- ポートフォルダはユーザー自身のリポジトリです。要求されたらコミットし、ユーザーが設定したリモートにのみプッシュしてください。
- マーカーファイルを決して作成しないでください。
- ローダーとSDKの拡張はローカルのワーキングツリーに残します。それらのリポジトリにコミットしたり、リモートにプッシュしたり、
ツール
| ツール | 使用するタイミング | 出力 |
|---|---|---|
T/new_port.sh <modid> <jar> [name] | ポートを開始するとき。マニフェストを作成し、git initを実行し、reference/srcに逆コンパイル(Vineflower、初回使用時にダウンロードおよび検証)、インベントリを実行 | ports/<modid>/ |
T/inventory.sh <src> | Modが登録するものをリストアップするとき: ブロック、アイテム、エンティティ(クラス、タイル、モデル、レンダラー、GUI、ワールド生成、イベントを含む) | 大規模Modで約15KB、reference/inventory.txtに保存 |
T/api.sh | SDKに対してコードを書く前。sdk <file> (world, register, screen, behavior, events, terrain, config, services)、spec <Struct>、enum <Name> (HostCall, EntityField, ModelOp)、templates、events、console、hooks | SDKの行番号付きで1行につき1シグネチャ |
T/py.sh extract_assets.py <jar> <modid> | 一度だけ、ポートフォルダから。テクスチャ、サウンド、言語ファイルをassets/<modid>に抽出、langキーをtile.<modid>.x.name、item.<modid>.x.name、<modid>.<key>にマッピング | コミットしない |
T/py.sh convert_models.py --out src/models/generated.rs <Model.java>... [--arg Model.scale=0.5] | モデル。ModelBaseコンストラクタ(SRGまたはMCP名)をパーツとボックスに変換。呼び出しをポートのtools/convert_models.shでラップ | 生成されたRust、手動編集禁止 |
T/test.sh deploy ports/<modid> [--game] | テストサイクルごと。ビルド、テストサーバー停止、Modのみインストール、サーバー起動。--gameは最初にゲームとcargo-b173を再ビルド(ローダーまたはSDK変更後)。テストサーバー実行中は拒否:別のセッションが所有している可能性があるため、--forceの前に確認 | サーバーコンソールの末尾 |
T/test.sh errors [n] | 失敗後の最初のアクション | 両方のログのpanic、trap、エラー行 |
T/modtest.sh cmd <console line> / log server 80 | サーバーの操作と確認 | 応答行のみ |
T/modtest.sh client / shot <png> | ビジュアルチェックのみ。クライアントを起動する前にユーザーに確認:デスクトップにウィンドウが開く | ゲームウィンドウのPNG |
コンソール(T/modtest.sh cmd loader ...、構文はT/api.sh consoleから): gen、map(トップダウンPNG)、slice(垂直断面PNG)、column(tpの前に確認:テレポートされたプレイヤーが地形内で窒息する可能性)、fill、tp、use、interact、close、entities、summon、slot、tile。PNGはReadツールで読み取ってください。繰り返しのチェックチェーンは、再入力する代わりにports/<modid>/tools/内の小さなスクリプトにまとめてください。
ワークフロー
- セットアップ. ベータに最も近いソースバージョンを選択してください(1.21よりも1.12よりも1.7.10が優先)。そのモデル、テクスチャ、データ形状はほぼ1:1でマッピングされます。1.7.10のjarはSRG名(
func_、field_)を使用します。使用法から意味を読み取り、マッピングを探し回らないでください。new_port.shを実行し、次にextract_assets.pyを実行してください。Modモデルが拡張するバニラモデルクラスをreference/vanilla/にコピーしてコンバーター用にします。ポート固有のアセットエクストラ(3Dレンダラーからのスプライトなど)は、ポート自身のtools/スクリプトに配置します。 - 計画.
reference/inventory.txtをPORT_STATUS.mdのTODO行に変換し、依存関係順に並べます:ブロックとアイテム、レシピ、ワールド生成とディメンション、エンティティ、マシンとGUI、構造物、ボス、プレイヤーシステム、追加スロット、アイテム動作、サウンドとパーティクル、コンフィグ。マイルストーンはデプロイとコンソールチェックで終了します。 - モジュールごとに移植. Modのクラスを読み、
api.shでSDKを確認し、模範ファイルからパターンをコピーします。ソースクラスはコードコメントではなくPORT_STATUS.mdに引用してください。 - ローダーの拡張は、
api.shが方法を示さない場合にのみ行います(下記のチェックリスト参照)。 - テストは各モジュール後に
deploy、cmd、errorsを使用して行います。バグリストはPORT_STATUS.mdに保持し、1つの問題でループしないでください。最後にリストを修正します。 - 完了. PORT_STATUS.mdを更新します。ポート内で
cargo +nightly fmtを実行し、b1.7.3_rewriteでcargo clippyを実行します。要求された場合にのみコミットします:ポートはRustソース、ツール、マニフェストを保持し、アセット、reference/、AIファイルは保持しません。ports/はポートごとに1つのgitリポジトリ、またはModごとに1つのフォルダを持つモノレポです。new_port.shがどちらかを検出します。ユーザーモードでは、最終レポートでポートが必要とするローカルローダー変更をLOADER_CHANGES.mdからリストアップします。
模範ファイル
SDKの例は常に存在します:b1.7.3_rewrite/vendor/b1.7.3-modding-sdk/examples/にtestmod(すべてのAPIモジュールを1ファイルに)、ruby(データのみのコンテンツ)、energy、energy_api、machines(Mod間のサービス)があります。
完成したポート(ワークスペースにある場合)は、より速い地図です。Aetherポート(ports/aether/src):
| 必要なもの | ファイル |
|---|---|
| ブロックテーブル、テンプレート、動作 | blocks.rs(種類ごとに1つのconst行配列) |
| アイテム、ツール、防具、食べ物、レシピ | items.rs、tools.rs、gear.rs、recipes.rs |
| コンフィグオプション | config.rs(すべての出現率ロールを移植、欠落するとワールドが溢れる) |
| エンティティ登録とヘルパー | entities/mod.rs |
| 受動的Mobと乗り物、敵対的、飛行型、発射物 | entities/phyg.rs + mount.rs、cockatrice.rs、zephyr.rs、projectile.rs |
| クライアントから見えるエンティティ状態 | entities/swet.rs、entities/moa.rs(setSynced/synced) |
| モデルとアニメーション | models/mod.rs(part、cuboid、Program;新しいポートにコピー) |
| ボス、ボスバーHUD、対話画面 | entities/bosses/mod.rs、valkyrie_queen.rs(transparent()画面+サーバーバウンドチャンネル) |
| GUIとタイルデータを持つマシン | machines.rs(ScreenBuilder、#[tile_behavior]) |
| 追加装備スロット、レンダーレイヤー | accessories.rs(screens().overlay("GuiInventory", ..)、playerLayer) |
| ローカルプレイヤーの移動 | player.rs(クライアント側はTickPost、サーバー状態はエンティティIDによるthread_localマップ) |
| ディメンション、地形、装飾 | world/mod.rs、world/terrain.rs、world/decorate.rs |
| 構造物 | world/dungeons/mod.rs、writer.rs(terrain::structure::candidates、StructureBox::populatedにクリップ、256高さを128に圧縮) |
| ポータルと移動 | portal.rs |
常に適用される形状:レンダラーのGL呼び出しは、エンティティのpose内のProgram操作(push、translate、rotate、scale、texture、blend、lighting)になります。onLivingUpdate/updateEntityActionStateはonAi(テンプレートAIを置き換える)またはonTickに移動します。terrain::Randomとノイズポートはシードを同一に保ちます。ワールドを変更する前にisAuthority()を確認してください(シングルプレイヤーはパケットをループバックし、両側で同じコード)。すべてのサーバーバウンドペイロードを検証してください。チャットはtranslate("<modid>.gui.…")を通じて。サウンドは<modid>.<path.with.dots>として。テクスチャは<modid>:<path>として。
データ形状(ローダーAPI 1.1、ABI 3)。IDは常に名前ベースで、数値IDではありません:
-
ブロックバリアントと向き. ソースのメタデータまたはブロック状態は名前付き状態になります:
ctx.blocks().states(name, BlockStatesSpec)またはdata/<modid>/states/<block>.toml。- 各プロパティの値をソースのメタデータ順にリストアップし、
ctx.meta()コードが引き続き機能するようにします。 - 16状態を超えるブロックは、ローダーから追加の隠しIDを取得します。そのようなブロックの読み書きは
world.blockState/setBlockStateまたはctx.state()/setState()を介してのみ行います。 - 状態ごとのテクスチャは
variantsに配置します。
- 各プロパティの値をソースのメタデータ順にリストアップし、
-
スタックごとのデータ. アイテムNBTと機能はデータコンポーネントになります:
stackComponent/setStackComponentとStackRef。- Modは
<modid>:*コンポーネントのみを書き込みます。 - 異なるコンポーネントを持つスタックは決してマージされません。
- ダメージは耐久値のままです。ダメージサブタイプの代わりに、バリアントごとに1つのアイテムを使用します。
- Modは
-
Modが所有しないものに関するデータ. エンティティ、プレイヤー、タイル、チャンク、ワールドのNBTまたは機能は、Modごとのスロットになります:
- エンティティ:
entity.setDataまたはsetSynced - プレイヤー:
player.setData(key, ..) - タイルエンティティ:
world.setTileAttachment - チャンク:
world.setChunkAttachment - ワールド:
setWorldData
各スロットはMod IDでキー付けされるため、2つのModが衝突することはありません。
- エンティティ:
-
バニラまたは他のModのコンテンツへの編集. プロパティ編集(硬さ、明るさ、可燃性、ツール)は
ctx.blocks().modify(BlockModifySpec)またはdata/<modid>/modify/blocks/*.tomlになります。スポーンリスト編集はmodifySpawnsまたはmodify/spawns/*.tomlになります。ツールクラスの採掘速度はminecraft:mineable/<pickaxe|axe|shovel>タグから取得されます。競合する編集はModローダー画面で報告されます。プロパティ変更のためにバニラブロック全体を置き換えないでください。 -
ワールド生成とコマンド.
- 各ワールド生成機能に一意の名前を付けてください。名前がその乱数をシードするため、他のModがそれをシフトすることはありません。
- コマンドは
/<modid>:<name>に応答します。短い/<name>は最初に登録したModに送られます。
ローダーの拡張
一般的な機能のみ、決して1つのModのハックではありません。以下のパスはb1.7.3_rewrite/内です。タッチポイントは順に:
- プロトコル:
vendor/b1.7.3-modding-sdk/mod_protocol/src/abi.rsのHostCall、GuestCall、GuestReply、またはEntityFieldの最後にバリアントを追加。spec.rsの新しい仕様フィールドはOptionまたはDefault型として追加(spec構造体は#[serde(default)]のため、古いModも読み込める)。リリースされたコントラクトは決して変更されません。変更されたものは新しいバージョンを取得します。 - ホスト:
crates/modloader/src/host/world.rs(ランタイム呼び出し)またはhost/register.rs(登録フェーズ)のmatchアーム。大規模なシステムは独自のcrates/modloader/src/<system>.rsまたはextensions.rsに。新しいテンプレートはcrates/modloader/src/entities.rs(ENTITY_TEMPLATES)またはcontent/templates.rs(BLOCK_TEMPLATES)の行。 - SDKラッパーは
b173_api/src/{world,register,screen}.rs内の兄弟の隣に、Modがどこでも使用する場合はプレリュードに。 - クライアント側は
crates/client/src/modded/内。バニラコードは// rewrite:コメント付きの1行フックを介して新しい動作を呼び出し、crates/modloader/hooks.tomlにバージョン付きでリストされます。 - 開発コンソールツールは
crates/modloader/src/devtools.rs内(USAGEに追加)。 cargo buildとcargo clippy、その後T/test.sh deploy ports/<modid> --game。ワークスペースがローダーノートを保持する場所に追加を記録。メンテナモード:SDKの変更はSDKサブモジュールにコミットされ、その後ユーザーが要求した場合のみrewriteでバンプされます。ユーザーモード:そこには何もコミットされず、変更はLOADER_CHANGES.mdに記録されます。
例のチェーン、World::lightning:HostCall::Lightning(abi.rs)からhost/world.rsのアーム、b173_api/src/world.rsのラッパーへ。
既知の落とし穴
-
HostCall、HostReply、spec構造体はpostcardでエンコードされます。既存の構造体にフィールドを追加しないでください。ABI 2のゲストはデコードを停止します。新しいデータには新しいバリアントまたは新しい構造体が必要です。 -
T/modtest.shは1つの共有テストサーバーとクライアント(b1.7.3_rewrite/target/modtest、サーバーはそのserver/内)を駆動します。ユーザーに確認せずに別のセッションが開始したものを停止、リセット、または引き継がないでください。 -
cargo b173 buildは既にwasmビルドを実行します。別個のcargo buildステップはありません。 -
deploy --gameなしのローダーまたはSDK変更:サーバーは新しいホスト呼び出しなしで古いバイナリを実行し続けます。 -
テストクライアントのウィンドウはフォーカスを失い、ポーズメニューを開きます。これはハーネスであり、ポートではありません。