Skip to main content
Glama
covalschi

dayz-agentic-modding-mcp

dayz-agentic-modding-mcp

Ein MCP-Server, der es einem Agenten ermöglicht, einen DayZ-Mod zu bauen, zu prüfen, dass der Client kompiliert, einen Testserver zu starten und ein strukturiertes Urteil statt eines Logs zu erhalten.

Der Server erledigt die Arbeit selbst: Er ruft FileBank, das Signierungswerkzeug und die Diagnose-Executable direkt auf. Ein Projekt benötigt kein eigenes Build-Skript.

Warum ein Profil

Der Server weiß nichts über einen bestimmten Mod. Alles Spezifische lebt in einem zweiteiligen Profil:

  • dayz-mcp.toml — portabel, im Mod-Repository eingecheckt: welche Mods gepackt werden sollen, wie ein gesunder Boot aussieht.

  • dayz-mcp.local.toml — maschinenspezifisch, niemals eingecheckt: wo das Spiel, die Werkzeuge und der Teststand leben.

Das Mischen der Hälften wird beim Laden abgelehnt: So hört ein Repository auf, auf der Maschine eines anderen zu bauen. Beginnen Sie mit dayz-mcp.example.toml.

Ein Mod wird einmal deklariert, per Name: Quellen standardmäßig in <root>/Name, Ausgabe @Name/addons/Name.pbo. build.sources kann die Quelle eines Mods woandershin umleiten (z. B. "." für einen Mod, dessen config.cpp direkt im Repository-Root liegt).

Die portable Hälfte — dayz-mcp.toml

Key

Bedeutung

project.name

wie dieses Projekt genannt werden soll

build.mods

ein Eintrag pro Mod, per Name; Quelle, PBO und @folder folgen daraus

build.sources

wo die Quelle eines Mods tatsächlich liegt, relativ zum Profil ("." = das Repository-Root selbst)

build.exclude

was niemals gepackt werden darf. Das Auflisten ersetzt die Standardliste vollständig; die Standardliste ist .git, *.blend, *.blend1, .gitignore, .gitattributes, README.md, *.ps1

build.stage

eine gefilterte Kopie packen, anstatt zu verweigern, wenn etwas Ausgeschlossenes vorhanden ist — das Layout, das ein Root-Layout-Mod benötigt

build.project_root

das Verzeichnis, gegen das jeder Modellpfad aufgelöst wird, relativ zu dieser Datei. Es muss den Präfix-Ordner des Mods enthalten. Wird von den Modellwerkzeugen benötigt und von sonst nichts — siehe „Die Asset-Pipeline“

build.pre_script

ein PowerShell-Skript, das vor dem Packen ausgeführt wird, für Projekte, die zuerst Code generieren

expect.ready_line

die Zeile, die der Mod ausgibt, wenn er das Laden abgeschlossen hat — siehe unten

expect.counters

key = value-Paare, die von dieser Zeile getragen werden, numerisch verglichen

expect.max_warnings

Warnungsbudget; den Schlüssel weglassen, um die Prüfung zu deaktivieren

expect.forbid

Teilzeichenfolgen, die einen Lauf unabhängig von allem anderen schlecht machen

expect.error_regex

reguläre Ausdrücke, die Skriptfehler markieren, die zu Ihnen gehören, verwendet von der Client-Kompilierungsprüfung

expect.noise

zusätzliches Engine-Rauschen, das ignoriert werden soll, zusätzlich zur eingebauten Liste

Die Maschinenhälfte — dayz-mcp.local.toml, niemals eingecheckt

Key

Bedeutung

machine.game

die Spielinstallation; wird automatisch erkannt, wenn nicht vorhanden

machine.tools

DayZ Tools; werden automatisch erkannt, wenn nicht vorhanden

machine.blender

die Blender-Executable, nur für asset_export; wird automatisch erkannt, wenn nicht vorhanden, und wird von nichts anderem benötigt

machine.stand_root

der vorbereitete Teststand. Der Server bootet dagegen und seine Logs werden aus <stand_root>/profiles gelesen. Standardmäßig <root>/testenv

machine.config

der Dateiname der Serverkonfiguration im Stand (Standard serverDZ.cfg). Es ist eine Einstellung, weil ein Stand eine Konfiguration enthalten kann, die nach der Weltkompilierung für immer hängt, und eine funktionierende unter einem anderen Namen; sie muss innerhalb von stand_root aufgelöst werden

machine.port

der Port, den server_start an den Server übergibt (Standard 2302)

mods.required

Mod-Ordnernamen, aufgelöst unter dem eigenen !Workshop-Ordner des Spiels — required = ["@CF"] bedeutet <game>/!Workshop/@CF

mods.extra

vollständige Pfade zu allem anderen, das geladen werden soll, für Mods, die nicht in !Workshop leben

mods.server_only

Ordnernamen, die an -serverMod statt an -mod weitergeleitet werden. Die Zuordnung erfolgt per Ordnernamen gegen jeden geladenen Mod, egal woher er stammt — mods.required, mods.extra oder die eigenen @Name-Ordner des Projekts; alles, was nicht aufgeführt ist, geht an -mod. Der Diagnose-Client lädt niemals Server-only-Mods, daher lässt die Client-Kompilierungsprüfung sie weg

Die Bereitschaftszeile

server_start endet, wenn expect.ready_line in einem Log erscheint, das nach diesem Boot-Beginn geschrieben wurde. Es ist das Einzige, das der Server nicht selbst herausfinden kann, und ohne es ändern sich zwei Dinge: Es kann nichts abgewartet werden, also startet der Boot-Job den Server, bestätigt einen Moment später, dass er noch lebt, und endet mit dieser Aussage; und log_verdict hat keine Zeile, von der Zähler abgelesen werden können, also passt expect.counters nie auf etwas. Fehler, Abstürze und das Warnungsbudget werden weiterhin beurteilt. Ein Profil ohne Bereitschaftszeile wird unterstützt, ist nicht kaputt — project_open sagt das in seinen Notizen.

Related MCP server: DayZ API MCP Server

Werkzeuge

Tool

Was es tut

project_open(path)

das Profil lesen, das Spiel und die Werkzeuge erkennen, melden, was fehlt

project_status()

aktuelles Projekt, laufender Server, letzte Aufträge

mod_build()

jedes deklarierte Mod packen und signieren; gibt eine Auftrags-ID zurück. Verweigert einen zweiten Build desselben Projekts, solange noch einer läuft

server_start(timeout)

den Testserver starten, fertig sein, wenn er bereit ist. Verweigert, wenn die Mission, die die Konfiguration nennt, nicht unter <game>/mpmissions liegt — die Engine sucht Missionen neben der ausgeführten ausführbaren Datei, nicht neben der -config. Gibt die pid sofort zurück — der Prozess wird gestartet, bevor der Aufruf zurückkehrt, sodass das nächste Tool bereits einen laufenden Server sieht. Verweigert, wenn der Spielport bereits von jemand anderem belegt ist, und verweigert sofort, wenn das Image nicht gestartet werden kann. Die Bereitschaft kommt von expect.ready_line, wenn deklariert, sonst von zwei Engine-Signalen zusammen — der gebundene Port UND das kompilierte Missionsmodul. Der Port bindet etwa 17 s bevor die Skripte es tun, und ein Boot, das dazwischen als bereit bezeichnet wird, lauscht ohne Mission: Es beantwortet Abfragen und verweigert jeden Spieler. Die Auftragszusammenfassung nennt, welche

server_status(pulse_seconds)

pid, ob der Prozess lebt, ob das Protokoll wächst (im Abstand von pulse_seconds abgetastet) und wie lange es gestockt hat

server_stop(pid)

den Server stoppen, den diese Sitzung gestartet hat (optionale pid für verwaiste Server)

server_signatures(value)

das verifySignatures des Stands lesen — oder absichtlich ändern. Ohne Argument meldet es nur. Es bearbeitet nur die Konfiguration, die das Profil als die dieses Stands benennt, verweigert eine, die außerhalb von machine.stand_root aufgelöst wird, verweigert, während ein Server dagegen läuft, behält die Kommentare und Zeilenenden der Datei bei und liest den Wert danach wieder aus der Datei

client_compile_check(extra_mods, wait_seconds)

den Diagnose-Client ausführen und seine Protokolle lesen

log_verdict(source, since)

bestanden/nicht bestanden mit Gründen: Zähler, verbotene Zeichenfolgen, Warnbudget. source ist "server" (das neueste Protokoll im Stand) oder "client" (das Protokoll, das der letzte client_compile_check erzeugt hat). since (ein Epochen-Zeitstempel, z. B. das since, das server_start zurückgibt) verweigert ein Protokoll, das vor dem zu bewertenden Lauf geschrieben wurde, sodass ein veraltetes Protokoll von einem früheren Boot nicht mit dem Ergebnis dieses Laufs verwechselt werden kann

log_tail(source, pattern, n)

letzte Zeilen, optional gefiltert; dieselben zwei Quellen

job_status(job_id)

Status eines lang laufenden Auftrags

job_wait(job_id, timeout)

auf das Ende eines Auftrags warten

job_artifacts(job_id)

Ausgaben eines abgeschlossenen Auftrags abrufen

bridge_build()

das Bridge-Mod packen, dessen Quellen mit diesem Server (bridge/) geliefert werden, nicht mit Ihrem Projekt; gibt eine Auftrags-ID zurück. Unsigned gebaut — siehe unten

bridge_status(window)

tickt die Bridge im laufenden Spiel noch: meldet die Tick-Nummer und ob sie sich über window Sekunden weiterbewegt hat. Erfolgreich nur bei einem Tick, der sich tatsächlich bewegt hat, oder bei einem Stand, der mitten in der Abtastung neu gestartet wurde

bridge_clear(force, probe_window)

den im Postfach feststeckenden Befehl verwerfen und benennen, was verworfen wurde. Verweigert, solange die Bridge lebendig aussieht, außer force=True

world_ready(timeout)

warten, bis die Bridge im Spiel tatsächlich Befehle beansprucht. Einmal aufrufen, nachdem der Boot-Auftrag abgeschlossen ist, vor dem ersten Weltbefehl — siehe „Server bereit ist nicht Bridge bereit" unten

world_state(class_name, radius, pos)

Momentaufnahme der Welt aus der einmal-pro-Sekunde-Veröffentlichung der Bridge: Spieler, Position, Gesundheit, Hände. Ohne Argumente kostenlos; mit class_name zählt es auch Objekte dieser Klasse in der Nähe (ein Befehls-Roundtrip)

world_spawn(class_name, where, pos, quantity)

ein Objekt auf dem Boden erzeugen (ohne Lebensdauer, sodass es mitten in der Prüfung nicht verschwinden kann), in den Händen des Spielers oder in seinem Inventar

world_teleport(pos)

den Spieler zu "x y z" bewegen — dasselbe Format, das world_state meldet, sodass eine gelesene Position direkt zurückgegeben werden kann

world_set(what, value, target)

health (Spieler oder gehaltenes Objekt) oder quantity (gehaltenes Objekt) setzen

world_delete(class_name, radius, pos)

Objekte einer Klasse in der Nähe löschen. Erfordert die Klasse; löscht nie einen echten Spieler

world_entities(class_name, radius, pos, limit)

welche Objekte in der Nähe sind, nicht wie viele: Klasse, Position, Entfernung und Gesundheit für jedes. Eine Seite, und es sagt es — die wahre Gesamtzahl kommt neben der Liste zurück

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

die Weltuhr bewegen. Jedes Feld, das auf -1 bleibt, behält seinen aktuellen Wert, der zuerst von der Engine zurückgelesen wird, weil die Engine ein Datum als fünf Zahlen auf einmal setzt

world_weather_set(what, value, seconds, duration)

Bewölkung, Regen, Nebel, Schneefall oder Wind bewegen. Ein Anstoß, keine Sperre: Die Engine simuliert das Wetter danach weiter, und sowohl das Tool als auch das Mod sagen das

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

eine eigene Aktion eines Mods durch das Tor der Engine ausführen — siehe unten

world_exec(verb, args)

die Notluke: ein beliebiges Verb über denselben Transport, in jeder Antwort als nicht standardmäßig markiert

client_start(timeout, extra_args)

den Spiel-Client starten und mit dem Stand verbinden; gibt eine Auftrags-ID zurück. Immer im Fenstermodus. Endet, wenn die Bridge players >= 1 meldet — eine Zählung, kein Timer

| client_status() | PID, Fenstergeometrie, ob das Fenster minimiert oder im Vordergrund ist, die Hintergrundeinstellung, die Spieleranzahl und ob ein virtueller Controller angeschlossen ist | | client_stop() | den Client stoppen, den diese Session gestartet hat, und den virtuellen Controller abziehen | | client_shot(path) | das Fenster des Clients als PNG erfassen, mit lit_fraction — der Zahl, die ein echtes Bild von einem komplett schwarzen unterscheidet. Kein Fokus nötig | | client_move(x, y, seconds) | die Spielfigur mit dem linken Stick laufen lassen. Analog, und der einzige Weg, der die Figur überhaupt bewegt. Kein Fokus nötig | | client_look(x, y, seconds) | die Kamera mit dem rechten Stick drehen. Kein Fokus nötig | | client_press(button, seconds) | eine Gamepad-Taste, aus einer geschlossenen Tabelle von vierzehn Namen. Kein Fokus nötig | | client_chat(text, color) | eine Zeile in den Chat schreiben — serverseitig von der Bridge zugestellt, also keine Tastatur, kein Fenster, kein Fokus | | client_type(text, submit) | in ein clientseitiges Eingabefeld mit echten Tastendrücken tippen. Das einzige Werkzeug hier, das in den Vordergrund tritt, und das sagt es auch in seiner Antwort | | client_verdict(since) | den laufenden Client anhand seiner eigenen .RPT beurteilen — ein Urteil über Fehler und Abstürze; siehe unten | | ui_menu() | was die Benutzeroberfläche des Clients gerade macht: offene Menüklasse, Cursor, Dialog. Kostenlos — wird bei jedem Tick neu veröffentlicht | | ui_tree(root, depth, limit) | der Widget-Baum des Clients: Pfad, Klasse, Name, Sichtbarkeit, Bildschirmrechteck, Tiefe und Text. Eine Seite, und das sagt es auch | | ui_find(name, class_name, text, root) | derselbe Durchlauf, im Client gefiltert, sodass der ganze Baum nie reisen muss | | ui_click(path, expect_name, expect_class, via) | ein Widget drücken. via="script" geht ohne Fokus durch den Handler des geöffneten Menüs; via="cursor" setzt die echte Maus auf zugehörige Rechteck | | ui_text(path, text, expect_name) | in ein Eingabefeld schreiben und den Wert aus dem Widget zurücklesen | | mod_lint(mod, strict) | das Enforce-Skript beurteilen, ohne irgendetwas zu packen oder zu starten. mod_build führt es zuerst aus und lehnt das ab, was es ablehnt | | knowledge_build(layer, full, only) | eine Ebene des API-Index bauen oder aktualisieren; gibt eine Job-ID zurück. only=[path] liest genau die Dateien erneut, die du nennst | | knowledge_status() | Übe, was jede Ebene enthält, wie alt sie ist und ob sie noch dem entspricht, was auf der Platte liegt | | knowledge_find(name, kind, owner, layer, prefix, limit) | eine Klasse, Methode, Konstante, Enum oder Konfigurationsklasse anhand eines Namens finden | | knowledge_show(name, ..., body) | eine Deklaration vollständig: Signatur, Mitglieder, Vererbungskette und der Quelltext selbst — direkt aus einem Archiv gelesen, falls es darin lebt | | knowledge_overrides(name, owner, layer) | wer diese Klasse oder Methode überschreibt | | knowledge_callers(name, kind, owner, layer) | wer diese Methode au (ruft oder diese Klasse baut — jede Aufrufstelle, samt Klasse und Methode, die den Aufruf ausgeführt hat | | asset_export(blend, mod, source, name) | ein Modell aus einer .blend in build.headless exportieren, ohne Benutzeroberfläche; gibt eine Job-ID zurück. Die optional erste Schritt — siehe unten | | asset_build(mod, source, deploy) | die Modelle eines Mods aus ihren MLOD-Quellen binarisieren, prüfen, was das herausgekommen, und erst dann in den Mod geben; gibt eine Job-ID zurück | | asset_check(mod, model) | die Modelle und Texturen beurteilen, die ein Mod bereits mitliefert. Baut nichts, braucht keine DayZ-Tools, antwortet in Millisekunden | | asset_convert(source, output) | eine Textur zwischen .png und .paa konvertieren und das Ergebnis beurteilen |

Signaturen – und warum die eigene Meldung der Engine auf die falsche Fährte führt. Unter verifySignatures = 2 lehnt eine Instanz jede Client, bei Code 118 und *"missing dta bin.pbo"* ab — ein Vanilla-Dateiname, ganz ohne Erwähnung von Signaturen. Die Ursache ist normalerweise der Schlüsselbund: Dieses Tool startet die Diagnose-Programmdatei aus der CLIENT-Installation, also liest die Engine die keysneben *dieser* ausführbaren Datei, währenddayz.bikey— der Schlüssel, der die eigenen Pbos des Spiels signiert — mit der separaten DayZServer-Installation geliefert wird. Einkeys-Ordner, der fehlt oder nur einen eigenen Mod-Schlüssel enthält, lässt den Server nichts mehr verifizieren, Vanilla eingeschlossen. client_startweigert sich und nennt dafür, welche der drei Fälle vorliegt;server_startbir warnt nur, weil eine Headless-Instanz bootst auch ohne Client noch nützlich ist. Eine unsignierte Pbo in der-mod`-Zeile des Clients wird auf eigene Weise benannt — einschließlich der Bridge dieses Servers, die absichtlich unsigniert gebaut ist.

Drei Grenzen, die die Engine den UI-Werkzeugen auferlegt, keine davon umgangen: eine schlichte TextWidget hat SetText und kein GetText in enwidgets.c, also eine BEDEMMA label überhaupt nicht gelesen werden kann — was die Bedeutung einer Mod-Oberfläche ist, bleibt eine Frage an die serverseitige, bei der die Daten real sind. Ein Klick auf Skriptebene erreicht nur das offene Skriptmenü, denn Widget hat SetHandler und kein GetHandler; viakurzur ist für alles andere da. Und der Client muss die Bridge laden: eine pbo trägt beide Hälften, wobei ein Profil, das sie unter mods.server_only führt, sie von der -mod-Zeile des Clients fernhält — diese Fall wird vor allem anderen mit Namen benannt, und nicht mit einem leeren Baum beantwortet.

job_wait ist das Werkzeug zum Warten, und sein timeout ist bei 600 Sekunden gedeckelt, egal wie groß der übergebene Wert ist. Zwei andere Werkzeuge schlafen: server_statusden Log zweimal abtastet,pulse_secondsAbstand, gedeckelt auf 10 Sekunden — diese Pause ist zu unterscheiden, das langsames oder hängendes Booten ist — undbridge_statuszweimal den tickt der Bridge ab mitwindow` Abstand, gedeckelt auf den gleichen 10 Sekunden, aus demselben Grund. Alles andere kehrt sofort zurück; Arbeit, die Minuten dauert, dass diese hinter einer Job-ID passiert.

Die Bridge-Mod

bridge_build packt das bridge/ aus dem eigenen Repository dieses Servers in das danebenliegende distance_Distribution-Bridge. Es ist der Mod dieses Servers, nicht deiner: Eine Kopie dient allen Projekten, nichts wird in dein Repository geschrieben außer dem Job-Protokoll, und auch kein Unterschlüssel eines Projekts benutzt — eine -serverMod pbo wird nie einem Client zu Prüfung übergeben, deshalb ist sie unsigniert gebaut, und das Ordner bleibt frei von Signaturen und SchlKennen.

Das Build lädt sie nicht. Das bleibt beweg deines Profils, denn die Bridge ist eine Extra-P0 in der Instanz, und ein Lauf ohne sie bleibt mögliche. Zum Ankoppeln fügst du data-plugins-local. das sind die Zeilen, die bridge_lint auch in der Job-Zusammenfassung druckt:

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

server_only ist Hairbei das, was sie zu lowerServerMod leitet statt zu -mod. Ohne das boot die Instanz ganz normal, und bridge_status meldet, dass die Bridge keinerlei Zustand geschrieben hat – ehrliches true, das leicht broken nur verwege für eine kaputte Bridge gehalten wird.

bridge_status meldet auch die command-Mailbox. In der Spielumgebung verlässt sie nur der Mod, indem er sich das Kommando geht; auf dieser Seite machen das bridge_clear und das Vorway- -putzen von server_start. Eine Kommando also, die geschickt wurde, während die Instanz unten war, wird nicht demandelt und verfällt nicht von selbst – es blockiert weiterhin den nächsten send, und eine shop, dieser Tools gebootete wird es aufgreifen. server_start räumt vor jedem Neustart beide Transferdateien ab, sodass ein über diesem Tool gestalteter Server or kein Kommando von einer vorherigen Session ausführt; das dient der Hygiene, nicht als Ersatz für das Wissen, dass das Kommando noch da ist. Der Zustand kommt als stale_command zurück, und bridge_clear() ist der Weg raus. Das Leeren ist vorsätzlich eigener Tool: Ein wartendes Kommando wegzuwerfen ist eine Entscheidung, keine Sache, die eine Statusabfrage überhinweg erledigen sollte. Es lehnt ab, so lange die Brücke lebendig aussieht, varias force=True übergibst, und meldet in jedem Fall die ID des Verw% discarded.

Was bridge_status mit einschwachen auseinanderhalten kann

Der Tick alleine reicht nicht aus, um eine Bridge zu beurteilen, denn er startet bei jedem Boot bei 0 neu, mentre die Zustandsdatei im Profilverzeichnis weiterlebt. Jede Antwort trägt das eigene Urteil des Kanals in heartbeat, und die vier sind tatsächlich _:

state

heartbeat

Bedeutung

alive

growing

Der Tick zeigte sich innerhalb einer Session — die einzige ok: true-Lebensantwort

restarted

restarted

Eine neue Welt kam zwischen den beiden Proben auf: lebendig, nicht eingefroren, und alles, was an die alte Session gesendet wurde, ist weg

frozen

stalled

dieselbe Welt zweimal gesehen, nicht bewegend — ein Problem auf Skript-Seite, also ist log_verdict der nächste Schritt

unknown

unmeasurable

MessSample konnte nicht gelesen werden (oder window=0): kein Vergleich wurde gemacht. Keine Diagnose — erneut versuchen

Jede Antwort, die eine Probe gelesen hat, trägt auch session_id — Die Id der gelebten Welt — und eine restarted Antwort trägt zusätzlich previous_session_id, damit der Aufrufer sagen kann, welche Welt verschwand.

no_server stale_command no_state_file invalid_state unreadable_state und outdated_bridge kommen all diesen vorweg: Nichts läuft, ein Kommando hängt fest, der Mod nicht geladen ist, das Zustandsdokument ist gültiges JSON mit einem falsch benannten Feld (das Tool hat das Feld genannt und prüft zweimal, bevor es es sagt), die Datei parst sie komplett nicht, oder sie parst, aber stammt aus einer Zeit zuvor als das Protokoll dieses Servers (dann mit bridge_build neu bauen).

Die Weltkommandos

Die Weltwerkzeuge sprechen mit der Bridge über zwei JSON-Dateien im -profiles-Verzeichnis des Servers: eine Kommando-Mailbox (von hier aus atomar geschrieben, von der Mod als Anspruch entfernt) und eine Zustandsdatei, die der Mod jeder Sekunde überschreibt. Enforce Script hat kein "rename", also kann der Mod nicht atomar schreiben – der Leser toleriert stattdessen anrissene Geschreibungen, eine fehlgeschlagene lesung ist nie Neu. Vier → Tatsachen, alle an einem Live-Server gemessen, legen fest:

Server-Bereit ist nicht Bridge-Bereit. Die Brücke beginnt Kommandos zig Sekunden, nachdem der Server reif gemeldet hat — die bisherige Spanne liegt bei 18 und 38 Sekunden und ändert sich Boot zu Boot. Ein in dieses Fenster gesendet Kommando wird nicht abgelehnt — es wird spät geholt und ausgeführt nachdem der Aufrufer längst aufgegeben hat. Also: server_start, warte auf Boot-Job, dann world_ready(), dann die richtigen Kommandos. Jedes Weltwerkzeug lehnt au Achtung sofort ab, wenn der Tick sich nicht bewegt, und nennt world_ready.

**Jeder Argumentwert reist als String über die Leitung. Der Parser des Linns ist strikt: eine JSON-Zahl irgendwo in args lehnt den ganzen args-Block ab. Die Tools erstellen Zahlen und boolesche Werte selbst und lehnen Werte ohne echt String-Darstellung ab (Listen, dicts, None). Positionen gehen als ein einziger String "x: y: z".

Weigerung ist ein Ergebnis. Die natives Satz des Mods kommen unverändert als Fehler aus („no player is on the server", „the class is Next", „the action's own Can() said no"). Niemand mit verbunden, ist der Normalfall einer Headless-Instanz, und jedes Verb, das eine gebrauchliche Spieler sagt das auch, statt still nichts zu tun.

Die SessionId schützt vor den Kommando von gestern. Jedes Kommando trägt die WorldMost – die Session, die die Brücke zuletzt veröffentlicht hat; der Mod lehnt jedes an eine andere Session gerichtete Kommando (oder ohne Session) ab, ohne es auszuführen. Ein Kommando, das geschrieben wurde, während die Instanz unten war, kann also nie in eine frisches gebootete Welt ausgelöst werden. Die Tools stempeln die Session automatisch — wichtig ist sie nur, wenn du das Maildrohnen von Hand belegst.

Ein semantisches Verb wie "die Probe abgeben" täuscht: In einer echten Mod bedeuten dieselben Wörter je nachdem, welches Gerät in der Nähe ist, welche Fraktion der Spieler hat und was bereits freigeschaltet ist, verschiedene Dinge. Dieser Kontext ist nicht aufzählbar, also versucht es die Bridge nicht. world_action nimmt den Klassennamen einer Aktion, ein Ziel und das gehaltene Item und bittet die Engine, es durch ihr eigenes Tor zu schicken — dasselbe, durch das ein Tastendruck geht. Die Anwendbarkeit entscheidet das eigene Can() der Aktion, und seine Ablehnung ist ein aussagekräftiges Testergebnis, kein Werkzeugfehler. Die unterscheidbaren Antworten: Manager beschäftigt, Spieler handelt bereits, Spieler sprintet, unbekannte Aktionsklasse und "das eigene Can() der Aktion sagte nein". "Akzeptiert" ist ebenfalls kein Erfolg — der Befehl läuft weiter, bis die Engine die Aktion tatsächlich freigibt, und jeder Fehlerpfad gibt den Manager frei, damit der Spieler danach weiter handeln kann.

world_exec ist der Notausstieg

Alles, was eine Mod offenlegt, das keine Aktion ist — "wie viele Punkte im Fraktionspool" — geht durch world_exec(verb, args): ein beliebiges Verb über denselben Transport. Jede Antwort ist als non_standard markiert: Dieser Server kennt das Verb nicht, validiert es nicht und steht nicht dafür ein, was die Mod damit tut. Ein Verb, das der Bridge-Build nicht kennt, kommt zurück und listet die Verben, die er kennt. Ein Projekt, das ein eigenes Verb braucht, bearbeitet seine eigene Kopie des Dispatchers der Bridge (der Kommentar über KnownVerbs() in bridge/scripts/5_Mission sagt genau, wo); es gibt bewusst keine Registrierungsmechanik — ein Verb, das dieser Server typisiert und validiert hat, wäre ein Verb, für das dieser Server einsteht.

Der Client: drei Eingabeebenen, und warum es drei sind

Die Bridge erreicht den Server. Was sie nicht kann, ist auf den Bildschirm des Clients zu schauen oder durch den Client zu handeln — eine Figur über den Boden laufen lassen, ein Menü öffnen, ein Feld ausfüllen, das eine Mod gezeichnet hat. Die client_*-Werkzeuge sind das, und sie nutzen drei verschiedene Wege, weil keiner von ihnen die Arbeit der anderen beiden erledigen kann. Jede Zeile unten ist eine Messung an einem laufenden Client, keine Designabsicht.

Weg

Was er tut

Braucht den Vordergrund

die Bridge (world_*, client_chat)

die Welt, und Text in den Chat

nein

ein virtuelles Gamepad, ViGEmBus (client_move / look / press)

Bewegung, Kamera und Teile der Oberfläche

nein

echte Tastendrücke, SendInput (client_type)

Text in ein Feld, das nur auf dem Client existiert

ja, und es nimmt ihn

Tastaturemulation bewegt die Figur nicht, und Fensternachrichten bewirken gar nichts. SendInput-Scancodes mit verifiziertem Vordergrund: 25 s vorwärts, 0 m. PostMessage/SendMessage WM_KEYDOWN in das Hauptfenster und seine Kinder: 0 m, und auch keine Reaktion von den Menüs. Die Engine liest Bewegung aus Raw Input und ignoriert emulierte Tasten, weshalb hier kein Werkzeug eine Fensternachricht anbietet.

Das virtuelle Gamepad bewegt sie, ohne Fokus, und es ist ANALOG — der Grund, warum es bleibt, selbst wo eine Taste genügen würde. Gemessen in einem Lauf, bei dem eine Drittanbieteranwendung durchgehend den Vordergrund hielt:

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)

Gleicher Weg, gleiche Figur, die 2,7-fache Geschwindigkeit allein durch Stick-Ausschlag. "Die Figur geht, nicht rennt" lässt sich mit einer Taste nicht ausdrücken, die nur an und aus kennt. Im selben Lauf lief die Figur etwa 141 m von selbst, und die eigene Zählung der Mod von Objekten in ihrem 10-m-Umkreis ging von 1 → 0 → 1, als sie den Ort verließ und zurückkam — eine Zustandsänderung, die durch Präsenz verursacht wird, die ein Teleport nicht erzeugen kann.

Ein Teil der Oberfläche reagiert auf das Pad, ein Teil nicht. Gemessen, während das Spielfenster die ganze Zeit hinter einer anderen Anwendung lag: back öffnet und schließt das Inventar, start öffnet das Pausemenü, b schließt es — alle bei einem Standard-Tap von 0,1 s, ein Tap also lang genug ist, damit die Engine es erfasst. Aber a bewegte nichts, weder bei 0,1 s noch bei 0,5 s, und das Steuerkreuz in diesen Bildschirmen auch nicht: Der Client wechselte nicht in den Controller-Navigationsmodus, es gab also kein fokussiertes Element, auf das eine Bestätigung wirken konnte. Behandle das Schließen von Menüs als Gamepad-Aufgabe und die Bestätigung von Menüs als unbewiesen.

Die Augen brauchen ebenfalls keinen Fokus. Eine Aufnahme ist ein Live-Frame mit dem Fenster ganz unten in der Z-Reihenfolge (lit_fraction 0,9997 ohne Fokus, 0,9997 mit Fokus in derselben Sitzung). Der eine Zustand, der sie besiegt, ist ein minimiertes Fenster, dessen Clientbereich auf 0×0 kollabiert — mit einem Grund abgelehnt, statt als gültig aussehendes leeres Bild gespeichert.

All dieses Hintergrundverhalten beruht auf einer Client-Einstellung, pauseMode (GAME → UPDATE IN BACKGROUND). Bei dem hier gemessenen Wert zeichnet und simuliert der Client weiter, während er keinen Fokus hat, weshalb der Frame live ist und der Stick die Figur weiter bewegt. Bei "keine Grafik" würden beide still stoppen — ein eingefrorener Frame sieht genauso aus wie ein lebendiger. Also LESEN client_start und client_status diese Einstellung und warnen; sie schreiben sie nie, weil sie dem gehört, dem die Maschine gehört.

client_type ist das einzige Werkzeug, das den Bildschirm übernimmt, und es ist ehrlich dabei: Die Antwort trägt foreground_taken und einen Satz, der besagt, dass die Person an der Maschine während des Laufs nicht in ihr eigenes Fenster tippen konnte. Es verifiziert den Vordergrund mit GetForegroundWindow, nachdem es darum gebeten hat, weil SetForegroundWindow Erfolg zurückgibt, ohne etwas getan zu haben, wenn Windows sich weigert — und blindes Tippen sendet die Tastendrücke in das Fenster, das die Person tatsächlich verwendet. Wenn der Vordergrund nicht erlangt werden kann, wird nichts getippt, und die Ablehnung nennt den Prozess, der ihn hält.

ViGEm ist die Emulation eines echten Geräts, und dies ist ein Prüfstand. Der Treiber ist signiert und installiert ohne Neustart, und das Gamepad ist ein neues Gerät, kein Filter über der eigenen Tastatur und Maus der Maschine — ein Filtertreiber wurde hier einmal ausprobiert und kostete den Besitzer der Maschine die gesamte Tastatur- und Mauseingabe, bis er von Hand rückgängig gemacht wurde. Nichts davon ist ein Versprechen bezüglich Anticheat auf einem Live-Server, und nichts in dieser Phase macht eines.

Der Wissensindex

Ein Agent, der eine Mod schreibt, stellt dem Spiel immer wieder dieselben Fragen: Gibt es eine solche API, wie heißt sie, wo ist sie deklariert, wer überschreibt sie. Sie zu beantworten bedeutete, scripts.pbo zu entpacken und den Text zu durchsuchen — und jede Sitzung zahlte erneut. knowledge_* verwandelt diese Arbeit in eine Frage.

Es ist eine einfache SQLite-Datei im eigenen .dayz-mcp/ des Projekts, gebaut von diesem Server aus dem Spiel, den Mods, die ein Projekt deklariert, und den eigenen Quellen des Projekts. Keine Einbettungen, kein externer Dienst, kein Schlüssel.

Drei Ebenen, und warum ihre Rhythmen sich unterscheiden

Ebene

Quelle

Wird veraltet, wenn

core

das Spiel: dta/scripts.pbo für die API, Addons/*.pbo für die Item-Klassen

das Spiel aktualisiert wird

deps

die Archive der Mods, die das Profil deklariert, gelesen ohne sie zu entpacken

eine Abhängigkeit aktualisiert wird oder sich die deklarierte Menge ändert

project

die eigenen Quellen der Mod, gelesen, wo sie liegen

jede Bearbeitung

Ein Index, der in einem Zug gebaut wird, wäre innerhalb einer Minute nach dem Richtigsein falsch: Das Spiel bewegt sich ein paar Mal im Jahr, eine Abhängigkeit ein paar Mal im Monat und das Projekt zwischen einem Agentenzug und dem nächsten. Also wird jede Ebene separat gebaut, gealtert und gemessen, und jeder Build ist inkrementell — unveränderte Quellen werden nach Größe und Änderungszeit übersprungen, und only=[path] überspringt sogar den Durchlauf, der sie entdeckt.

Eine Antwort trägt das Alter der Ebene, aus der sie stammt

Veraltung wird gemessen, nicht geraten: Eine Ebene erfasst Größe und Änderungszeit jeder Quelle, die sie gelesen hat, und das wird mit den Dateien verglichen, wie sie jetzt sind.

  • Jede Antwort nennt die Ebenen, die sie verwendet hat, und wie alt jede ist. Eine Antwort mit keinen Ergebnissen nennt jede Ebene, die sie durchsucht hat — "nicht gefunden" ist genau so viel wert, wie die Ebenen dahinter aktuell sind.

  • Die Aktualität der Projekt-Ebene wird bei jeder Suche gemessen, ob sie beigetragen hat oder nicht. Das ist der gefährliche Fall: Ein Agent fügt eine Klasse hinzu, fragt danach, und eine vor einer Minute gebaute Ebene sagt "nicht gefunden" — eine selbstbewusste Aussage über Code, der existiert.

  • Eine Suche über eine Ebene, die nie gebaut wurde, wird abgelehnt, und die Ablehnung nennt den Aufruf, der sie baut. "Nicht gefunden" und "nicht gesucht" sind verschiedene Tatsachen, und nur auf eine davon ist es sicher zu handeln.

  • Eingrenzung trägt dieselbe Falle eine Ebene tiefer, also meldet eine leere eingegrenzte Antwort, wo der Name tatsächlich existiert: Wenn man kind='class' nach einem Namen fragt, den das Spiel nur in einer Konfiguration deklariert, erhält man ein echtes "Nein", das sich liest als "das Spiel hat keine solche Klasse".

Konfigurationsklassen liegen unter kind='config', nicht unter kind='class'. Gezählt im eigenen Index dieser Maschine des Spiels: 88 102 Konfigurationsklassen gegen 43 595 Skriptdeklarationen aller Art zusammen, in eine Art gemischt, begraben sie also jede Skriptantwort. Getrennt ist "hat das Spiel eine Item-Klasse namens X" eine Frage, die man genau stellen kann.

Was er nicht beantwortet

Der Index beantwortet, was existiert: Klasse, Methode, Signatur, wo deklariert, wer überschreibt. Er beantwortet nicht, was richtig ist — dass modded class X extends X kompiliert und still nicht angewendet wird, dass _co den Alphakanal kostet, dass binarize Verzeichnisse statt Dateien nimmt. Nichts davon ist aus den Quellen ableitbar; es wurde auf die harte Tour gelernt und lebt in der Modding-Skill und in der Mod selbst. Der Index versucht nicht, eines von beiden zu ersetzen, und er versucht nicht zu verstehen, was ein Feld bedeutet oder warum eine Klasse da ist.

Semantische Suche ist bewusst nicht hier

Die Entscheidung wurde durch Messung getroffen, nicht durch Vorsicht: Jede Suche, die die früheren Phasen dieses Servers geprägt hat, war eine Suche nach Namen. Und ein Einbettungsindex würde die Regel brechen, die der Rest dieses Servers einhält — installieren und es funktioniert, ohne externen Dienst und ohne Schlüssel. Das Vorgängerprojekt, auf dem dieses bewusst nicht aufbaut, dokumentiert seine Wissensebene als lokal und kostenlos, während sein Code einen kostenpflichtigen Einbettungs-Client importiert, ohne Schlüssel scheitert und fest verdrahtete Preise trägt. Seine beiden Suchwerkzeuge hängen auch für immer, weil der Client dahinter ohne Timeout erstellt wurde; daher die Obergrenze, unter der jede Suche hier läuft. Wenn sich exakte Suche als nicht ausreichend erweist, ist semantische Suche eine separate Phase mit einer Bedingung: Das Modell wird innerhalb der Auslieferung mitgeliefert.

Die gemessenen Zahlen

Auf dieser Maschine — das Spiel mit 2810 Skriptdateien, 35 installierten Mods, einem echten Projekt mit 41 Quellen — durch die Werkzeuge, nicht durch deren Interna:

Build

Ergebnis

Zeit

core

2927 Quellen, 131 697 Deklarationen (41 ergaben nichts)

70,2 s

deps, vier deklarierte Mods

8 Archive, 10 925 Deklarationen

0,9 s

deps, jede hier installierte Mod

523 Archive, 204 768 Deklarationen, 3 Archive nicht lesbar und benannt

139–147 s

project

41 Quellen, 1196 Deklarationen

0,12 s

Der Index auf der Platte: 74,7 MB für die drei Ebenen eines echten Projekts; 110 MB für die 523 Abhängigkeitsarchive allein. Diese Archive sind 92 GB, und keines davon ist entpackt.

Aufrufstellen sind das, wofür der Index bezahlt. Die eigenen Skripte des Spiels enthalten 43 579 Deklarationen und 113 703 Aufrufstellen, und das Aufzeichnen der zweiten Menge verdoppelt den Index grob: allein auf der Spiel-Ebene gemessen 23,8 MB und 3,7 s zum Bauen ohne sie, 49,6 MB und 4,5 s mit. Das ist der Preis dafür, beantworten zu können, „wer ruft das auf“, und er wird hier genannt, statt später auf einer vollen Festplatte entdeckt zu werden.

Antwort

Zeit

knowledge_find, exakter Name

4,2 ms Ende zu Ende, davon 3,0 ms für den Projekt-Durchlauf

knowledge_find, Präfix, Limit 500

3,2 ms Abfragezeit

knowledge_overrides

4,2 ms

knowledge_callers, 23 Aufrufstellen von 113 703

0,38 ms Abfragezeit

mod_lint bei einem 76-Dateien-Mod

277 ms Textprüfungen, 7 ms Indexprüfungen

Gemessen an einem Live-Stand, drei Bootvorgänge: world_time_set(hour=3, minute=7) setzte die Uhr auf 2026-09-20 03:07 und ließ das Datum unverändert; world_weather_set("fog", 0.9, seconds=2) änderte den veröffentlichten Nebel von 0,085 auf 0,900 und hielt ihn; world_entities(pos="7500 0 7500", radius=150, limit=5) listete 5 von 171 Objekten mit truncated: true. Die Entfernungen kamen bei einem Radius von 150 m als 320 m zurück, bis sie horizontal gemacht wurden, was der eigene Radiustest der Engine misst.

| knowledge_show, eine Klasse mit 400 Mitgliedern und ihrer Vererbungshierarchie | 6,8 ms | | knowledge_status, alle drei Ebenen gemessen | 41 ms (110 ms beim ersten Aufruf nach einem Build) |

Inkrementalität, im echten Projekt: ein vollständiger Rebuild 136 ms; eine bearbeitete Datei, die durch den Durchlauf gefunden wurde, 8,8 ms (15×); dieselbe Datei, die über only= benannt wurde, 5,8 ms (23×). Bei einem Baum mit 2810 Dateien dominiert der Durchlauf, und only= ist weit mehr wert — aber bei einem Projekt dieser Größe ist 15× das, was ein gewöhnlicher Rebuild tatsächlich einbringt.

Die Obergrenze schlägt tatsächlich zu: Eine Abfrage, die mit 77 ms gemessen wurde, wurde unter einer Obergrenze von 19,3 ms bei 19,9 ms gestoppt, und die Verbindung antwortete weiterhin.

Die Asset-Pipeline

Ein Modell aus Blender in einen Mod zu bringen, sind zehn Schritte, und bis zu dieser Phase wurden alle von Hand ausgeführt. Der Wert liegt nicht darin, die Werkzeuge zu starten. Er liegt darin, dass jedes Werkzeug in dieser Kette strukturell nicht in der Lage ist, einen Fehler zu melden, und jedes dieser Schweigen hatte bereits Tage gekostet.

Gemessen an den echten Binärdateien, nicht angenommen:

Was geschah

Was das Werkzeug zurückgab

binarize wurde eine Datei übergeben, wo es ein Verzeichnis erwartete

0, ein leeres Ausgabeverzeichnis, keine einzige Textzeile

binarize mit einem Material, das nicht geladen werden konnte

0, ein ODOL von 46.190 Bytes, wo ein korrekter Build 58.644 Bytes umfasst

binarize wurde ein bereits binarisiertes Modell übergeben

0xC0000005 und eine Datei der Länge null im Ausgabeverzeichnis, zusätzlich zu dem, was bereits dort war

der Blender-Exporter mit seinen eigenen Standardargumenten

FINISHED, Exit 0, ein gültiges MLOD mit 2 der 5 LODs des Modells und keine Erwähnung davon in 169 Logzeilen

Also lautet die Regel, auf der dieser gesamte Namensraum aufbaut: Das Urteil wird vom Artefakt abgelesen, niemals vom Bericht des Werkzeugs. Der Exit-Code wird aufgezeichnet, aber weder als Erfolg noch als Fehler gewertet.

Die Wurzel wird deklariert, nicht angenommen

binarize hat überhaupt keine Projektwurzel-Option — die vollständige Schalterliste wurde anhand der echten Binärdatei ermittelt. Die Wurzel ist das Arbeitsverzeichnis des Prozesses. Derselbe Befehl, dieselbe Eingabe, ein anderes Verzeichnis, und heraus kommt ein gültiges ODOL mit plausiblen Texturpfaden, das die Engine untexturiert rendert, mit einem Erfolgscode und ohne Beschwerde. Das exportierende Blender-Add-on hat dieselbe Wurzel in einer eigenen Einstellung, die von dem zuletzt geöffneten Projekt gespeichert ist: Auf dem Rechner, auf dem dies entwickelt wurde, zeigte sie auf ein Verzeichnis aus einer nicht zusammenhängenden Sitzung, und bei einer falschen Wurzel schlägt auch das Add-on nicht fehl — es entfernt den Laufwerksbuchstaben, behält den Rest und schreibt Pfade, die wie Pfade aussehen.

build.project_root ist dieses Verzeichnis, einmal im portablen Teil des Profils angegeben. Der Server setzt es als Arbeitsverzeichnis des Binarizers und schiebt es für die Dauer des Laufs in das Add-on, sodass das, was das Add-on gespeichert hat, nichts entscheidet (es wird berichtet, damit du es beheben kannst). Genau das macht eine falsche Wurzel unmöglich statt nur erkennbar, und deshalb ist der Schlüssel erforderlich, bevor überhaupt etwas Modellbezogenes läuft.

Die Ablehnungen, die es erzeugt, geschehen, bevor ein Prozess existiert — gemessen bei 0,0003 s — und ein abgelehnter Build lässt das Modell, das der Mod bereits ausliefert, byte für byte unberührt.

Zwölf Prüfungen am Artefakt, und vier davon verweigern

asset_check führt sie aus, ohne etwas zu bauen und ohne DayZ Tools, denn ein frischer Klon muss in der Lage sein zu fragen, ob das, was er ausliefert, gesund ist. Vier verweigern: Ein gebautes Modell ist vorhanden und ist ein ODOL (C1), keine Referenz verlässt den Mod (C3), ein Material wurde tatsächlich eingebettet (C4), und nichts bereits Binarisiertes wird binarize erneut angeboten (C10). Der Rest warnt: verwaiste Referenzen, ein rvmat, das in einen anderen Mod zeigt, eine durch DXT1 verlorene Transparenz (C7), eine Animation, die das Artefakt nie erreicht hat, eine model.cfg, die nicht diejenige ist, aus der das Artefakt gebaut wurde, ein struktureller Fingerabdruck, der nicht mehr dem entspricht, was der letzte Build bereitgestellt hat. Jeder Befund sagt, was zu tun ist.

C4 ist diejenige, über die man Bescheid wissen sollte. Wenn binarize ein rvmat auflöst, kopiert es die eigenen Stufentexturen dieses Materials in das Modell — fresnel, #(argb,8,8,3), env_land_co.paa, _nohq, _smdi — Zeichenketten, die kein MLOD enthält. Sechs von sechs Artefakten wurden durch diesen einen Test korrekt unterschieden, und er fand auf diesem Rechner ein defektes Modell, von dem niemand wusste.

Der Blender-Schritt ist optional

asset_export ist das einzige Werkzeug hier, das Blender benötigt, und alles nachgelagerte funktioniert mit einer .p3d von überall her — einem Hand-Export, einer Datei von einem Partner, einem vor Jahren eingecheckten Modell. Ein Rechner ohne Blender baut und liefert einen Mod einwandfrei; die Verweigerung sagt genau das, statt sie als kaputte Installation darzustellen. Es erfordert jedoch, dass das Export-Add-on im gefundenen Blender aktiviert ist, und es schreibt niemals Blender-Benutzereinstellungen zurück (verifiziert: Die Einstellungsdatei war nach jedem Lauf byte-identisch).

Export und Build sind zwei Aufrufe statt einem, weil jede Hälfte ihr eigenes Urteil hat, und ein Build, der von der einen verweigert und von der anderen erlaubt wird, ist keine Entscheidung.

Byte-Gleichheit wird nie versprochen

Keine Hälfte dieser Pipeline ist reproduzierbar, und das Design sagt genau das, statt etwas anderes vorzugeben:

  • Der Export. Sieben Exporte derselben unveränderten Quelldatei — drei aus einer Sitzung, drei aus einer anderen und einer, der Monate zuvor von Hand in der GUI erstellt wurde — ergaben sieben verschiedene SHA-256-Hashes bei konstanten 334.032 Bytes. Der Unterschied ist die Reihenfolge eines internen Blocks.

  • binarize. Vier Läufe mit einer unveränderten Eingabe ergaben drei verschiedene Ergebnisse: Die Größe änderte sich um 5 Bytes, und zwei 8-Byte-Fragmente entkamen komprimierten Bereichen.

Ein Modell wird also nie über einen Inhalts-Hash gecacht oder verglichen. Was verglichen wird, ist ein struktureller Fingerabdruck — die Art der Datei, ihre LOD-Anzahl und ihre Menge an Namen. Über alle sieben dieser Exporte hinweg war dieser Fingerabdruck ein einziger Wert.

Die gemessenen Werte

Ein kleines Modell, auf diesem Rechner, durch die Werkzeuge:

Schritt

Ergebnis

Zeit

asset_export

MLOD, 334.032 B, 5 LODs, sauber

2,1 s (etwa 8 s bei einem Kaltstart)

asset_build

ODOL v55, 58.646 B, 4 LODs, alle fünf C4-Marker

43,8 s (75,6–78,7 s, gemessen bei vier früheren Läufen)

asset_check

1 Modell und 10 Texturpaare bewertet

Millisekunden

asset_convert

ein PNG zu DXT1, 50.764 B

0,52 s

eine Ablehnung bei falscher Wurzel

bevor irgendein Prozess gestartet ist

0,0003 s

Beide Logs sind fast vollständig Standardtext, und was stummgeschaltet ist, wird gezählt statt verworfen: Blenders 169 Zeilen wurden auf 4 reduziert und binarizes 91 auf 6 — eine dieser sechs war die einzige echte Beschwerde des Modells.

Ende an Ende verkettet reproduzierten der Export und der Build ein Modell, das Monate zuvor von Hand erstellt worden war: gleiche Art, gleiche 4 LODs, dieselben 50 Strings und eine Größe, die ein Byte auseinanderlag.

Was nichts davon beantwortet

Ob das Modell richtig aussieht, richtig skaliert ist, richtig gewickelt ist, eine Kollision hat. Nichts außerhalb des Spiels beantwortet das. C1–C12 verkürzen den Weg dorthin; sie ersetzen ihn nicht.

Bekannte Einschränkungen

  • Stale-pbo-Erkennung basiert auf der mtime, nicht auf dem Inhalt. mod_build lehnt ein frisch gebautes pbo ab, das älter ist als seine Quellen – die übliche Ursache ist ein laufender Server, der die alte Datei noch offen hält, sodass das Packen stillschweigend nichts erzeugt hat. Aber git checkout ändert die Änderungszeit einer Datei, ohne den Inhalt zu ändern; ein einwandfreies pbo, das direkt nach dem Branchwechsel gebaut wurde, kann diese Prüfung daher ebenfalls auslösen. Wenn mod_build unmittelbar nach einem Branchwechsel „stale pbo“ meldet, ist das der wahrscheinliche Grund, kein echter Packungsfehler – neu bauen, dann besteht die Prüfung. Ein ausgereiftes Werkzeug in diesem Bereich ist genau aus diesem Grund auf einen Inhalts-Hash umgestiegen; das ist hier künftige Arbeit, nicht in dieser Phase erledigt (siehe packer.py, pack_one).

  • Ein Mod-Quellordner wird als Ganzes gepackt. mod_build weigert sich, einen Mod zu packen, dessen Quellordner etwas enthält, das auf build.exclude passt (die Standardliste mit sieben Mustern ist oben aufgeführt), anstatt es stillschweigend in das veröffentlichte pbo zu packen. Es verweigert das Packen unabhängig von build.exclude, wenn die Quelle die eigenen Artefakte dieses Servers enthält – die Signatsschlüssel, die eine oder andere Hälfte des Profils, den Job-Speicher, den vorherigen Build des Mods –, denn sich diese Dateien zu packen veröffentlicht den privaten Signatsschlüssel, und kein Projekt sollte das erst wegkonfigurieren müssen. Standardmäßig wird nicht zuerst eine gefilterte Kopie angelegt: Eine Kopie ist immer neuer als die Quellen und würde die Stale-pbo-Prüfung oben dauerhaft deaktivieren, wenn diese Prüfung die Kopie messen würde. build.stage = true wählt das Kopieren trotzdem – das ist nur sicher, weil der Stale-pbo-Vergleich immer den Original-Quellbaum misst und nie die Kopie. Das ist die Struktur, die ein Mod braucht, dessen Quelle das Repository-Wurzelverzeichnis ist (es enthält immer mindestens .git).

  • Ein Valeur 1 beurteilt das gesamte Log, nicht nur die Zeilen deines Mods.** log_verdict liest das Log der Instanz, auf die es gerichtet ist. Wenn sich eine Instanz mit anderen Mods geteilt wird, werden deren Warnungen gegen dein expect.max_warnings-Budget gerechnet und ihre Fehler als Ablehnungsgründe gezählt. Zwei Projekte, die sich einen machine.stand_root teilen, sehen die Baseline des jeweils anderen. Entweder bekommt jedes Projekt eine eigene Instanz, oder du setzt das Budget in dem Wissen, was sonst noch geladen ist. Eine projektbezogene Filter, die symmetrisch zu expect.error_regex ist, wäre die offensichtliche Weiterentwicklung eine Filterung, ist aber nicht implementiert.

  • expect.noise kann eine Zeile, die bereits als Fehler zählt, nicht retten. Die Klassifizierung ist geordnet forbid → crash → error → noise → warning; eine Zeile, die ERROR oder FATAL (oder einen deiner forbid-Strings) enthält, wird also entschieden, bevor noise überhaupt herangezogen wird. Diese Reihenfolge ist absichtlich so – würde zuerst Noise passen, könnte eine harmlose Teilzeichenkette eine fatale Zeile schlucken – aber sie bedeutet, dass noise nur Warnungen und harmlose Zeilen unterdrücken kann, niemals eine Fehlerklassierung bestzur:

  • client_verdict ist ein Fehler-und-Absturz-Verdikt, kein Bereitschafts-Verdikt. [expect] beschreibt das Log des Servers: Dessen Bereitschaftszeile und die Zähler stammen von der serverseitigen Initialisierung eines Mods, und max_warnings is a Budget, das über dieses selbe Log verklärt. Ein Client-.RPT enthält davon nichts. Diese drei Schlüssel werden hier deshalb bewusst nicht angewandt, und die Antwort listet sie in not_applied auf. forbid, error_regex und noise betreffen den Text einer Logzeile und gelten weiterhin. Es gibt keine client-seitige Bereitschaftszeile zu melden; ob der Client es ins Spiel geschafft hat, wird durch die Spielerzahl beantwortet, auf die client_start wartet, nicht durch sein Log.

  • Chat ist vom Gamepad aus nicht erreichbar – und eine Menü-Bestätigung auch nicht. Das Spiel bindet seine Chatzeile ausschließlich an Enter, es gibt keine Bildschirmtastatur; Text ist also entweder eine Bridge-Nachricht (client_chat, kostenlos) oder echte Tastenanschläge (client_type, kostet den Vordergrund). client_type("", submit=True) sendet allein Enter; so wird die Chatzeile geöffnet – das ist, nach dem obigen Beleg, die einzige Bestätigung, die das Toolset hatgot get.

  • Jede Wissenssuche bezahlt einen Durchlauf durch den Projektbaum. Mit diesem Durchlauf wird bei jeder Antwort die Veraltung der Projektebene gemessen; das ist die eine Eigenschaft, für die der Index überhaupt existiert. Er kostet 3,0 ms bei einem echten 41-Quellen-Mod (der Baum hat etwa 1800 Einträge) und 21 ms auf einem Baum mit 2810 Dateien. Wer das für ein bis zwei Sekunden, entfallen auf den Kosten und stellen genau das Fenster der Stille wieder her, das das Design nicht zulässt; wenn es irgendwann zu teuer wird, muss dieser Kompromiss bewusst eingegangen werden, nicht versehentlich.

  • Ein Build läuft immer über einen Job, und der Job kostet mehr als ein kleiner Build. Gemessene Durchlaufzeit: 70 – 95 ms bei einem Projekt-Rebuild von 6 ms: job_wait pollt alle 100 ms. Diese eine Form ist beabsichtigt – ein Aufrufer muss nicht wissen, welcher Build blockiert – und niemand zwingt ihn zu warten, denn die nächste Suche misst die Ebene selbst.

  • Die Abhängigkeitsebene wird gegen das Profil in dessen aktuellen Zustand gemessen. Fügst du einen Mod zu mods.required hinzu, kommen dessen Archive als added; entfernst du einen, lauten seine Archive missing. Das ist die Anforderung (die deklariert Menge gehört zum Aufbau der Ebene), aber es wirkt so, als wäre der Index veraltet, wenn sich in Wirklichkeit das Profil geändert hat.

  • core enthält immer die Konfigurationen des Spiels, und das ist der größte Teil seiner Kosten. 70 s mit ihnen, gegen circa 4 s für die Scripts allein. Es gibt keinen Schalter: Ohne die Configs lässt sich die Frage „Gibt es eine Item-Klasse X?“ nicht beantworten, und ein zweite Achse würde die Alterungsmessung mehrdeutig machen – der Durchlauf wüsste nicht, ob die Addons-Archive zu erwarten sind.

  • knowledge_show antwortet die nächste Ebene zuerst. Bei einer Klasse, die eine Abhängigkeit mit modded class wiedereröffnet, kommt die Deklaration des Mods vor der des eigentlichen Spiels. Das ist die richtige Reihenfolge, aber auch eine überraschende; für die eigene des Spiels übergibt man layer='core'.

  • Bedingte Kompilierung wird indexiert, nicht aufgelöst. 4,9 % der Scripteilzeichen des Spiels liegen in #ifdef-Blöcken, darunter auch etwa hundert Klassen-Deklarationen. Dieser Server Treibserver-, Client- und Diagnose-Builds, also gibt es keine einzige korrekte Defines-Menge: Es wird alles indexiert und die Guard-Bedingung an der Deklaration vermerkt. Ein Name kann also gemeldet werden, den ein bestimmter Build ausschließt – die Alternative, nach einer einzigen Schätzung der Defines zu filtern, würde die Existenz von Methoden verleugnen, die im tatsächlichen Build vorhanden sind.

  • Der Fingerabdruck von C12 enthält die Größe, und die Größe von binarize ist nicht stabil. Das Neubauen eines Modells, das niemand verändert hat, erzeugte ein Artefakt, das ein Byte größer war als das ausgelieferte, mit demselben Typ, der gleichen LOD-Zahl und den gleichen fünfzig Linux-Strings – und a different digest, because size is part of it. Darum kann C12 vor einem Rebuild wahrnehmen das nichts gab. C12 warnt und verweigert nicht – genau aus diesem Grund – und the parts of which it is made are reported last, so can compare be done by hand. The obvious refinement, to then the digest into a stable half and a size, is not done.

  • A partial export warns, not refuses. With the exporter's own default arguments, a model came out with 2 of its 5 LODs and passed every other check. This server does not pass those arguments, so should it actually not happen; but if an object as a LOD is marked and not in the scene is integrated, it counters on one side of the comparison and not on the other, for an which there is a legitimate reason, quindi eine Verweigerung hätte falsche Positive. Sieh dir E3 an.

  • Die Containment-Regel kann nicht jede falsch Wurzel erkennen. It refuses roots that does not have the mod's prefix folder; that is the failure that is measured. A root however, which does contain a folder with this name — for example a repository whose own mod directory is named like the prefix – passes it, and then is that case by C10 or C3/C4 a layer deeper. Measured: With such a root the build refused, then nothing was deployed and the shipped artifact remained, but the refusal came from the job rather than the call.

  • asset_export needs the exporting add-on is enabled in Blender, and cannot install it. Blender is launched with the real settings of the machine owner, because a launch with --withfactory-startup would cause the add-on disappearing. Its root is kicked other add-ons from the search path for this run (two of the ones installed should reach the network at startup and are supposed to be or the crashes called), which Blender as "Add-on not loaded" into the log – that line is the server's own doing, not a fault.

  • A binarized config has no body to show. knowledge_show(body=True) reads a declaration back out of the file or archive which is the one index came, but a config.bin contains the binary form while the index exists what CfgConvert has made. The answer says instead of returning nothing.

Install

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

Register it in your MCP client:

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

Licence

mit GPL-3.0-or-later. In NOTICE.md see.

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