Modular multiplayer 3D living-world game framework (Godot 4.3)
Find a file
Mark 7256c2b9b1
Some checks failed
CI / check (push) Has been cancelled
CI / export-linux (push) Has been cancelled
fix(admin-spirit): admin-spawned spirits default to manifested (was invisible, veilwalkers #15)
The admin toolkit's 'Spawn at look-point' created a spirit entity but left
it in presence mode (is_manifested == false), so the only feedback the admin
got was the Population counter ticking up. The mesh existed and was at the
right position but the SpiritComponent contract says presence = "no body",
so admins saw nothing.

Fix: in scripts/admin/world_edit_commands.gd::_apply_spawn, when def_id is
"spirit", set is_manifested = true and current_power = max(1, current_power)
on the SpiritComponent AFTER EntityRegistry.spawn_entity returns. The
scripted summon_spirit() path already sets these explicitly after on_attach,
so it still wins there. SpiritPopulation is also incremented to match what
summon_spirit does.

Tests:
  - tests/unit/test_spirit_spawn_visible.gd + .tscn: regression guard that
    spawns via the same WorldEditCommands.apply path the admin uses, asserts
    is_manifested == true and current_power >= 1, and (with a GPU renderer)
    counts purple pixels in the rendered viewport.
2026-10-06 16:25:42 -07:00
.forgejo/workflows ci: install Godot to /tmp not /opt (runner container /opt is read-only) 2026-09-29 19:14:54 -07:00
.gsd veilwalkers: address code-review findings on integration tests 2026-09-29 20:56:28 -07:00
data Phase 7 materials: data layer (tiers, lifecycle gates, craft spec) 2026-10-06 12:56:17 -07:00
docs docs: add SCRIPTING.md spell-author guide 2026-10-01 21:44:10 -07:00
scenes fix(admin-toolkit): auto-arm selection on Edit tab + regression test (#12) 2026-10-06 15:56:20 -07:00
scripts fix(admin-spirit): admin-spawned spirits default to manifested (was invisible, veilwalkers #15) 2026-10-06 16:25:42 -07:00
tests fix(admin-spirit): admin-spawned spirits default to manifested (was invisible, veilwalkers #15) 2026-10-06 16:25:42 -07:00
.gdlintrc fix: re-enable all gdlint rules per Mark/veilwalkers#4 2026-09-22 09:36:05 -07:00
.gitignore chore: ignore *.gd.uid files (Godot-generated, not for commit) 2026-10-06 15:57:49 -07:00
export_presets.cfg init: project bootstrap from archive 2026-09-21 11:42:50 -07:00
icon.svg init: project bootstrap from archive 2026-09-21 11:42:50 -07:00
icon.svg.import checkpoint: phase 2 complete (incl. godot 4.7 import artifacts) 2026-09-21 11:44:28 -07:00
LICENSE docs: add Apache-2.0 LICENSE 2026-09-22 09:21:53 -07:00
project.godot Phase 7 materials: data layer (tiers, lifecycle gates, craft spec) 2026-10-06 12:56:17 -07:00
README.md ci: add headless Linux/X11 release export job + pin Godot 4.3 2026-09-22 09:14:23 -07:00
SPEC.md init: project bootstrap from archive 2026-09-21 11:42:50 -07:00

VEILWALKERS

A modular, multiplayer, 3D living-world game framework for Android, Linux, and Windows, built with Godot 4.3 (GDScript, GL Compatibility renderer).

Inspired by Luanti/Minetest's moddability — but instead of a box/voxel world, VEILWALKERS is a living world: entities wander, metabolize planar energy, and reproduce on their own. Players are planeswalkers: they harvest mana from five planes (Aether, Ember, Tide, Grove, Void) and channel it into world-changing effects. Administrators (and trusted builders) get a full in-game creation toolkit: spawn, edit, and script entities live, terraform the ground, and manage the server's security — all without restarting the server.


Feature Map

System What it does Where
Living world core Component-based entities (health, wander, reproduction, energy) ticked by a LivingWorld scheduler; population cap; JSON save/load scripts/core/
Self-reproducing entities ReproductionComponent — entities store energy and spawn offspring when they can afford it scripts/core/components/
Multiplayer ENet server-authoritative networking, host/join menu, entity snapshot sync, chat scripts/net/, scripts/autoload/network_manager.gd
Security Role system (guest/player/builder/admin), permission checks, rate limiting, audit log scripts/sec/, scripts/autoload/security.gd
Script sandbox Runtime-compiled GDScript entity scripts restricted to a whitelisted ScriptEntityAPI; blacklist scan; per-tick CPU budget with auto-detach scripts/autoload/script_engine.gd
Admin creation toolkit In-game window (F1): entity browser/spawner, live property editor, script editor with templates + compile check, terraform brush, save/load, security/points panel scenes/admin/, scripts/admin/
Creator points Server-side currency for world-altering actions; passive earning; grant/spend audited scripts/autoload/creator_points.gd
Player systems Inventory (24 slots), skills (harvesting/channeling/weaving/terraforming with XP levels), 5-plane mana pool scripts/player/
Controls WASD + mouse (desktop), virtual joystick + touch look (Android) scenes/player/, scenes/hud/

Running

  1. Install Godot 4.3 (standard build, exact version — see Forward-compat policy below). Open project.godot.
  2. Press F5 → Host World to start a server, or run a second instance and Join World (127.0.0.1:43210). Headless server (no editor, no display):
    godot --headless -- --server          # default port 43210
    godot --headless -- --server --port 43210
    
  3. In game: WASD move, mouse look, F harvest glowing entities, 1/2/3 channel mana effects, F1 admin toolkit, Enter chat.

Exporting

export_presets.cfg ships with skeleton presets for Windows Desktop, Linux/X11, and Android (arm64). Install Godot export templates (Editor → Manage Export Templates), and for Android configure the Android SDK in Editor Settings, then Project → Export.

CI exercises the Linux/X11 release export on every push to main: it downloads a pinned Godot 4.3 binary + matching export templates, runs godot --headless --export-release "Linux/X11" headlessly, smoke-tests the produced binary's --version, and archives it as the veilwalkers-linux-x86_64 workflow artifact (retained 14 days). A failing export fails the export-linux job and the overall workflow, so a broken preset can't merge undetected. See .forgejo/workflows/ci.yml.

Forward-compat policy

VEILWALKERS is built against Godot 4.3 exactly — not "4.3 or newer". project.godot pins config/features=PackedStringArray("4.3", "GL Compatibility"), and the Linux/X11 export CI job downloads the official 4.3 stable binary regardless of which Godot the runner happens to have installed.

Newer Godot releases have already broken us once: under Godot 4.7 an inner VirtualJoystick class collided with the engine's built-in VirtualJoystick (a native class shadowing the script), which was fixed in commit c16562f by renaming the script's class to TouchJoystick. The lesson generalizes — Godot periodically introduces built-in classes whose names collide with existing user code, and the only safe response is to lock the editor version until the collision surface has been audited.

Concretely:

  • Dev hosts: install Godot 4.3 stable. A 4.4+ editor will open the project but may silently re-export the project with newer metadata that 4.3 can't read, or shadow a user class.
  • CI: the export-linux job downloads Godot 4.3 explicitly; don't replace that with ${{ runner.godot }} style indirection.
  • Upgrading: bumping the target version is a deliberate migration task (re-test every system, audit for native-class collisions, regenerate .godot/ import artifacts on the new version, then re-pin CI). It's not something the CI does for you.

Headless verification

All tests run without a display. From the repo root:

godot --headless --path . res://tests/e2e.tscn                                # integrated smoke (34 checks)
godot --headless --path . res://tests/unit/test_mana_pool.tscn                # 5-plane mana: add/spend/cap
godot --headless --path . res://tests/unit/test_skill_book.tscn               # XP/level math + leveled_up signal
godot --headless --path . res://tests/unit/test_inventory.tscn                # add/remove/stack + slot cap
godot --headless --path . res://tests/unit/test_net_protocol_security.tscn    # payload validation + role/perm matrix
godot --headless --path . res://tests/unit/test_living_world.tscn             # tick scheduler + population cap

Each test prints PASS:/FAIL: lines and a final UT_RESULT <name> pass=N fail=N line, and exits with code 1 if any check failed. CI runs the full set; see .forgejo/workflows/ci.yml.

Modding / extending

Everything is modular by contract (see docs/ARCHITECTURE.md):

  • New entity: drop a JSON def in data/entity_defs/ (shape, color, components, script).
  • New behavior: write a component in scripts/core/components/ and reference it in the def.
  • New live script: use the toolkit's Script tab — scripts are sandboxed to ScriptEntityAPI.
  • New permission: add a constant in scripts/sec/permissions.gd and check Security.require.

Safety notes

The script sandbox blocks obvious escape hatches (OS, FileAccess, ClassDB, load(), get_tree(), …) and enforces a per-tick CPU budget — but GDScript is not a formally hardened sandbox. Only grant the builder/admin roles (which carry entity.script) to people you trust, the same trust model as Luanti's server/basic_privs privileges.