Nothing in the collection should require a fork. A change that makes one of the points below harder to reach is treated as a regression even when every test still passes.
There are five kinds of extension point, and knowing which kind you are looking at tells you where to look for it.
- Subclass a base class
An abstract class with a documented set of
_-prefixed methods to override. Stores, sources, codecs, rules, screens, modules. Roughly thirty of them.- Assign a Callable
A field on a node, usually named
*_fnor*_source, that answers one question the addon deliberately cannot answer itself: where a player is, which team they are on, where bytes go. About fifty.- Author a Resource
A
.tresa designer edits in the inspector: tunables, schemas, catalogues, rules, styles, surfaces. No code at all.- Publish under a registry name
Duck-typed seams. Register an object with one method under an agreed name and two addons cooperate without either importing the other.
- Layer a setting
Every
DotConfig: exported defaults, then a JSON or YAML file, then the environment, then the command line.
The whole index
| To change | Where |
|---|---|
| Which node a component attaches to | DotNodeRef on every component |
| Content sources (CDN, disk, in-band) | DotCloudSource subclass |
| How a player authenticates (Steam, Epic, your own) | DotAuthProvider subclass, DotAuthServer.add_provider |
| Where bans live (shared DB, HTTP service) | DotBanStore subclass on DotBanManager |
| Site groups → server permissions | DotAuthAdminSource, or any object with lookup() / source_name() |
| Server commands and behaviour | DotModule subclass, or DotConsole.command |
| What a server query reports about the game | DotQueryProvider subclass on dot_query_source, or DotModule.add_query_provider |
| What a server punishes, and how far it escalates | DotSecurityRule documents, in JSON or built in code |
| Where a server reads ban lists from | DotBanFeed entries on DotBanFeeds, chained onto dot_ban_source |
| Whether a movement claim is possible | DotAntiCheat.movement_reference, a Callable that re-simulates one tick |
| How a world is lit, and how much of it this machine draws | DotLightDocument and DotLightProfile, applied by DotLightRig |
| Reacting to or vetoing anything | DotEventBus.hook_pre / hook_post |
| What replicates, and how precisely | DotNetVar declarations; Type.CUSTOM for any type |
| Simulation, controls, messages | DotNetBehaviour, DotNetInput, DotNetMessage subclasses |
| Who sees what | DotNetInterest subclass |
| How entities are constructed | DotNetSpawner.register_factory |
| Where bytes go | DotNetManager.send_fn, DotTransport subclass |
| Where voice comes from or goes | DotVoiceSource / DotVoiceSink subclass |
| The voice codec | DotVoiceCodec subclass, DotVoiceCodec.register |
| Who hears whom on voice | DotVoiceRouter.team_fn / position_fn / listener_filter |
| Who may speak at all | Anything with is_voice_muted(peer) under dot_mute_source |
| Where punishments live | DotPunishmentStore subclass |
| Which database holds them | DotSqlDriver subclass, or DotPunishmentStoreRest |
| How a peer maps to a person | DotModerationManager.key_for_peer |
| Whose ban list a server enforces | anything registered as dot_ban_source |
| How many clients one address may hold | sv_max_connections_per_ip, DotAddressGuard |
| How a moderator names a player | DotServer.find_sessions — name, username, id, ip:, @me |
| Where a player is, and how to move them | DotModTools.position_fn / teleport_fn |
| What happens when a player joins | DotPlatformModule, or DotPlatformHub.admit directly |
| Where entitlements come from | DotPlatformHub.entitlement_source |
| Where profiles live | DotUserStore subclass on DotUserManager |
| What a name may be | DotUserManager.name_filter |
| What a player may wear | DotAvatarSchema + DotAvatarEntitlements |
| Where avatars live | DotAvatarStore subclass |
| How a part id becomes an asset | DotAvatarCatalogue.resolver |
| Movement feel | DotFpsTunables, per instance or per JSON file |
| A named variation on it — sideways, low gravity, prebhop | DotFpsStyle + DotTimerStyle, paired by id |
| How one kind of ground behaves | DotFpsSurface in a DotFpsSurfaceSet |
| A temporary change — speed pad, stun, launcher | DotFpsModifier |
| A whole new way to move — ladder, water, grapple | DotFpsMoveMode subclass |
| What the player collides against | DotFpsBody subclass, via DotFpsController._make_body |
| Input devices, bots, demo playback | DotFpsSampler / Dot2DSampler, or build a command yourself |
| What a timer reacts to in the world | DotTimerZone.Kind, drawn in the editor or with DotTimerZonePainter |
| Where records and leaderboards live | DotTimerStore / DotLeaderboardStore subclass |
| What a map is, and what plays next | DotMapDef in a catalogue; DotMapRotation, DotMapVote |
| How a map change reaches clients | DotMapSyncHost.send_fn / DotMapSyncClient.send_fn |
| What a client will load on a host’s say-so | DotMapSyncClient.accept_unknown_maps |
| Every policy in a vote | DotVoteRules, 55 settings, layered like every DotConfig |
| What can be voted for, and what “play this” means | DotVoteSource subclass, or DotVoteGameSource / DotVoteMapSource / DotVoteListSource |
| Who counts as a player, an admin or a spectator in a vote | DotVoteDirector.player_count_fn / is_admin_fn / is_spectator_fn |
| What one player’s vote is worth | DotVoteDirector.weight_fn |
| What the vote’s commands are called | DotVoteCommands.prefix / names |
| What can be spawned, and how much of it | DotPropCatalogue, DotPropLimits |
| A new sandbox tool, or a weapon | DotPropTool subclass |
| A prop that has code | DotPropDef.meta.script, a PATH |
| What a weapon is | DotWeapon, and DotDamageType for how it hurts |
| Where a shot goes | DotTrace subclass — DotTracePhysics or DotTraceFlat |
| Named body regions and their multipliers | DotHitGroup, a table rather than an enum |
| Friendly fire, self damage, clamping | DotDamageRules, plus DotDamageResolver.adjust |
| Turning lag compensation on | DotCombatManager.rewind_fn / restore_fn, two callables |
| What an item is, and what fits where | DotItem, DotLoadoutSlot, DotLoadoutSchema |
| Where loadouts live | DotLoadoutStore subclass |
| What winning means | DotMatchRules subclass — _round_outcome, _round_winner |
| Where players appear | DotSpawnPoint, DotSpawnSelector.danger_fn / filter_fn |
| What a screen is | DotScreen subclass, registered with a DotScreenStack |
| What the interface looks like | Theme, or DotUiTheme’s palette. dot-ui ships no art |
| How a settings screen is built | Nothing: DotSettingsPanel reads your DotConfig |
| How a 2D thing moves | Dot2DTunables, and Dot2DMassRules for a mass-based game |
| What a 2D thing collides against | Dot2DBody subclass |
| Long work without blocking a frame | DotJob subclass on a DotScheduler |
| Where log records go | DotLogSink, or DotLog.add_sink with a Callable |
Subclass points, by addon
Each of these is an abstract base with a small, documented set of methods. The ones marked must have no useful default.
- DotTransportdot-core
_create_server,_create_client,_transport_name,_is_available,supports_web_clients,scheme._is_availableis checked before use, so the failure is “this build cannot host over ENet” at boot rather than a null peer three calls later.- DotJobdot-core
_step(a bounded slice; return true if more remains),_progress(0..1, or -1 for genuinely unknown — an honest indeterminate bar beats one that jumps backwards),_setup,_teardown,is_thread_safe.- DotConfigdot-core
env_prefix,cli_prefix,sensitive_keys,validate.- DotLogSinkdot-core
A record handler.
DotLog.add_sink(callable)is the one-liner version.- DotCloudSourcedot-cloud
source_name,is_supported,can_serve(manifest),fetch. Afetchmust write toDotCloudStore.partial_path, resume frompartial_sizewhere it can, and finish throughcommit_partial— which is the only place the hash is verified. Writing into the object directory directly is how unverified content gets mounted.- DotAuthProviderdot-auth
_provider_name,_handles(credential)(cheap and purely structural),_authenticate(credential),_validate. A provider that claimed a credential and then rejected it has made a decision — the server does not fall through to another one.- DotNetBehaviourdot-net
_register_net_vars,_net_ready,_net_removed,_net_simulate,_net_state_applied,_net_interpolated,_net_read_property,_net_write_property. The class a game subclasses most.- DotNetInputdot-net
_write,_read,_sanitise(clamps anything a client could exaggerate — quantisation bounds each field, this is for the relationships between them),_equalsfor redundancy suppression.- DotNetMessagedot-net
_type_name(namespaced; yours should not start withnet.),_write,_read,_validate. Validate in_validate, not in the handler — a handler that validates is a handler somebody copies without the validation.- DotNetInterestdot-net
_is_relevant(override this and nothing else for most strategies),_candidatesfor a bulk filter,_prepareto rebuild an index once per snapshot,_scorefor prioritisation under a tight budget.- DotModuledot-server
_module_name,_module_version,_module_description,_module_author,_module_load,_module_unload,_module_game_changed. Commands, cvars and hooks registered through the helpers are undone automatically; override_module_unloadfor timers, files and sockets.- DotBanStoredot-server
_store_name,_load,_put,_remove,_refresh,_writable.- DotQueryProviderdot-server-query
_provider_name,_contribute(snapshot)— how a game publishes its own state into a server query. The bot count is the one that proves the point: nothing in a server framework can know it, because a bot never connects.- DotSecurityRuledot-server-security
An event name, a threshold, a window, a scope and a ladder of steps. Written as JSON or built in code, and
guard.report_session(&"your.event", session)is enough to put a game’s own event through the whole engine.- DotBanFeeddot-server-security
url,auth,token_file,list_path,on_failure. Six auth modes and several list shapes, because a blocklist you cannot point this at is one you will copy into a file by hand, and then it is stale.- DotUserStore / DotAvatarStore / DotLoadoutStoredot-user, dot-user-avatar, dot-loadout
The same shape in all three:
_fetch,_store,_remove,_open,_close,_writable,_store_name. A successful null means “this player has none”, which is not an error.- DotPunishmentStoredot-moderation
store_name,is_writable,load_all,put,update,remove. Paired withDotSqlDriver(dialect,open,execute,query,batch) when the backend is a database.- DotTimerStore / DotLeaderboardStoredot-timer, dot-leaderboard
put,top/page,best_for/entry_for,rank_of,count_on,remove. The manager sorts; the store does not have to.- DotMatchRulesdot-match
_points_for_kill,_round_outcome,_round_winner,_respawn_delay.DotRulesEliminationis a worked example of what one is for.- DotTracedot-combat
_world_rayand nothing else. The hitbox walk, the wall-wins tie-break and the restore are the base class’s.- DotFpsBody / Dot2DBodydot-player-controller, dot-2d
sweep/move,overlaps,describe. The collision queries the motor needs, with a flat analytic implementation for determinism and a physics one for real geometry.- DotFpsMoveModedot-player-controller
_name,_enter,_exit,_simulate,_uses_crouch. A ladder, water, a grapple — using the motor’s own collision and acceleration.- DotVoiceCodecdot-voice
id,encode,decode,bytes_for,is_stateful,reset,duplicate_codec. ThenDotVoiceCodec.register(...)and nothing else changes. This is the seam an Opus GDExtension drops into.- DotVoiceSource / DotVoiceSinkdot-voice
start,stop,has_frame/can_accept,read_frame/write_frame,is_running. Putting the devices behind an interface is the only reason the whole path has a headless test suite.- DotVoteSourcedot-vote
source_name,choices,current_id,supports_apply,apply. A source that cannot change anything is legitimate — the director emitschange_dueand the host acts on it.- DotPropTooldot-props
effective_reach,target,may_act_on.DotPhysGunandDotGravGunare the shipped subclasses.- DotScreen / DotHudWidgetdot-ui
_screen_id,_on_push,_on_pop,_on_cover,_on_reveal,_can_pop; and_on_valuefor a widget. Use_can_popsparingly — a screen that refuses to close is a screen a player can be trapped in.
Callable hooks
The addon knows how to do the work; it does not know your world. These are the questions it refuses to guess at, grouped by what they answer.
Where the bytes go. DotNetManager.send_fn, DotVoiceManager.send_fn,
DotVoiceRouter.send_fn, DotMapSyncHost.send_fn,
DotMapSyncClient.send_fn. None of these addons owns a socket, which is why
dot-net works over a loopback, a real transport, or your own.
Where a thing is. DotMatch.position_fn, DotVoiceRouter.position_fn,
DotModTools.position_fn / teleport_fn. DotModTools takes Variant
positions on purpose, so 2D is a first-class user rather than something to work
around.
Who counts as what. DotDamageResolver.team_of,
DotVoiceRouter.team_fn / listener_filter,
DotVoteDirector.player_count_fn / is_admin_fn / is_spectator_fn /
voters_fn / weight_fn, DotRulesElimination.alive_fn,
DotModerationManager.key_for_peer, DotModTools.immunity_fn.
What a player owns or may be called.
DotLoadoutManager.entitlement_source, DotPlatformHub.entitlement_source,
DotUserManager.name_filter, DotItemCatalogue.resolver,
DotAvatarCatalogue.resolver.
Where a shot rewinds to. DotCombatManager.rewind_fn / restore_fn. Two
lines turn lag compensation on; unset, shots resolve against the present, which
is right for a listen server and wrong for anyone with latency.
Everything else. DotSpawnSelector.danger_fn / filter_fn,
DotDamageResolver.adjust, DotFpsMotor.surface_resolver,
DotPickupField.wants_fn / tags_fn, DotHudWidget.source,
DotStatBar.max_source, DotConVar.validator, DotConCommand.completer,
DotBackboneClient.roster_provider / stats_provider,
DotVoteDirector.announce_fn, DotVoteListSource.apply_fn,
DotVoiceManager.sink_factory, DotNetVar.read_fn / write_fn /
change_handler, DotNetSpawner.register_factory.
A hook nothing assigns is not an error
Almost every one of these has a defensible unset behaviour, which is exactly why
a forgotten one is hard to find: an unwired entitlement_source means a player
owns nothing, an unset rewind_fn means no lag compensation, an unset
key_for_peer means punishments key on the wrong thing. Each addon’s
describe_lines() reports the ones it is missing — read it before assuming the
feature is broken.
Resources you author
No code, no subclass — a .tres or a JSON file.
| Resource | What it decides |
|---|---|
DotFpsTunables |
Every movement number. Layered: defaults < JSON < DOT_FPS_* < --fps-* |
DotFpsSurface / DotFpsSurfaceSet |
Ice, mud, a conveyor. Multipliers, never absolute values, so retuning the base feel carries |
DotFpsModifier |
A temporary change: speed pad, slow field, stun, launcher |
DotFpsStyle |
A named variation — sideways, backwards, low gravity, prebhop |
Dot2DTunables / Dot2DMassRules |
Top-down, thrust or blob; and how mass becomes size, speed and the right to eat somebody |
DotWeapon / DotDamageType / DotHitGroup |
What a weapon is, how it hurts, and what a headshot is worth |
DotDamageRules |
Friendly fire, self damage, falloff, clamping — in one order, once |
DotItem / DotLoadoutSlot / DotLoadoutSchema |
A game mode’s equipment rules |
DotAvatarSchema / DotAvatarSlot / DotAvatarPart |
What parts exist and what values are legal |
DotMatchRules |
What winning means and what a kill is worth |
DotSpawnPoint / DotTeam |
Where players appear and which side they are on |
DotTimerZone / DotTimerZoneSet / DotTimerStyle |
What the timer reacts to, and what a ranked style is |
DotMapDef in a catalogue JSON |
A map: an id, a version, a scene, optionally a content pack |
DotPropDef / DotPropCatalogue / DotPropLimits |
What can be spawned, and how much of it |
DotVoteRules |
Fifty-five settings, from the counting method to the tie-break |
DotLeaderboardDef / DotStatsSchema |
What a board orders, and how a new reading meets an old one |
DotUiTheme |
A whole Theme from a palette and a scale. No assets |
Registry seams
Publish an object under one of these names and something else finds it, with neither side importing the other.
| Name | Published by | Consulted by |
|---|---|---|
dot_ban_source |
DotModerationManager, and dot-server-security’s feeds |
dot-server’s admission check — both are asked, and both must say yes |
dot_query_host, dot_query_stats |
dot-server-query |
dot-server’s attach_query_host hook |
dot_security, dot_anticheat, dot_security_bans |
dot-server-security |
its own console commands, and your own code |
dot_random_source |
DotRandomManager, in dot-core |
anything wanting a stream it can reproduce |
dot_mute_source |
DotModerationManager |
DotVoiceRouter |
dot_cloud_client |
DotCloudClient |
dot-map’s loader, dot-server’s game change, dot-user-avatar’s catalogue |
dot_user_manager |
DotUserManager |
dot-server, dot-platform |
dot_avatar_manager |
DotAvatarManager |
dot-platform |
dot_auth_server |
DotAuthServer |
dot-server, dot-platform |
dot_platform |
DotPlatformHub |
DotPlatformModule |
dot_server, dot_console, dot_event_bus, dot_game_manager, dot_module_host, dot_admin_manager, dot_ban_manager, dot_chat_manager, dot_audit_log, dot_rcon_server, dot_query_server, dot_query_source, dot_a2s_server, dot_vote_manager |
dot-server |
modules, and your own code |
dot_client_link |
DotClientLink |
a game’s client code |
dot_net_manager |
DotNetManager |
bridges |
dot_match, dot_combat, dot_loadout, dot_fps_controller, dot_screen_stack, dot_2d_arena, dot_2d_controller, dot_voice_manager, dot_voice_router, dot_moderation, dot_mod_tools, dot_vote_director |
their own managers | anything that needs them |
Managers that can legitimately exist more than once in a tree take a
service_scope, so two of them register under distinct names.
Server events you can hook or veto
DotEventBus.hook_pre(name, callable) runs before the event and may cancel
it; hook_post runs after. Hooks run in registration order, and a hook that
cancels stops the remaining pre-hooks — a cancelled event should not have half
its handlers run.
| Event | Cancellable |
|---|---|
server_start, server_shutdown |
no |
client_connect, client_disconnect |
no |
client_spawn |
no |
client_kicked |
no |
player_chat |
yes |
player_command |
yes |
game_changing |
yes |
game_changed |
no |
vote_started, vote_ended |
no |
admin_action |
no |
declare(name, description) registers your own, which is what makes it show up
in the event_list console command — how a module author discovers what to hook
without reading the source.
`client_spawn` carries `userid`, not `peer_id`
A module looking a session up by peer_id gets null every time and adds nobody,
silently, because a null session is a legitimate thing to find. This is the
single most repeated bug in the collection’s own history: two separate games
shipped it, and neither errored.
