Development¶
This page is for you if you want to build the editor from source, change the addon or write docs.
Building¶
Clone with submodules, the Godot addon in godot/addons/func_godot is one:
git clone --recursive https://github.com/Paraxdev/TrenchDot.git
Already cloned without it? Run git submodule update --init. The editor builds without the addon, but the tests and
the Godot project need it.
cargo run -p td_editor --release # start the editor
cargo run -p td_editor --release -- map.tdm # open a map
You need a GPU with Vulkan, DX12 or Metal, and on Linux an X11 or Wayland session.
Rust 1.95 is the minimum, egui 0.36 needs it. rust-toolchain.toml pins 1.98.1, the version CI uses, so rustup installs that on your first build.
Checks¶
Run ./tools/fmt --install once per clone. It enables a pre-commit hook that formats staged Rust.
./tools/fmt --check
python tools/gen_licenses.py --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace
cargo test -p td_editor --test e2e -- --ignored --test-threads=1
godot --headless --path godot --import # twice on a fresh checkout
godot --headless --path godot --script res://tests/run_tests.gd
godot --headless --path godot --script res://tests/build_showcase.gd
CI runs all of these, so run them before you push.
The e2e tests launch the real editor and drive it with simulated input. That needs a GPU and a desktop session, so
cargo test skips them unless you pass --ignored. CI runs them on Linux with software Vulkan instead. To test the
live link without a window, see Live link protocol.
Note
godot --headless --script renders nothing (screenshots and rendered checks come out black) and doesn't
run project autoloads, only the script itself. Write tests against data and nodes, not pixels or autoload state.
To time map builds, run godot --headless --path godot --script res://tests/bench_build.gd -- runs=5 threaded=1.
Don't use build_showcase.gd for that, it overwrites the committed showcase scenes.
Map files in git¶
.tdm maps are binary, see the .tdm map format. The repository's .gitattributes already marks
them. For readable diffs, all you need is the git config line from
Reviewing changes, once per clone.
Generated content¶
Some files are made by a script, not by hand. When you change what a script reads, run it again.
| Command | Regenerates |
|---|---|
cargo run -p td_formats --example demo_maps |
The test maps in godot/tests/maps and the small demo maps in godot/demo/maps |
python tools/fetch_demo_textures.py |
The demo's CC0 photo textures, see texture size |
python tools/fetch_demo_props.py |
The CC0 Poly Haven props in godot/models/polyhaven, with the alpha maps of glass and flames restored. --fix-alpha only restores them |
cargo run -p td_samples --bin showcase --release |
The demo Blockbench models |
cargo run -p td_editor --example export_addon_fgd |
The addon's core FGD files and the Gameplay entities pack template from builtin_entities.json and gameplay_entities.json, written into the addon submodule, then the demo project's installed copy of the pack |
python tools/gen_licenses.py |
The Rust crate table in LICENSES.md |
The Godot addon¶
The addon has its own repository, trenchdot_func, checked out at
godot/addons/func_godot. Its tests live here in godot/tests, since they need the demo project. To change the addon:
- Commit the change inside
godot/addons/func_godotand push it to trenchdot_func. - Stage the new submodule commit here with
git add godot/addons/func_godotand commit it with the TrenchDot changes that need it.
Push the addon first. A TrenchDot commit pointing at an addon commit GitHub doesn't have fails to check out in CI.
plugin.cfg's version matches Cargo.toml's
workspace version. Bump both together, the editor warns when a project's addon doesn't match it.
Documentation¶
The docs are the Markdown pages in docs/, and Zensical turns them into the site.
zensical.toml in the repository root sets it up, and docs/SUMMARY.md is the table of contents. A page missing there
still gets built, it just has no place in the navigation. docs/requirements.txt pins Zensical,
docs/stylesheets/td.css holds the colors, docs/javascripts/td.js the MCP switch and the glossary cards, and
docs/overrides replaces a few theme templates.
Run these from the repository root:
python3 -m venv .venv # once
. .venv/bin/activate # .venv\Scripts\activate on Windows
pip install -r docs/requirements.txt # once, and again when the pin changes
zensical serve # live preview on http://localhost:8000/TrenchDot/
zensical build --clean --strict # the build CI runs, lands in _site
python3 tools/check_site.py # the link check CI runs on _site
--strict fails the build on a link to a page or heading that doesn't exist. A mistyped SUMMARY.md entry, a missing
image or a page left out of the navigation get through it, tools/check_site.py catches those.
A heading's id is its text in lower case, spaces become hyphens and other punctuation is dropped, so ## path_corner
is #path_corner. The editor's help links in
help.rs point at these ids, and a test
checks them, so rename a heading only together with its links.
Callouts are GitHub style alerts: > [!NOTE], > [!TIP] or > [!WARNING] on a line of their own, with the text on
the quoted lines below. That way they render on GitHub too. Everything that belongs to a numbered step, a second
paragraph, an image or a code block, is indented by 4 spaces, or it falls out of the list.
Pictures for a page go in docs/assets/<page>/.
If a newcomer wouldn't know a term, give it an entry in GLOSSARY.md: a ## term heading with a one paragraph
definition. Its first use on every page then links to it and shows the definition when you hover it. Code, links and
headings are left alone.
.github/workflows/pages.yml builds the site for pull requests that touch docs/ or zensical.toml, and publishes it
to GitHub Pages once they reach main.
CI¶
CI skips pushes to main that only change docs or Markdown. Every other green push to main replaces the
rolling beta with fresh editor, addon, demo and content
downloads.
The editor downloads the addon and content from the GitHub releases only. itch.io is a storefront that gets the same
files on its own channels, addon, nature-pack, demo-content and so on. The rolling beta is the only build, so it
fills those plain channels.
| Zip in the release | Installed by |
|---|---|
func_godot-trenchdot-addon.zip |
Godot > Install or Update Addon… |
trenchdot-nature-pack.zip |
Godot > Add Content to Project… |
trenchdot-demo-content.zip |
Godot > Add Content to Project… |
The folders each zip holds are listed in
content.rs and
addon_install.rs, and tests
check the zip commands in ci.yml against them.
The editor finds the zips through the GitHub API. CI builds it with TRENCHDOT_CHANNEL=beta, so it looks in the
rolling beta first. A local build tries a release named after its own version first, then falls back to the beta. To
try a zip before it's published, point the editor at another server with the TRENCHDOT_RELEASES_API environment
variable.
CI also stamps each beta, in TRENCHDOT_BUILD for the editor and in the zipped addon's plugin.cfg, as
0.1.0-beta.202611041532.d194b57: the release number, the UTC commit time and the commit. That is how the editor
tells an addon from an older beta and offers the update. A local build keeps the plain version.
MCP¶
Contributing with an AI agent? AGENTS.md lists the rules and the checks a pull request has to pass.
The e2e tests drive the editor over the MCP server, and example_mcp_scripts_replay replays every script in
examples/mcp.
To drive an editor of your own from the shell:
trenchdot --mcp-http=7850 --default-prefs &
TRENCHDOT_MCP_URL=http://127.0.0.1:7850/mcp python3 tools/mcp_client.py call get_state
Set TRENCHDOT_MCP_TOKEN to the same value for both to try it with a token.
MCP in the docs¶
The MCP switch in the header, off by default, hides everything about MCP so the pages stay readable for people who
never touch it. On a page, put all MCP text in one ## MCP section at the very end, inside a td-mcp block:
<div class="td-mcp" markdown>
## MCP
What only MCP users need.
</div>
Keep the blank lines around the content, GitHub only reads the Markdown inside with them. Pages only about MCP belong
in the MCP part of SUMMARY.md, which the switch hides too.