Registration & Multiplayer Play
Registration is what makes a mod selectable#
Pushing files to GitHub is not, by itself, enough to play a mod with anyone else. A room can only be started with mods that the DiceyTable server already knows about — its list of playable mods is built from mods that have been registered: scanned from a public GitHub repository and recorded as compatible (or at least recorded, with whatever compatibility issues the scan found). A repository that exists on GitHub but was never registered isn't in that list, and can't be picked when starting a table.
In the editor, Publish is what registers a mod — it's the third and final step
after the full push to wip and the merge into main (see
The Publish Flow). Registration re-scans whatever
is currently on the mod's main branch, so what you get back is a live read of the
published repository, not a cached assumption.
Unpublished drafts cannot be played in multiplayer — by design#
An unpublished draft — including everything sitting on wip from your auto-checkpoints
— cannot be selected for a room and cannot be played with other people, even though
it's sitting safely on GitHub. Two independent facts combine to make this true:
- Only a Publish action registers a mod. Auto-checkpoints intentionally never call
registration, so a
wipbranch full of hours of work is still invisible to the mods library until you explicitly publish. - Even if it were registered, DiceyTable has no server-side copy of your mod's files to serve. Every peer in a room pulls a published mod's manifest, setup, scripts and assets directly from GitHub — never from the table host, never from the DiceyTable server. If what you want to test isn't on GitHub in a form the mod registry knows about, there is nothing for a teammate's client to fetch.
If you want a collaborator to playtest something before it's ready for the public mods library, the mod still needs a real Publish — there is no "preview with friends" path that skips it.
How each peer loads a published mod#
When a room starts with a registered mod, every peer — host, players and spectators alike — independently:
- Resolves the mod's owner/repo and its content version — the immutable commit sha
when the registration recorded one, otherwise the scanned
artifactHash. Either way it is a value that changes whenever the mod is republished, never the branch name. - Opens (or creates) a local cache scoped to that exact
owner/repo/<version>, backed by the browser's origin-private file system (falling back to IndexedDB on browsers without it), and deletes the superseded versions of the same mod so repeated publishes don't leave a full copy of the assets behind each time. - Downloads every file the manifest declares that isn't already in that cache, in parallel with the loading screen's progress bar.
- Renders from the local cache from then on — a repo-relative asset path resolves to a local blob URL once it's primed.
A partial pull never blocks the table from loading. Each file is fetched independently,
and a single file's failure (a dropped connection, a 404 on a renamed asset) is
recorded and reported, not thrown — the rest of the pull continues, and the table opens
with whatever came down. Anything that failed to download, or was never cached at all,
still has a second chance: the runtime's asset resolver falls back to the asset's
public raw.githubusercontent.com URL directly, so a failed or still-in-progress pull
degrades to a live network fetch instead of an invisible placeholder box.
When the manifest lists a file's expected content hash, the pull step checks the downloaded bytes against it before caching them — defense in depth on top of the server-side scanner, which remains the real acceptance gate for what a mod is allowed to contain. In practice, hand-authoring that hash list is the only way to get it today: nothing in Edit Mode currently writes it for you when you publish, so most editor-published mods don't have hashes to verify against, and pulled assets are trusted by path and by whatever the scanner already validated at registration time.
How a publish reaches players: the content version#
The pull cache never revalidates a file it already has — a path present in the scope is treated as final, which is what makes the cache cheap. Everything therefore depends on the scope's last segment changing when you publish. Two separate values do two separate jobs:
- The asset ref — the git ref a
raw.githubusercontent.comURL addresses. The commit sha when one is known, otherwise the branch. - The content version — the value the local cache is scoped by, and the
?v=cache-buster appended to branch-addressed URLs. The commit sha when known, otherwise the mod'sartifactHash(a content hash of its manifest, script and asset digests, computed by the scanner with no GitHub API call involved). Never the branch name.
Publishing records the sha from the merge it just performed, over the author's own
authenticated GitHub connection, and hands it to registration — so the version does not
depend on the scanner's separate, unauthenticated sha lookup succeeding. That lookup is
now a fallback, and even when it fails the artifactHash still moves on every real
change.
A supplied sha is proven, not trusted. It must be the full 40 characters, and GitHub's
commits API must answer with exactly that sha; otherwise registration refuses the scan as
unpinned-scan and records no sha.
An abbreviated sha is refused without a lookup, because cafe1234 is also a legal branch name.
The platform's publish flow always supplies the full sha it just merged, so this only affects
hand-written registrations.
A registration that finds changed content and no sha now clears the previously recorded one rather than carrying it forward: a sha that names a commit the mod no longer matches is worse than none, because every cache keys on it.
Historical note. Both jobs above used to be one value,
commitSha ?? ref. When the sha lookup failed — routinely, on a deployment with no GitHub token, at 60 requests/hour per IP — that collapsed to the branch namemain, which is identical before and after a publish. The result was not a cache that went stale for a while: the scope never changed, so no file in it was ever re-downloaded, and every branch-addressed URL stayed byte-identical for the browser and GitHub's CDN to keep serving. Republishing a mod could not reach anyone who had already played it, and reloading did not help. If you are debugging a report of "I published but they still see the old version" against an older client, that is the cause.
Private and deleted mods#
Two owner controls live on a published mod: on its game page next to Copy link, and in Mod Details once the project is published. Both are registry state on the server, not manifest fields, so neither is ever written to your repository, and neither needs a republish.
Private hides the mod from every listing except yours: the games and packs catalogues,
search, the home page spotlight, the sitemap, favourites, and the in-table mod browser. Its
public page (/games/<slug>) answers 404.
- Only you can host it. Putting someone else's private mod on a table is refused
(
private-mod-not-hostable), whether through creating a table, changing a lobby's game, or installing a collection. - Its tables are always private lobbies. People join only by your invite. Choosing another visibility for the table is refused, and making a mod private switches tables already running it to private. Friends who aren't invited see only Private game in your status: no table name and no join button.
- Who can turn it on: the mod's owner, with a paid membership (private lobbies are a paid feature) or an admin account. Making it public again needs neither, so a lapsed subscription never traps a mod in private.
- Unlisted, not secret. Players you invite still load the mod by its id to play, and the repository behind it is public on GitHub.
- A re-publish keeps the mod private.
Delete takes the mod off DiceyTable. It disappears from every listing, its page, and your editor Projects list. The server keeps the record, though:
- One repository, one mod. A repository that has been registered can't be imported again
as a different mod, whether the old one is deleted or not. Registering it under a new manifest
idis refused (repo-already-registered), and so is linking it to a project from another account. - Importing it again as its owner reactivates it. Link the repository to a project, or
publish or register it with the same manifest
id, and the original mod comes back with its id, versions, ratings and players' stats intact, rather than as a new game. - Players keep the achievements and play history they earned with it.
- Your GitHub repository is not touched.
Delete is refused (409) while another published pack depends on the mod, or while a table
with players at it has the mod loaded.
