Skip to main content
Glama
covalschi

dayz-agentic-modding-mcp

dayz-agentic-modding-mcp

エージェントがDayZ MODをビルドし、クライアントがコンパイルされることを確認し、 テストサーバーを起動して、構造化された判定結果をログの代わりに得られるようにするMCPサーバーです。

サーバーは処理を自ら行います。FileBank、署名ツール、診断実行ファイルを 直接呼び出します。プロジェクト側でビルドスクリプトを持つ必要はありません。

プロファイルの理由

サーバーは特定のMODについては何も知りません。具体的な内容はすべて 2つの部分からなるプロファイルに記述されます:

  • dayz-mcp.toml — 移植可能で、MODリポジトリにコミットされます:どのMODをパックするか、 正常なブートとはどのようなものか。

  • dayz-mcp.local.toml — マシン固有で、コミットされません:ゲーム、ツール、テストスタンドが どこにあるか。

2つの部分を混在させることは読み込み時に拒否されます。これにより、リポジトリが 他の誰かのマシン上でビルドされ続けることはありません。dayz-mcp.example.tomlから始めてください。

MODは名前で一度だけ宣言されます:ソースはデフォルトで<root>/Nameにあり、出力は @Name/addons/Name.pboです。build.sourcesでMODのソースを別の場所に リダイレクトできます(例:config.cppがリポジトリルート直下にあるMODの場合は".")。

移植可能な部分 — dayz-mcp.toml

キー

意味

project.name

このプロジェクトの呼び名

build.mods

MODごとに1エントリ。名前、ソース、PBO、@folderはすべてそこから導出されます

build.sources

MODのソースが実際にある場所。プロファイルからの相対パス("." = リポジトリルートそのもの)

build.exclude

絶対にパックしてはならないもの。これを指定するとデフォルトの除外リストが置き換えられます。デフォルトは.git*.blend*.blend1.gitignore.gitattributesREADME.md*.ps1です

build.stage

除外されたものが存在する場合に拒否する代わりに、フィルタリングしたコピーをパックする — ルートレイアウトのMODに必要なレイアウト

build.project_root

すべてのモデルパスが解決される基準ディレクトリ。このファイルからの相対パス。MODのプレフィックスフォルダを含んでいなければなりません。モデルツールにのみ必要で、他のツールには不要です — 「アセットパイプライン」を参照

build.pre_script

パック前に実行されるPowerShellスクリプト。コードを先に生成するプロジェクト向け

expect.ready_line

MODが読み込みを完了したときに出力する行 — 下記参照

expect.counters

その行に含まれるキー = 値のペア。数値として比較されます

expect.max_warnings

警告の許容上限。このキーを省略するとチェックは無効になります

expect.forbid

実行を失敗させる部分文字列。これ以外は問いません

expect.error_regex

あなたのMODに属するスクリプトエラーを示す正規表現。クライアントのコンパイルチェックで使用されます

expect.noise

組み込みリストに加えて無視するエンジンのノイズ出力

マシン固有の部分 — dayz-mcp.local.toml

キー

意味

machine.game

ゲームのインストール先。省略時は自動検出されます

machine.tools

DayZ Tools。省略時は自動検出されます

machine.blender

Blenderの実行ファイルasset_exportでのみ使用されます。省略時は自動検出され、他の何にも必要ありません

machine.stand_root

準備済みのテストスタンド。サーバーはここから起動し、ログは<stand_root>/profilesから読み取られます。デフォルトは<root>/testenvです

machine.config

スタンド内のサーバー設定ファイル名(デフォルトはserverDZ.cfg)。これは設定項目です。なぜなら、スタンドはワールド生成後にハングする設定と、別の名前で正常に動作する設定の両方を保持できるからです

machine.port

server_startがサーバーに渡すポート(デフォルトは2302

mods.required

MODフォルダ名。ゲーム自身の!Workshopフォルダの下で解決されます — required = ["@CF"]<game>/!Workshop/@CFを意味します

mods.extra

!Workshopに存在しない、読み込むその他のもののフルパス

mods.server_only

-modではなく-serverModにルーティングするフォルダ名。照合は、読み込まれるすべてのMOD(mods.requiredmods.extra、プロジェクト自身の@Nameフォルダ)のフォルダ名に対して行われます。リストにないものはすべて-modに送られます。診断クライアントはサーバー専用MODを読み込まないため、クライアントのコンパイルチェックではそれらは除外されます

レディライン

server_startは、expect.ready_lineが、そのブート中に書き込まれたログに 現れたときに完了します。これはサーバー自身では判断できない唯一の事項であり、 これがない場合、2つのことが変わります:何も待機できないため、ブートジョブは サーバーを起動し、少し後にまだ生きていることを確認して、完了を報告します。 また、log_verdictにはカウンターを読み取る行がないため、expect.countersは 決して一致しません。エラー、警告上限、クラッシュは依然として判定されます。 レディラインのないプロファイルもサポートされています — 壊れているわけではありません。project_openが そのメモに記載しています。

Related MCP server: DayZ API MCP Server

ツール

ツール

機能

project_open(path)

プロファイルを読み、ゲームとツールを検出し、不足しているものを報告する

project_status()

現在のプロジェクト、実行中のサーバー、最近のジョブ

mod_build()

宣言されたすべてのmodをパックして署名し、ジョブIDを返す。同じプロジェクトのビルドが実行中の間は、2回目のビルドを拒否する

server_start(timeout)

テストサーバーを起動し、準備ができたら完了する。設定が指定するミッションが <game>/mpmissions 以下にない場合は拒否する — エンジンは実行中の実行ファイルの隣でミッションを探すのであって、-config の隣ではない。pid を即座に返す — プロセスは呼び出しが返る前に生成されるため、直後のツールはすでに実行中のサーバーを認識する。ゲームポートが他の誰かに既に占有されている場合は拒否し、イメージを起動できない場合はその場で拒否する。準備完了は、expect.ready_line が宣言されている場合はそこから得られ、それ以外の場合は2つのエンジンシグナル — ポートのバインド完了とミッションモジュールのコンパイル完了 — の両方から得られる。ポートはスクリプトより約17秒早くバインドされる。その間に準備完了と判定された起動は、ミッションなしで待ち受けることになり、クエリには応答するがすべてのプレイヤーを拒否する。ジョブの要約はどちらであるかを示す

server_status(pulse_seconds)

pid、プロセスが生存しているかどうか、ログが増加しているかどうか(pulse_seconds 間隔でサンプリング)、そして停止してからの時間

server_stop(pid)

このセッションが起動したサーバーを停止する(孤児サーバーにはオプションの pid)

server_signatures(value)

スタンドの verifySignatures を読み取る — または意図的に変更する。引数なしの場合は報告のみ行う。プロファイルがこのスタンドのものとして指定する設定のみを編集する。machine.stand_root の外に解決されるものは拒否し、その設定に対してサーバーが実行中の場合は拒否する。ファイルのコメントと改行コードを保持し、その後ファイルから値を読み戻す

client_compile_check(extra_mods, wait_seconds)

診断用クライアントを実行し、そのログを読む

log_verdict(source, since)

理由付きの合格/不合格:カウンタ、禁止文字列、警告予算。source"server"(スタンド内の最新ログ)または "client"(最新の client_compile_check が生成したログ)。since(エポックタイムスタンプ。例:server_start が返す since)は、判定対象の実行より前に書き込まれたログを拒否する。これにより、以前の起動の古いログが今回の結果と誤認されることはない

log_tail(source, pattern, n)

最後の行を返す。オプションでフィルタリング。ソースは同じ2つ

job_status(job_id)

長時間実行ジョブのステータス

job_wait(job_id, timeout)

ジョブが完了するまで待つ

job_artifacts(job_id)

完了したジョブから出力を取得する

bridge_build()

ブリッジmodをパックする。そのソースはあなたのプロジェクトではなく、このサーバーに同梱されている(bridge/)。ジョブIDを返す。未署名でビルドされる — 後述参照

bridge_status(window)

実行中のゲーム内のブリッジがまだ動作しているかを確認する。ティック番号と、window 秒の間に進行したかどうかを報告する。実際に移動したティック、またはサンプリング中に再起動したスタンドに対してのみ成功する

bridge_clear(force, probe_window)

メールボックスに詰まったコマンドを破棄し、何を捨てたかを示す。ブリッジが生存しているように見える間は force=True でない限り拒否する

world_ready(timeout)

ゲーム内のブリッジが実際にコマンドを受け付けるまで待つ。起動ジョブの完了後、最初のワールドコマンドの前に1回呼び出すこと — 下記の「サーバーレディはブリッジレディではない」を参照

world_state(class_name, radius, pos)

ブリッジが毎秒公開するワールドのスナップショット:プレイヤー、位置、体力、手に持っているもの。引数なしならコストなし。class_name を指定すると、近くにあるそのクラスのオブジェクトも数える(コマンド往復1回分)

world_spawn(class_name, where, pos, quantity)

地面(ライフタイムなしなので、チェック中に消えることはない)、プレイヤーの手の中、またはインベントリ内にアイテムを生成する

world_teleport(pos)

プレイヤーを "x y z" に移動させる。これは world_state が報告するのと同じ形式なので、読み取った位置をそのまま渡せる

world_set(what, value, target)

health(プレイヤーまたは手持ちアイテム)または quantity(手持ちアイテム)を設定する

world_delete(class_name, radius, pos)

近くにある1つのクラスのオブジェクトを削除する。クラスの指定が必要。実際のプレイヤーを削除することは決してない

world_entities(class_name, radius, pos, limit)

近くにどのオブジェクトがあるか。数ではなく、それぞれのクラス、位置、距離、体力を返す。1ページ分の結果で、その旨も示される。実際の合計はリストとともに返る

world_time_set(hour, minute, day, month, year)

ワールドの時計を進める。-1 のままにしたすべてのフィールドは現在の値を保持する。エンジンは日付を5つの数値として一度に設定するため、現在の値はまずエンジンから読み戻される

world_weather_set(what, value, seconds, duration)

曇り、雨、霧、降雪、または風を変化させる。ロックではなく微調整:その後もエンジンは天候をシミュレートし続け、その旨はツールとMODの両方が明示する

world_action(action_class, target_class, subject, radius, pos)

エンジンのゲートを介してMOD独自のアクションを実行する — 後述参照

world_exec(verb, args)

逃げ道:同じトランスポートを介して任意のverbを実行する。すべての応答で非標準とマークされる

client_start(timeout, extra_args)

ゲームクライアントを起動してスタンドに接続し、ジョブIDを返す。常にウィンドウモード。ブリッジが players >= 1 を報告した時点で完了する — タイマーではなく数の条件

| client_status() | クライアントのPID、ウィンドウが最小化されているか最前面にあるか、背景設定、仮想コントローラが接続されているかどうか | | client_stop() | このセッションが起動したクライアントを停止し、仮想コントローラを取り外す | | client_shot(path) | クライアントのウィンドウをPNGにキャプチャする。lit_fraction は実際のフレームと全面黒のフレームを区別する数値。フォーカスは不要 | | client_move(x, y, seconds) | 左スティックでキャラクターを歩かせる。アナログ操作であり、キャラクターを動かせる唯一のツール。フォーカスは不要 | | client_turn(x, y, seconds) | 右スティックでカメラを回転させる。フォーカス不要 | | client_press(button, seconds) | 14個の名前からなる固定リストからゲームパッドのボタンを1つ押す。フォーカス不要 | | client_chat(text, color) | チャットに1行送る — ブリッジがサーバー側で配信するため、キーボードもウィンドウもフォーカスも不要 | | client_type(text, submit) | 実際のキー入力でクライアント側の入力フィールドに文字を打ち込む。ここで唯一フォアグラウンドを必要とするツールで、その旨が応答に記載される | | client_verdict(since) | ライブのクライアントを、それ自身の .RPT によって判定する — エラーとクラッシュの判定。後述 | | ui_menu() | クライアントのUIの状態(開いているメニュークラス、カーソル、ダイアログ)を返す。無料 — 毎ティック再公開される | | ui_tree(root, depth, limit) | クライアントのウィジェットツリー: パス、クラス、可視性、画面の矩形、深さ、テキスト。1ページ分であり、その旨が明記される | | ui_find(name, class_name, text, root) | 同じ走査をクライアント側でフィルタするため、ツリー全体を転送する必要がない | | ui_click(path, expect_name, expect_class, via) | ウィジェットを押す。via="script" はフォーカスなしで開いているメニューのハンドラを通る。via="cursor" は実際のマウスをその矩形に置く | | ui_text(path, text, expect) | 編集ボックスに書き込み、その値をウィジェットから読み戻す | | mod_lint(mod, strict) | パックや起動をせずに Enforce Script を判定する。mod_build はそれを最初に実行し、拒否した場合は拒否する | | mod_build(mod, source, deploy) | モッドのスクリプトを検証し、パックしてから配置する。Job id を返す。検証は mod_lint を呼び出し、それが拒否した内容を拒否する | | mod_status(mod) | モッドのビルド状態の概要: どのソースが読み込まれ、どの出力が配置されているか、さらに検証結果 | | server_start() | ブート、進行状況、レポート、利用可能なプラットフォームとセッションの一覧を返す | | server_stop() | ブート済みのサーバを停止し、プロファイルディレクトリをロックする | | server_console(command) | サーバコンソールと同じコマンドを発行し、その応答を返す | | server_message(text, announce) | ゲーム内メッセージをブロードキャストする(rcon/say 形式) | | world_ready() | サーバープロセス、ログ、ブリッジがそれぞれの準備完了状態にあることを確認する | | world_tick() | ワールドが進行中であることを確認する(ライブスタンドのティック変位、約2秒) | | log_verdict(range, offset, level, match) | 最近のサーバーログを確認し、クラッシュ、スタック、および関連メッセージの判定を返す。オプションで指定したコンテキスト行数を含む | | log_msg(needle, since, limit) | サーバーログからメッセージを検索する。オプションの after でブリッジが停止した箇所から再開できる | | server_tasks() | 実行中のバックグラウンドタスクを一覧表示する。どのサーバーにも同じ key がある | | server_wait(signal, timeout, raise) | セマフォを待つ。timeout=0 で無限待機し、raise=false なら、呼び出し側の処理を中断する代わりに "timeout" を返す | | server_notify(name) | セマフォを設定または解除する | | mod_pack(mod, options) | リポジトリ内の mod を Arma 3 pbo にパックする | | mod_lint(mod, strict) | パックやブートをせずに mod を検査する。厳密モードは警告をエラーとして扱う | | mod_diagnose(mod) | サーバー起動時に mod がクラッシュを起こす理由を検査する。ログの検索と差分比較を実施する | | mod_apply(mod) | 設定を再生成し、mod をパックして、サーバーを再起動する | | mod_repo(mod) | mods/ 配下のファイルのみを対象に、mod のリポジトリ内ファイルを一覧表示する | | mod_settings(mod) | サーバーの -config ファイルから mod の設定値(classvaluetype)を読み取る | | mod_set(mod, key, value, type) | -config ファイル内の設定値を変更する。復元は type=auto で行う | | bridge_build() | リポジトリ内の bridge/@DZMCP_Bridge にパックする。リポジトリと keys/ フォルダの外には何も書き込まれない | | bridge_status() | ブリッジの状態を判定する。応答にはtick番号と前回サンプルからの経過セッションが常に含まれる | | bridge_clear(force) | ブリッジのコマンドキューをクリアする。ブリッジがアクティブな間は force=false では拒否される | | bridge_wait(signal, timeout) | ブリッジがコミット状態を報告するまで待機する。デフォルトは timeout=30 で、poll 間隔は約0.2秒。ブリッジがダウンしている場合は即座に失敗する | | log_ship(path, lines) | サーバーまたはクライアントのログから直近の行を抽出する。パスが空の場合は自動検出される | | log_verdict(mod) | 利用可能なすべてのログソースをクロスチェックし、ブート失敗の診断結果を返す | | log_crash(mod) | Windows イベントログとプロファイルディレクトリ内の .mdmp ファイルを検索する | | pbo_list(path) | Arma 3 PBO 形式のアーカイブを一覧表示する | | pbo_extract(path, files) | PBO からファイルのサブセットを抽出する。パスは常に / セパレータで、mod-relative かつ / で始まる形式 | | debug_mcp_whitelist(mod) | admin.add の現在の値を返す。これは「MCP ブリッジが誰を信頼するか」のサーバー側の真実である | | debug_mcp_grant(mod) | admin.add を一時的に設定する。MCP管理下の mod にのみ作用し、ブリッジが再起動時に再適用する |

[mods]
extra       = ["<path printed by bridge_build>/@DZMCP_Bridge"]
server_only = ["@DZMCP_Bridge"]

「サンプルを手渡す」のような意味論的な動詞は嘘をつく。実際のMODでは、同じ言葉が、近くにあるデバイス、プレイヤーの派閥、すでにアンロックされているものによって異なる意味を持つ。その文脈は列挙不可能なので、ブリッジはそれを試みない。world_action はアクションのクラス名、ターゲット、保持中のアイテムを受け取り、エンジンにそれ自身のゲート——キー押下が通るのと同じゲート——を通して実行するよう依頼する。適用可能性はアクション自身の Can() が決定し、その拒否はツールの失敗ではなく、意味のあるテスト結果である。区別可能な応答は次のとおり: マネージャーがビジー、プレイヤーがすでに行動中、プレイヤーがスプリント中、未知のアクションクラス、そして「アクション自身の Can() がノーと言った」。 「受理」も成功ではない——エンジンが実際にアクションを解放するまでコマンドは実行中のままであり、すべての失敗経路はマネージャーを解放するため、プレイヤーはその後も行動できる。

world_exec は逃げ口である

MODが公開するものでアクションではないもの——「派閥プールに何ポイントあるか」——は world_exec(verb, args) を通る: 同じトランスポート上の任意の動詞である。すべての応答は non_standard とマークされる: このサーバーはその動詞を知らず、検証もせず、MODがそれで何をするかについても答えない。ブリッジのビルドが知らない動詞は、それが知っている動詞のリストを返す。独自の動詞を必要とするプロジェクトは、ブリッジのディスパッチャーの自身のコピーを編集する(bridge/scripts/5_MissionKnownVerbs() の上のコメントが正確な場所を示している)。意図的に登録機構はない——このサーバーがタイプし検証した動詞は、このサーバーが責任を持つ動詞になるからだ。

クライアント: 3つの入力層、そしてなぜ3つなのか

ブリッジはサーバーに到達する。しかしクライアントの画面を見たり、クライアントを通じて行動したり——キャラクターを地面の上を歩かせたり、メニューを開いたり、MODが描いたフィールドを埋めたり——することはできない。client_* ツールはそれを担い、3つの異なる経路を使用する。なぜなら、どれ1つとして他の2つの仕事をこなせないからだ。以下の各行は、設計意図ではなく、生きたクライアントに対する測定値である。

経路

機能

フォアグラウンドが必要

ブリッジ (world_*client_chat)

ワールド、およびチャットへのテキスト

いいえ

仮想ゲームパッド、ViGEmBus (client_move / look / press)

移動、カメラ、および一部のインターフェース

いいえ

実際のキーストローク、SendInput (client_type)

クライアントにのみ存在するフィールドへのテキスト

はい、そしてそれを奪う

キーボードエミュレーションはキャラクターを動かさず、ウィンドウメッセージはまったく何もしない。 フォアグラウンドを確認した状態での SendInput スキャンコード: 前方へ25秒、0 m。メインウィンドウとその子への PostMessage/SendMessage WM_KEYDOWN: 0 m、メニューからの反応もない。エンジンは生の入力から移動を読み取り、エミュレートされたキーを無視する。これが、ここでどのツールもウィンドウメッセージを提供しない理由である。

仮想ゲームパッドは、フォーカスなしでそれを動かし、しかもアナログである——キーで足りる場合でもそれが残る理由。 サードパーティ製アプリケーションがフォアグラウンドを終始保持した状態での1回の実行で測定:

stick fully forward,   10.0 s  ->  38.40 m   (3.84 m/s)
stick at 0.3 forward,   8.0 s  ->  11.34 m   (1.42 m/s)

同じ経路、同じキャラクターで、スティックの倒し込みだけで速度が2.7倍。 「キャラクターが歩いている、走っていない」は、オンとオフしか知らないキーでは表現できない。同じ実行で、キャラクターは自発的に約141 m歩き、MOD自身の10 m以内のオブジェクト数は、その場を離れて戻ってくるにつれて 1 → 0 → 1 と変化した——これは存在によって引き起こされた状態変化であり、テレポートでは生み出せない。

インターフェースの一部はパッドに応答し、一部は応答しない。 ゲームウィンドウが終始別のアプリケーションの背後にある状態で測定: back はインベントリを開閉し、start はポーズメニューを開き、b はそれを閉じる——すべてデフォルトの0.1秒タップで、つまりタップはエンジンがラッチするのに十分な長さである。しかし a は0.1秒でも0.5秒でも何も動かさず、それらの画面内でのdパッドも同様だった: クライアントはコントローラーナビゲーションモードに切り替わらなかったため、確定ボタンが作用するフォーカスされた要素がなかった。メニューの閉じるはゲームパッドの仕事として扱い、メニューの確定は未証明として扱え。

目もフォーカスを必要としない。 キャプチャは、ウィンドウがzオーダーの最下部にある状態でのライブフレームである(同じセッションで、フォーカスなし lit_fraction 0.9997、フォーカスあり 0.9997)。それらを無効にする唯一の状態は最小化されたウィンドウであり、そのクライアント領域は0×0に潰れる——有効に見える空の画像として保存されるのではなく、理由付きで拒否される。

そのバックグラウンド動作のすべては、1つのクライアント設定 pauseMode (GAME → UPDATE IN BACKGROUND) に依存している。 ここで測定された値では、クライアントはフォーカスがない間も描画とシミュレーションを続ける。これが、フレームがライブであり、スティックが依然としてキャラクターを動かす理由である。「グラフィックスなし」では、両方とも静かに停止する——凍結したフレームはライブのフレームとまったく同じに見える。したがって client_startclient_status はその設定を読み取り、警告する。決して書き込まない。なぜなら、それはマシンを所有する人のものだからだ。

client_type は画面を奪う唯一のツールであり、それについて正直である: 応答には foreground_taken と、実行中はそのマシンの前にいる人が自分のウィンドウに入力できなかったという一文が含まれる。SetForegroundWindow はWindowsが拒否したときに何もせずに成功を返すため——そして盲目的なタイピングは、その人が実際に使用しているウィンドウにキーストロークを送ってしまうため——要求後に GetForegroundWindow でフォアグラウンドを検証する。フォアグラウンドを取得できない場合、何もタイプされず、拒否はそれを保持しているプロセスを名指しする。

ViGEm は実デバイスのエミュレーションであり、これはテストスタンドである。 ドライバーは署名済みで再起動なしでインストールされ、ゲームパッドはマシン自身のキーボードとマウスに対するフィルターではなく新しいデバイスである——フィルタードライバーはかつてここで試され、手で巻き戻されるまでマシンの所有者のすべてのキーボードとマウス入力を奪った。そのどれも、ライブサーバー上のアンチチートについての約束ではなく、このフェーズでは何も約束しない。

知識インデックス

MODを書くエージェントは、ゲームに同じ質問を繰り返し尋ねる: そのようなAPIはあるか、何と呼ばれるか、どこで宣言されるか、誰がそれをオーバーライドするか。それらに答えることは、scripts.pbo を展開してテキストをくまなく調べることを意味し——そして毎セッションで再びコストを払った。knowledge_* はその作業を質問に変える。

これは、プロジェクト自身の .dayz-mcp/ にあるプレーンなSQLiteファイルであり、このサーバーがゲーム、プロジェクトが宣言するMOD、プロジェクト自身のソースから構築する。埋め込みなし、外部サービスなし、キーなし。

3つの層、そしてなぜそれらのリズムが異なるのか

ソース

陳腐化するタイミング

core

ゲーム: API用の dta/scripts.pbo、アイテムクラス用の Addons/*.pbo

ゲームが更新されたとき

deps

プロファイルが宣言するMODのアーカイブ、展開せずに読み取られる

依存関係が更新されたとき、または宣言されたセットが変更されたとき

project

MOD自身のソース、置かれている場所で読み取られる

編集のたび

1回で構築された1つのインデックスは、正しくなってから1分以内に間違ったものになるだろう: ゲームは年に数回動き、依存関係は月に数回動き、プロジェクトはエージェントの1ターンと次のターンの間に動く。したがって、各層は独自に構築、熟成、測定され、すべてのビルドは増分である——変更されていないソースはサイズと変更時刻でスキップされ、only=[path] はそれらを発見するウォーク自体もスキップする。

応答は、それが由来する層の古さを運ぶ

陳腐化は推測ではなく測定される: 層は、読み取ったすべてのソースのサイズと変更時刻を記録し、それが現在のファイルと比較される。

  • すべての応答は、使用した層と各層の古さを名指しする。結果がない応答は、検索したすべての層を名指しする——「見つからない」は、その背後にある層が最新であるのとまったく同じだけの価値がある。

  • プロジェクト層の鮮度は、寄与したかどうかに関係なく、すべての検索で測定される。それが危険なケースである: エージェントがクラスを追加し、それについて尋ね、1分前に構築された層が「見つからない」と言う——存在するコードについての自信に満ちた発言。

  • 構築されたことのない層に対する検索は拒否され、拒否はそれを構築する呼び出しを名指しする。「見つからない」と「調べていない」は異なる事実であり、そのうちの1つだけが行動しても安全である。

  • 絞り込みは同じ罠を1レベル下に運ぶため、空の絞り込み応答は、名前が実際に存在する場所を報告する: ゲームがconfigでのみ宣言する名前について kind='class' と尋ねると、「ゲームにはそのようなクラスがない」と読める真の「ノー」が得られる。

Configクラスは kind='config' の下にあり、kind='class' ではない。このマシン自身のゲームのインデックスで数えた: あらゆる種類のスクリプト宣言43 595件に対して88 102のconfigクラス。したがって、1つの種類に混ぜると、すべてのスクリプト応答を埋没させる。分離すれば、「ゲームにXというアイテムクラスがあるか」は正確に尋ねられる質問になる。

それが答えないこと

インデックスは何が存在するかに答える: クラス、メソッド、シグネチャ、宣言場所、誰がオーバーライドするか。何が正しいかには答えない——modded class X extends X がコンパイルされて静かに適用に失敗すること、_co がアルファチャンネルを犠牲にすること、binarize がファイルではなくディレクトリを受け取ること。そのどれもソースから導出可能ではなく、苦労して学ばれ、MOD作成スキルとMOD自体に生きている。インデックスはどちらかを置き換えようとはせず、フィールドが何を意味するか、クラスがなぜそこにあるかを理解しようともしない。

セマンティック検索は意図的にここにない

その決定は慎重さではなく測定によってなされた: このサーバーの初期フェーズを形作ったすべてのルックアップは名前によるルックアップだった。そして埋め込みインデックスは、このサーバーの残りが守る規則を破るだろう——インストールすれば動作し、外部サービスもキーもない。このプロジェクトが意図的に基盤としなかった前身プロジェクトは、その知識層をローカルで無料と文書化している一方で、そのコードは有料の埋め込みクライアントをインポートし、キーなしでは失敗し、ハードコードされた価格を運ぶ。その2つの検索ツールも、背後にあるクライアントがタイムアウトなしで作成されたため、永遠にハングする。したがって、ここでのすべての検索が実行される上限がある。正確な検索で不十分であることが判明した場合、セマンティック検索は1つの条件付きの別フェーズである: モデルが配信物の内部に同梱されること。

測定された数値

このマシン上で——2810のスクリプトファイル、35のインストール済みMOD、41ソースの1つの実際のプロジェクトを持つゲーム——内部ではなくツールを通して:

ビルド

結果

時間

core

2927ソース、131 697宣言(41は何も生み出さなかった)

70.2秒

deps、宣言された4つのMOD

8アーカイブ、10 925宣言

0.9秒

deps、ここにインストールされたすべてのMOD

523アーカイブ、204 768宣言、3アーカイブは読み取り不能で名前が挙げられた

139–147秒

project

41ソース、1196宣言

0.12秒

ディスク上のインデックス: 実際のプロジェクトの3層で74.7 MB。523の依存関係アーカイブだけでは110 MB。それらのアーカイブは92 GBであり、どれも展開されていない。

コールサイトこそがインデックスの代償である。 ゲーム自身のスクリプトには43 579件の宣言と113 703件のコールサイトがあり、後者の集合を記録するとインデックスはほぼ倍になる。ゲームレイヤー単体で測定すると、それらなしの構築は23.8 MB・3.7秒、ありでは49.6 MB・4.5秒。これが「誰がこれを呼んでいるか」に答えられるための代償であり、ディスクが満杯になってから発見されるのではなく、ここに明記されている。

回答

時間

knowledge_find、完全一致名

エンドツーエンドで4.2 ms、うち3.0 msはプロジェクト走査

knowledge_find、プレフィックス、limit 500

クエリ3.2 ms

knowledge_overrides

4.2 ms

knowledge_callers、113 703件中23件のコールサイト

クエリ0.38 ms

76ファイルのmodに対するmod_lint

テキスト検査277 ms、インデックス検査7 ms

実機スタンドで3回起動して測定: world_time_set(hour=3, minute=7) は時計を2026-09-20 03:07に動かし、日付はそのままだった。world_weather_set("fog", 0.9, seconds=2) は公開されているフォグ値を0.085から0.900に変えて保持した。world_entities(pos="7500 0 7500", radius=150, limit=5) は171オブジェクト中5件をtruncated: true付きで列挙した。距離は水平距離に修正されるまで150 m半径に対して320 mで返ってきたが、これはエンジン自身の半径テストが測定するものと同じである。

knowledge_show、400メンバーとその祖先を持つクラス

6.8 ms

knowledge_status、3レイヤーすべてを測定

41 ms(ビルド後の最初の呼び出しでは110 ms)

実プロジェクトでのインクリメンタル性: フルリビルド136 ms。走査で見つかった編集済みファイル1件8.8 ms(15倍)。同じファイルをonly=で指定した場合5.8 ms(23倍)。2810ファイルのツリーでは走査が支配的でonly=の価値ははるかに大きい——しかしこの規模のプロジェクトでは、通常のリビルドが実際に得られるのは15倍である。

天井は現実に効く: 77 msと測定されたクエリを19.3 msの天井の下で実行すると19.9 msで停止され、接続は応答し続けた。

アセットパイプライン

Blenderからモデルをmodに取り込むのは10ステップで、このフェーズまではすべて手作業で実行されていた。価値はツールを起動することではない。このチェーン内のすべてのツールが構造的に失敗を報告できないことであり、その沈黙のひとつひとつがすでに数日を費やしていた。

実バイナリで測定したものであり、推測ではない:

何が起きたか

ツールが返したもの

binarizeにディレクトリを期待する場所へファイルを渡した

0、空の出力ディレクトリ、テキスト1行もなし

読み込みに失敗したマテリアルを持つbinarize

0、正しいビルドが58,644バイトのところ、46,190バイトのODOL

すでにバイナライズ済みのモデルをbinarizeに渡した

0xC0000005と、出力ディレクトリ内のゼロ長ファイル(既存のものの上に)

独自のデフォルト引数を持つBlenderエクスポーター

FINISHED、終了コード0、モデルの5つのLODのうち2つを運ぶ有効なMLOD、169行のログにその言及なし

したがって、この名前空間全体が基づくルールは次のとおり: 判定はアーティファクトから読み取るのであり、ツールの報告からではない。 終了コードは記録されるが、どちらの方向でも信じられない。

ルートは宣言されるのであって、推測されない

binarizeにはプロジェクトルートオプションがまったく存在しない——完全なスイッチ一覧は実バイナリに対して列挙された。ルートはプロセスの作業ディレクトリである。同じコマンド、同じ入力、異なるディレクトリ、そして出力されるのは、もっともらしいテクスチャパスを持つ有効なODOLであり、エンジンはそれをテクスチャなしでレンダリングする。成功コード付きで、何の不満もなく。エクスポート用のBlenderアドオンも、独自のプリファレンスに同じルートを持ち、最後に開いていたプロジェクトから記憶されている。この開発マシンでは、無関係なセッションのディレクトリを指しており、間違ったルートに対してもアドオンは失敗しない——ドライブ文字を削除し、残りを保持し、パスのように見えるパスを書き出す。

build.project_root はそのディレクトリであり、プロファイルのポータブルな半分に一度だけ記述される。サーバーはそれをバイナライザーの作業ディレクトリとして設定し、実行中はアドオンにプッシュする。したがって、アドオンが保存しているものは何も決定しない(報告されるので、修正しに行ける)。これこそが、間違ったルートを「検出可能」ではなく不可能にするものであり、モデル関連の何かが実行される前にこのキーが必須である理由である。

それが生み出す拒否は、プロセスが存在する前に発生する——0.0003秒と測定——そして拒否されたビルドは、modがすでに配布しているモデルをバイト単位で無傷のまま残す。

アーティファクトに対する12のチェック、うち4つは拒否

asset_check は何もビルドせず、DayZ Toolsもなしでそれらを実行する。なぜなら、新しいクローンが自分が配布しているものが健全かどうかを問い合わせられる必要があるからだ。4つは拒否する: ビルド済みモデルが存在しODOLであること(C1)、参照がmodから逃げ出さないこと(C3)、マテリアルが実際にインライン化されたこと(C4)、すでにバイナライズされたものをbinarizeに戻さないこと(C10)。残りは警告する: ぶら下がった参照、別のmodを指すrvmat、DXT1で失われた透明度(C7)、アーティファクトに到達しなかったアニメーション、アーティファクトがビルドされた元ではないmodel.cfg、最後のビルドがデプロイしたものと一致しなくなった構造フィンガープリント。すべての指摘は何をすべきかを言う。

C4は知っておく価値がある。binarizeがrvmatを解決するとき、そのマテリアル自身のステージテクスチャをモデルにコピーする——fresnel#(argb,8,8,3)env_land_co.paa_nohq_smdi——MLODが含まない文字列だ。6つのアーティファクトのうち6つがこの1つのテストで正しく分離され、このマシンで誰も知らなかった壊れたモデルを発見した。

Blenderステップは任意である

asset_export はここでBlenderを必要とする唯一のツールであり、下流のすべてはどこからの.p3dでも動作する——手動エクスポート、パートナーのファイル、何年も前にコミットされたモデル。Blenderのないマシンでもmodを完全にビルドして配布できる。拒否はそれを壊れたインストールとして提示するのではなく、そのように言う。見つけたBlenderでエクスポート用アドオンが有効になっている必要があり、Blenderのユーザープリファレンスを書き戻すことは決してない(検証済み: プリファレンスファイルは毎回の実行後にバイト単位で同一だった)。

エクスポートとビルドは1つの呼び出しではなく2つである。なぜなら、それぞれの半分に独自の判定があり、一方が拒否し他方が許可するビルドは決定ではないからだ。

バイト等価性は決して約束されない

このパイプラインのどちらの半分も再現可能ではなく、設計はそれを装うのではなく明言する:

  • エクスポート。 1つの変更されていないソースファイルの7回のエクスポート——1つのセッションから3回、別のセッションから3回、そして数ヶ月前にGUIで手動で行った1回——は、一定の334,032バイトで7つの異なるSHA-256を与えた。違いは1つの内部ブロックの順序である。

  • binarize 1つの変更されていない入力に対する4回の実行は3つの異なる結果を与えた: サイズは5バイト動き、2つの8バイト断片が圧縮領域から漏れ出した。

したがって、モデルはコンテンツハッシュでキャッシュまたは比較されることは決してない。比較されるのは構造フィンガープリント——ファイルの種類、LOD数、名前の集合——である。それらの7つのエクスポートすべてにわたって、そのフィンガープリントは1つの値だった。

測定された数値

このマシンで、ツールを通した1つの小さなモデル:

ステップ

結果

時間

asset_export

MLOD、334,032 B、5 LOD、クリーン

2.1秒(コールドスタートでは約8秒)

asset_build

ODOL v55、58,646 B、4 LOD、C4マーカー5つすべて

43.8秒(以前の4回の実行では75.6〜78.7秒と測定)

asset_check

1モデルと10テクスチャペアを判定

ミリ秒

asset_convert

PNG 1枚をDXT1へ、50,764 B

0.52秒

間違ったルートでの拒否

プロセスが開始される前

0.0003秒

両方のログはほぼすべて定型文であり、ミュートされたものは破棄されるのではなく数えられる: Blenderの169行は4に、binarizeの91行は6に——その6つのうち1つはモデルの唯一の正当な不満である。

エンドツーエンドで連結すると、エクスポートとビルドは数ヶ月前に手作業で作られたモデルを再現した: 同じ種類、同じ4つのLOD、同じ50の文字列、そしてサイズは1バイト違い。

それのどれも答えないこと

モデルが正しく見えるか、正しくスケーリングされているか、正しく巻かれているか、コリジョンがあるか。ゲームの外では何もそれに答えない。C1〜C12はそこへの道を短くする。それらは道を置き換えない。

既知の制限

  • stale-pbo の検出はコンテンツベースではなく mtime ベースです。 mod_build は、ソースより古い新しくビルドされた pbo を拒否します。通常の原因は、古いファイルを開いたまま保持しているサーバーが実行中であることで、パッキングは黙って何も生成しません。しかし git checkout はファイルの内容を変えずに変更時刻を変えるため、ブランチを切り替えた直後にビルドされた完全に正常な pbo もこのチェックに引っかかることがあります。ブランチ切り替え直後に mod_build が「stale pbo」を報告した場合、これは実際のパッキング失敗ではなく、この可能性が高いです。再ビルドすれば通ります。この分野の成熟したツールは、まさにこの理由でコンテンツハッシュに移行しました。これはここでの将来の作業であり、このフェーズでは行われていません (packer.pypack_one を参照)。

  • mod ソースフォルダーは全体がパッキングされます。 mod_build は、ソースディレクトリに build.exclude に一致するもの(7 パターンのデフォルトは上記に記載)が含まれている mod を、公開された pbo 内に黙って出荷するのではなく、パッキングを拒否します。ソースにこのサーバー自身の成果物(署名キー、プロファイルのどちらか半分、ジョブストア、mod の以前のビルド)が含まれている場合、build.exclude に関係なく拒否します。なぜなら、それらをパッキングすると秘密の署名キーが公開されることになり、どのプロジェクトもそれを設定で除外する必要があるべきではないからです。デフォルトでは、最初にフィルタリングされたコピーをステージングしません。コピーは常にソースより新しいため、もしそのチェックがコピーを測定していたら、上記の stale-pbo チェックを恒久的に無効化することになります。build.stage = true は、とにかくコピーすることを選択します。stale-pbo の比較が常に元のソースツリーを測定し、コピーを決して測定しない場合にのみ安全です。これは、ソースがリポジトリのルートである mod が必要とするレイアウトです(常に少なくとも .git が含まれます)。

  • 判定は、あなたの mod の行だけでなく、ログ全体を判断します。 log_verdict は、それが指すスタンドのログを読み取るため、他の mod と共有されるスタンドは、その警告をあなたの expect.max_warnings の予算に数え、そのエラーを理由として数えます。1 つの machine.stand_root を共有する 2 つのプロジェクトは、お互いのベースラインを見ることになります。各プロジェクトに独自のスタンドを与えるか、他に何がロードされているかを知った上で予算を設定してください。expect.error_regex と対称的なプロジェクトスコープのフィルターが明らかな改良であり、実装されていません。

  • expect.noise は、すでにエラーとして数えられている行を救うことはできません。 分類は forbid → クラッシュ → エラー → ノイズ → 警告の順序であり、ERROR または FATAL(または forbid 文字列の 1 つ)を含む行は、ノイズが参照される前に決定されます。その順序は意図的です。ノイズが最初に一致すると、無害な部分文字列が致命的な行を飲み込む可能性があります。しかし、それは noise が警告と通常の行のみを抑制でき、エラーレベルの行を決して降格できないことを意味します。

  • client_verdict は、準備完了の判定ではなく、エラーとクラッシュの判定です。 [expect]サーバー のログを記述します。その準備完了行とカウンターは mod のサーバーサイドの init によって出力され、max_warnings は同じログに対して数えられる予算です。クライアントの .RPT にはそのどれも含まれていないため、これら 3 つのキーはここでは意図的に適用されず、回答はそれらを not_applied にリストします。forbiderror_regexnoise はログ行のテキストに関するものであり、引き続き適用されます。クライアント側の準備完了行を宣言するものはありません。クライアントが入ったかどうかは、client_start が待つプレイヤー数によって回答され、そのログによってではありません。

  • クライアントツールは、このセッションが開始しなかったスタンドに参加します。client_chat はできません。 client_start は、ポート上にすでにあるものに喜んで接続し、それが誰のものかを示します。しかし、チャットはサーバーサイドで配信され、world_* ツールと同じチャネルを通じて行われ、そのチャネルはこのセッションが開始したサーバーにのみ作用します。したがって、借用したスタンドでは、client_chat を除くすべてが機能します。拒否は、それらを拒否する server_start を提案するのではなく、ポートを保持している pid を指名します。

  • チャットはゲームパッドからは到達できず、メニューの確認もできません。 ゲームはチャット行を Enter にのみバインドし、他には何もバインドせず、オンスクリーンキーボードもないため、テキストはブリッジメッセージ(client_chat、無料)または実際のキーストローク(client_type、フォアグラウンドを消費)のいずれかです。client_type("", submit=True) は Enter のみを送信します。これがチャット行を開く方法であり、上記の証拠によれば、ツールセットが持つ唯一の確認方法です。

  • すべての知識検索は、プロジェクトツリーのウォークを支払います。 そのウォークは、プロジェクトレイヤーの陳腐化がすべての回答で測定される方法であり、インデックスが存在する唯一のプロパティです。実際の 41 ソースの mod(ツリーには約 1800 エントリが含まれます)では 3.0 ms、2810 ファイルのツリーでは 21 ms かかります。1、2 秒キャッシュするとコストが削除され、デザインが拒否するまさにその沈黙のウィンドウが復元されます。それが高価になりすぎた場合、そのトレードオフは意図的に行う必要があり、偶然ではありません。

  • ビルドは常にジョブを経由し、ジョブは小さなビルドよりもコストがかかります。 6 ms のプロジェクト再ビルドに対して 70〜95 ms のターンアラウンドが測定されました。job_wait は 100 ms でポーリングします。単一の形状は意図的です。呼び出し元はどのビルドがブロックするかを知る必要はありません。次の検索がレイヤー自体を測定するため、待つことを強制するものはありません。

  • 依存関係レイヤーは、現在のプロファイルに対して測定されます。 mods.required に mod を追加すると、そのアーカイブは added として到着します。削除すると、そのアーカイブは missing として読み取られます。それが要件です(宣言されたセットはレイヤーが構築されるものの一部です)。しかし、実際に変更されたのはプロファイルであるのに、インデックスが古くなったように見えます。

  • core には常にゲームの設定が含まれ、それがコストの大部分です。 設定ありで 70 秒、スクリプトのみで約 4 秒です。スイッチはありません。設定なしでは「X というアイテムクラスはあるか」に答えることができず、2 番目の軸があると陳腐化の測定が曖昧になります。ウォークは Addons アーカイブを期待するかどうかを知らないからです。

  • knowledge_show は最も近いレイヤーから最初に答えます。 依存関係が modded class で再オープンするクラスの場合、mod の宣言がゲームの宣言より先に来ます。それは正しい順序であり、驚くべき順序です。ゲーム自身のものには layer='core' を渡してください。

  • 条件付きコンパイルはインデックス化され、解決されません。 ゲームのスクリプト行の 4.9% が #ifdef 内にあり、約 100 のクラス宣言が含まれます。このサーバーはサーバー、クライアント、診断ビルドを駆動するため、単一の正しい定義セットはありません。すべてがインデックス化され、ガードは宣言に記録されます。したがって、特定のビルドが除外する名前が報告される可能性があります。代替案である定義の 1 つの推測でフィルタリングすると、実行中のビルドにあるメソッドの存在を否定することになります。

  • C12 のフィンガープリントはファイルのサイズを保持し、binarize のサイズは安定していません。 誰も編集していないモデルを再ビルドすると、出荷されたものより 1 バイト大きいアーティファクトが生成され、同じ種類、同じ LOD 数、同じ 50 の文字列、そしてサイズが一部であるため異なるダイジェストが生成されました。したがって、C12 は何も変更していない再ビルドについて警告できます。まさにこの理由で、拒否ではなく警告し、それが構築される部分がその隣に報告されるため、手動で比較できます。ダイジェストを安定した半分とサイズに分割することが明らかな改良であり、行われていません。

  • 部分的なエクスポートは警告します。拒否はしません。 エクスポーター自身のデフォルト引数では、モデルは 5 つの LOD のうち 2 つを運び、他のすべてのチェックに合格して出てきました。このサーバーはそれらの引数を渡さないため、これは起こるべきではありません。しかし、LOD としてマークされ、シーンにリンクされていないオブジェクトは、比較の一方の側で数えられ、もう一方では数えられません。これはカウントが異なる正当な理由であり、拒否は誤検出を持つことになります。E3 を読んでください。

  • 包含ルールは、すべての間違ったルートを見ることはできません。 それは、mod のプレフィックスフォルダーを保持しないルートを拒否します。これは測定された失敗です。その名前のフォルダーを保持するルート(たとえば、独自の mod ディレクトリがプレフィックスのように綴られているリポジトリ)はそれを通過し、そのケースを捕まえるのは C10 または 1 層下の C3/C4 です。測定済み:そのようなルートを指すと、ビルドは拒否され、何もデプロイせず、出荷されたアーティファクトは変更されませんでしたが、拒否は呼び出しではなくジョブから来ました。

  • asset_export は、Blender でエクスポートアドオンを有効にする必要があり、インストールできません。 Blender はマシン所有者の実際の設定で起動されます。--factory-startup で起動するとアドオンが完全に取り除かれるためです。彼らの他のアドオンは、実行の検索パスから除外されます(ここにインストールされている 2 つは起動時にネットワークに到達し、クラッシュの原因とされています)。Blender はログに「Add-on not loaded」と報告します。その行はこのサーバー自身の仕業であり、障害ではありません。

  • バイナリ化された設定には、表示する本体がありません。 knowledge_show(body=True) は、インデックス化されたファイルまたはアーカイブから宣言を読み戻しますが、config.bin はバイナリ形式を保持し、インデックスは CfgConvert がそれから作ったものを保持します。答えは、何も返す代わりにその旨を述べます。

インストール

python -m pip install -e ".[dev]"
python -m pytest

MCP クライアントに登録します:

{ "mcpServers": { "dayz": { "command": "dayz-mcp" } } }

ライセンス

GPL-3.0-or-later。NOTICE.md を参照してください。

Install Server
A
license - permissive license
B
quality
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Related MCP Servers

View all related MCP servers

Related MCP Connectors

  • An MCP server that gives your AI access to the source code and docs of all public github repos

  • A MCP server built for developers enabling Git based project management with project and personal…

  • Augments MCP Server - A comprehensive framework documentation provider for Claude Code

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/covalschi/dayz-agentic-modding-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server