Server-side bridge between a Don't Starve Together cluster and the Axogc
community platform core service.
- Long-polls
corefor downstream commands (player.notify,server.broadcast,player.kick,chat.from_web,metrics.list,leaderboard.fetch,player.stats.fetch,admin.command.run). - Reports upstream events (
player.joined,player.left,player.died,heartbeat,chat.message,binding.request). - Locally accumulates 6 DST-native stat axes (days_survived / play_time / sanity_avg / structures_built / mobs_killed / bosses_defeated) and serves them via the pull-mode leaderboard protocol introduced in PLAN §10.6.
- Master shard only. A DST cluster typically has Forest (Master) and
Caves (Slave).
modmain.luaearly-returns on Slave shards. HTTP bridge work is per-cluster, not per-shard. - Long-poll with short hold.
TheSim:QueryServerhard-timeouts at ~5s, so/api/srv/pollis called withmax_hold_ms=3000(PLAN §4.2). Downstream command latency is still ~100ms RTT —max_hold_msonly bounds idle reconnect cadence. /bindover chat. DST has no real slash-command registry; the mod listens on each player'sonsayand intercepts the/bindprefix. As a side-effect the code is visible to other players (P1: implement atalker:Sayhook to suppress public broadcast).- Single in-memory stats file at
TheSim:GetPersistentString("axo_platform_stats.json"), rewritten every 60s plus on everyms_worldsavedand on player leave.
modinfo.lua / modmain.lua / modworldgenmain.lua
scripts/platform/
├── settings.lua # BASE_URL, TOKEN, intervals, boss prefab whitelist
├── util.lua # tiny helpers (trim, find_player, log)
├── transport/
│ ├── http.lua # post_srv / get_srv / reply_event / parse_envelope
│ └── poll.lua # long-poll loop with max_hold_ms=3000
├── stats/
│ └── axes.lua # the 6 DST metric definitions (metrics.list payload)
├── observation/
│ ├── stats.lua # in-memory cache + PersistentString backing
│ ├── walk_sampler.lua # 1s position sampler → walk_distance_m
│ └── events.lua # SINGLE subscription source: chat / death / killed / build / join / leave
├── chat/
│ └── chat.lua # upstream chat.message + loopback dedup
├── commands/
│ └── bind.lua # /bind handler + 3-fails/10min lockout
├── handlers/
│ ├── dispatch.lua # downstream command router
│ ├── notify.lua / broadcast.lua / kick.lua / chat_from_web.lua
│ ├── stats_fetch.lua
│ ├── metrics_list.lua / leaderboard_fetch.lua
│ ├── whitelist.lua # always NOT_SUPPORTED — DST has no whitelist mode
│ └── admin_command.lua
├── rank/
│ └── snapshot.lua # pre-sorted snapshot every 5min, sliced on fetch
└── init.lua # boot sequence wired into AddPrefabPostInit("world")
This mod is a server-side bridge — clients don't need to install it.
No Steam Workshop upload required (and not recommended — token is a secret).
Copy the entire repo into your DST dedicated-server install:
/srv/dst/dst_server/mods/axogc-platform/
├── modinfo.lua
├── modmain.lua
├── modworldgenmain.lua
└── scripts/...
The folder name (axogc-platform) is arbitrary, but it MUST match the key
you use in modoverrides.lua below.
DST clusters typically live under
/srv/dst/data/DoNotStarveTogether/Cluster_<N>/<ShardName>/modoverrides.lua.
Edit both the Master shard and any secondary shard (Caves, etc.); the mod
loads on Slaves too and harmlessly early-returns from modmain.lua, but the
declaration has to be present so DST keeps the mod enabled.
return {
["axogc-platform"] = {
enabled = true,
configuration_options = {
base_url = "https://www.axogc.net", -- no trailing slash
token = "abcdef0123456789...", -- from platform admin panel
},
},
}This is DST's standard GetModConfigData mechanism (see modindex.lua in
the engine source) — modoverrides.lua values bypass the dropdown UI and are
fed straight into GetModConfigData("base_url") / ("token") calls in
modmain.lua. The mod folder itself stays free of credentials.
DST has no mod hot-reload — restart the whole cluster after any change to
modoverrides.lua or any file under mods/axogc-platform/.
Watch the Master shard's console for:
[platform] booting bridge…
[platform] stats loaded — N players
[platform] poll loop starting (max_hold_ms=3000)
[platform] chat hook installed
[platform] bridge online
If you see base_url not configured or token not configured, the
modoverrides.lua keys are missing, mis-spelled, or the mod folder name
doesn't match the table key.
After restart, the Master shard should immediately heartbeat. On core:
# Logs (heartbeats are noisy; filter):
docker compose logs core | grep -E "heartbeat|player\.joined|chat\.message"
# Redis:
redis-cli ZSCORE server:status <server_id> # presence
redis-cli HGET server:online <server_id> # online countWhen a player joins and runs /bind ABCD12, you should see one POST to
/api/srv/binding.request immediately and one downstream player.notify
delivered to the player within seconds.
- Hardcoded 5s timeout on
TheSim:QueryServercannot be raised. Anything that needs more than ~3s server-side will time out client-side. - Every
QueryServercall writes one line to the BDS log (SimLuaProxy::QueryServer()); at 3s poll cadence that's ~28,800 lines/day. Use logrotate; do not try to silence them. - Default DST sandbox blocks
localhost. Use the host's real IP or a public domain even when DST and core run on the same machine. Player.userid(KU_xxx) is permanent across renames, but the platform protocol still uses gamename as the identity key (per PLAN §9). userid is attached asexternal_idfor future rename tolerance.
/bindcode visible in public chat — needsAddComponentPostInit("talker", ...)interceptor.- Day / season / boss status upstream (originally in PLAN §15.5) — deferred, not yet implemented.
- Caves shard player events not aggregated into Master — accept cluster-level granularity for MVP.