port-a-mod について
port-a-modは、既存のModを別のゲームバージョンやローダーへ忠実に移植するためのコーディングスキルです。固定した元リリースを仕様とし、データ抽出、単位ごとの実装、ライセンス確認、根拠に基づくテストを重視します。
主な機能
- 01元リリースの固定と監査
- 02サブシステムの適応状況を一覧化
- 03登録データを再入力せず抽出
- 04単位ごとの移植とテスト
- 05サーバー・クライアントテストで検証
- 06ライセンスと既知の差異を記録
活用シーン
- 01ゲームバージョン間のMod移植
- 02移植内容と元Modの差異の監査
- 03ゲームプレイとクライアント動作のテスト計画
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ rehan-remade/ universal-modder --skill port-a-modインストール後、Claude Code で/port-a-modを入力して読み込まれたか確認します。
同梱ファイルreferences/minecraft-1-7-10-forge-to-1-20-fabric.md
詳細
Mod を忠実に移植する
既存の Mod を移植する場合、元の Mod が仕様です。移植は、コードが足りないために失敗することはほとんどありません。失敗の原因は逸脱です。
- 記憶や Wiki から思い出した値
- 同じ Mod の後年のリライトから借りてきた仕組み
- さりげない「改善」
- おおむね正しく見えるレンダラー
- 元の Mod ではクライアント側で行っていたチェックを、移植版では忘れてしまうこと
以下の手法により、Thaumcraft 4.2.3.5 を Minecraft 1.7.10 Forge から 1.20.1 Fabric へ移植しました。
- 元の 859 クラスを、約 77,000 行の移植コードに変換しました。
- 323 件の GameTest でカバーし、テストでは到達できない部分をスクリプト化した実クライアントのシナリオで確認します。
- 作業は多くのセッションと 2 つのエージェントアカウントにまたがりました。
フィールドノート knowledge/games/minecraft/porting-thaumcraft-4-1-7-10-forge-to-minecraft-1-20-1-fabric.md に注意点を記載しています。Minecraft 固有のマッピングについては references/minecraft-1-7-10-forge-to-1-20-fabric.md を参照してください。
このスキルを中心に mod-any-game のループ(intake、recon、lab、verify、publish、field note)を実行します。このスキルでは、その「vertical slice」ステップを、元の Mod の単位ごとに実施する長い一連のスライスに置き換えます。あるゲームのコンテンツや仕組みを別のゲームに持ち込む場合は、このスキルではなく mashup-mods スキルを使います。
始める前に:ライセンス
元の Mod のコードとアセットは作者に帰属します。移植はそれらを基に、多くの場合は逆コンパイルしたものを基に作られるため、移植版の何を公開できるかは元のライセンスによって決まります。(これは法的助言ではありません。不明な点がある場合は作者に確認してください。)
- 移植するリリースのライセンスを確認します。Mod のリポジトリ(
LICENSE)、Mod のページ、jar 内のファイル、または作者のリリース告知を確認してください。リンクとともに、ステータス文書のセクション 7 に記載します。 - オープンライセンス(MIT、LGPL、GPL、Creative Commons ライセンス)の場合は、その条件に従います。著作権表示を残し、作者のクレジットを記載し、必要な場合は同じライセンスを使用します(GPL、LGPL、CC BY-SA)。
- **All rights reserved、ライセンスなし、または「移植禁止」条項がある場合:**移植版のいかなるもの(jar、公開リポジトリ、元のテクスチャやサウンド)も、公開には作者または現在の権利者の許可が必要です。許可を求め、その回答(リンクまたは引用)をステータス文書に保存します。許可が得られない場合、移植版は非公開のままにします。
- ここで取り上げる実例の Thaumcraft 4 はクローズドソースです。2014 年、作者は逆コンパイル版を GitHub から削除させました (DMCA notice)。
- **開始前に、どのケースに該当するかをユーザーに伝えます。**自分の Mod パック用に非公開で移植するかどうかは、ユーザー自身の判断です。
- 移植版とリリースの両方で元の作者をクレジットし、移植版を公式バージョンとして提示しないでください。
0. 信頼できる情報源を 1 つに固定する
- **正確なリリースを固定します。**jar、そのハッシュ(SHA-1)、入手元、変更履歴を記録します。開発が終了した Mod 系列の場合、通常は最終リリースが適切です。
- **参照するバージョンは 1 つだけにします。**同じ名前の後年のリライト(Thaumcraft 4 に対する Thaumcraft 5 や 6、「Reborn」や「Legacy」フォーク)は別の Mod です。
- Thaumcraft の移植では、最初に誤ったベースを選び、途中で切り替えました。
- 数週間後も、ゲームプレイの比較はすべて TC4 を基準にしていたにもかかわらず、TC5 のメッシュ、テクスチャ、入力ハンドラー、スイングフックがコード内に残っていました。
- ベースを変更する場合は、以前の状態をアーカイブし、各領域を keep / adapt / replace / remove に分類してから、コメントと識別子を検索し、以前のバージョン名を確認します。
- **今後のすべてのエージェントが読む場所にルールを記載します。**プロジェクトの
AGENTS.mdに記載し、エージェント固有の指示ファイルからもそのファイルを参照させます。例: 「 だけをゲームプレイの参照元とします。その値、癖、観察可能なバグを維持してください。後年のバージョンを参照しないでください。ゲームプレイを黙って改善しないでください。ビルドやテストを実行していないのに、成功したと主張してはいけません。」
1. 非公開のリファレンスを作成する
- オリジナルのソースが公開されている場合(リポジトリ、sources jar など)は、固定したリリースのタグにあるソースを使い、jar の逆コンパイルは両者の一致を確認するためだけに行います。
- **Vineflower で最初に逆コンパイルします。**失敗するクラスには CFR を使用し、そのクラス数を記録します。リバースエンジニアリングスキルにツールがあります。
- 読みやすい名前にリマップします。
- 1.7.10 の Forge jar には SRG 名(
func_12345_a、field_1234_b)が使われています。MCP のstable_12CSV(methods.csv、fields.csv)を使ってリマップします。トークンを置き換えるスクリプトで十分です。1.12 ではstable_39を使います。 - 移植先側では、ローダーのキャッシュにあるマッピング済みゲーム jar を
javapで読み取ります。ミックスインの名前、ラムダ名、ローカル変数スロットを確認できます。Loom のgenSourcesを使うと読みやすいソースを取得できます。
- 1.7.10 の Forge jar には SRG 名(
- オリジナルのアセット(テクスチャ、モデル、lang、サウンド)を抽出し、逆コンパイルしたコードの隣に置きます。これにより、アセット監査でオリジナルとハッシュを比較できます。
- **非公開に保ちます。**逆コンパイルしたものを配布したり、公開リポジトリにコミットしたりしてはいけません(絶対厳守)。
- 今後のすべてのセッションからアクセスできる状態にしておきます。別の非公開リポジトリに置くか、移植先のリポジトリ外に固定フォルダーを用意します。
- 移植先のリポジトリの履歴にも含めないでください。後からリポジトリを公開すると、履歴全体が公開されます。
- Thaumcraft の移植では、非公開リポジトリに
reference/を置いていたため、新しいクラウドセッションでも変更を加える前にオリジナルを参照できました。
2. 棚卸しと適応マトリックス
- **オリジナルのすべてのサブシステムを列挙します。**レジストリ、イベントハンドラー、パケット、GUI と画面、レンダラー、ワールド生成、ルート、設定、コマンド、公開 API、互換性フックなどです。
- **マトリックスを作成します。**各領域について、オリジナルのクラス、移植先のクラス、判定(維持/適応/置換/削除/未対応)を記載します。別のベースから引き継いだ移植コードも同様に扱います。
- 依存関係に沿ってマイルストーンの順序を決めます。
- データ基盤:ID、アイテムとブロックのレジストリ、MOD のコアデータ(アスペクト、研究)。
- コアメカニクス。
- 装置、クラフト、ワールド生成、モブ。
- レンダリングと FX。
- 互換性。 最初の日からステータス文書を維持します(セクション 7)。
3. データを抽出し、手入力しない
- 登録コードは、姿を変えたデータです(
ConfigItems、ConfigResearch、レシピクラスなど)。- 小さなトークナイザースクリプトで逆コンパイルしたコードの呼び出しチェーンを解析し、移植先のリソースに JSON を書き出します。
- スクリプト内にソースの行番号を記録します。
- 出力を手作業で編集するのではなく、スクリプトを再実行します。
- Thaumcraft の移植では、これにより 48 個のアスペクト、457 件のオブジェクトとアスペクトの登録、ページとワープを含む研究ツリー、すべてのレシピを生成しました。
- **レガシー ID。**古いアイテムは多くの場合、
(item, metadata)の組み合わせですが、新しいエンジンにはメタデータがありません。- 各組み合わせに、フラット化した ID を割り当てます。
- 移植した各アイテムに、置き換えるレガシー参照を登録させます。
- データファイル内の参照は、そのマップを使って解決します。
- カタログ項目が未登録のままなら、起動時に失敗させます。
- ベースゲーム自体で名前が変更された ID には、移植先のデータフィクサーを使います。
- **すべてを数えます。**オリジナルの各アイテムとメタデータのバリエーションが存在することを確認するテストで、見落としを検出できます(Thaumcraft の移植では、アイテム 225 個とブロックアイテム 119 個)。
- **オリジナルの順序を維持します。**古い MOD の検索では、最初に一致したものが使われることがよくあります。最初に一致したレシピが選ばれ、最初に一致したカテゴリが使われます。値が蓄積される場合もあります(登録時にアスペクトが 2 回追加されるなど)。項目をソートしたり、マップのキーを上書きしたりする抽出ツールは、動作を変えてしまいます。
- **まだ解決できない参照を追跡します。**各処理の後に未解決の参照数を記録します(Thaumcraft の移植では、コンテンツの追加に伴い 101 → 47 → 24)。これにより、何も気づかれないまま放置されるのを防げます。
- **移植する前に、オリジナルでそのコードが実際に呼び出されているか確認します。**逆コンパイルしたコード内の呼び出し箇所と送信箇所を探し、デッドコードを記録しておくと、後の監査で未実装として数えずに済みます。Thaumcraft 4 には、一度も呼び出されなかった FX クラスが 7 個、送信されなかったパケットが 3 個含まれていました。
4. ユニットごとに移植する
各ユニット(ブロック、デバイス、モブ、メカニクス)は、同じ手順に従います。
- 元のクラスを読む。
- 移植する。
- 維持する癖を記録する。
- テストを書く。
- 実行する。
- ステータス文書にユニットを記録する。
- コミットする。
-
観察可能な癖やバグは維持し、 次のエージェントが「修正」しないよう、元のクラス名または行を示すコメントを付けます。Thaumcraft の移植での例:
- キーを押していると、元のキー処理がすべてのブロックに対して「処理済み」と応答したため、すべてのブロックの右クリックが止まります。
- オプションをオフにしていても、ボスにチャンピオンの修飾効果が付与されます。
- コンテナーアイテムを持つ素材は、アスペクトを追加しません。
ただし、クラッシュやクライアントの同期ずれは再現しないでください。それぞれのケースを列挙します。
-
強制的な変更(エンジンの制限や削除された API)にはすべて、その理由をステータス文書の「既知の変更」リストに記録します。
-
ログでは次の2つのラベルを使います。
- 「維持した癖」:意図的に維持した元のバグ。
- 「変更」:新しいエンジンでは再現できない、または再現すべきでないもの。必ず理由を添えます。 これにより、後から読む人が維持されたバグと移植時のバグを見分けられます。
-
元のコードは旧エンジンのコードを継承しています。 バニラのモブを継承するサブクラス、バニラの樹木生成器、旧エンジンの移動コードなどは、新しいエンジンのものとは異なります。旧エンジンの部分も移植し、新しいスーパークラスが追加する機能は無効にします(Minecraft では、1.20 のゾンビサブクラスにドラウンドへの変化が追加されます)。
-
記憶に基づくプラットフォームの挙動には印を付けます。 MOD の逆コンパイル結果には旧エンジンやローダー(Forge、バニラの 1.7.10)が含まれていません。記憶から補ったものには「未検証」と印を付けるか、それらも逆コンパイルしてください。
-
まだ存在しないコンテンツには代替手段を使います。 不足している依存関係には名前付きのフックまたはタグを用意し、保留中として列挙して、そのユニットが追加されたら接続します。
-
サーバーを権威あるものとして保ちます。 元の実装が信頼していたクライアントのリクエストを再検証します。また、元のクライアント側の事前チェックとメッセージも移植します。
-
忠実度とプレイしやすさのどちらを優先するかは、ユーザーが決めます。 新しい地形ではほとんど通らない地点チェック、新しいエコシステムでは不要になるフォールバック、元の範囲を超えるアートなどは、それぞれ未決定のユーザー判断事項として記録します。黙って決めないでください。
-
エンジンの概念を意識して対応付けます。 バージョン間でイベントの順序、ティック処理、メタデータ、レンダリング、ワールド生成はすべて変わっています。Minecraft の対応表は
references/minecraft-1-7-10-forge-to-1-20-fabric.mdにあります。作業を進めながら、使用するエンジン向けの対応表を作成してください。 -
記憶から移植しないでください。 Wiki と逆コンパイル結果が食い違う場合は、逆コンパイル結果を優先します。
5. オラクル:手軽なものから実環境のものへ
一般的な手法は knowledge/techniques/oracles-how-agents-know-a-mod-works.md にあります。移植時は、手軽なものから実環境のものへ、次の順で確認します。
- 実際のサーバーでのヘッドレステスト(Minecraft GameTests:
runGametest)でロジックを検証します。JUnit XML を解析し、コンソール出力だけを信用しないでください。セットアップと注意点については、knowledge/techniques/fabric-1-20-1-gametests-as-the-oracle-of-a-large-mod-port.mdを参照してください。- 失敗しうるテストを書きます。 誤った挙動で結果が変わる入力を選び、アサーションを書く前に元の実装の値を確認します。
- 各テストでは、自身の位置周辺にあるグローバル状態をクリーンアップします(テスト間で共有状態が漏れるためです)。
- クリック操作、画面、レンダリング、自然なワールド生成、2人目のプレイヤーを対象とするスクリプト化された実クライアント:
knowledge/techniques/a-scenario-harness-in-a-minecraft-fabric-dev-client-real-cli.md。- サーバーテストはアイテムのコードを直接呼び出します。移植で元のクライアント側の事前チェックとメッセージが失われたことは検出できません。
- 実際のサーバーでのリリース jar と、実際の Modpack の jar。 リマッピング、jar-in-jar、依存関係の競合は、そこでしか発生しません。
- 上記のどれでも確認できない内容は、人が実際にプレイして確認します。 依頼してください。確認したと主張してはいけません。
システムごとに、証拠のレベルを正直に報告します。
- 実装済み
- テスト済み
- 実クライアントで確認済み
- 未検証
実行していないテストについて「合格」と書いてはいけません。ホストやツールに接続できない場合は、実行できなかった内容を明記します。
6. レイヤーごとにオリジナルと照合して監査する
機能ごとに作業を進めると、単一のテストでは検出できない体系的なずれが生じます。
- 機能がそろったら、一度に1つのレイヤーを監査します。
- イベントハンドラー、パケット、アイテム、ブロック、エンティティ、GUI。
- ワールド生成、戦利品、研究とレシピ、設定。
- レンダラーはタイル、ブロック、エンティティ、アイテムに分けます。
- 各監査は読み取り専用のレビューとして実行します。 レイヤー内のオリジナルの各クラスと移植版を行単位で比較し、ファイル名と行番号を含む優先度順の指摘を返します。
- グループごとにレビュー担当を1人ずつ、複数人で並行して進めれば、1回でレイヤー全体を確認できます。
- 指摘事項を修正してから、対応するビジュアルシナリオを再実行します。
- Thaumcraft のレンダラー監査では、ゲームプレイテストがすべて成功した後に、約60件の差異が見つかりました。
- ベースまたはソースのバージョンを変更した後は、残存箇所を監査します。以前のバージョン名を grep で検索し、すべてのテクスチャとモデルをオリジナルの jar とハッシュ比較します。
- オリジナルがクライアント側で行っていたチェックを確認します。チャットメッセージを伴う拒否、ツールチップ、キャンセルされたスイングなどです。移植版ではサーバー側の再チェックだけが残り、何も表示されずに拒否されることがよくあります。
7. 移植作業を再開可能な状態に保つ
大規模な移植作業は、1回のセッションのコンテキストだけでは完了しません。リポジトリに進捗を残し、信頼する前にビルドします。Thaumcraft の移植では、セッション終了時にコードが一部だけ変換された状態で残っていました。旧ベースのメソッド呼び出しや古い enum 値が残っていました。毎セッションの開始時にビルドし、壊れた呼び出しはスタブ化せず、参照元に基づいて再移植します。
- ステータスドキュメント(
docs/MIGRATION_STATUS.md):- オリジナルのライセンスと、必要な場合は作者の許可。
- 固定したバージョンと適応マトリックス。
- ユニットごとの記録:移植した内容、オリジナルのクラス、維持した仕様上の癖、相違点、テスト。
- 既知のバグと相違点。
- 未検証だと分かっている範囲。
- 次に行う具体的なタスク。
- コミット数ではなく、証拠のレベルに基づく完了見積もり。
- 簡潔な引き継ぎドキュメント(
docs/HANDOFF.md):現在の状況、譲れない決定事項、コマンド、最新の実際のテスト結果、ユーザーへの未解決の質問。 - ソースの正本と検証ルールを記載したエージェント向け指示(
AGENTS.md)。 - ユニットごとに1つのコミットを作成し、毎セッション、プッシュします(公開可能な移植版でない限り、プライベートリモートにプッシュします)。一時的なクラウドコンテナでは、プッシュされていないものはすべて失われます。
- ビルドの前提条件を文書化します。 クラウド環境では、ローダーの Maven とゲームベンダーのダウンロードホストへのアクセスを許可する必要があります。Minecraft の場合は、
maven.fabricmc.net、piston-meta.mojang.com、piston-data.mojang.com、libraries.minecraft.net、resources.download.minecraft.netに加えて、各依存関係の Maven が必要です。これらが許可されていないと、最初の Gradle ビルドを実行できません。 - 作業中にフィールドノート(
docs/field-notes/)を記録します。各ノートには、証拠とともに1つの事実を記載します。最後にナレッジベースのノートになります。
8. 共有する
- 移植ごとにゲームノートを1つ作成します(
um kb new)。バージョン、移植方法、オリジナルのライセンス、移植版が公開されているかどうか、注意点を記載します。 - 他の MOD にも応用できる手法について、レンダリング状態のマッピング、テストハーネス、データ抽出などの技術ノートを作成します。
- share-field-notes スキルを参照します。PR を作成する前に、人間に確認します。
厳守事項(mod-any-game の規則に追加)
- オリジナルのライセンスで許可されていない限り、逆コンパイルした参照コードとオリジナルのアセットを同梱してはいけません。
- 他者の MOD の移植版を公開できるのは、そのライセンスで許可されている場合、または作者(もしくは現在の権利者)から許可を得ている場合に限ります。それ以外の場合は非公開のままにします。いずれの場合も、オリジナルの作者をクレジットします。
- 実装しやすいからという理由で、異なるバージョンの MOD の仕組みを取り込んではいけません。
- 実行していないテスト、ビルド、ビジュアルチェックを実施したと主張してはいけません。