7.0 KiB
For developers
How the code is organised, how to build and test it, and where the engineering notes are.
Build
Requirements: a JDK 25 installed (the Gradle toolchain compiles with it; there is no
auto-download). Gradle itself and Spotless currently need to run on Java 21, e.g.
JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 ./gradlew … on Debian/Ubuntu.
./gradlew spotlessApply build # compile, format, run the tests
./gradlew fatJar # build/libs/llamamod-<ver>-all.jar
java -jar build/libs/llamamod-*-all.jar # launcher
java -jar build/libs/llamamod-*-all.jar --editor # the editor alone
./gradlew packageLinux # tar.gz with its own Java runtime
./gradlew packageWindows # zip with LlamaMod.exe + runtime (built on Linux)
./gradlew packageAll # both systems, full and launcher (no editor) edition
Details (jlink, launch4j, editions, version and build id): notes/llamamod-app.md.
Tests that need the game. Golden and in-game-data tests read the CD folder given by
-Dllamamod.cd=<…/Setup/<lang>/Cd> or the environment variable LLAMAMOD_CD (default:
~/.wine-lama/iso-dk/Setup/Eng/Cd); if that folder does not exist they are skipped. Byte-exact round trips over all level WADs (WAD, TRAK, MAP, TEXT, strat code) are the
safety net for every format change.
Code map
Package root src/main/java/com/eliasmueller/llamamod/:
| Package | Content | Notes |
|---|---|---|
format |
WAD, TRAK, STPC, MAP, TEXT, collision, COR | Reference |
format.strat |
strat disassembler, assembler, script view, compiler | Strats, notes/strat-language.md |
format.stpcmerge |
importing object types between levels (code, models, textures, sounds, texts) | notes/editor-cross-import.md |
levelbuild |
Java level builder (LevelBuilder, LevelScript) |
Levels from scripts |
mods |
mod framework (Mod, ModContext, ModManager, options) and the built-in mods |
notes/llamamod-app.md, notes/mod-*.md |
exe |
patching groove.exe: PE image, code caves, a tiny x86 emitter |
notes/llamamod-app.md |
platform |
install from ISO/CD, data folder, config, registry, Wine/Windows launch | notes/llamamod-app.md |
save |
KSV save games, TestLevel chapters | Game files |
ui |
launcher window | |
editor.model, editor.io |
scene model, commands/undo, project format, WAD import/build | notes/editor-api.md, notes/editor-format.md |
editor.render |
OpenGL viewport | notes/editor-render.md |
editor.ui |
editor shell and plugin API | notes/editor-ui.md |
editor.tools.* |
editor plugins: geometry, objects, environment, textures, catalog (cross-world import), strats, playtest, gltf, help | notes/editor-tools-*.md, notes/editor-playtest.md |
Writing a mod
A mod implements Mod: id, name,
description, category (LEVELS, GAMEPLAY, VISUAL, CHEATS), options() and apply(ModContext).
apply reads files of the game as earlier mods left them (ctx.read) and writes replacements
(ctx.write); ctx.exe() gives access to groove.exe for patches. Mods are registered in
ModRegistry. LlamaMod caches
mod outputs and never touches the original files.
Good examples: mods/testworld/ (a
level built in code), mods/arena/ (level
- new strat types, walkthrough: Arena),
mods/fog/(data-only change of all levels),mods/practice/(exe code caves with hotkeys). Full API: notes/llamamod-app.md.
Every mod should be checked in the running game; tools/apptest.py does
this unattended on Linux (Tools).
The editor manual
docs/manual/ is the editor's user manual and its built-in help: the build
bundles the folder into the jar (processResources in build.gradle), the Help window renders it
with its own small Markdown converter, and F1 maps editor topics to chapter anchors
(editor/tools/help/context-help.txt). Rules for that folder:
- Only the Markdown subset described at the top of
index.md; every chapter must be linked fromindex.md; links must stay insidedocs/manual(the help window cannot open other files). - Blocks between
generated:NAMEcomments (shortcuts, menus, mod reference) are rewritten from the live program byManualTest– do not edit them by hand. ./gradlew test --tests '*ManualTest*'checks links, anchors and images.
Maintenance notes: notes/editor-manual.md.
Engineering notes
notes/ is the project's lab notebook: one file per format, editor area and
mod, with addresses in groove.exe, confidence levels, what was verified in the game and known
limits. Process material (agent briefs, review reports) is in notes/dev/.
Releasing
Releases are built by Gitea Actions (.gitea/workflows/release.yml):
- Add a
## <version> – …section at the top of CHANGELOG.md (it becomes the release text; the workflow fails if it is missing). git tag v<version> && git push origin v<version>
The workflow builds and tests (tests that need the game CD skip themselves on the runner), runs
packageAll with -PreleaseVersion=<version> (the tag decides the version; without it
build.gradle's default is used), writes SHA256SUMS.txt and creates the release with the four
packages via tools/release/publish_gitea.py (standard library
only; re-running replaces files of the same name). .gitea/workflows/build.yml
builds every push.
Requirements on the Gitea side: Actions enabled ([actions] ENABLED = true), an act_runner with
the label ubuntu-latest (an image with python3, curl/network access – the build downloads the
Temurin JDK/jmods and launch4j), and an attachment size limit above ~60 MB ([attachment] MAX_SIZE
in app.ini). If the built-in token may not create releases, add a repository secret
RELEASE_TOKEN with write access. Locally the same packages come from
./gradlew -PreleaseVersion=<version> packageAll (output in build/dist).