Skip to content

feat: sltool dte build compiles mission source into a mission file - #1137

Merged
vdmkenny merged 4 commits into
mainfrom
feat/mission-source-compiler
Oct 13, 2026
Merged

vdmkenny merged 4 commits into
mainfrom
feat/mission-source-compiler

Conversation

@vdmkenny

@vdmkenny vdmkenny commented Oct 13, 2026 •

Copy link
Copy Markdown
Collaborator

The compiler step of #1126: mission source back into a standard .DTE, on top of the parser (#1132).

  • The compiler (formats/dte/source/compile.zig, and compile/code.zig for the script): declarations in the file's one namespace, each kind's records in the order of its declarations, and code through the statements model. It stops at the first error, with its line and column.
  • Object numbers: the shipped missions number objects in the order their authors made them, with gaps, which the source doesn't keep. The compiler numbers the objects last_event names first, since the script names those by a byte (mission 271 needs this), then ships, flight groups and squads.
  • Unread data: fields take what most shipped records hold (dte.Ship.blank and the others, which mission 0 now shares), and unread sections stay empty.
  • sltool dte build <source> <mission>.
  • The round trip (formats/dte/same.zig): the same records and code, decoded, with objects compared by the records the game takes them for. sltool dte check decompiles, builds again, compares and binds. Every mission in the game folder comes back as the same mission.
  • Decompiler fixes: objects named by the game's rule (resolveObjects), squad members and formation points as the game walks them, and triggers, squads and formations kept to 100 columns.
  • Bridge: each local gets a number, so a local that hides a parameter is told apart from it.
  • source.Symbols, the YAML plan's table, had no users and is removed.
  • Names where the decompiled missions had numbers: every pilot with a face of its own (pilots 15 and 16 are Ivan and Nicolai Petrov), the deathmatch markers (types 991 to 994), game variables 6 (nicolai_petrov_alive) and 35 (red_dragon_alive), and the environment effects SetEnvironmentFX takes. Scripts see the new pilot, ship type and variable names; make definitions updated the API docs, and multiplayer_list_spawns joins the Ghidra names. A test ties the hand-written pilot names to the generated faces table, so a pilot with a face of its own can't be left without a name.
  • One place each: the squad member and formation point walks (dte.zig, used by the engine too), the string pool builder (mission 0 and the compiler), and the command lookup by name (the executor).

Left: mission 0 still fails dte check's byte-exact statements step, as before, because its script is assembled by hand; #610 builds it from source. The .d.luau, the guide and the worked example follow in #1126.

Closes #1136

The compiler reads the declarations in the file's one namespace, builds
each kind's records in the order of its declarations, and compiles the
code of parts and triggers through the statements model. Errors stop the
build with their line and column.

The shipped missions number their objects in the order their authors made
them, with gaps, which the source doesn't keep. The compiler numbers the
objects that last_event names first, since the script names those by a
byte, then the ships, flight groups and squads. Unread fields take what
most shipped records hold (the records' blank values, which mission 0 now
shares), and unread sections stay empty.

dte/same.zig defines the round trip's "same mission": the records and code
decoded, objects by the records the game takes them for. sltool dte check
decompiles each mission, builds it again, compares the two and binds the
result. Every mission in the game folder comes back as the same mission.

The decompiler now names objects, lists squad members and lists formation
points as the game finds them, and keeps triggers, squads and formations
to 100 columns. source.Symbols, the YAML plan's table, goes unused and is
removed.

Closes #1136
The format, the engine and the decompiler each walked a squad's members
and a formation's points. The walks now live once in dte.zig
(SquadMember.run, FormationPoint.Walk), which the binder and the
formation orders use too. The formation walk is OpenReliant's fix of the
original's, so its description says so.

Mission 0 and the compiler share one string pool builder
(dte.write.Strings), the executor looks a command up by name at run time
next to its comptime lookup, and the decompiler writes fields and lists
through one table writer. The compiler's messages take their limits from
the constants they describe, such as the assembler's longest text, which
is 253 bytes, not 254. The test mission now covers the layouts of a line
that doesn't fit.
…d the environment effects

Decompiling the shipped missions left numbers where names belong: 66
markers, most pilots, two game variables and the environment effects.

- Every pilot of pilotstats.bin has a name, spelled out from its record,
  and the pilots past it with faces of their own, after their face films.
  Pilots 15 and 16 are Ivan and Nicolai Petrov, as the radio's aces are
  numbered by their pilots; the radio now names its aces by these names.
- Types 991 to 994 are the deathmatch maps' markers. The game lists the
  players' spawn points, type 993, as a multiplayer game starts
  (multiplayer_list_spawns, now in the Ghidra names).
- Variable 6 is Nicolai Petrov's alive flag, which the Dreamcast's mission
  12 clears as he dies, and 35 the Red Dragon's, which brings him into
  mission 26.
- SetEnvironmentFX takes its effect by name in mission source.

Scripts see the new pilot, ship type and variable names, and make
definitions wrote them into the API's docs.
The pilots' names are written by hand, while the faces table is generated
from the payload, and nothing tied the two together. The test checks that
each pilot whose face no earlier pilot has gets a name, and that every name
is a pilot in the table, so a pilot left without a name now fails the tests.
@vdmkenny
vdmkenny merged commit 6830dd2 into main Oct 13, 2026
4 checks passed
@vdmkenny
vdmkenny deleted the feat/mission-source-compiler branch October 13, 2026 06:25
vdmkenny pushed a commit that referenced this pull request Oct 13, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.13.0](v0.12.0...v0.13.0)
(2026-10-13)


### Features

* CANCEL stops a download on the GET MODS screen
([#1133](#1133))
([0f7ef45](0f7ef45)),
closes [#1040](#1040)
* definitions of mission source for luau-lsp
([#1143](#1143))
([3232a4b](3232a4b)),
closes [#1142](#1142)
* Luau's own parser reads mission source
([#1134](#1134))
([466f5cd](466f5cd)),
closes [#1132](#1132)
* mission scripts read as statements and lower back to the same bytes
([#1128](#1128))
([3e0b0e8](3e0b0e8)),
closes [#1127](#1127)
* OpenReliant has an icon
([#1161](#1161))
([cb84620](cb84620)),
closes [#670](#670)
* releases list checksums and attest their builds
([#1158](#1158))
([a861aaa](a861aaa)),
closes [#1157](#1157)
* scripts can have a missile home, as the AI's Screamers do
([#1154](#1154))
([f8eec1e](f8eec1e)),
closes [#1119](#1119)
* sltool dte build compiles mission source into a mission file
([#1137](#1137))
([6830dd2](6830dd2)),
closes [#1136](#1136)
* sltool dte decompile writes a mission as Luau source
([#1131](#1131))
([38e85b5](38e85b5)),
closes [#1130](#1130)
* the mods screen says which installed mods have an update
([#1135](#1135))
([a5542d6](a5542d6)),
closes [#1041](#1041)
* the music example replaces the main menu's piece and keeps its loop
point ([#1129](#1129))
([98a5f9a](98a5f9a))
* the reference of mission source, generated from the game's tables
([#1147](#1147))
([e061179](e061179)),
closes [#1146](#1146)
* the Windows executables carry version information and a manifest
([#1156](#1156))
([022abc2](022abc2)),
closes [#1138](#1138)


### Fixes

* GET MODS gives up on stalled servers, never holds up quitting, and
keeps remote links off the player's machine
([#1124](#1124))
([0d05d1e](0d05d1e)),
closes [#1114](#1114)
[#1069](#1069)
[#1070](#1070)
* the scripting API's definitions load in current luau-lsp
([#1141](#1141))
([87fa067](87fa067)),
closes [#1140](#1140)
* the scripting definitions load in luau-lsp's new type solver
([#1152](#1152))
([b049449](b049449)),
closes [#1139](#1139)


### Documentation

* a guide to writing missions, for beginners
([#1149](#1149))
([04289ef](04289ef)),
closes [#1148](#1148)
* the mission guide shows how to change one of the game's missions
([#1151](#1151))
([fb7d9e7](fb7d9e7)),
closes [#1150](#1150)
* why a trigger's code and fields aren't typed by its condition
([#1153](#1153))
([3a08501](3a08501)),
closes [#1144](#1144)

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Mission source: the compiler and sltool dte build

1 participant