Imported from ZenkakuHiragana/splashsweps (
AGENTS.md). Install upstream withnpx skills add ZenkakuHiragana/splashsweps. Copyright stays with the author.
Agent notes
Repo shape
- This is a Garry's Mod addon, not a conventional app/library build. Runtime entrypoint is
lua/autorun/splashsweps_includes.lua. - New shared/client library files are wired through
lua/splashsweps/shared/autorun.lua,lua/splashsweps/client/autorun.lua, orlua/splashsweps/server/autorun.lua. Follow that pattern instead of adding ad-hoc mid-file includes. SplashSWEPsis the shared namespace.lua/splashsweps/server/autorun.luaandlua/splashsweps/client/autorun.luaestablish different realm state before loading shared code.
High-value paths
lua/weapons/weapon_splashsweps_test.luais the fastest in-game smoke-test surface. It defines the spawnableInkling baseweapon mentioned inREADME.md.- Map paint/cache generation is driven from
lua/splashsweps/server/autorun.luaand consumed fromlua/splashsweps/client/autorun.lua. Debug map-cache issues againstdata/splashsweps/<map>.json,<map>_ldr.json, and<map>_hdr.json. - The README's cache-file note is stale: current code writes compressed JSON caches named
*.json,*_ldr.json, and*_hdr.json, not<map>.txt. - Ink types are loaded by
ss.LoadInkTypes()frommaterials/splashsweps/inktypes/**/*.vmt, not from a separate JSON content directory. - Shader sources live in
shaders/src/*.hlsl; compiled.vcsoutput lives undershaders/fxc/splashsweps/; the matching material stubs are inmaterials/splashsweps/shaders/.
Runtime and debugging gotchas
- Large maps are a real stress case.
README.mdexplicitly warns that 32-bit GMOD can crash on some large maps; do not treat those reports as ordinary logic regressions until you rule out build/VRAM limits. - If paint stops working after map load, check generated cache artifacts before touching gameplay code. Server cache rebuild is gated by BSP CRC in
lua/splashsweps/server/autorun.luaandserver/mapcache/cachebuilder.lua. - The client chooses HDR vs LDR surface data at runtime in
lua/splashsweps/client/autorun.lua; lighting or surface mismatches can be cache-selection issues, not shader math bugs. - Multiplayer testing has an asset-distribution constraint from
README.md: all players need the addon assets. Missing materials on one client can look like a code bug. - Client readiness is explicit networking state, not just player spawn.
ss.PlayersReadyis filled only after the client sendsSplashSWEPs: PlayerInitialSpawninclient/autorun.luaandserver/playerconnection.lua.
Runtime access via MCP bridge
- A running Garry's Mod instance can be controlled and observed through the MCP bridge.
gmod_-prefixed tools cover host launch and close, console execution, Lua execution, cvar state, screenshots, and debug hooks in both client and server realms. - MCP-driven sessions run with the game window unfocused. Frame-rate limiters that apply while unfocused (such as
fps_max_nofocus) are readable throughgmod_cvar_state_cl, but their values are environment-dependent. Do not assume or document a fixed value.
Code conventions that matter here
- Shared typed data is built with
ss.struct "TypeName" { ... }and instantiated withss.new "TypeName"; follow that pattern when adding new structured shared data. - LuaLS annotations are pervasive in
lua/splashsweps/**. Mirror---@classand---@typeusage, especially for structured tables and empty table initializers, to keep realm-specific diagnostics useful. - Do not define
SplashSWEPsnamespace state from shared realm files. Realm-specific state belongs to that realm's autorun bootstrap or a file-localss.Localstable. - Prefer designs where one file owns one responsibility. If state is file-local, define
ss.Locals.<short role>at the top of that file, mirror existing annotations, alias the local table, and keep fields under that table. - If
SplashSWEPsstate must be shared across files in a realm, define it in theSplashSWEPsbootstrap block in that realm'sautorun.lua. autorun.luaandss.Localstable guards are the hot-reload boundary. Do not add per-fieldor {}reinitialization guards for namespace state.
Verification and tooling
- There is no repo test suite or CI lint/typecheck workflow.
.github/workflows/actions.ymlonly uploads agit archivezip on pushes tomaster. - Use the correct LuaLS realm config for edits, and check through warnings, not only errors.
.luarc.server.jsontreatslua/autorun/clientandlua/splashsweps/clientas ignored, while.luarc.client.jsonignores the server equivalents. - Run LuaLS checks from the repo root and keep
--check=.as the workspace when using the repo configs. The relativeworkspace.ignoreDirandworkspace.libraryentries in.luarc.client.jsonand.luarc.server.jsonare written for that workspace shape. - Gate-style LuaLS commands:
lua-language-server --check=. --configpath=.luarc.client.json --checklevel=Error --check_format=prettyandlua-language-server --check=. --configpath=.luarc.server.json --checklevel=Error --check_format=pretty. - For warning review, rerun the same realm command without narrowing to
--checklevel=Error, then separate real code warnings from environment noise caused by missing generated type libraries. lua/types/is gitignored/generated but referenced by both.luarc.*.jsonfiles as workspace library input. Diagnostics in a clean clone may differ until those types exist.- Do not assume
stylua .will touch Lua files here:.styluaignorecurrently excludes*.luaand**/*.lua. - Do not "clean up" or revert build-script side effects just because they look like generated churn. First check this file, the build script behavior, and whether the change is required for the current runtime workflow.
- Before reverting any modified file, distinguish repo-irrelevant accidental edits from user/runtime-required edits. If the reason to revert is not explicitly grounded in project rules or the user's request, leave it alone.
- Treat edits to
lua/weapons/weapon_splashsweps_test.luaas possible in-game smoke-test controls, not disposable noise. Preserve radius, ink type, or debug behavior tweaks unless the current task explicitly says to reset them.
Exact commands worth knowing
- Compile a shader with the repo's wrapper:
pwsh -ExecutionPolicy Bypass -File "shaders/src/build.ps1" "shaders/src/debug_vs30.hlsl" shaders/src/build.ps1compiles both shader stages for the base name, updatesmaterials/splashsweps/shaders/*.vmt, writesshaders/fxc/splashsweps/*.vcs, and bumps.vscode/refresh_count_{vs,ps}.txtfor hot reload when GMOD is running.- Do not revert the shader names that
shaders/src/build.ps1writes intomaterials/splashsweps/shaders/*.vmt. Numbered names such assplashsweps/2_inkmesh_ps30are intentional hot-reload targets while GMOD is running, not accidental churn. - GMOD loads a shader file into memory only the first time that shader name is read.
build.ps1hot reload works for material stubs because it writes new numbered shader names into the matching VMT. Materials created throughCreateMaterialdo not get that VMT rewrite automatically; after compiling, update those liveIMaterials withSetString("$vertexshader", "splashsweps/<numbered>_..._vs30"),SetString("$pixshader", "splashsweps/<numbered>_..._ps30"), thenRecompute(). - When validating shader changes, run the wrapper from
shaders/srcif the direct repo-root invocation cannot find sibling shader sources. Preserve the wrapper's resulting VMT shader names and.vcsoutputs unless the user explicitly asks otherwise. - Do not run multiple
shaders/src/build.ps1shader compiles in parallel; the wrapper uses shared intermediate/output paths and can race with itself. Compile shader pairs sequentially. - Do not normalize shader material stubs after debugging just because values look temporary. Sampler bindings such as
$texture7 "shadertest/cubemap"may be intentional shader-input probes; keep them unless the current task or user says to restore defaults.