SourceMod plugin collection for the saka-gg Team Fortress 2 Dodgeball
ecosystem.
This repository is both:
- a source archive for the current saka plugins
- a build/release repo for producing deployable
.smxbundles
The code here is centered around TF2 Dodgeball servers. Several plugins depend on each other through public SourcePawn include files and plugin libraries.
If you are an AI agent or contributor about to edit this repo, read AGENTS.md first — it is the short, action-oriented version of this document with the editing rules you need to follow.
The main exported plugins are:
sakaBASEcombined QoL gameplay pluginsakaCOLORSchat colors, tags, and chat filteringsakaDONATEdonator status and donation data- sakaGameDescription
Steam server-browser Game column set to
saka Dodgeball - sakaMapDelete console-only map-file cleanup
sakaMAPVOTEcombined map voting, nominations, nextmap, and RTVsakaNERnever-ending-rounds logic for Dodgeball- sakaSESSION asynchronous session, event and chat logging
sakaSOLOsolo-queue / spectator queue featuressakaSTATSplayer statistics with an optional season system (toggle viaenableSeasonSysteminsakaSTATS/configs/sakastats.cfg)
The repo also carries shared include files used by those plugins:
- public saka APIs like
include/sakaDONATE.inc,include/sakaSOLO.inc,include/sakaSTATS.inc - direct shared implementation includes such as
include/sakaDatabase.incandinclude/sakaWeightedVoting.inc - third-party non-SourceMod headers used by the plugins, such as
multicolors,tfdb(production-pinned TF2-Dodgeball 1.9.4),chat-processor,tf2attributes, andSteamWorks
sakaSESSION ships as sakaSESSION.smx in the main release. Existing servers
must remove or disable the old basemenus.smx during a full stop/start before
installing it; see its upgrade notes.
Third-party patches and external dependencies have a different release path;
see release contents and dependency policy.
Core directories:
sakaBASE/,sakaCOLORS/,sakaDONATE/,sakaMAPVOTE/,sakaNER/,sakaSESSION/,sakaSOLO/,sakaSTATS/,sakaGameDescription/,sakaMapDelete/— plugin source treesinclude/project-specific and third-party non-SourceMod headers used by this repovendor/sourcemod-current/include/separate vendored current SourceMod headers for reference onlyscripts/build, packaging, regression checks, and operational utilities. CI invokesbuild-all.shandpackage-release.sh; release verification runs during packaging. Operational utilities are not automatically executed by CI.check-source-patterns.sh— invariants a runnable test cannot express ("never reintroduce shape X", plus security/privacy/fail-closed properties). Prefer absence gates; see the scope note at the top of the file before adding one.test-colors-filter.py— masking semantics and color normalisation, modelled independently of the SourcePawn implementationtest-stats-api.py— competition ranking and season/config boundsverify-smx-dependencies.py— parses the compiled.smxdependency records so optional providers stay optionalcheck-optional-native-contracts.py— ensures every native in a first-party optional provider include is marked optionaltest-tfdb194-bounds.py— validates the production TFDB 1.9.4 fixed-array hardening patch and a local vendor tree when presenttest-donor-entitlements.py— donor refresh and BASE live-entitlement state contractstest-gameplay-policies.py— NER/SOLO/STATS bot, population, and death classification policies../sakaMAPVOTE/tests/test_mapvote_lifecycle.py— config/timer ownership and frozen vote-epoch behaviortest-audit-report.py— remediation HTML and rollout-guide coveragefixtures/*.sp— compiled as external consumers to prove the public includes still build standalone
.github/workflows/CI and release automation
Typical plugin layout:
PLUGIN/scripting/*.spplugin entrypoint sourcePLUGIN/scripting/PLUGIN/*.inclocal split modules when that plugin has been modularizedPLUGIN/translations/*.txtphrase files for SourceModPLUGIN/configs/*.cfgplugin-specific runtime config files when neededPLUGIN/cfg/*.cfgSourceMod-generated or supporting cfg files when needed
Important top-level files:
spcompvendored SourcePawn compiler binary currently used by CI and local builds- scripts/build-all.sh supported full-repo build entrypoint
- scripts/package-release.sh creates the deployable zip artifact
- vendor/sourcemod-current/README.md explains why official SourceMod headers are kept separate
There are two main plugin styles in this repo:
- monolithic entrypoint plugins
most logic is in a single
.spfile - split plugins
a thin
.spentrypoint includes focused local modules underPLUGIN/scripting/PLUGIN/*.inc
Examples of split plugins:
- sakaMAPVOTE/scripting/sakaMAPVOTE.sp
- sakaCOLORS/scripting/sakaCOLORS.sp
- sakaSTATS/scripting/sakaSTATS.sp
Shared ecosystem patterns:
AskPluginLoad2registers plugin libraries and natives for cross-plugin usepublic Plugin myinfoplugin metadata and versioninclude/saka*.incpublic API surface that other plugins can compile against- plugin-local
PLUGIN/*.inchelpers private implementation helpers kept inside each plugin tree - translations and configs are stored next to each plugin, then packaged into a SourceMod-style release zip
Some plugins are intentionally reusable by other saka plugins:
sakaDONATEprovides thesakaDONATElibrary andSD_*nativessakaSOLOprovides thesakaSOLOlibrary andSakaSolo_*nativessakaSTATSseasonal build provides thesakaSTATSlibrary andSakaStats_*nativessakaMAPVOTEpreserves themapchooserlibrary contract expected by dependent pluginssakaWeightedVotingshared include used bysakaMAPVOTEandsakaNER
Important include families:
include/saka*.incpublic plugin APIsinclude/sakaDatabase.inc,include/sakaWeightedVoting.incdirect shared implementation includes used by the current pluginsinclude/multicolors*chat formatting support
Explicit contract reference:
- CROSS_PLUGIN_CONTRACTS.md provider/consumer matrix for plugin libraries, natives, and shared includes
- PRODUCTION_ROLLOUT_2026-08-28.md audited TFDB 1.9.4 old-plugin migration, staging, and rollback runbook
Use this table when checking whether plugin docs match the code. It lists only
first-party include/saka*.inc headers that are used by in-repo plugins.
| Include | Contract type | Direct in-repo consumers |
|---|---|---|
sakaClientValidation |
shared stock helpers | sakaBASE, sakaCOLORS, sakaDONATE, sakaMAPVOTE, sakaSOLO, sakaSTATS |
sakaColors |
shared chat-color constants | sakaBASE, sakaMAPVOTE |
sakaConstants |
shared buffer-size constants | sakaBASE, sakaCOLORS, sakaMAPVOTE |
sakaDatabase |
shared MySQL-only database methodmap; rejects other DBI drivers before schema work | sakaBASE, sakaCOLORS, sakaDONATE, sakaSTATS |
sakaDONATE |
public plugin API; consumer-selected binding (sakaBASE required, sakaCOLORS / sakaWeightedVoting optional) |
sakaBASE, sakaCOLORS, sakaWeightedVoting |
sakaLogging |
shared structured logging helpers | sakaBASE, sakaCOLORS, sakaDONATE, sakaMAPVOTE, sakaNER, sakaSOLO, sakaSTATS |
sakaMAPVOTE |
public plugin API; optional binding in sakaNER |
sakaNER |
sakaMessageFormatting |
shared chat-prefix formatter | sakaBASE, sakaMAPVOTE |
sakaSOLO |
public plugin API; optional binding in sakaNER and sakaSTATS |
sakaNER, sakaSTATS |
sakaSTATS |
public plugin API; include supports consumer-selected binding | no current in-repo consumers |
sakaWeightedVoting |
shared compile-time vote helper | sakaMAPVOTE, sakaNER |
The database-backed plugins require SourceMod's MySQL DBI driver. This is a
hard runtime contract, not a generic-DBI promise: the schemas and queries use
MySQL syntax and metadata. MySQL-compatible servers such as MariaDB are
supported only when the corresponding databases.cfg entry selects the
mysql driver.
Configure these four named connections with "driver" "mysql":
| Plugin | databases.cfg name |
|---|---|
sakaBASE whitelist |
sakabase |
sakaCOLORS |
sakacolors |
sakaDONATE |
sakaDONATE |
sakaSTATS |
sakastats |
Example skeleton (replace the non-driver values for your environment):
"sakabase"
{
"driver" "mysql"
"host" "127.0.0.1"
"database" "saka"
"user" "saka"
"pass" "replace-me"
}
Repeat that shape for the other names. The driver is inspected immediately
after connecting and any identifier other than mysql (SQLite included) is
rejected before charset or schema initialization. A rejected connection causes
the owning plugin's existing startup/retry failure path to run; it never
attempts MySQL DDL against the wrong backend.
COLORS, DONATE, and STATS open private, non-persistent connections. Do not
enable connection pooling for these plugins: legacy session plugins can issue
unlocked synchronous queries, and different config names with identical
credentials can still share the same pooled MySQL connection. Production's
2026-09-11 disconnect crash showed a STATS worker query and a legacy
UPDATE sakaSession using the same connection while reconnect/SSL cleanup
raced. Private connections isolate that legacy work; the balanced locks remain
necessary for each plugin's own synchronous/threaded operations.
sakaSESSION additionally requires a sakasession MySQL connection and existing
legacy tables. It uses its own DBI connections rather than SakaDatabase; it
does not implement the shared driver-validation path described below. See its
setup and migration notes.
Two connect paths enforce the shared driver contract:
sakaCOLORS,sakaDONATE, andsakaSTATSuse theSakaDatabaseconstructor, which validates the driver internally. It connects synchronously, which is acceptable because these connect once at plugin start.sakaBASE's whitelist connects asynchronously (Database.Connect) and validates through the sharedSakaDB_IsMysqlDriverhelper in its connect callback, then applies the charset and creates its table with threaded queries. This path doubles as the reconnect-timer body, so a blocking connect there would stall the game thread for a full connect timeout on every retry while the database is unhealthy.
Current supported compiler:
./spcomp- version:
SourcePawn Compiler 1.13.0.7246 - binary type: macOS
arm64 - build-time verifier: Python 3
Supported full-repo build:
./scripts/build-all.shWhat that script does:
- downloads the matching official SourceMod
1.13.0-git7246include bundle - compiles all ten plugins, including sakaSESSION, sakaGameDescription and sakaMapDelete
- parses every SMX dependency table and enforces the required/optional loader
contract documented in
CROSS_PLUGIN_CONTRACTS.md - writes all ten
.smxfiles into.build/plugins/
Supported packaging step:
./scripts/package-release.sh v2026.04.30That creates:
.build/artifacts/saka-gg-sourcemod-plugins-v2026.04.30.zip.build/artifacts/saka-gg-sourcemod-plugins-v2026.04.30.zip.sha256.build/artifacts/<plugin>.smxfor every built plugin.build/artifacts/<plugin>.smx.sha256matching each plugin
The main zip includes all ten plugins and installation notes for sakaSESSION, sakaGameDescription and sakaMapDelete.
Each .sha256 records only the basename so you can verify a single
downloaded file with shasum -a 256 -c <plugin>.smx.sha256 in whatever
directory you saved it to.
Manual compiles should use include paths in this order:
- matching official SourceMod
1.13.0-git7246headers - repo
include/ - plugin-local
PLUGIN/scripting
Example:
./spcomp sakaMAPVOTE/scripting/sakaMAPVOTE.sp \
-i/path/to/sourcemod-1.13.0-git7246/addons/sourcemod/scripting/include \
-iinclude \
-isakaMAPVOTE/scripting \
-osakaMAPVOTE.smxThis order matters. The repo now keeps official SourceMod headers outside
include/ so manual -iinclude builds are less likely to accidentally shadow
the compiler-matched header set.
Workflow:
Current behavior:
- builds on push to
main - builds on pull requests
- supports manual release creation via
workflow_dispatch - uploads compiled
.smxfiles plus a packaged release zip as artifacts - creates or updates a GitHub release when
release_versionis provided in the manual workflow run - attaches both the bundled release zip and each individual plugin
.smxto that release, each with its own.sha256checksum, so you can pull and verify a single plugin without downloading the full bundle - third-party patches do not automatically become binaries in the release zip (see release contents)
Manual GitHub release flow:
- open
Actions - choose
Build and Release Plugins - click
Run workflow - select
main - fill
release_version, for examplev2026.04.30 - run the workflow
This repo is for building and packaging plugins, not for carrying a full live server install.
At runtime, servers still need the relevant SourceMod / extension environment, including where applicable:
nextmap- the
mapchoosercompatibility library provided bysakaMAPVOTE(never co-load SourceMod's stockmapchooser.smxwith this replacement) - DBI / database support
tf2,tf2_stocks,sdkhooksclientprefstfdbfrom TF2-Dodgeball 1.9.4, installed asTF2Dodgeball.smxchat-processorregex(also required by sakaMapDelete)SteamWorksextension for sakaGameDescription; install separately for the server OS, architecture and SourceMod version (see its plugin guide)
Plugin-specific runtime assets are packaged from each plugin directory:
- tracked
saka*/translations/*.txtfiles go toaddons/sourcemod/translations/; packaging requires a Git worktree and rejects duplicate translation basenames instead of overwriting them - plugin configs go to
addons/sourcemod/configs/ - tracked sakaCOLORS upgrade scripts and their ordered runbook go to
addons/sourcemod/configs/sakacolors/migrations/ - extra cfg files go to
cfg/sourcemod/ - the packaged sakaCOLORS filter cfg disables persistent filtered-message
logging by default; merge it deliberately with existing installs because
SourceMod preserves an already-saved ConVar value; its access/retention
runbook ships at
addons/sourcemod/configs/sakacolors/README_CHAT_FILTER.md - compiled plugins go to
addons/sourcemod/plugins/
sakaMAPVOTEis a compatibility-preserving consolidation of older map vote plugins.sakaBASEis a compatibility-preserving consolidation of several smaller gameplay/QoL plugins.sakaSTATSships as a single seasonal build; the season system can be disabled at runtime viaenableSeasonSystem=0insakaSTATS/configs/sakastats.cfg. The previously shippedsakaSTATS_w-o_seasons.spis retired — see sakaSTATS/README.md for the upgrade path.sakaCOLORSandsakaSTATSare good examples of the split-module direction used for maintainability improvements.
-
Documentation index — current guides and historical records
-
Release contents — shipped plugins and dependency builds
-
sakaSESSION/README.md — database prerequisites and legacy rename
-
AGENTS.md / CLAUDE.md short editing guide for AI agents and contributors
-
documentation-web/ interactive audit report, published to GitHub Pages at https://saka-gg.github.io/sourcemod-plugins/
Possible later improvements:
- export more standalone saka plugins
- add a machine-readable plugin manifest if downstream tooling needs it