Release 1.1.2 — the default path works end to end

Fix release for 1.1.1: the minimal path end to end, engine commands that
run on Godot, Unity and Unreal, hook warnings you can see, and verdicts
that say when they could not check. Details in CHANGELOG.md; upgrade
steps in UPGRADING.md (v1.1.1 → v1.1.2).

Fixes #67
Fixes #131

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WuJn2FdynEybfL5KC697nJ
This commit is contained in:
Donchitos
2026-09-29 21:32:19 +10:00
co-authored by Claude Opus 5.5
parent 7ed2c3e9c4
commit b21fa0f7f2
383 changed files with 22888 additions and 10618 deletions
+1 -1
View File
@@ -42,7 +42,7 @@ Engine-specialist agents are instructed to:
- After upgrading the engine version
- When the LLM model is updated (new knowledge cutoff)
- After running `/refresh-docs` (if available)
- After running `/setup-engine refresh`
- When you discover an API the model gets wrong
### How to Update
@@ -53,7 +53,7 @@ Changes between Godot versions, focused on post-LLM-cutoff changes (4.4+).
| Core | `OS.execute_with_pipe` | Added optional `blocking` parameter |
| Core | `RegEx.compile/create_from_string` | Added optional `show_error` parameter |
| Rendering | `RenderingDevice.draw_list_begin` | Many parameters removed; `breadcrumb` parameter added |
| Rendering | Shader texture types | Parameter/return types changed from `Texture2D` to `Texture` |
| Rendering | `Shader.set_default_texture_parameter()` / `get_default_texture_parameter()` | Parameter/return type changed from `Texture2D` to `Texture`; the shading language did not change |
| Particles | `.restart()` method | Added optional `keep_seed` parameter (CPU/GPU 2D/3D) |
| GUI | `RichTextLabel.push_meta` | Added optional `tooltip` parameter |
| GUI | `GraphEdit.connect_node` | Added optional `keep_alive` parameter |
@@ -7,21 +7,36 @@ This supplements (not replaces) the agent's built-in knowledge.
## GDScript (4.5+)
- **Variadic arguments**: Functions can accept arbitrary parameter counts
- **Variadic arguments**: Functions can accept arbitrary parameter counts. The
rest parameter is written `...name`, comes last, and is typed `Array` or left
untyped — `values: Variant...` and `...values: Array[int]` do not parse
```gdscript
func log_values(prefix: String, values: Variant...) -> void:
func log_values(prefix: String, ...values: Array) -> void:
for v in values:
print(prefix, ": ", v)
```
- **Lambdas capture local variables by value** (every Godot 4 version): a local
is captured once, when the lambda is created, and a lambda cannot reassign an
outer local — `var hit := false` / `func(): hit = true` leaves the caller's
`hit` false. Share state through a reference type (a Dictionary, an Array or
an object) and mutate its contents. A rest parameter works in a lambda too, so
`func(...args)` connects to a signal with any number of arguments (observed on
4.6.1; a one-argument lambda on a zero-argument signal errors "Method expected
1 argument(s), but called with 0")
```gdscript
var state := {"emitted": false}
some_signal.connect(func(...args): state.emitted = true)
```
- **Abstract classes and methods**: Use `@abstract` to enforce inheritance
```gdscript
@abstract
class_name BaseEnemy extends CharacterBody3D
@abstract
func get_attack_pattern() -> Array[Attack]:
pass # Subclasses MUST override
# No body: "a newline or a semicolon is expected after the function header".
# Subclasses MUST override it.
@abstract func get_attack_pattern() -> Array[Attack]
```
- **Script backtracing**: Detailed call stacks available even in Release builds
@@ -99,6 +114,49 @@ This supplements (not replaces) the agent's built-in knowledge.
`rg --type gdscript` is a hard error — the search never executes.
Always use `rg --glob "*.gd"` (shell) or `glob: "*.gd"` (Grep tool) to filter GDScript files.
## Command Line — Tests and Parse Check
Flags, from Godot's command-line tutorial: `--headless` (headless display and a
dummy audio driver, "useful for servers and with `--script`"), `--path <dir>`
(a directory holding `project.godot`), `-s`/`--script <script>`, `-d`/`--debug`
(the local stdout debugger), `--remote-debug <protocol>://<host>[:<port>]`,
`--import` (starts the editor, waits for the import, and quits),
`--quit-after <n>` (iterations), `--export-debug <preset>` (implies `--import`)
and `--check-only` (parse for errors and quit, with `--script`). The editor is
not on `PATH` by default: on Windows and Linux run the binary by its relative or
absolute path; on macOS run `Godot.app/Contents/MacOS/Godot` inside the bundle.
gdUnit4's runner is `res://addons/gdUnit4/bin/GdUnitCmdTool.gd`; `-a <dir|suite>`
adds suites, reports go to `res://reports/` by default (`-rd` changes it), and
the documented exit codes are 0 (all passed), 100 (failures) and 101 (warnings).
The command the framework uses:
`godot --headless -s -d --remote-debug tcp://127.0.0.1:0 res://addons/gdUnit4/bin/GdUnitCmdTool.gd -a res://tests --ignoreHeadlessMode`.
Observed on Godot 4.6.1 with gdUnit4 6.1.3 — not in the docs:
- 101 is also the exit when every test passed but a test leaked orphan nodes;
105 means a test script does not parse; 103 means gdUnit4 refused to run
headless (`--ignoreHeadlessMode` missing). 104 is gdUnit4 refusing a Godot
older than 4.3 — not reproducible on the pin.
- Over zero tests the runner prints `No test cases found` and exits 0.
- Without `--remote-debug tcp://127.0.0.1:0`, a script error opens the `-d`
debugger and the run waits at a `debug>` prompt; with it, every run prints two
`ERROR:` lines about the remote port, which are expected.
- A fresh clone has no `.godot/` class cache: run
`godot --headless --path . --import` first, or `GdUnitCmdTool.gd` does not
load and the run exits 1 having run nothing.
- `--check-only` with `-s <file>` exits 1 on valid code that names an autoload
(`Identifier not found`), and `--import` / `--quit-after` exit 0 on a parse
error — neither is a parse check. The framework's parse check is
`godot --headless --path . -s res://.claude/scripts/godot-parse-check.gd -- res://<file>.gd …`
(exit 1 when a script does not load).
- On Windows, a quoted full path to the editor (`"C:/Program Files/Godot/…exe"`)
runs from Git Bash with its stdout and exit code intact.
Sources:
- Command line tutorial: https://docs.godotengine.org/en/stable/tutorials/editor/command_line_tutorial.html
- gdUnit4 command line tool: https://godot-gdunit-labs.github.io/gdUnit4/latest/advanced_testing/cmd/
- GDScript reference, lambda functions and rest parameters: https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_basics.html
## Platform (4.5+)
- **visionOS export**: First new platform since open-sourcing (windowed app mode)
@@ -38,6 +38,6 @@ with the "Use Instead" column.
| String-based `connect()` | Typed signal connections | Type-safe, refactor-friendly |
| `$NodePath` in `_process()` | `@onready var` cached reference | Performance: path lookup every frame |
| Untyped `Array` / `Dictionary` | `Array[Type]`, typed variables | GDScript compiler optimizations |
| `Texture2D` in shader parameters | `Texture` base type | Changed in 4.4 |
| `Texture2D` in `Shader.set_default_texture_parameter()` / `get_default_texture_parameter()` | `Texture` base type | Changed in 4.4; the shading language's `sampler2D` / `texture()` did not change |
| Manual post-process viewport chains | `Compositor` + `CompositorEffect` | Structured post-processing (4.3+) |
| GodotPhysics3D for new projects | Jolt Physics 3D | Default since 4.6; better stability |
@@ -19,7 +19,9 @@ Last verified: 2026-02-12 | Engine: Godot 4.6
### 4.4 Changes
- **`RenderingDevice.draw_list_begin`**: Many parameters removed; optional `breadcrumb` added
- **Shader texture types**: Changed from `Texture2D` to `Texture` base type
- **`Shader` default-texture methods**: `set_default_texture_parameter()` takes and
`get_default_texture_parameter()` returns `Texture`, not `Texture2D` (the shading
language's `sampler2D` / `texture()` did not change)
- **Particles `.restart()`**: Added optional `keep_seed` parameter
### 4.3 Changes (in training data)
@@ -55,5 +57,7 @@ Windows default backend: D3D12 (was Vulkan pre-4.6)
## Common Mistakes
- Assuming Vulkan is the default backend on Windows (D3D12 since 4.6)
- Using manual viewport chains instead of Compositor for post-processing
- Using `Texture2D` in shader uniform types (use `Texture` since 4.4)
- Passing or expecting `Texture2D` in `Shader.set_default_texture_parameter()` /
`get_default_texture_parameter()` (the type is `Texture` since 4.4 — the shading
language's `sampler2D` / `texture()` did not change)
- Not using Shader Baker for projects with many shader variants
@@ -191,6 +191,13 @@ public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer
**Replaces:** Old `CommandBuffer.Execute()` pattern.
**This is URP's API.** `RecordRenderGraph` overrides URP's `ScriptableRenderPass`.
HDRP custom passes are a separate API: a `CustomPass` subclass, run from a Custom
Pass Volume, overrides `Setup(ScriptableRenderContext renderContext, CommandBuffer cmd)`,
`Execute(CustomPassContext ctx)` and `Cleanup()` (HDRP 17.3, *Create a Custom
Pass in a C# script*:
https://docs.unity3d.com/Packages/com.unity.render-pipelines.high-definition@17.3/manual/Custom-Pass-Scripting.html).
---
## Performance
@@ -296,6 +303,40 @@ public IEnumerator Player_TakesDamage_HealthDecreases() {
---
## Command Line — Tests
Documented (Unity Manual, *Run tests from the command line*; Test Framework
command-line reference):
`<Unity editor> -runTests -batchmode -projectPath <path> -testPlatform <platform> -testResults <file>`.
`-batchmode` removes the need for user input. `-testPlatform` takes `EditMode`,
`PlayMode` (Play Mode tests in the Editor) or a player platform; with no value,
tests run in Edit Mode. `-testResults` defaults to the project root, and the
file follows NUnit's XML format: the `<test-run>` element carries
`testcasecount`, `result`, `passed` and `failed`, and each `<test-case>` a
`fullname` and a `result` of `Passed`, `Failed`, `Inconclusive` or `Skipped`.
Unity defines no common exit codes for test runs.
Observed on Unity 6000.3.23f1 — not in the docs:
- `-runTests` exits 0 when every test passes and 2 when one fails; 1 is a
compile error (no results file is written), 3 a failed run, 4 an unknown
`-testPlatform`. It exits by itself — no `-quit`.
- Tests outside `Assets/` are never compiled: the run reports
`testcasecount="0"`, `result="Passed"` and exits 0 — a false pass.
- Without the `com.unity.test-framework` package, `-runTests` hangs rather
than failing.
- `Unity` on `PATH` may be Unity's separate CLI, not the editor; it rejects
`-batchmode` and `-version` with exit 2. Name the editor executable by its
full path.
- `-batchmode -quit -projectPath . -logFile -` exits 1 on a compile error and
0 when clean.
Sources:
- Run tests from the command line: https://docs.unity3d.com/6000.3/Documentation/Manual/test-framework/run-tests-from-command-line.html
- Test Framework command-line arguments: https://docs.unity3d.com/Packages/com.unity.test-framework@1.4/manual/reference-command-line.html
- NUnit test result XML format: https://docs.nunit.org/articles/nunit/technical-notes/usage/Test-Result-XML-Format.html
---
## Debugging
### Use Logging Best Practices
@@ -366,9 +366,11 @@ async void CheckForUpdates() {
// ❌ OLD: Resources.Load (synchronous, blocks frame)
GameObject prefab = Resources.Load<GameObject>("Enemies/Goblin");
// ✅ NEW: Addressables (async, non-blocking)
var handle = await Addressables.LoadAssetAsync<GameObject>("Enemies/Goblin").Task;
GameObject prefab = handle.Result;
// ✅ NEW: Addressables (async, non-blocking) — keep the handle so it can be released
AsyncOperationHandle<GameObject> handle = Addressables.LoadAssetAsync<GameObject>("Enemies/Goblin");
GameObject prefab = await handle.Task;
// ... when the asset is no longer needed:
Addressables.Release(handle);
```
---
@@ -21,6 +21,42 @@ These are production-ready recommendations as of UE 5.7.
---
## Command Line — Build and Automation Tests (per platform)
Build the project's own editor target before running tests (a fresh checkout has
no compiled game module), then run the automation tests with the editor. Epic
recommends building `<Project>Editor` rather than `UnrealEditor`, so the modules
the `.uproject` disables stay disabled. Give the project as an absolute path.
| Step | Windows | Linux | macOS |
|------|---------|-------|-------|
| Build the editor target | `Engine/Binaries/DotNET/UnrealBuildTool/UnrealBuildTool.exe <Project>Editor Win64 Development -Project="<abs>/<Project>.uproject"` | `Engine/Build/BatchFiles/Linux/Build.sh <Project>Editor Linux Development -Project="<abs>/<Project>.uproject"` | `Engine/Build/BatchFiles/Mac/Build.sh <Project>Editor Mac Development -Project="<abs>/<Project>.uproject"` |
| Editor executable for a command-line run | `Engine/Binaries/Win64/UnrealEditor-Cmd.exe` | `Engine/Binaries/Linux/UnrealEditor` | **NOT SOURCEABLE** — Epic documents only the `Engine/Binaries/Mac/UnrealEditor.app` bundle, not a command-line editor inside it |
| Packaged build | `Engine/Binaries/DotNET/AutomationTool/AutomationTool.exe BuildCookRun … -platform=Win64` | `Engine/Build/BatchFiles/RunUAT.sh BuildCookRun … -platform=Linux` | `Engine/Build/BatchFiles/RunUAT.sh BuildCookRun … -platform=Mac` |
The automation arguments are the same on every platform:
`"<abs>/<Project>.uproject" -ExecCmds="Automation RunTests <Project>.; Quit" -unattended -nullrhi -stdout -FullStdOutLogOutput`.
`RunTests` is a partial (substring) match. Each test prints
`Test Completed. Result={<status>}`, and the run ends with
`**** TEST COMPLETE. EXIT CODE: <n> ****` — exit code 0 means no test failed.
Only one `Automation` run request is allowed per `-ExecCmds`, and `-testexit=` is
legacy.
The Windows column was run on UE 5.7. The Linux and macOS columns come from
Epic's documentation and forum answers by Epic staff (sources below); Epic never
prints the full Linux test line, so it is assembled from the executable and the
platform-neutral arguments.
Sources:
- Build.sh on Linux and Mac, platform names — *Create an Installed Build*: https://dev.epicgames.com/documentation/en-us/unreal-engine/create-an-installed-build-of-unreal-engine?application_version=5.7
- `RunUBT.sh <Target> [Linux|Mac] Development` — *Unreal Insights*: https://dev.epicgames.com/documentation/en-us/unreal-engine/unreal-insights-in-unreal-engine?application_version=5.7
- `Engine/Binaries/Linux/UnrealEditor`, `Engine/Binaries/Mac/UnrealEditor.app` — *Onboarding Licensees*: https://dev.epicgames.com/documentation/en-us/unreal-engine/onboarding-licensees-in-unreal-engine?application_version=5.7 and *Linux Development Quickstart*: https://dev.epicgames.com/documentation/en-us/unreal-engine/linux-development-quickstart-for-unreal-engine?application_version=5.7
- `-nullrhi`, `-unattended`, `-stdout`, `-ExecCmds` — *Command-Line Arguments Reference*: https://dev.epicgames.com/documentation/en-us/unreal-engine/unreal-engine-command-line-arguments-reference?application_version=5.7
- Running tests from the command line — *Run Automation Tests*: https://dev.epicgames.com/documentation/en-us/unreal-engine/run-automation-tests-in-unreal-engine?application_version=5.7 ; result lines and exit code — *Review Test Results*: https://dev.epicgames.com/documentation/en-us/unreal-engine/review-test-results-in-unreal-engine?application_version=5.7
- `RunUAT.sh` from `Engine/Build/BatchFiles` on Mac/Linux — *Unreal Automation Tool Overview*: https://dev.epicgames.com/documentation/en-us/unreal-engine/unreal-automation-tool-overview-for-unreal-engine?application_version=5.7
---
## C++ Coding
### Use Modern C++ Features (C++20 in UE5.7)
@@ -107,8 +107,8 @@ void OnRep_Health() {
### Server RPC (Client → Server)
```cpp
// Client calls, server executes
UFUNCTION(Server, Reliable)
// Client calls, server executes. WithValidation declares Server_TakeDamage_Validate.
UFUNCTION(Server, Reliable, WithValidation)
void Server_TakeDamage(int32 Damage);
void AMyCharacter::Server_TakeDamage_Implementation(int32 Damage) {
@@ -176,6 +176,21 @@ void UMyAttributeSet::OnRep_Health(const FGameplayAttributeData& OldHealth) {
// Implement other OnRep functions similarly...
```
### Clamping Attributes
Attribute Sets have no built-in clamping: the Data Table `Min Value` and
`Max Value` columns have no effect. Clamp in the Attribute Set:
- `PreAttributeChange()` is called just before any modification to an
attribute, and is the hook for rules like
`Health = Clamp(Health, 0, MaxHealth)` — its `NewValue` is mutable.
- `PreAttributeBaseChange()` is called just before a change to an attribute's
**base** value when an aggregator exists, and `PostGameplayEffectExecute()`
just after a Gameplay Effect is executed to modify the base value — clamp
base-value changes from instant effects there.
Stacking rules govern re-applications of the same Gameplay Effect; they do not
bound one attribute pushed by different effects.
---
## Gameplay Abilities
@@ -391,4 +406,5 @@ void AMyCharacter::OnHealthChanged(const FOnAttributeChangeData& Data) {
## Sources
- https://docs.unrealengine.com/5.7/en-US/gameplay-ability-system-for-unreal-engine/
- Clamping: *Gameplay Attributes and Attribute Sets* https://dev.epicgames.com/documentation/en-us/unreal-engine/gameplay-attributes-and-attribute-sets-for-the-gameplay-ability-system-in-unreal-engine ; `UAttributeSet` API https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Plugins/GameplayAbilities/UAttributeSet ; `PreAttributeChange` https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Plugins/GameplayAbilities/UAttributeSet/PreAttributeChange
- https://github.com/tranek/GASDocumentation (community guide)