Skip to content

About

Collection of SourceMod plugins for the saka Team Fortress 2 Dodgeball server ecosystem

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

230 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

sourcemod-plugins

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 .smx bundles

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.

What This Repo Contains

The main exported plugins are:

  • sakaBASE combined QoL gameplay plugin
  • sakaCOLORS chat colors, tags, and chat filtering
  • sakaDONATE donator status and donation data
  • sakaGameDescription Steam server-browser Game column set to saka Dodgeball
  • sakaMapDelete console-only map-file cleanup
  • sakaMAPVOTE combined map voting, nominations, nextmap, and RTV
  • sakaNER never-ending-rounds logic for Dodgeball
  • sakaSESSION asynchronous session, event and chat logging
  • sakaSOLO solo-queue / spectator queue features
  • sakaSTATS player statistics with an optional season system (toggle via enableSeasonSystem in sakaSTATS/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.inc and include/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, and SteamWorks

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.

Repository Layout

Core directories:

  • sakaBASE/, sakaCOLORS/, sakaDONATE/, sakaMAPVOTE/, sakaNER/, sakaSESSION/, sakaSOLO/, sakaSTATS/, sakaGameDescription/, sakaMapDelete/ — plugin source trees
  • include/ project-specific and third-party non-SourceMod headers used by this repo
  • vendor/sourcemod-current/include/ separate vendored current SourceMod headers for reference only
  • scripts/ build, packaging, regression checks, and operational utilities. CI invokes build-all.sh and package-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 implementation
    • test-stats-api.py — competition ranking and season/config bounds
    • verify-smx-dependencies.py — parses the compiled .smx dependency records so optional providers stay optional
    • check-optional-native-contracts.py — ensures every native in a first-party optional provider include is marked optional
    • test-tfdb194-bounds.py — validates the production TFDB 1.9.4 fixed-array hardening patch and a local vendor tree when present
    • test-donor-entitlements.py — donor refresh and BASE live-entitlement state contracts
    • test-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 behavior
    • test-audit-report.py — remediation HTML and rollout-guide coverage
    • fixtures/*.sp — compiled as external consumers to prove the public includes still build standalone
  • .github/workflows/ CI and release automation

Typical plugin layout:

  • PLUGIN/scripting/*.sp plugin entrypoint source
  • PLUGIN/scripting/PLUGIN/*.inc local split modules when that plugin has been modularized
  • PLUGIN/translations/*.txt phrase files for SourceMod
  • PLUGIN/configs/*.cfg plugin-specific runtime config files when needed
  • PLUGIN/cfg/*.cfg SourceMod-generated or supporting cfg files when needed

Important top-level files:

How The Code Is Structured

There are two main plugin styles in this repo:

  • monolithic entrypoint plugins most logic is in a single .sp file
  • split plugins a thin .sp entrypoint includes focused local modules under PLUGIN/scripting/PLUGIN/*.inc

Examples of split plugins:

Shared ecosystem patterns:

  • AskPluginLoad2 registers plugin libraries and natives for cross-plugin use
  • public Plugin myinfo plugin metadata and version
  • include/saka*.inc public API surface that other plugins can compile against
  • plugin-local PLUGIN/*.inc helpers 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

Shared APIs And Cross-Plugin Dependencies

Some plugins are intentionally reusable by other saka plugins:

  • sakaDONATE provides the sakaDONATE library and SD_* natives
  • sakaSOLO provides the sakaSOLO library and SakaSolo_* natives
  • sakaSTATS seasonal build provides the sakaSTATS library and SakaStats_* natives
  • sakaMAPVOTE preserves the mapchooser library contract expected by dependent plugins
  • sakaWeightedVoting shared include used by sakaMAPVOTE and sakaNER

Important include families:

  • include/saka*.inc public plugin APIs
  • include/sakaDatabase.inc, include/sakaWeightedVoting.inc direct shared implementation includes used by the current plugins
  • include/multicolors* chat formatting support

Explicit contract reference:

First-Party Saka Include Usage

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

Database Driver Requirement

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, and sakaSTATS use the SakaDatabase constructor, 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 shared SakaDB_IsMysqlDriver helper 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.

Build Model

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.sh

What that script does:

  1. downloads the matching official SourceMod 1.13.0-git7246 include bundle
  2. compiles all ten plugins, including sakaSESSION, sakaGameDescription and sakaMapDelete
  3. parses every SMX dependency table and enforces the required/optional loader contract documented in CROSS_PLUGIN_CONTRACTS.md
  4. writes all ten .smx files into .build/plugins/

Supported packaging step:

./scripts/package-release.sh v2026.04.30

That 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>.smx for every built plugin
  • .build/artifacts/<plugin>.smx.sha256 matching 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 Per-Plugin Builds

Manual compiles should use include paths in this order:

  1. matching official SourceMod 1.13.0-git7246 headers
  2. repo include/
  3. 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.smx

This 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.

CI And Releases

Workflow:

Current behavior:

  • builds on push to main
  • builds on pull requests
  • supports manual release creation via workflow_dispatch
  • uploads compiled .smx files plus a packaged release zip as artifacts
  • creates or updates a GitHub release when release_version is provided in the manual workflow run
  • attaches both the bundled release zip and each individual plugin .smx to that release, each with its own .sha256 checksum, 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:

  1. open Actions
  2. choose Build and Release Plugins
  3. click Run workflow
  4. select main
  5. fill release_version, for example v2026.04.30
  6. run the workflow

Runtime Expectations

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 mapchooser compatibility library provided by sakaMAPVOTE (never co-load SourceMod's stock mapchooser.smx with this replacement)
  • DBI / database support
  • tf2, tf2_stocks, sdkhooks
  • clientprefs
  • tfdb from TF2-Dodgeball 1.9.4, installed as TF2Dodgeball.smx
  • chat-processor
  • regex (also required by sakaMapDelete)
  • SteamWorks extension 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/*.txt files go to addons/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/

Plugin Notes

  • sakaMAPVOTE is a compatibility-preserving consolidation of older map vote plugins.
  • sakaBASE is a compatibility-preserving consolidation of several smaller gameplay/QoL plugins.
  • sakaSTATS ships as a single seasonal build; the season system can be disabled at runtime via enableSeasonSystem=0 in sakaSTATS/configs/sakastats.cfg. The previously shipped sakaSTATS_w-o_seasons.sp is retired — see sakaSTATS/README.md for the upgrade path.
  • sakaCOLORS and sakaSTATS are good examples of the split-module direction used for maintainability improvements.

Related Docs

Planned Follow-Ups

Possible later improvements:

  • export more standalone saka plugins
  • add a machine-readable plugin manifest if downstream tooling needs it

About

Collection of SourceMod plugins for the saka Team Fortress 2 Dodgeball server ecosystem

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Used by

Contributors

Languages