Skip to content

fix: DeleteOutput parsing, late load support, config reload cache - #34

Open
Rushaway wants to merge 3 commits into
masterfrom
fix/deleteoutput-parsing-and-late-load
Open

fix: DeleteOutput parsing, late load support, config reload cache#34
Rushaway wants to merge 3 commits into
masterfrom
fix/deleteoutput-parsing-and-late-load

Conversation

@Rushaway

Copy link
Copy Markdown
Member

Summary

Fixes a parsing bug that breaks a shipped map config, plus two robustness/UX improvements around config (re)loading.

1. DeleteOutput with a target/input is broken (bug)

In RestoreLevel(), DeleteOutput values are "<output> <target>[,<input>[,<parameter>]]". The parser:

  • Calls FindCharInString(sValue[Target], ','), which returns an offset relative to the sValue[Target] substring, then uses it as an absolute index (sValue[Input] = 0). This writes the terminator into the middle of the output name.
  • Guards the parameter step with if(Input == -1) — a copy/paste of the previous check. Input is already known non-negative here, so the "target + input, no parameter" branch was never reachable.

Net effect: only the output-only and output + target forms ever worked. "DeleteOutput" "m_OnUser4 score10,ApplyScore" in ZE_FFVII_Mako_Reactor_V6_B08.cfg truncates the output name and deletes the wrong thing (or nothing).

Fix: make the offsets absolute consistently and test the parameter offset.

2. No late-load / sm plugins reload support (improvement)

AskPluginLoad2() takes late but ignores it, and OnMapStart() is the only place the map config is loaded. Reloading the plugin mid-map (or any late load) leaves every map "not supported" until the next map change.

Fix: store the flag and call the loader from OnPluginStart() when late (guarded so it does not double-run on a normal boot).

3. sm_savelevel_reload wipes the saved-level cache and always claims success (bug)

Command_ReloadConfig called OnMapStart(), which does delete g_PlayerLevels; g_PlayerLevels = new StringMap();. Reloading a config therefore drops the saved levels of players who already disconnected. It also printed "Map config file has been reloaded." even when the file was missing or failed to parse.

Fix: config loading moves into LoadMapConfig() (only touches g_Config). OnMapStart() still resets the cache (new map = stale data). The reload command reports the real result. This also matches the architecture already described in .github/copilot-instructions.md, which references a LoadMapConfig() that did not exist.

4. Malformed math config line → runtime error (minor)

In GetLevel(), a math entry missing a comma makes FindCharInString return -1, then sValue[-1] = 0 throws an array-index runtime error that aborts level detection for the whole map. Added a guard so the bad entry is skipped.

Testing

  • No local SourcePawn toolchain; relying on CI (spcomp 1.12.x) to compile.
  • Traced the DeleteOutput parser by hand against every DeleteOutput line in addons/sourcemod/configs/savelevel/ (m_OnUser1 leveling_counter, m_OnUser4 score10,ApplyScore, m_OnUser2 leveling_counter, m_OnUser3 map_wandlevels): output-only and output+target behaviour is unchanged, output+target+input now resolves correctly.

🤖 Generated with Claude Code

- RestoreLevel(): fix `DeleteOutput` entries that specify a target and
  input (e.g. "m_OnUser4 score10,ApplyScore"). FindCharInString() returns
  an offset relative to the `sValue[Target]` substring, but it was used as
  an absolute index, corrupting the output-name portion of the buffer. The
  parameter guard also re-tested `Input` instead of the parameter offset
  (copy/paste bug), so it never took the "no parameter" path. Only the
  output-only and output+target forms worked before.

- Support late loads / `sm plugins reload`: AskPluginLoad2() received
  `late` but ignored it, and OnMapStart() (the only place the config
  loads) does not fire until the next map. Store the flag and load on
  OnPluginStart() when late.

- Split config loading into LoadMapConfig() so `sm_savelevel_reload` no
  longer wipes the saved-level cache (OnMapStart() recreating
  g_PlayerLevels dropped levels of players who already disconnected). The
  reload command now reports whether a config was actually loaded instead
  of always claiming success.

- GetLevel(): guard the `math` entry parser against a missing comma so a
  malformed config line skips that entry instead of triggering a negative
  array-index runtime error that aborts level detection.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 10, 2026 20:52

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Failed config reloads must preserve the last known-good configuration.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Fixes SaveLevel parsing and improves late-load, config reload, and malformed-config handling.

Changes:

  • Corrects DeleteOutput parsing and malformed math handling.
  • Adds late-load support and centralized config loading.
  • Preserves the player cache during reloads and reports failures.
File summaries
File Summary Findings
addons/sourcemod/scripting/SaveLevel.sp Updates parsing, loading, and reload behavior. Moderate (2 votes): failed reloads can discard the active configuration instead of preserving the last known-good config.
Review details
  • Files reviewed: 1/1 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread addons/sourcemod/scripting/SaveLevel.sp Outdated
Rushaway and others added 2 commits September 11, 2026 09:48
Removed unused late load variable and related logic.
LoadMapConfig() unconditionally deleted g_Config before attempting to
load the replacement file. If the file was missing or failed to parse,
g_Config was left null for the rest of the map even though
Command_ReloadConfig() reported failure, silently disabling level
saving/restoration.

Load into a temporary KeyValues and only swap it into g_Config once it
has been fully validated. A new `keepOnFailure` parameter distinguishes
the two callers: OnMapStart() (false) still clears g_Config on failure,
since the previous map's config is meaningless on a new map; the
reload admin command (true) now leaves the active configuration
untouched when the reload itself fails.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Late-load and plugin-reload support remains incomplete.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details
  • Files reviewed: 1/1 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread addons/sourcemod/scripting/SaveLevel.sp
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants