This is the reference game for TMC's Dot collection. If you want to see the assets working together before you commit to any of them, start here.
The Dot collection is a set of open source Godot 4 assets that provide modular building blocks for games and applications in the TMC ecosystem, covering core functionality, networking, authentication, cloud integration, and more. This project is built out of them, so it doubles as a worked example of what they look like in a real game rather than in a demo.
This project and the assets under it are COMPLETELY OPEN SOURCE. You are free to use, modify, and distribute them under the terms of the MIT license. The only thing not open source is the back-end web infrastructure. So if you opt into using your own authentication backend instead of integrating with TMC, you will need to build and integrate your own back-end infrastructure.
This project, along with every asset it is built on, was built initially with Claude Code and will continue to be maintained and extended using it. This is because I (gamemann) cannot build the entire TMC platform alone (I wish I could lol).
Please treat this as partially tested. It has its own headless test suite and that suite passes, but very little of this has been in front of real players yet. Expect rough edges, and please report anything you run into.
I intend on reviewing code, testing, and editing documentation regularly. If you're interested in helping out, please let me know!
A 3D dev-textured arena deathmatch, built entirely out of the dot-* family. Four weapons, one map, a match that ends on its score limit, and a HUD and menus with no art assets anywhere.
This is the reference game. It exists to prove the addons compose, to show what the bridges between them look like, and to be the thing a new game is copied from.
ln -s ../../dot-core/addons/dot_core addons/dot_core # and the other seven
godot --path . # bots, no server neededNo dedicated server, no dot-cloud, no downloads: with no dot_client_link registered the client is the authority, loads dm_atrium out of its own build and adds three bots. -- --offline forces that even when a link is available.
| WASD | Move |
| Space | Jump. Hold it, auto-hop is on |
| Ctrl | Crouch |
| Mouse 1 | Fire |
| R | Reload |
| 1..4 / wheel | Weapon slot |
| Tab (hold) | Scoreboard |
| Esc | Pause menu, and release the mouse |
The match starts in warmup. It is WARMUP, then COUNTDOWN, then LIVE, and nobody spawns until LIVE, because dot-match's respawn queue is what places a player, and it does not run before then. On the shipped rules that is about thirteen seconds at 64 ticks, so a screenshot taken earlier than that shows an empty room and a player at the origin, which is correct and looks exactly like a broken spawn.
godot --headless --path . res://examples/headless_match.tscn # a whole deathmatch
godot --headless --path . res://examples/headless_net.tscn # the netcode
godot --headless --path . res://examples/dedicated.tscn # a real DotServer
godot --headless --path . res://examples/headless_admin.tscn # an admin's live tools on itOr, once dot-serve is installed:
dotserve --game res://examples/dedicated.tscn --name "My arena"| dot-core | Everything shared. |
| dot-player-controller | Movement. Classic strafe acceleration, air-strafing, auto-hop. |
| dot-combat | Health, weapons, hit registration. |
| dot-loadout | What you take in, and what you may take. |
| dot-match | Rounds, scoring, spawning, respawning. |
| dot-ui | HUD, pause menu, settings, controls, scoreboard. |
| dot-server | The dedicated server, through one module. |
maps/arena_map.gd: a level is a list of boxes, and that list becomes three things: meshes, physics bodies, and analytic geometry for a headless server. Building them separately means three descriptions that drift, and the drift is invisible until shots start passing through something clients can see.
game/arena_player.gd: movement, weapons, health and hitboxes on one body. Every addon says this wiring belongs in the game, because an addon that did it would dictate a scene shape.
game/arena_game.gd: the seam. Six addons, each correct alone; this is the fifty lines where a combat kill becomes a match score and a match respawn becomes a loadout.
game/arena_module.gd: the only file that names dot-server. About forty lines, and it is the entire dedicated-server integration.
Four, because they are four genuinely different answers to "how do I close distance", and a deathmatch with fewer has one right answer.
| Pistol | Never runs out, always loses a fair fight. Which is what makes picking something up worth doing. |
| Rifle | Wins at range, loses in a corridor. Bloom punishes holding the trigger. |
| Shotgun | Nine pellets in a learnable ring, so range is a skill rather than a dice roll. |
| Rocket Launcher | Low direct damage, high splash. The interesting part is what it does to the floor, and to your own feet. |
Loadouts are two weapons on a six-point budget: a rifle and a shotgun, or a rocket launcher and a pistol, and not a rocket launcher and a shotgun. Four numbers instead of an enumeration.
The vote for the next map is dot-vote, and the rules in game/arena_vote.gd are only this game's defaults. A server owner overrides any of dot-vote's settings without touching code, in user://cfg/arena_vote.json, then DOT_VOTE_*, then --vote-* — later wins — or, on a TMC server, under metadata: map_vote: in the game's game.yml. A file that does not validate is refused whole and the defaults stand, with the reason in the log.
The end-of-map vote and the option to extend the current map:
{ "end_vote": true, "vote_lead_sec": 120, "include_extend": true, "extend_seconds": 600, "max_extends": 3 }end_vote: false turns the end-of-map ballot off (the map still ends, on the rotation); include_extend: false takes "extend" off the ballot; extend_seconds is how much one extension adds and max_extends how many there may be. Every setting is in dot-vote's README, and its docs/parity.md maps the long-standing community map-chooser plugins' settings onto them.
godot --headless --path . --import
find . -name '*.gd' -not -path './.godot/*' -not -path './addons/*' | while read f; do
godot --headless --path . --check-only --script "res://${f#./}"
done
godot --headless --path . res://examples/headless_match.tscn
godot --headless --path . res://examples/headless_net.tscn
godot --headless --path . res://examples/dedicated.tscn
godot --headless --path . res://examples/headless_admin.tscnheadless_match: 311 checks. Plays an entire deathmatch: four bots, real movement, real shots, real kills, real respawns, ending on the score limit. Then builds the HUD and the menus and drives them.
headless_net: 136 checks. Runs a server and a client in one process over a loopback that drops packets: the wire round-trips, a spawn is mirrored, movement replicates, the client moves on the tick it presses, the two stay together under loss, a player an admin noclips on the server is predicted flying by their own client rather than rubber-banding, and an admin's blind reaches the blinded player's client and no other while a beacon reaches both.
dedicated: 100 checks. Boots a real DotServer, binds a port, loads the module, runs its console commands, unloads it and loads it again.
headless_admin: 44 checks over nine sections. The same real server, with an administrator, a moderator and a player typing !noclip, !god, !freeze, !slay and the rest: a noclipped player goes through the wall and the floor, a godded one takes a hit and loses nothing, a frozen one holding forward does not move, a slain one dies and is counted, a moderator cannot noclip anybody, a player with no flags is refused, a blind lifts on its own when its seconds are up, a slay inside spawn protection still kills, and blind and beacon survive a respawn.
dot-moderation's live tools, with this game's verbs in game/arena_mod_tools.gd. Everything is typable in chat with a !:
| Command | Flag | |
|---|---|---|
noclip, god, buddha [player] [on|off] |
cheats |
bare, they act on you |
hp <player> <n>, speed <player> <x>, gravity <player> <x> |
cheats |
speed and gravity are steps from 0.25× to 3×, so the client can predict them |
give <player> <weapon>, strip <player> |
cheats |
|
freeze <player> [seconds], unfreeze, slay, slap <player> [damage], respawn, rename, burn |
slay |
|
blind <player> [on|off|seconds] |
slay |
blacks out that player's own screen and nobody else's |
beacon <player> [on|off] |
slay |
a pulsing ring, a column through walls and a ping, on every screen, until turned off |
bring, goto, send <player> <to>, return |
teleport |
|
modtools [player] |
generic |
what this game supports, or what is on somebody |
Targets are anything kick takes, plus @me, @all, @others, @alive, @dead and @team:<n>. Blind and beacon both outlive a death, as god does; noclip and freeze end with the body. tools/screenshot.sh dm_box --admin renders both.
MIT. See LICENSE.