5.3 KiB
| description | globs | alwaysApply |
|---|---|---|
| GDScript naming, formatting, and Godot-idiomatic patterns (Godot docs style guide). | **/*.gd | true |
GDScript style (Neon Sprawl)
Follow the Godot GDScript style guide. Prefer clarity and consistency with existing scripts in the repo.
CI also enforces gdlint and gdformat (see gdlintrc and .github/workflows/gdscript.yml) on client/scripts/ and client/test/. When this guide and personal preference differ, follow the linted version below so pushed files pass CI.
Naming
- Functions, variables, signals:
snake_case. - Classes (
class_name, custom resources):PascalCase. - Constants (
const):ALL_CAPSwhen they are true fixed values; otherwise match Godot/node conventions used nearby. - “Private” / internal: leading underscore
_like_thisfor members meant as implementation detail (not enforced by the language).
Types and API
- Add static types on parameters, locals, and return values where reasonable (
-> void,var x: int, etc.). - Prefer
@export(Godot 4) over legacyexportsyntax. - Use
@onreadyfor node references set after the scene tree is ready when that pattern fits.
Formatting
- One statement per line; CI enforces max line length = 100.
- Indentation: use tabs for new Godot work (editor default). Do not mix tabs and spaces in the same file; match the file if it already uses spaces.
- Blank lines: separate functions with two blank lines; sparing single blank lines inside functions for logical groups.
- Trailing commas in multi-line collections/function args when it improves diffs.
- No trailing whitespace.
Structure
class_nameonly when the type must be referenced globally; otherwise anonymousextendsis fine.- Declaration order should match
gdlintclass-definitions-order:@tool→class_name→extends→ file/class docstrings →signal→enum→const→ static vars →@exportvars → public vars → private vars (_foo) →@onreadypublic vars →@onreadyprivate vars → everything else. - Virtual overrides (
_ready,_process,_physics_process, etc.): keep small; extract helpers with leading_. - Scene / main script shape: For
client/, follow godot-client-script-organization — thinmain.gd, split picking, networking, and similar concerns into child scripts.
CI-enforced lint rules
- Names: keep functions, local vars, loop vars, arguments, signals, and class vars in
snake_case; keep classes / enums inPascalCase; keep enum elements and true constants inALL_CAPS. - Signal callbacks:
_on_Node_signalstyle handlers are allowed; otherwise use normalsnake_case. - Preload / load identifiers: when storing a preloaded script or type-like handle, prefer
PascalCasenames such asCameraStateScript; keep ordinary constants inALL_CAPS. - Avoid duplicated
preload()s for the same path in one file. - Avoid bare expressions that are not assigned / returned / awaited.
- Remove unnecessary
pass. - Keep files reasonably small: CI enforces max 1000 lines, max 20 public methods, max 10 function arguments, and max 6 returns per function.
- Prefer early-return style.
gdlintforbidselse/elifbranches that directly follow areturn. - Keep unused arguments intentional and named clearly if you truly need them.
Local verification
- Before pushing GDScript changes, run the same tools CI uses:
pip install "gdtoolkit==4.5.0"gdlint client/scripts client/testgdformat --check client/scripts client/test - If formatting fails, run:
gdformat client/scripts client/test - Install the repo’s local pre-push hook from the repo root to enforce those checks when
client/scripts/orclient/test/.gdfiles change in the push:./scripts/install-git-hooks.sh
GdUnit test layout (AAA)
Mandatory for every new or changed test function in client/test/**/*.gd (GdUnit suites, test_* methods).
-
# Arrange,# Act, and# Assert— each appears once per test, in that order, as a full-line comment at the same indentation as the test body. No blank line is required after those comments; the next statement may follow immediately. -
Arrange — Transports, clients,
enqueue/connectsetup, test data. -
Act — The call(s) under test only (e.g.
request_cast,callinto SUT). Noassert_that/assert_*in Act. -
Assert — All expectations; parse or inspect results here when parsing exists only to support assertions.
gdlint / gdformat do not enforce AAA text; review and agents do. Match the spirit of server xut snippets: three labeled phases, one scenario per test.
Comments
- Use
#comments; document non-obvious why, not what the next line literally does. - Doc comments: Godot 4 supports
##above declarations for the in-editor help—use for public-ish APIs when helpful.
Anti-patterns
- Avoid
get_node()string paths repeated everywhere; use@onready, exports, or groups when scale demands it. - Prefer typed collections and enums over “magic” string/int state when it stays readable.