Chickens Must Die.Technical docs / Godot client

Godot 4 / Architecture

Client Architecture

A code-based map of the Godot client: autoload services, scenes, player components, Colyseus boundaries, application lifecycle and the Tiled map pipeline. Active, partial and missing functionality are identified separately.

Godot 4.7 project formatScene compositionColyseus GDExtension

1. Scope and source of truth

This document describes the Godot 4 client as it exists in this repository. It is based on the current GDScript, scenes, project settings, resources, Colyseus integration, and map compiler. It does not describe the missing backend except where the client establishes a concrete contract with it.

The documentation uses the following status terms:

  • Implemented: there is an active, end-to-end code path in the current client. This is a source-level assessment; the game was not executed as part of this documentation audit.
  • Partial: code exists, but it is disabled by default, lacks a consumer or UX, has an explicit TODO, or does not complete the full behavior suggested by its name.
  • Not implemented: no functioning client code exists. A visible button, signal, placeholder scene, or README roadmap entry is not treated as an implementation.

Related documents:

2. Runtime profile

The application is a 2D Godot client named Chickens Must Die. project.godot declares Godot feature version 4.7 with the Compatibility renderer. The logical viewport is 1600 x 1200, canvas items are stretched with expand aspect behavior, and texture filtering defaults to nearest-neighbor.

The configured main scene is:

res://core/screens/menu_screen/main_menu_screen.tscn

The client depends on two enabled editor plugins:

  • Colyseus SDK for Godot — native GDExtension used for room transport, state callbacks, and reconnection. addons/colyseus/version.json reports 0.17.11; plugin.cfg reports plugin version 0.17.0.
  • Damped Oscillator 1.0 — provides spring-like property animation for UI buttons, the menu logo, and the global swipe impact.

The repository includes Colyseus binaries for Windows, Linux, macOS arm64, iOS arm64, Android, and Web. Presence of a binary does not mean the project has an export preset or a tested release path for that platform. Only Web and Windows Desktop presets are configured.

3. Architectural style

The client combines four patterns:

  1. Autoload services own process-wide configuration, networking, logging, scene transitions, settings, global UI, and audio.
  2. Scene composition assembles screens and gameplay entities from small Godot scenes.
  3. Component-based actors keep movement input, movement simulation, interpolation, reconciliation, animation, and camera behavior separate from player identity and mass.
  4. Server-authoritative replication treats Colyseus room state as the authoritative source for player and seed existence and positions.

At a high level:

project.godot
  -> autoload services and GlobalUi
  -> MainMenuScreen
      -> JoinScreen
          -> NetworkManager.join_or_create("my_room")
              -> joined_room signal
                  -> Game scene
                      -> World/MapLoader
                      -> PlayerManager
                      -> SeedManager
                      -> ChatController + ChatPanel

The backend is not included. The client cannot enter the gameplay scene through its normal flow until a compatible room join succeeds.

4. Project structure

Path Responsibility
actors/ Player scenes, player behavior components, and the replicated seed entity.
core/globals/ Autoload services and global configuration.
core/screens/ Main menu, join screen, and optional splash/intro screen.
game/ Gameplay composition, replicated entity managers, and chat bridge.
ui/ Global UI, settings, chat presentation, buttons, cursor, and parallax effect.
world/ Runtime world scene, map loader, source Tiled JSON, compiled client JSON, and TypeScript compiler.
addons/colyseus/ Vendored Colyseus GDExtension and platform binaries.
addons/DampedOscillator/ Vendored UI animation plugin and its demo.
assets/ Fonts, sprites, UI textures, cursor graphics, music, and sound effects.
docs/ Technical and product documentation.

There are 37 application GDScript files and 20 non-addon .tscn scenes, excluding temporary .tmp scene files.

5. Autoload services

Autoloads are declared in project.godot in the following order.

Autoload Type Responsibility Important state
Config Script Build environment, client version, grid size, movement mode, player speed, log endpoint. CLIENT_ENV, MOVEMENT_TYPE, PLAYER_BASE_SPEED.
NetConfig Script Environment-dependent Colyseus URL and synchronization tuning. Debug filters, interpolation speeds, send interval, reconciliation settings.
SceneManager Script Scene path registry and scene changes; returns to menu when a session ends. is_transitioning.
NetworkManager Script Owns the Colyseus client/room, connection state machine, room lifecycle, outgoing messages, and incoming message routing. client, room, callbacks, connection_state.
GameLogger Script Structured in-memory/development logging and rate-limited HTTP reporting for critical events. history, report queue, debug log throttles.
GlobalEffects Script Runtime-created fade and swipe overlays. Active tweens and swipe offsets.
SettingsManager Script Loads, applies, and saves three audio volume values. master_volume, music_volume, sfx_volume.
GlobalUi Scene Always-present canvas layer with settings button, mute button, settings menu, and custom mouse cursor. UI node state.
UiAudioManager Scene Plays the shared UI click sound on the SFX bus. One AudioStreamPlayer.
MusicManager Scene Two-player music crossfade service. Current track and active player.
DampedOscillator Plugin script Animates arbitrary properties with damped spring motion. Plugin-managed animation instances.

Dependency direction

Most scene scripts may call autoloads. Autoloads avoid direct references to gameplay scenes, with two deliberate exceptions:

  • SceneManager knows scene paths and listens to NetworkManager.game_session_ended.
  • GlobalUi contains reusable UI scenes that call NetworkManager and SettingsManager.

NetworkManager does not reach into the current scene. It emits signals, and screen/game controllers decide how to react. This keeps room lifecycle separate from scene composition.

6. Scene architecture

Main menu

main_menu_screen.tscn is the startup scene. It contains a darkened parallax background, OPTIONS and EXIT buttons, a clickable logo, START, inactive LEADERBOARD and CREDITS buttons, a version label, and a local SettingsMenu instance.

MainMenuScreen._ready() starts the only registered music track (menu) and displays Config.VERSION. START changes directly to the join scene. Clicking the logo loads the splash scene; the splash is therefore optional and is not an automatic boot scene.

Join screen

join_screen.tscn contains:

  • a four-skin CharacterPreview,
  • previous/next buttons,
  • a nickname LineEdit,
  • join status text,
  • join and back buttons,
  • another parallax background.

The screen validates only that the trimmed nickname is non-empty. It disables editing while connecting and delegates the actual join to NetworkManager. joined_room loads the gameplay scene; connection_error restores the form.

Gameplay scene

game/game.tscn is intentionally small:

Game (game.gd)
|- World (world.tscn)
|  `- Map (map_loader.gd)
|- PlayerManager (player_manager.gd)
|- SeedManager (seed_manager.gd)
`- Chat (chat_panel.tscn + chat_controller.gd)

Game initializes the PlayerManager with the room callback adapter and local session ID. It listens to full room state changes and applies the players and seeds collections. A deferred first application handles state that may have arrived before the scene finished loading.

Player inheritance and composition

base_player.tscn provides a CharacterBody2D, animated sprite, nickname label, animation controller, and circle collider.

BasePlayer
|- AnimatedSprite2D
|- PlayerName
|- AnimationController
`- CollisionShape2D

The local and remote player scenes inherit it:

LocalPlayer adds                  RemotePlayer adds
|- MovementInput                 `- PositionInterpolationController
|- MovementController
|- ReconciliationController
|- NetworkInputSender
|- PositionInterpolationController
|- Camera2D + CameraController
`- EatSound

BasePlayer owns replicated identity, skin, position initialization, mass-driven scale/collision, and the mass_changed signal. Subclasses implement _apply_server_position():

  • RemotePlayer always updates an interpolation target.
  • LocalPlayer updates either an interpolation target or a reconciliation target, depending on NetConfig.RECONCILIATION_ENABLED.

7. Application lifecycle

7.1 Startup

  1. Godot creates the autoloads.
  2. NetworkManager creates a Colyseus.Client for NetConfig.COLYSEUS_URL.
  3. SettingsManager loads user://settings.cfg if it exists and applies Master, Music, and SFX values.
  4. GlobalEffects creates its overlay canvas at runtime.
  5. GlobalUi initializes the global settings/mute/cursor controls.
  6. The main menu scene starts the menu music and displays version 1.0.0.

The current configured environment is DEVELOPMENT, so the endpoint is currently http://172.22.111.106:8080. The production branch in NetConfig.gd uses https://chickensmustdie.com.

7.2 Join lifecycle

JoinScreen.join()
  -> validate nickname and DISCONNECTED state
  -> NetworkManager.join_game(nickname, character_key)
  -> state = CONNECTING
  -> client.join_or_create("my_room", options)
  -> configure reconnection and room signals
  -> room.joined
       state = CONNECTED
       callbacks = Colyseus.Callbacks.of(room)
       emit joined_room
  -> JoinScreen loads game.tscn

If join_or_create() returns null, or the room emits error/left while connecting, the current code clears the room and returns to DISCONNECTED before emitting connection_error. A new join attempt is therefore allowed.

7.3 Gameplay state lifecycle

The gameplay scene consumes both collection callbacks and complete state snapshots:

Colyseus room state
  |- callbacks.on_add("players") ------> PlayerManager creates local/remote scene
  |- callbacks.on_remove("players") ---> PlayerManager frees scene
  `- room.state_changed ---------------> Game gets full state
                                          |- PlayerManager.apply_server_state()
                                          `- SeedManager.reconcile()

The full snapshot pass is also a safety net: it creates missing players and removes stale ones even if collection callbacks did not produce the expected local dictionary state.

7.4 Leave and disconnect lifecycle

The settings menu calls NetworkManager.leave_game() while connected or reconnecting. The manager changes to LEAVING, disables reconnect, invokes room.leave(), and waits for left. A 1.5 second verification timer handles missing completion:

  • if the room is no longer connected, it completes local cleanup;
  • if the room still reports connected, it sends a critical log report, retains ownership, restores CONNECTED and reconnection options, and emits connection_error. The user can retry LEAVE; the client does not claim the native transport closed.

Completion disconnects the old room's manager signals, clears callbacks and room, changes to DISCONNECTED, emits game_session_ended, and causes SceneManager to load the main menu. Scene-owned callback handles and state subscriptions are removed on tree exit.

An unexpected transport drop changes the state to RECONNECTING; an SDK reconnection changes it back to CONNECTED. The current gameplay scene remains loaded. No reconnect overlay, input freeze, countdown, or terminal failure screen is implemented.

8. State ownership

State is split by lifetime and authority:

State Owner Authority/lifetime
Connection state and room handle NetworkManager Process lifetime; driven by Colyseus lifecycle.
Players and seeds Backend room state, mirrored by managers Server-authoritative per room session.
Local input direction and input sequence Local player components Frame/component lifetime; sequence is sent but not acknowledged by the client.
Interpolation/reconciliation targets Player network components Per player instance.
Nickname, skin, mass BasePlayer mirror of room state Server state after join.
Current scene Godot scene tree via SceneManager Process lifetime.
Audio volumes SettingsManager and AudioServer Persisted in user://settings.cfg when the settings menu closes.
Mute toggle MuteButton/AudioServer Runtime only; not persisted by SettingsManager.
Music track MusicManager Process lifetime across scene changes.
Log history GameLogger Development-mode process memory, capped at 100 entries.

There is no central Redux-style game state. The room state is read at the gameplay boundary and distributed to specialized nodes.

9. Networking boundary

NetworkManager is the only class that owns the Colyseus Client and Room. Other classes use its public signals and send_message() method. The exception is Game, which reads NetworkManager.room and its callback object after a successful join.

Outgoing application messages currently used:

  • move from NetworkInputSender;
  • chat_send from ChatController.

Incoming application messages routed to signals:

  • game_config — routed, no consumer;
  • tick_sync — routed, no consumer;
  • chat_message — displayed by chat;
  • chat_error — displayed by chat;
  • player_eaten — may play the local predator's eating sound;
  • __playground_message_types — debug log only.

Unknown message types produce a warning.

10. Map architecture

The checked-in world uses a compile-then-load pipeline:

Tiled JSON (map.json)
  -> compile-map-client.ts
  -> compact client schema (map.client.json)
  -> MapLoader at game-scene startup
  -> runtime TileSet + TileMapLayer nodes + StaticBody2D collisions

The current map is finite and orthogonal: 50 x 38 tiles at 16 x 16 pixels, producing an 800 x 608 world. It contains 20 atlas tilesets, three rendered layers (Ground, Objects, Buildings), 2,112 non-empty cells, and nine collision rectangles. The loader also creates four 64-pixel-thick boundary walls outside the map.

The map is built every time world.tscn enters the tree. There is no runtime map selection, cached generated Godot scene, or server-provided map identifier.

11. Implementation status

Implemented and active

  • Main menu, optional splash sequence, join screen, and direct scene transitions.
  • Four selectable chicken skins with walk, stand, and idle animations.
  • A single Colyseus room type named my_room.
  • Join error cleanup, normal leave, SDK reconnection configuration, and lifecycle signals.
  • Server-driven player/seed creation, update, and removal.
  • Input messages at change time plus a 20 Hz heartbeat.
  • Default local and remote position interpolation.
  • Free eight-direction input with diagonal normalization in the optional local simulation path.
  • Mass-based sprite/collider sizing, nickname display, camera smoothing, chat, settings, music, UI sound, cursor, and parallax.
  • Runtime construction of the one checked-in map.
  • Structured logging and critical HTTP reports.

Partial, disabled, or unused

  • Client prediction/reconciliation exists but is disabled by default. It corrects only while idle and does not replay unacknowledged inputs.
  • Grid movement is implemented behind a config enum, disabled by default, and marked with an explicit TODO to verify it.
  • Reconnect UX is absent even though SDK reconnect is configured and signals are emitted.
  • Leave timeout recovery permits another attempt when still connected; the wrapper exposes no force-close API.
  • game_config and tick_sync are routed but have no consumers.
  • Global swipe transitions are implemented but no current screen uses load_scene_with_swipe().
  • Mute persistence is separate from stored Master volume and lasts only for the process.
  • SFX routing is incomplete: the UI click uses the SFX bus, while the eating sound and splash audio use the default Master bus.
  • Matchmaking cancellation is not exposed by the SDK wrapper; BACK is disabled until the join request resolves.
  • game/managers/seed_manager.tscn is an empty, unused placeholder; game.tscn attaches seed_manager.gd directly to a node.

Not implemented in the current client

  • Leaderboard and credits behavior, despite visible main-menu buttons.
  • Matchmaking, room selection, authentication, or session restoration after application restart.
  • A reconnect/failure screen or defeat screen.
  • Automated tests or CI configuration.
  • Multiple runtime-selectable maps.
  • Mobile controls; the input map contains keyboard controls only.

Explicit source TODOs identify server-provided camera/gameplay configuration and verification of grid movement as future work. They must not be treated as current functionality.