1. Repository prerequisites
The client project declares Godot feature version
4.7 and uses the Compatibility renderer. Use a Godot
build capable of opening that project format and loading the
included native Colyseus GDExtension.
The repository already contains:
- the Godot project and imported-source assets;
-
Colyseus native libraries under
addons/colyseus/bin/; - the Damped Oscillator addon;
- Web and Windows Desktop export presets;
- source and compiled forms of one Tiled map.
It does not contain:
- the Colyseus backend;
- a package manifest or pinned TypeScript toolchain for the map compiler;
- automated tests;
- CI configuration;
- configured export presets for Linux, macOS, Android, or iOS.
The project can display menu/join/splash UI without a backend, but its normal gameplay transition requires a successful room join.
2. Initial setup
-
Open
project.godotin the appropriate Godot editor. -
Confirm the Colyseus SDK and Damped Oscillator plugins are enabled. They are listed in
project.godot. -
Provide a compatible backend implementing the contract in Network Protocol.
-
Set the intended environment and endpoint in
core/globals/Config.gdandcore/globals/NetConfig.gd. -
Confirm the backend exposes a room named
my_room. -
Use the project main scene to begin at the main menu.
Current checked-in network selection:
Config.CLIENT_ENV = DEVELOPMENT
Development URL = http://172.22.111.106:8080
Production URL = https://chickensmustdie.com
Do not assume the development address is reachable from another machine, container, phone, or browser. It is a hard-coded LAN/private address in the current source.
3. Configuration guide
General gameplay
Edit core/globals/Config.gd:
| Setting | Effect |
|---|---|
VERSION |
UI version and log report version. |
CLIENT_ENV |
Endpoint branch and development log behavior. |
GRID_SIZE |
Step size for grid simulation only. |
PLAYER_BASE_SPEED |
Local simulation speed when prediction/reconciliation is enabled. |
MOVEMENT_TYPE |
FREE or GRID for local simulation.
|
LOG_API_URL |
Critical-report HTTP endpoint. |
Network tuning
Edit core/globals/NetConfig.gd:
-
LOCAL_INTERPOLATION_SPEEDandREMOTE_INTERPOLATION_SPEEDcontrol exponential visual convergence. -
INPUT_SEND_INTERVALcontrols heartbeat spacing; direction changes still send immediately. -
RECONCILIATION_ENABLEDswitches the local player from interpolation to local simulation plus idle correction. - reconciliation interval, duration, idle delay, minimum error, and hard-snap distance tune that partial feature.
- debug state/message flags and intervals control filtered development logs.
Changing movement or reconciliation constants without matching backend behavior can increase visible correction and collision disagreement.
Camera and mass
Mass formulas are constants in
actors/player/base_player.gd. Camera mass and zoom
thresholds are exported values on the local
Camera2D scene. The source contains a TODO to obtain
camera/gameplay mass configuration from the server, but no consumer
exists today.
Audio
default_bus_layout.tres defines Music and SFX under
Master. Settings persist to user://settings.cfg, not
the repository. To make a sound respond to the SFX slider, its
AudioStreamPlayer.bus must explicitly be
SFX; the current eating and splash players do not do
this.
4. Development conventions visible in the code
- Global services are autoloads; scene-specific orchestration remains on scene roots.
-
Shared player behavior is split into typed
class_namecomponents. - Network lifecycle is exposed through signals instead of direct UI references.
- Replicated entities are stored in dictionaries keyed by server IDs.
- Full server collections are treated as authoritative and reconciled against local nodes.
-
Scene node references use typed
@onreadyfields and unique-name%Nodelookup where configured. -
Runtime tuning is concentrated in
Config,NetConfig, exported camera fields, and a small number of player constants. -
Animation names follow
<skin>_<state>. -
Logs should go through
GameLoggerwhen they represent application/network events.
There is no enforced formatter, linter configuration, static-analysis script, or CI gate in the repository. Existing code style is not completely uniform, so follow the surrounding file and preserve typed signatures where practical.
5. Common extension recipes
Add a chicken skin
-
Add its sprite sheet to
assets/sprites/characters/chickens/. -
Add
walk,stand, andidleanimations to bothbase_player.tscnand the join preview's SpriteFrames. -
Use exact names
<new_key>_walk,<new_key>_stand, and<new_key>_idle. -
Add the same key and display name at matching indexes in
CharacterPreview.CHARACTER_KEYSandCHARACTER_NAMES. -
Ensure the backend accepts the join value and republishes it as
playerColor. -
Test preview cycling plus local and remote rendering.
If any animation is missing, the runtime controller warns and keeps the previous visual state.
Add a server message
Keep transport routing in NetworkManager and feature
behavior in a consumer:
# NetworkManager.gd
signal server_example_message_received(payload: Variant)
func _route_server_message(message_type: Variant, payload: Variant) -> void:
match str(message_type):
"example":
server_example_message_received.emit(payload)
Then connect from the relevant scene/controller in
_ready(). Avoid making
NetworkManager depend on a node that exists only in one
screen. Define payload requirements in the protocol documentation
and handle absent/invalid fields deliberately.
Add an outgoing message
Call NetworkManager.send_message(type, payload). It
already guards for the CONNECTED state. Decide
explicitly whether the message may be lost during reconnection:
current sends are discarded rather than queued by the application.
Add a replicated collection
Follow the PlayerManager or
SeedManager pattern:
-
create a small entity scene with
setup(state)andapply_server_state(state); preload it in a manager;
store instances by stable server ID;
spawn missing IDs;
update existing IDs;
queue and erase stale IDs;
-
call the manager from
Game._on_state_changed(); -
add Colyseus collection callbacks only if immediate add/remove behavior is useful.
Full snapshots should remain authoritative unless the protocol is deliberately changed.
Add a music track
Add a preload to MusicManager.TRACKS:
const TRACKS: Dictionary = {
&"menu": preload("res://assets/music/time_for_adventure.mp3"),
&"game": preload("res://assets/music/new_track.ogg"),
}
Call MusicManager.play_track(&"game") from the
owning screen lifecycle and decide when to stop or restore it. Both
internal players already use the Music bus.
Add an SFX sound
Create or configure an AudioStreamPlayer and set
bus = &"SFX" in the scene if it must follow the SFX
slider. The current eating sound is a useful structural example but
is routed to Master, not SFX.
Add a screen
-
Create the
.tscnand controller. -
Add its path to
SceneManagerif it is a first-class route. -
Choose direct
load_scene()or the currently-unusedload_scene_with_swipe(). -
Connect transitions once, either in the scene or code.
-
Verify interaction with the persistent
GlobalUilayer and its settings overlay.
Complete leaderboard or credits
The menu buttons already exist, but they have no signal connections or handlers. A real implementation must add the destination UI/data source and explicitly wire the buttons. Their presence alone is not a feature stub with behavior.
6. Map workflow
Files
| File | Purpose |
|---|---|
world/map/map.json |
Source export from Tiled. |
world/map/compile-map-client.ts |
Validator/compiler. |
world/map/map.client.json |
Checked-in runtime format. |
world/map/map_loader.gd |
Godot runtime builder. |
world/world.tscn |
Map-loader host scene. |
Current artifact
| Metric | Value |
|---|---|
| Tiled version | 1.11.2 |
| Tiled format | 1.10 |
| Orientation | Orthogonal |
| Finite | Yes |
| Grid | 50 x 38 |
| Tile size | 16 x 16 px |
| World size | 800 x 608 px |
| Atlas tilesets | 20 |
| Render layers | Ground, Objects, Buildings |
| Non-empty cells | 1,689 + 367 + 56 = 2,112 |
| Collision rectangles | 9 |
Compiler constraints
Use finite orthogonal tile maps, atlas tilesets, and complete
tile-layer arrays. Put collision rectangles on an object layer named
Collision (case-insensitive). Collision objects must be
unrotated rectangles with positive dimensions. Ellipses, points,
polygons, polylines, infinite chunks, and collection-of-images
tilesets are unsupported.
All tileset images are rewritten to:
res://assets/sprites/village/<source image filename>
The repository has no package.json or pinned runner.
With a Node version that supports TypeScript type stripping, the
intended command shape is:
node --experimental-strip-types world/map/compile-map-client.ts world/map/map.json world/map/map.client.json
A configured tsx, ts-node, or
tsc workflow can also execute/compile the file. Pin one
before relying on it in CI.
Updating the map
Edit/export the finite Tiled JSON.
-
Keep referenced atlas image filenames available under
assets/sprites/village/. -
Compile to
world/map/map.client.json. -
Review the compiler summary and diff the generated JSON.
-
Confirm the output remains version 1 and contains expected dimensions/layers/collisions.
-
Update the local camera limits in
local_player.tscnif world dimensions change. -
Update the backend's collision and boundary model to the same geometry.
-
Manually verify rendering order, transform flags, obstacles, boundaries, and server spawn points.
The compiler creates directories and overwrites its output path. Keep the output target explicit.
7. Logging and network diagnostics
Development logs
GameLogger includes source location and retains 100
entries when Config.CLIENT_ENV is
DEVELOPMENT. Use get_history() only for
diagnostic UI/tools; it returns a deep copy.
State logging
Enable in NetConfig.gd:
const DEBUG_STATE_LOG_ENABLED := true
const DEBUG_STATE_INTERVAL_MS := 5000
const DEBUG_STATE_LOG_KEY := "players"
It works only in a debug build. An empty key logs the complete state; a non-empty missing key emits a warning.
Message logging
Enable DEBUG_MESSAGE_LOG_ENABLED. Set
DEBUG_MESSAGE_TYPE to an exact type string to filter,
or leave it empty for all messages.
DEBUG_MESSAGE_INTERVAL_MS throttles globally, not
separately per message type.
Diagnostic order
For a failed join or missing gameplay state, check:
-
Config.CLIENT_ENVand the selected endpoint; -
whether
my_roomexists; -
join option names
nicknameandplayerColor; -
transition
DISCONNECTED -> CONNECTING -> CONNECTED; -
joined_roomand creation ofNetworkManager.callbacks; -
playersandseedscollection shapes; local session ID matching the player collection key;
state/message debug output;
-
missing animation warnings or malformed numeric fields.
For movement disagreement, additionally compare backend speed, diagonal normalization, map geometry, world bounds, and update rate. Remember that default local behavior is interpolation, not local simulation.
Critical reporting
Only GameLogger.critical() sends HTTP. Review privacy
and authentication requirements before adding user data to
context; the current report is JSON sent to
Config.LOG_API_URL with no application-level
authorization header.
8. Testing strategy
Current automated-test status
No application test files, test framework configuration, or CI
workflow were found. The map compiler exports a pure
compileClientMap() function, which is suitable for unit
tests, but none are included.
Until automation is added, changes require structured manual regression. Source review alone cannot establish that a native GDExtension, backend contract, renderer, or export target works at runtime.
Minimum offline UI smoke test
This subset does not require a backend:
- main menu loads and displays
Version 1.0.0; - menu music starts once and does not restart on a repeated same-track call;
- parallax follows the mouse without exposing background edges;
- OPTIONS/global settings open and close;
- all three sliders change their intended buses;
- closing settings persists values across application restart;
- mute icon and Master bus agree at startup and after toggling;
- cursor swaps while the left button is held;
- START opens join; BACK returns to menu;
- all four skins show the correct name and cycle stand/idle/walk;
- empty/whitespace nickname is rejected;
- logo click plays the splash and returns to menu;
- EXIT closes the application on desktop.
Also verify the known settings limitation: direct mute is not persisted and can be cleared by applying a positive Master slider value.
Connected single-client test
- valid join loads
game.tscn; - invalid/rejected join shows an error and allows immediate retry;
-
local session creates
LocalPlayer, notRemotePlayer; - initial nickname, skin, position, and mass match server state;
- movement sends immediately on direction changes and continues heartbeats;
- default local movement follows server positions smoothly;
- map collisions/bounds are enforced by server positions;
- seeds spawn, move, and disappear from state;
- mass changes update radius, sprite, label, and later camera zoom;
- chat send waits for server broadcast and displays local time;
- chat error shows for three seconds;
-
local predator
player_eatenplays randomized-pitch crunch; - Leave game returns to the main menu and hides the leave button.
Test a non-base initial mass specifically; the current camera may remain at zoom 3 until the next mass change.
Two-client synchronization test
- each client identifies itself by its own session ID;
- the other session uses
RemotePlayer; - joins and disconnects create/remove nodes once without duplicates;
- remote motion is smooth at the configured interpolation speed;
- hard position changes snap at the 100-pixel threshold;
- mass/skin/name updates render consistently for both clients;
- seed removal is visible to both;
- chat order, timestamp, nickname, and text are consistent;
- eating audio plays only for the client whose session ID is the predator.
Reconnection test
- interrupt transport while connected;
-
confirm
RECONNECTINGis reached and sends are suppressed; - confirm gameplay remains visible and note the lack of status UI;
-
restore transport within the retry budget and verify
CONNECTEDplus fresh state; -
exhaust retries and verify whether the SDK emits
leftand returns the client to menu; - test intentional leave during unstable transport;
- test the 1.5-second leave verification branch.
Because there is no reconnect UI, observe logs and connection state directly during this test.
Optional prediction/reconciliation test
This feature is disabled by default and should be tested separately before enabling:
- verify free movement speed and diagonal normalization against the backend;
- verify obstacle sliding with the same collision rectangles;
- introduce controlled latency and packet loss;
- measure error while moving and after becoming idle;
- verify sub-0.5-pixel error is ignored;
- verify moderate error corrects over 0.3 seconds;
- verify 32-pixel-or-greater error snaps;
- confirm a correction is cancelled when movement resumes;
- document divergence caused by the lack of input acknowledgement/replay.
Grid mode needs its own collision/corner/diagonal/high-delta test matrix because the source explicitly marks it for verification.
Map regression test
- compare compiler output counts with expected counts;
- verify all atlas textures load;
- inspect Ground/Objects/Buildings order and opacity;
- inspect flipped, vertically flipped, and transposed tiles;
- collide with all nine rectangles from multiple directions;
- verify all four boundary walls;
- verify camera bounds still match the map;
- verify backend spawn points never place a growing circle inside geometry.
Audio regression test
- Master affects every sound;
- Music affects the menu track;
- SFX affects the UI click;
- eating/splash remain controlled by Master in the current implementation;
- zero slider value mutes and positive value unmutes;
- crossfade does not leave both tracks audible after completion;
- rapid track calls kill the previous fade tween safely.
9. Recommended automation targets
These are test recommendations, not implemented features:
-
Unit-test
compileClientMap()with valid layers, all GID flags, multiple tilesets, and every rejection branch. -
Add deterministic tests for movement direction normalization and mass formulas.
-
Add component tests for interpolation thresholds and reconciliation timing.
-
Add a protocol fixture test that feeds representative
players,seeds, and message payloads. -
Add a headless scene smoke test for map loading and replicated entity reconciliation, if the native SDK can be loaded in CI.
-
Add a two-client integration environment against the actual backend contract.
Automation should not label optional prediction or grid mode as supported until their tests pass under realistic latency and collisions.
10. Export configuration
Web/PWA
The Web preset exports to ../exports/web/index.html and
currently enables:
- extension support;
- PWA output;
- canvas focus on startup;
- experimental virtual keyboard;
- cross-origin isolation header requirement;
- desktop and mobile VRAM compression.
Thread support is disabled in the preset. The included Colyseus Web GDExtension declares that dlink-enabled export templates are required. A production page served over HTTPS must use a compatible secure backend endpoint and hosting headers.
Windows Desktop
The Windows preset targets x86-64 and exports to
../exports/windows/chickensmustdie.zip. It does not
embed the PCK, disables code signing, and leaves application
icon/version/company/product metadata empty.
Before distribution, configure release metadata, signing policy, templates, and artifact packaging. Do not infer support for other desktop/mobile platforms solely from vendored Colyseus libraries.
11. Known development risks
| Risk | Current source behavior |
|---|---|
| Backend drift | No shared schema package or contract tests are present. |
| Client/server simulation drift | Speed, mass, camera, and collision assumptions are local constants. |
| Reconnect visibility | Chat retains unsent input; no dedicated connection status overlay. |
| Leave timeout | A still-connected room is retained and LEAVE can be retried; no native force-close API. |
| Join cancellation | BACK is disabled during matchmaking; native cancellation/timeout behavior needs verification. |
| Invalid server values | Position, mass, skin, and most payload fields are lightly validated or trusted. |
| Camera/map coupling | Camera bounds are serialized separately from map dimensions. |
| Prediction naming | Current reconciliation is not replay-based and is disabled. |
| Audio grouping | Eating and splash sounds bypass SFX bus. |
| Settings consistency | Mute and stored Master volume are separate states. |
| Chat growth | No message-history cap within a gameplay scene. |
| Map build reproducibility | No pinned Node/TypeScript toolchain or generated-file CI check. |
| Repository hygiene |
Temporary .tscn*.tmp files and built binaries
are present; ownership/distribution should be explicit.
|
| Licensing | No top-level license or asset provenance inventory is present. |
12. Source-marked incomplete areas
Only the following planned/incomplete areas are directly evidenced by active source structure or explicit comments:
-
grid simulation requires verification (
movement_controller.gdTODO); -
camera mass configuration is intended to come from the server
(
camera_controller.gdTODO); -
game_configandtick_syncrouting awaits consumers; - leaderboard and credits await handlers and content;
- reconnect awaits user-facing state/failure handling;
-
the placeholder
seed_manager.tscnis not integrated; - global swipe transitions await call sites.
Broader roadmap ideas are not described as client capabilities until corresponding code exists.