Architecture¶
This page is a map of Goanna's code: what runs where, how data gets from a Luanti server onto the screen, and which file to open for a given change. Read it before reading the code. It names files and functions so you can jump straight to them, and it links to the systems pages for how each part works in detail rather than repeating them. Where this page and the code disagree, the code is right; please fix the page.
Goanna is a Godot 4.5 GDExtension that speaks Luanti's own network protocol to ordinary, unmodified servers. Luanti's client logic does the protocol, the world state, movement and the meshing rules; Godot does the window, the scene, the renderer, materials, lighting, sound and the interface.
The big picture¶
| Path | What it holds |
|---|---|
src/ |
The C++ GDExtension, Goanna's own code |
src/transplant/ |
Luanti files copied in and changed as little as possible |
luanti/ |
Luanti, a pinned submodule; part of it is compiled straight from here |
godot-cpp/, whisper.cpp/ |
Submodules: the GDExtension bindings, and speech to text |
project/ |
The Godot project: GDScript, scenes and shaders (its map) |
goanna_server_mod/ |
An optional server mod that grants extra capabilities |
tools/ |
Developer and agent tools (its index) |
The extension registers four classes for GDScript
(register_types.cpp): GoannaClient, the
whole client; GoannaIrisEffect, a shader pack runner; GoannaRipples;
and GoannaSpeechInput. Almost everything goes through GoannaClient.
A running client has these threads:
- Godot's main thread. All GDScript, every Godot object, and every
GoannaClientmethod.project/game/main.gdcalls the client once or more per frame from_process. - The session thread. One per connection, started by
GoannaSession::startand runningGoannaSession::threadMain(goanna_session.cpp). It receives and handles packets, decodes map blocks into Luanti's ownMap, and sends position updates and block acknowledgements. - Luanti's connection threads. The session uses Luanti's reliable UDP
layer, compiled from
luanti/src/network/mtp/, which runs its own send and receive threads (ConnectionSendThread,ConnectionReceiveThread). Goanna does not touch them directly. - Mesh workers. A
MeshPool(goanna_mesh_pool.h) of several threads that build near block meshes, near regional batches, far region meshes and baked surface meshes. - The prepared terrain worker. One
LodStoragethread (goanna_lod_storage.h) that reads, derives and caches far LOD chains on disk. - Short lived threads. The horizon panorama bake
(
GoannaClient::horizon_bake_pollstarts one at a time), the item icon rasteriser (ItemIconCache::flushingoanna_item_icons.cppsplits a large batch across threads and joins them before returning), and speech recognition (GoannaSpeechInput, one job at a time). - Godot's renderer. Goanna adds no code here except the Iris
CompositorEffect, whose programs are compiled and drawn on the render thread (goanna_iris_effect.h).
flowchart LR
server[("Luanti server")]
net["Luanti connection threads"]
session["Session thread<br/>GoannaSession::threadMain"]
main["Godot main thread<br/>main.gd and GoannaClient"]
pool["Mesh workers<br/>MeshPool"]
lodst["Prepared terrain worker<br/>LodStorage"]
horizon["Horizon bake thread"]
render["Godot renderer"]
server <-->|UDP| net
net <--> session
session -->|"map under mapLock, queues under mutexes"| main
main -->|"pose, keys, interact, chat"| net
main -->|"captured job input"| pool
pool -->|"MeshPool::next"| main
main <-->|"request and next"| lodst
main <-->|"snapshot and result"| horizon
main -->|"meshes, materials, nodes"| render
The main thread sends packets as well as the session thread: calls such as
GoannaSession::sendInteract and sendChat run on the main thread, and
Luanti's Connection::Send queues them for its own send thread.
Threads and the handoff¶
The rule¶
The session thread and the worker threads never touch Godot objects: no
Node, no Resource (ArrayMesh, Material, Texture2D, Image), no
scene tree. They work on plain C++ and Luanti types, and the main thread
turns their results into Godot objects. The rule is stated in
CONTRIBUTING.md, "Code style", and for jobs in
goanna_mesh_pool.h: a job must not touch
Godot objects or read live client state, and carries everything it needs,
captured on the main thread when it is made.
Why: Godot's scene and resources belong to the main thread and are not safe to change from another one, and the session thread runs on its own clock, so anything it touched would race whatever the frame was doing with the same object. Keeping every Godot object on one thread also gives one place where a mesh replaces its predecessor, which is what lets a far region keep its old mesh until the new one is published.
Godot's packed arrays (PackedVector3Array and the rest) are values, not
objects. Near batch, far-region and prepared baked-surface jobs fill them
on workers in
goanna_client_jobs.cpp. The main
thread creates ArrayMesh resources. Near batches publish together; far
regions and baked-surface chunks stage one surface at a time and keep
their old visible geometry until the replacement is complete.
From the session to the main thread¶
Everything the session receives lands in one of two places:
- The map, under
mapLock().GoannaSessionkeeps Luanti'sMap(goanna_map.h), the transplantedLocalPlayer, the active objects, the inventory and the interaction state together under one mutex,m_map_mutex, exposed asGoannaSession::mapLock(). The main thread takes it to read blocks (poll_blocks), step the player (step_player), point and dig (step_interact) and step entities (sync_entities). Map changes also push block positions ontom_new_blocks, which the main thread drains withGoannaSession::takeNewBlocks. - Queues under their own mutexes. Chat and shown forms
(
m_hud_mutex), sounds, particles and dug node bursts (m_sound_mutex), sky state (m_sky_mutex), media (m_media_mutex), the player pose (m_pose_mutex), and server mod options and replies (m_server_opts_mutex). Each has atakemethod that swaps the vector out under the lock, for exampleGoannaSession::takeChat. TheGoannaClientmethod of the same name (take_chat,take_sounds,take_shown_formspecs,take_particles) converts the plain structs toArrayandDictionaryfor GDScript.
The session's plain structs (ChatLine, SoundEvent,
ParticleSpawnerEvent, NodeDugEvent, ShownFormspec in
goanna_session.h, SkyState in
goanna_sky.h) are the types that cross.
From the main thread to the workers¶
The main thread captures a job's whole input, submits it with
MeshPool::submit, and later collects results with MeshPool::next. Jobs
are keyed (MeshJobKey: near block, LOD region, near batch or surface) and
stamped with a generation, so a newer job replaces a queued one with the
same key, and a result built from data that has since changed is dropped
on collection. Admission and order come from one priority rule shared by
every scheduler, ViewPriority in
goanna_schedule.h.
| Job | Captured input | Built on the worker |
|---|---|---|
NearBlockJob |
a MeshMakeData copy of the block and its neighbours (gatherMeshData), and a light field |
Luanti's MapBlockMesh and per vertex light |
NearBatchJob |
the member blocks' surfaces | merged packed arrays per material, and occluder triangles |
LodRegionJob |
a LodRegionSnapshot of the region's LOD chains |
a LodRegionMesh |
SurfaceJob |
baked surface tiles | the surface mesh arrays |
LodStorage and the horizon bake follow the same shape with their own
queue and mutex: LodStorage::request in, LodStorage::next out, and
horizon_bake_request and horizon_bake_poll for the panorama, whose
snapshot is extracted on the main thread
(goanna_horizon.h).
From the main thread to the session¶
Settings the session reads are set before connect_to or through small
setters (setContentHold, setTextureMap, wantedRange). The player pose
and pressed keys go in through GoannaSession::setPlayerPose and
setPlayerKeys under m_pose_mutex, and the session thread sends them in
its position packets.
The layers in src¶
Each file in src/ starts with a comment saying what it is for; read that
first. Tests sit beside the code as *_test.cpp
(testing).
Session and protocol¶
goanna_session.h and
goanna_session.cpp: GoannaSession, a
Luanti client session with no rendering. It implements Luanti's IGameDef
and con::PeerHandler, so Luanti's node and item definition managers,
MapBlock and inventory code work against it unchanged. threadMain runs
the connection, handle dispatches each packet to an on... handler
transplanted from Client::handleCommand_*, and SessionState tracks the
handshake from Connecting to Ready. It also owns the goanna:v1 mod
channel to the server mod (joinGoannaChannel, onModChannelMsg), the
transplanted movement step (stepPlayer), and pointing and digging
(stepInteract). See protocol coverage
for what each packet does and how GDScript reads it.
The GDExtension surface¶
goanna_client.h and
goanna_client.cpp: GoannaClient, a
Node3D that owns one GoannaSession and everything drawn from it. Every
method GDScript can call is registered in GoannaClient::_bind_methods, in
goanna_client.cpp with the connection, the plain settings and
render_stats. The main groups are:
- connection and status:
connect_to,disconnect_from_server,status; - the per frame steps:
poll_blocks,update_lod,step_player,step_interact,sync_entities,update_lights,step_node_animation; - data for the interface:
take_*,hud_state,inventory_state,item_icon,texture,sky_state; - settings: the
set_*methods, mostly mirrored fromproject/game/.
The rest of the class is in one file per area, named in the sections
below; the list is also at the top of goanna_client.cpp. The files share
their includes and a few helpers through
goanna_client_internal.h.
goanna_client_player.cpp: the local player (step_player,set_player_pose), pointing and digging (step_interact), the wielded item, the entity renderer hookup, and the map queries GDScript asks (node_name_at,ground_heightand the rest).goanna_client_interface.cpp: chat, the HUD, inventories and item icons, forms, sounds, particles, media, and the overseer view.goanna_client_jobs.cpp: the three mesh job types, what captures and submits them, andlodCollectMeshes, which takes every finished job back. It is the only part ofGoannaClient's code that runs on a worker.
GoannaClient::render_stats reports where the client's time goes, which
the control channel and benchmarks read.
The map, the store and near meshing¶
goanna_map.h: Luanti's ownMap, with sectors created as blocks arrive.goanna_mesher.h: meshing on top of Luanti'sMapBlockMesh, split intogatherMeshData(reads the map, under the lock) andmeshGathered(runs on a worker). Output is Irrlicht CPU mesh buffers, whichGoannaClient::poll_blocksconverts to Godot arrays.goanna_luanti_client.h: the stand-inClientthat the transplanted meshing code calls.goanna_store.h:BlockStore, every block the server sent, on disk, one directory per server, written as blocks arrive inGoannaSession::onBlockData.- In
goanna_client_near.cpp:poll_blocksdrains new blocks nearest and most visible first, andnearAssignandnearPublishBatchmerge finished blocks into regional batches by material.nearSubmit, which hands a block to the workers, andnearRebuild, which submits a dirty batch, are ingoanna_client_jobs.cpp.
See mesh attributes for the vertex layout every terrain mesh carries, and animated node tiles.
The far field and LOD¶
goanna_lod.h: a chain of coarser levels per mapblock (cells of 1, 2, 4, 8 and 16 nodes) and the per region tier mesher that works from chains alone.goanna_lod_storage.h: prepared chain records on disk and theLodStorageworker.goanna_occlusion.h: the voxel occupancy and ambient occlusion tracer, shared by near and far.goanna_schedule.h: the one priority rule all the build queues share.goanna_horizon.h: the horizon panorama beyond the drawn far field.goanna_surface.handgoanna_fine.h: baked surface tiles and fine node data, both sent by the server mod.- In
goanna_client_lod.cpp:update_lodretiers blocks as the player moves, takes server summaries and requests more, and publishes finished regions (lodPublishRegion);lodRebuild, at the end ofpoll_blocks, picks the dirty regions to capture for the workers. - In
goanna_client_far.cpp: the far field beyond the live map.lodUpdateFardraws stored blocks within the granted distance,lodRequestSummariesandlodTakeSummariesask the server mod for summaries and read them, andhorizon_bake_requestandhorizon_bake_pollmake the horizon panorama. - In
goanna_client_jobs.cpp:lodBuildRegioncaptures a region for its job, andlodCollectMeshestakes finished regions back. - In
goanna_client_surface.cppandgoanna_client_fine.cpp: the main thread side of baked surface tiles and fine node data.
See far rendering, prepared terrain storage, terrain surfaces and baked terrain.
Materials and textures¶
goanna_textures.h: the texture and shader sources the transplanted code asks for.GoannaTextureSourceruns Luanti's own texture modifier language (ImageSource) on CPU images, with Godot decoding the media (goanna_image_hooks.h).goanna_materials.h: the per node classifier, a material class and a Minecraft block for each node.goanna_overlay_companions.handgoanna_texture_size.h: LabPBR companions for composed textures, and the texture resolution cap.goanna_lava.handgoanna_flame.h: the special surfaces.- In
goanna_client_materials.cpp:keyForIrrturns a Luanti material into aMaterialKey, andmaterialForbuilds and caches the Godot material for it, from the shaders inproject/shaders/(nodes_array.gdshaderfor most terrain). Animated node tiles (step_node_animation) are there too.
See materials, and the lava, fire, ice, portal and water pages.
Entities¶
Players, mobs and dropped items are active objects. Their state is
Luanti's: GoannaActiveObject, the state half of GenericCAO,
transplanted in
src/transplant/client/content_cao.h
and stepped by GoannaSession::stepObjects. Drawing them is Goanna's:
goanna_entities.h:EntityRenderer, sprites, cubes, meshes, nametags and attachments in the scene;goanna_models.h: Luanti's model loaders behind a stub scene manager, conversion toArrayMeshwith bones, and the animator;goanna_animation.h,goanna_limbs.handgoanna_upright_sprite.h.
GoannaClient::sync_entities, in
goanna_client_player.cpp, takes the
map lock, steps the objects and calls EntityRenderer::sync. See the
first person body and
knees, elbows and strokes.
Light¶
- Vertex light and ambient occlusion:
goanna_light.h. Luanti bakes light into vertex colour; Goanna turns that off (g_goanna_no_light, ingoanna_mesh_flags.h) and writes light into its own attribute, because Godot lights the scene and would otherwise light it twice. - Node lights:
GoannaClient::harvestLightsnotes bright nodes as blocks are meshed, andupdate_lightskeeps a budget ofOmniLight3Ds on the nearest, both ingoanna_client_light.cppwith the ambient motes andsky_state. - Sky, sun and fog:
goanna_sky.hdecodes what the server sent;sky_statehands it tomain.gd's_apply_skyandsky_director.gd.
See mesh attributes, sky orchestration and graphics tiers.
Iris shader packs¶
src/iris/ loads an Iris or OptiFine shader pack
(iris_pack.h), translates its GLSL for
Vulkan (iris_glsl.h), and runs its screen
space programs as a CompositorEffect
(goanna_iris_effect.h). The
gbuffers programs are not run. main.gd loads a pack when
GOANNA_SHADERPACK names one, or else the one the Shader pack setting
chose from user://shaderpacks (project/game/shader_packs.gd). See
Iris shader packs and
shader pack testing.
Transplanted Luanti code¶
Luanti code reaches Goanna in three ways, in this order of preference:
- Compiled from the submodule, untouched.
cmake/luanti_core.cmakebuilds a subset ofluanti/srcinto theluanti_corestatic library: the network layer, serialisation, node and item definitions,MapBlockandMap, inventory, authentication, the raycast, and Irrlicht's CPU only image and model code. - Given a stand-in. Where upstream code needs something Goanna does
not have, Goanna supplies a class with the name upstream expects:
Clientingoanna_luanti_client.h, the image hooks ingoanna_image_hooks.h. - Copied into
src/transplant/. Files that reach for Irrlicht's renderer or forClientitself: the block mesher (client/mapblock_mesh.cpp,content_mapblock.cpp,node_visuals.cpp), the image source, the active object, the wield mesh and item visuals, the movement code (localplayer.cpp,collision.cpp) and the raycast. They keep Luanti's style and headers and are never restyled.luanti_shims.cppholds a few verbatim pieces of server only files the core needs.
luanti/ itself is never patched. The rules, and the inventory of every
copied file, are in transplanting Luanti code.
Smaller pieces¶
goanna_item_icons.handgoanna_icon_raster.h: inventory icons for node items, drawn as upstream draws them, on the CPU.goanna_raycast.h: the transplanted pointed thing raycast.goanna_radial_form.handgoanna_mining_cycle.h: the shape of a partly dug node and the clock of a dig.goanna_ripples.h:GoannaRipples, the water around the eye (weather, "Ripples").goanna_grass.h: the optional procedural grass.goanna_render_scope.h: per player shader globals for local multiplayer.goanna_speech_input.h:GoannaSpeechInput, voice typing through whisper.cpp.
The GDScript layer¶
The Godot project's folders are mapped in
project/README.md. The parts a newcomer meets
first:
| Where | What |
|---|---|
project/menu/menu.gd |
The main scene: start a local game or join a server |
project/game/main.gd, main.tscn |
The game scene, which owns the GoannaClient |
project/game/local_play.gd |
Split screen: one game scene per player slot |
project/net/local_server.gd |
Running a Luanti server on this machine |
project/ui/game_ui.gd, formspec.gd |
HUD, chat, inventory, and server forms |
project/game/effects/ |
Sky, clouds, weather, particles, water and wakes |
project/shaders/ |
Every world shader |
project/agents/ |
The control channel and the player agent channel |
project/input/gamepad.gd |
Controllers; the one autoload, Gamepad |
From the menu to a session¶
run/main_scene in project/project.godot is res://menu/menu.tscn. For
a local game the menu starts a Luanti server through local_server.gd and
waits for it; to join, it takes the address from the form. Either way it
sets the GOANNA_* environment variables (GOANNA_HOST, GOANNA_PORT,
GOANNA_NAME, GOANNA_PACK and others) and calls _go_to_game, which
changes scene to res://game/main.tscn, or to local_play.tscn when there
is more than one local player.
main.gd's _ready reads those variables and the saved settings in
user://goanna.cfg, creates GoannaClient.new(), adds it and the
interface (ui/game_ui.tscn) to the scene, builds the camera, lights and
environment, and calls client.connect_to(...). With GOANNA_CONTROL set
it also adds the control channel. Starting Godot with GOANNA_HOST already
set skips the menu, which is how the tools start test clients.
The game scene¶
main.gd's _process drives the client in a fixed order each frame:
step_node_animation, the sky (_apply_sky), status for the connection
overlay, then movement: step_player and step_interact when walking, or
set_player_pose for the free flying camera. After that come
poll_blocks, prune_blocks every two seconds,
update_lights, update_motes, update_lod, sync_entities, and the
environment and wield updates. Other nodes read their own queues:
game/audio.gd takes sounds, game/effects/particles.gd takes particles
and dug node bursts, and ui/game_ui.gd takes chat and forms and reads
hud_state and inventory_state.
The formspec renderer, ui/formspec.gd, is a GDScript rewrite of
GUIFormSpecMenu's layout rules, not a transplant. See
interface style and
formspec conformance.
The life of a session¶
A walk from joining to digging a node, naming the functions on the way.
Connecting¶
main.gdcallsGoannaClient::connect_to, which clears what the last session left, makes aGoannaSession, and callsGoannaSession::start. That opens the block store and starts the session thread.connect_tothen starts theMeshPool.- On the session thread,
threadMainconnects and sendsTOSERVER_INIT(sendInit).onHello,startAuth,onSrpBytesSBandonAuthAcceptcarry the handshake and SRP login. onNodeDef,onItemDef,onAnnounceMediaandonMediacollect the content. When everything has arrived,maybeReadysetsSessionState::ContentReady.- On the main thread,
poll_blockscallsGoannaSession::prepareContentIfReady. It loads the media and any texture pack into the texture source, fills Luanti's node visuals (NodeVisuals::fillNodeVisuals), builds the material table and the animation arrays, and sets a flag. It runs on the main thread because Godot decodes the media images (goanna_image_hooks.h).setContentHoldcan hold it while material bundles download. - The session thread sees the flag, sends
TOSERVER_CLIENT_READY(sendReady), reachesSessionState::Readyand joins thegoanna:v1channel. If the server mod answers, its options arrive inonModChannelMsg.
The first blocks on screen¶
TOCLIENT_BLOCKDATAarrives.GoannaSession::onBlockData, under the map lock, deserialises the block into the map, queues it and its neighbours withqueueBlockUpdate, and queues an acknowledgement. Out of the lock it writes the bytes to the store.threadMainsends the acknowledgements (sendGotBlocks) and the player position (sendPlayerPos).- Next frame,
poll_blockstakes the map lock and the new blocks (takeNewBlocks), orders them withViewPriority, and, within a time budget (6 ms by default), callsnearSubmitfor each:gatherMeshDatacopies the block and its neighbours, and aNearBlockJobgoes to the pool. Blocks beyond the near distance go to their far region instead (lodAssign). - On a worker,
NearBlockJob::runmeshes the copy with Luanti'sMapBlockMesh(meshGathered) and samples vertex light. update_lodcallslodCollectMeshes, which moves finished near blocks intom_near_ready. The nextpoll_blocksconverts each into Godot arrays by material (NearSurface), notes its lights, and callsnearAssignto mark its region dirty.nearRebuild, at the end ofpoll_blocks, submits aNearBatchJobfor each dirty region. When it returns,nearPublishBatchbuilds theArrayMesh, binds materials frommaterialFor, and swaps it onto the region'sMeshInstance3Dwith its occluder. Godot draws it next frame.
Far terrain runs the same way at a coarser grain: lodRebuild captures
regions into LodRegionJobs, and lodCollectMeshes publishes them with
lodPublishRegion.
Digging a node¶
main.gdcallsstep_interactevery frame with the button state.GoannaClient::step_interacttakes the map lock and callsGoannaSession::stepInteract.stepInteractcasts the pointing ray with the transplantedcontinueRaycast, works out the dig time from the tool's capabilities, and sendsINTERACT_START_DIGGING. As the dig goes on it raises the crack level and queues that block withqueueBlockUpdate. The nextpoll_blocksremeshes it inline withmeshBlock, not on a worker, because each crack step remeshes it and the crack level comes from the main thread's interaction state. Burst events go intom_dug_nodesforparticles.gd.- When the dig completes it predicts the result as Luanti's client does
(
removeNodeAndUpdate, or the node'snode_dig_prediction), queues the affected blocks (queueBlocksAround), and sendsINTERACT_DIGGING_COMPLETED. - The server decides. A dig it accepts comes back as a node change,
TOCLIENT_REMOVENODEorTOCLIENT_ADDNODE, handled byonRemoveNodeoronAddNode; a dig it refuses makes it send the block again (SetBlockNotSentin Luanti'sserverpackethandler.cpp), which arrives throughonBlockData. Either way the session thread updates the map and queues the blocks, and the nextpoll_blocksremeshes them as above.
Outside the client¶
The server mod¶
goanna_server_mod/ is optional. A
server without it renders as any vanilla client renders it; the mod only
grants. It talks over the goanna:v1 mod channel, which the session joins
after CLIENT_READY. It relays every goanna_* setting as an option the
client reads (GoannaClient::server_options, for example the far
rendering grant), and its submods answer requests: far summaries
(far_summary.lua), fine node data (fine.lua), baked surfaces
(surface.lua), shared dig damage (damage.lua), remembered dig progress
(dig_progress.lua), and the director (director/). The client takes
grants and terrain only from messages the server itself sent, never from
another player on the channel (the empty sender test in
GoannaSession::onModChannelMsg). Operators set
it up through hosting; the boundaries it keeps are in
capabilities.
The control channel and agents¶
With GOANNA_CONTROL set, main.gd adds
project/agents/control_channel.gd,
a loopback socket that runs commands inside the client. tools/goanna-mcp
and tools/goanna-control talk to it. The player agent channel
(player_agent_channel.gd) is a separate, narrower interface for an agent
playing the game. Start with the agents section:
agent interfaces,
the control channel and
the director.
Tools¶
tools/README.md lists every tool. The ones a
developer meets first: tools/goanna-headless starts test clients off the
desktop and guards the GPU, tools/goanna-render takes rendered frames,
tools/check-style.sh is the text style gate, and tools/docs/ builds
this site. The rules for running clients are in
agent interfaces, "Rules for test
clients".
Where to start¶
| I want to change | Start in |
|---|---|
| How a packet is handled, or handle a new one | GoannaSession::handle and its on... handlers in src/goanna_session.cpp; protocol coverage |
| What GDScript can call | src/goanna_client.h, and GoannaClient::_bind_methods |
| Movement and collision | GoannaSession::stepPlayer, src/transplant/localplayer.cpp, collision.cpp |
| Pointing, digging, placing | GoannaSession::stepInteract |
| How a node is meshed | src/goanna_mesher.cpp, src/transplant/client/content_mapblock.cpp (upstream's rules; see transplanting first) |
| Vertex data on terrain | GoannaClient::poll_blocks, src/goanna_lod.cpp; mesh attributes |
| Which block is built first | src/goanna_schedule.h |
| Far terrain | GoannaClient::update_lod, lodTierFor, src/goanna_lod.cpp; far rendering |
| A terrain material or shader | GoannaClient::materialFor, src/goanna_materials.cpp, project/shaders/nodes_array*.gdshader; materials |
| Mobs, players and items | src/goanna_entities.cpp, src/goanna_models.cpp |
| Sky, fog and the sun | main.gd _apply_sky, project/game/effects/sky_director.gd; sky orchestration |
| Weather and water | project/game/effects/; shader weather, water optics |
| Graphics presets and switches | project/game/graphics_profiles.gd, render_features.gd; graphics tiers |
| HUD, inventory and forms | project/ui/game_ui.gd, project/ui/formspec.gd |
| The main menu | project/menu/menu.gd |
| A control channel command | project/agents/control_channel.gd; control channel |
| Something the server must grant | goanna_server_mod/, then read it with server_options |