08. Debugging And Fast Iteration¶
Debug STS2 mods from the loader inward. The first failed layer usually invalidates evidence from every later layer.
Log Sources¶
Check both game-root and Godot user logs:
<STS2>/sts2_stdout.log<STS2>/sts2_stderr.log%APPDATA%/SlayTheSpire2/logs/...- the in-game mod loader status and error display
The root logs can be stale if the game has not recently written them. Compare timestamps before treating their version header as current.
Iteration Loop¶
- close the game when replacing loaded DLLs or PCKs
- build the DLL against the current installed assemblies
- rebuild the PCK only when resources changed, but never reuse a stale pack accidentally
- recreate the staged mod directory from known outputs
- copy the whole directory to
<STS2>/mods/<ModId>/ - launch and inspect loader messages before testing gameplay
- test the smallest path that exercises the change
Staging should be reproducible. Incrementally copying individual files leaves obsolete resources and manifests behind.
Loader Failure Signatures¶
Manifest not detected¶
Check:
- the file has a
.jsonextension below<STS2>/mods - the JSON is valid
idis present- player consent and disabled-mod state permit loading
DLL or PCK reported missing¶
The loader resolves payloads beside the manifest using the exact id:
Check has_dll, has_pck, case, and basenames.
Dependency failure¶
Check that every dependency string equals another manifest's id. Display names and assembly names are not dependency identifiers. Inspect logs for missing or circular chains.
Initializer failure¶
Check:
- the attributed type is present in the loaded DLL
- the named method exists and is static
- registration does not enumerate a pool before all additions are complete
- the exception is not wrapped by reflection in
TargetInvocationException
If no initializer exists, remember that the loader automatically calls Harmony PatchAll; an unexpected patch can therefore originate from an assembly that appears to have no entry point.
Content Failure Signatures¶
Sequence contains no matching element from PotionModel.Pool or RelicModel.Pool¶
The model exists but belongs to no current pool. Register it with ModHelper.AddModelToPool before initialization freezes the pool.
Model can be created but never appears naturally¶
Discovery and selection are separate. Verify pool membership for cards, potions, and relics. For events, verify the target act or shared event list is patched.
Missing localization¶
Check the PCK for:
Then check the model slug, table name, flat JSON key, and language code. The manifest ID controls the prefix.
Missing image or invalid cast to Texture2D¶
Check whether the model expects a PNG or a .tres texture resource. Potion art uses a fixed atlas-resource path; placing only a namespaced PNG does not satisfy that lookup.
Card effect runs but card flow breaks¶
The effect is attached too early or owns the wrong async chain. Prefer the model's OnPlay for a custom card and AfterCardPlayedLate for external post-resolution behavior.
Save-State Troubleshooting¶
Active saves may exist under both:
%APPDATA%/SlayTheSpire2/steam/<SteamId>/...<Steam>/userdata/<AccountId>/2868840/remote/...
Modded runs use modded profile save paths. Before editing or deleting a save, close the game and preserve both copies. Cloud synchronization can restore the copy that was not changed.
Custom models serialize by model ID through the game's save types. Renaming a model class changes its generated entry ID unless compatibility code maps the old ID. Treat class names as persistent data once a release can appear in a save.
Instrumentation¶
Log high-value boundaries:
- initializer entered and completed
- each pool registration
- each Harmony patch group
- event list augmentation
- GUI controller attachment
- model effect entry with owner and state identifiers
Avoid per-frame logs. They hide the first meaningful error and make UI debugging harder.
Update Regression Matrix¶
After a game update, test:
- loader detection
- new run creation
- every custom content type through natural generation
- hover, inspect, and library views
- save and reload with custom content owned
- event entry and completion
- GUI open, close, focus, and tooltip ordering
This matrix detects semantic drift that a successful build cannot prove absent.