Skip to content

perf(memory): replace fixed-size sound arrays with per-set StringMaps - #46

Merged
cmer81 merged 2 commits into
masterfrom
perf/memory-usage
Oct 6, 2026
Merged

cmer81 merged 2 commits into
masterfrom
perf/memory-usage

Conversation

@Rushaway

@Rushaway Rushaway commented Oct 6, 2026

Copy link
Copy Markdown
Member

Closes #45

@cmer81 could you review this one?

Memory

Sounds now live in one StringMap per set ("<type>:<num>" -> path + config), so only the configured ones use memory.

Before After
Data size (spcomp 1.12) 202,312,232 bytes 27,680 bytes
.smx 1.28 MB 16 KB

Other changes

  • LoadSet() loads every section with one loop over g_sSoundSections[] instead of 10 copy-pasted blocks.
  • On each kill, the sound is picked once per set instead of once per client (which copied six 256-byte strings per client), then sent with one EmitSound() call per set.
  • sm_quakesounds_volume now applies without a map change.
  • Version 4.3.0. Config files and the cookie format are unchanged.

Behavior changes (bug fixes)

  1. Headshots: the streak sound (1, 3 hattrick, 5 headhunter) now comes first, else the generic 0. The old generic branch was keyed on the kill streak (L928): you could get the hattrick sound with the "Headshot" text, or nothing at all on a 2nd headshot.
  2. Teamkill used the selfkill config (L832).
  3. Sound and text preferences are independent. Disabling sounds also hid the text (L922), while the selfkill sound played anyway (L805).
  4. Victim streak was not reset when sm_quakesounds_selfkill or sm_quakesounds_teamkill is 0, because of an early return (L800-L801, L824-L825).
  5. Past the last killsound, the random sound comes from the set's configured kill sounds instead of a hard-coded list (L1116). It is the same list for male and female. It uses that sound's own config instead of forcing "everyone".
  6. "config" "0" sounds are no longer downloaded nor precached, as the README says.
  7. Late load: no more join sound and announce sent to everyone. The saved sound set is kept: cookies were read before the sets were loaded, so everyone fell back to the first set.
  8. Errors: streaks of 999+ kills indexed out of bounds (L928). A missing translation phrase threw and stopped the client loop; it is now logged instead.

Testing

  • Builds with spcomp 1.12.0.7255, no warnings.
  • Not tested in game yet. To check: first blood, 1 to 5 headshots in a row, knife, grenade, combo, teamkill, selfkill, and sm plugins reload QuakeSounds with players connected.

The [255][999][256] sound arrays reserved ~200 MB of plugin data
(~405 MB at runtime). Sounds now live in one StringMap per set, so only
the configured ones use memory: data size goes from 202,312,232 to
27,680 bytes.

- Load every section with one loop instead of 10 copy-pasted blocks
- Pick the kill sound once per set instead of once per client, and emit
  it with a single EmitSound() call per set
- Fix the headshot streak logic, the teamkill config, the sound/text
  preferences, victim streak resets, late load and out-of-bounds indexes
- Do not download nor precache sounds with config 0

Closes #45
@Rushaway
Rushaway requested review from cmer81 and a balanced review from Copilot October 6, 2026 19:18

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.

Copilot review overview

🔵 Needs a closer look

Core event, configuration, and playback paths were extensively rewritten without in-game validation.

Review effort: Balanced
Findings: None

What changed in this PR

Replaces oversized fixed sound arrays with per-set dynamic maps, substantially reducing plugin memory use while consolidating sound handling and fixing event/preference behavior.

Changes:

  • Stores configured sounds in StringMap and ArrayList structures.
  • Centralizes sound selection, targeting, loading, and playback.
  • Updates contributor guidance for the new architecture.
File Description
addons/​sourcemod/​scripting/​QuakeSounds.sp Refactors sound storage and event handling, plus preference and behavior fixes.
.github/​copilot-instructions.md Documents the dynamic sound-storage design and extension process.

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

@cmer81

cmer81 commented Oct 6, 2026

Copy link
Copy Markdown
Member

Code review

No blocking issues found. Checked for bugs and CLAUDE.md compliance; the head compiles cleanly on SourceMod 1.13.0.7427 with no new diagnostics against master. A few non-blocking notes:

  1. The SoundType enum comment and the "Adding New Sound Events" guide say numbered sounds just need to stay before Sound_FirstBlood, but LoadSet() hardcodes type <= Sound_Combo. A new numbered type added after Sound_Combo would be parsed as a non-numbered section. A Sound_LastNumbered marker, or a note in the guide, would avoid that.

}
bool numbered = type <= Sound_Combo;
if (kv.GotoFirstSubKey() != numbered)

  1. A killsound section with "config" "0" is skipped at load and never pushed to g_aSetKillNums. If it was the highest kill number, the "past the last sound" threshold drops and the random replay starts earlier. A non-numeric section name also becomes kill number 0 via StringToInt. The shipped configs are not affected.

if (num >= 0 && LoadSound(kv, set, type, num, setFile) && type == Sound_Kill)
g_aSetKillNums[set].Push(num);
} while (kv.GotoNextKey());
}
g_aSetKillNums[set].Sort(Sort_Ascending, Sort_Integer);
delete kv;

  1. LoadQuakeSetConfig() re-reads the sets on every map start, but g_iSoundPreset[client] is only re-clamped on connect and cookie read. If the set list shrinks between maps, a connected player keeps an out-of-range preset and hears nothing until they cycle the sound pack in the menu. This was already the case before the PR, and only matters if sets.cfg changes.

public void OnMapStart()
{
LoadQuakeSetConfig();
if (g_evGameEngine == Engine_HL2DM)

  1. Behavior changes that the PR description does not list: sounds with config 0 are no longer precached or added to the downloads table, and the random replay past the last streak now honors that entry's own target bits instead of always playing to everyone.

…preset clamp)

- Add Sound_LastNumbered so LoadSet() no longer hardcodes Sound_Combo
- Skip non-numeric or negative numbered sub-sections with a warning
- Re-clamp connected clients' sound preset after the sets are reloaded on map start
@cmer81
cmer81 merged commit db4514c into master Oct 6, 2026
8 checks passed
@cmer81
cmer81 deleted the perf/memory-usage branch October 6, 2026 19:33
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.

Plugin uses ~400 MB of RAM because of oversized global sound arrays

3 participants