# Assigning a Room or Table

> How a game picks the published room and table packs it uses, why the pick is pinned to an exact version, and why nobody can change either mid-session.

- Source: https://diceytable.com/docs/editor/workflows/assign-a-room-or-table
- Last verified: 2026-10-13

A room and a table are not part of your game's scene document. They are **published packs** —
each with its own repository, its own versions and its own author — and your game holds a
*reference* to one of each.

That is why the [Game Editor](https://diceytable.com/docs/editor/concepts/editor-urls#the-modes) shows one `Room` row
and one `Table` row instead of a floor, a ceiling, four walls, their posters and a light list.
Composition and construction are different jobs, done in different editors.

## Open the Game Editor

Go to `/editor/game/{id}`, or open the mod and use **Mod ▸ Open In ▸ Game Editor** in the menu
bar. (It reloads the page, so unsaved changes are confirmed first.)

The Hierarchy collapses to four rows:

```
Room          Noir Parlour 1.2.0
Table         Hex Wargame 2.0.1
Player Zones (10)
Entities (14)
```

**Nothing was deleted.** If your scene has hand-edited room internals — an extra light, a
poster, a moved wall — every byte of it is still there. Only the rows are hidden, and the Room
Editor is the way back to them.

## Where a new game's room and table come from

The **create dialog** asks. After you choose that you are making a Game, the next step offers a
**Room** and a **Table**, and both are real assignments — they are written into your game's
`setup.json` as ordinary pins, exactly as if you had assigned them from the rows below, and every
player gets them.

Leaving either on its **Default** option is a real answer, not a blank: a new game with no room
pick is pinned to **Modern Room**, and one with no table pick to **Square Table** — the platform
defaults (see [The platform defaults](https://diceytable.com/docs/editor/workflows/assign-a-room-or-table#the-platform-defaults-modern-room-and-square-table)).
Nothing here is permanent — the rows below change either one in a click.

> The same step appears for a room, table or asset pack, but it means something different there:
> those kinds cannot assign packs at all, so their picks are **previews**, remembered in your
> editor workspace and never published. See
> [Preview and Photo Mode](https://diceytable.com/docs/editor/workflows/preview-and-photo-mode).

## The assign inspector

Click `Room` or `Table`. The Inspector shows what is assigned:

- the pack's **cover, name and author**;
- its **pinned version**, and the commit that version resolved to;
- its **licence**;
- **Change…** / **Remove**, two ways to open the pack's own editor, and the two content-move
  buttons described under [Moving content in and out of a pack](https://diceytable.com/docs/editor/workflows/assign-a-room-or-table#moving-content-in-and-out-of-a-pack).

With no pin the inspector reads **Default** — *no room/table pack is assigned, so every peer uses
the platform default, Modern Room/Square Table* — and the Hierarchy row reads
`(default: Modern Room)` or `(default: Square Table)`. It is a normal state rather than an empty
slot, and **Remove** always gets you back to it in one click.

### The viewport shows the table you assigned

Assigning, bumping or removing a table re-reads the pinned pack's document and rebuilds the
table in the viewport — its model, its colliders and its play surface — so what you author on is
what your players will get. The pack is read at the pinned commit, not at whatever is on its
branch today.

If that document cannot be read — the pack is gone, the version is missing, the file is
malformed — the table is **refused** and named, and the editor does *not* quietly put a
default table in its place. Object positions are authored against the play surface, so a viewport
guessing a height you have not assigned would write positions no player ever sees into your mod.

### The viewport shows the room you assigned, too

The same is true of a room, and it replaces more than the walls. Assigning one swaps the whole
room the viewport renders: its floor, ceiling and walls with their materials and posters, its
lights, its openings, its **static scenery** (the pack's decor placements), and the environment
fields a room owns — the skybox, the fog and the ambient light. The room you authored in *this*
project is not used while a pack is assigned.

Nothing is migrated or destroyed: your own room stays in the document exactly as you left it.
It does not come back when you **Remove** the assignment, though — with no pin the game plays in
the default room, Modern Room.

A room that cannot be read behaves differently from a table that cannot be read, on purpose: the
room **degrades**. You are told which pack failed and why, and the editor falls back to the room
authored in your project so you still have a viewport you can work in. Nothing in a room moves a
drag target, so there is nothing here that could write a wrong number into your mod — which is
exactly why the table refuses instead.

> The two `Table Surface` environment fields are **not** the room's. A room owns the sky, the
> fog and the ambient; the table owns how its own play surface looks. One writer per field.

A room pack's scenery brings its own **materials and textures** with it, and it needs to. Model
import strips a GLB's embedded images out into separate files, so an imported model's textures
are re-attached from its `<model>.meta.json` sidecar, which names materials from the pack's own
library by id. Both the pack's library and its sidecars are loaded alongside the room — you do
not have to copy anything into your project, and a model that ships no sidecar simply keeps the
materials it was exported with.

### Choosing a pack

**Change…** opens the picker. It lists every published pack of the right type — a room picker
never shows a table pack, and neither ever shows a whole game — with **Default** always first.
Its card reads *Modern Room, the platform default* (or *Square Table, the platform default*), and
its button **Use the default room** / **Use the default table** clears the pin.

Every card carries the **licence**. That is deliberate: your game's listing credits the packs it
uses automatically, so the picker is where you learn what you are agreeing to. Before you can
apply a pack you tick an acknowledgement naming its licence and its author.

> DiceyTable does **not** check whether one licence is compatible with another. A machine
> verdict on licence compatibility would be confident and often wrong, and every file involved
> is public on GitHub either way. The licence is shown, prominently, and the judgement is yours.

### The platform defaults: Modern Room and Square Table

DiceyTable has no built-in room or table of its own any more. A game that assigns no room plays in
**Modern Room** (`modern-room` `1.0.2`), and one that assigns no table plays at **Square Table**
(`square-table` `0.1.6`). Both are ordinary published packs, pinned to an exact version and commit
like any assignment, so every peer resolves the same bytes. A blank table — `/table` with no room
and no game — loads the same two.

You will rarely see a game without the pins, because the editor writes them in for you:

- **New games** are created pinned to Modern Room and Square Table.
- **Opening an older game** that pins neither shows the defaults in the viewport straight away. The
  pins are saved with your next save; opening alone does not mark the project as changed.
- **At play time and in the preview**, a game with no pin gets the same defaults on every peer.

**Older games are lifted onto Square Table.** A game that never assigned a table was laid out on
the old built-in table, whose play surface was at `y = -0.02`. Square Table's is at `y = 2.0723`,
2.09 ft higher, so the pieces are moved up by exactly that difference — the same straight-up move
the editor offers when you [change the table](https://diceytable.com/docs/editor/workflows/assign-a-room-or-table#changing-the-table-can-leave-your-pieces-floating--and-it-offers-to-fix-it).
Only what the old table was holding up moves: anything at or above its surface and inside its
5.46 × 2.88 ft footprint, with the seat ring, snap points, snap-point grids and table zones
carried along. Floor and room content stays where it is.

### The built-in Dining Table

The Table picker always lists one pack that is not published by anyone: **Dining Table**, by
DiceyTable. It is an oak table at a real dining table's height - the play surface sits at
`y = 1.5`, 2.45 ft above the floor (Square Table's is at `y = 2.0723`).

It behaves like any other table pack - a pin to an exact version (`diceytable.dining-table`
`1.0.0`), shown in the `Table` row and removable in one click - with three differences:

- **It ships with DiceyTable.** Its document and model are served by the app itself, so it needs no
  registry, no GitHub and no network. There is no licence to acknowledge, and it is never
  "unavailable".
- **Its collider runs from the play surface to the floor**, so nothing slides under the table.
- **A version never changes.** If DiceyTable ever ships a different height it will be a new version
  you choose to move to; a game pinned to `1.0.0` keeps exactly this table.

New games no longer start on it - they start on Square Table - but a game that pins it keeps it,
because every position in a game is an absolute height against the table it was authored on.

To move a game onto it:

1. Open the game in the Game Editor and click the `Table` row.
2. **Change…** → **Dining Table** → **Use this table**.
3. Read the preview that appears under the row: it lists every piece and the height it moves to
   (`-0.57 ft` coming from Square Table), and moves your seat ring by the same amount.
4. **Apply** to switch the table and move them in one step - or **Cancel** to keep the table you
   have, with every piece exactly where it was.

To go back, **Remove** the assignment: that returns to the default table, Square Table, and offers
the opposite move.

## Pins, and why there are no version ranges

An assignment names **one exact version** — `1.2.0`, never `^1.2.0` or `1.x`.

A range would make your table non-reproducible: two players who resolved it a week apart could
be running different builds of the same room, and for a *table* that is not cosmetic — the table
supplies the play-surface height every peer computes drag targets against.

So a pin never moves on its own. When a newer version exists, the inspector says so and offers a
button:

```
(i) 1.3.0 is available.   [ Update pin to 1.3.0 ]
```

Until you press it, your game keeps using the version you chose. A published version is
immutable — the label can never be re-pointed at different content — so a pin you made last
month still means exactly what it meant then.

If a pinned pack disappears from the registry the row is badged **unavailable** and the inspector
names the id that failed. **The pin is kept**, and the game cannot load that room or table until
the pack is published again or you assign another. Silently swapping in the default would hide a
problem you need to know about.

**"Unavailable" is only ever said when the registry actually answered.** If the list of published
packs could not be loaded at all — you are offline, or the API is having a moment — the inspector
says so instead, with a **Try again** button, and your pin is shown exactly as you saved it:

> Could not load published packs. This pin has not been checked — it is shown exactly as saved,
> and nothing has been changed.

That is a different message on purpose. A failed request is not evidence that anybody's pack was
unpublished, and a red "your pin is broken" warning raised by a dropped connection would send you
looking for a problem that does not exist.

## Two ways to open the pack's own editor

Both are on the assign inspector, and they do different things:

- **Open Room pack / Table pack tab** — an in-app editor tab, alongside your model and script
  tabs. No reload, no re-authentication, and your unsaved work in this project is untouched. It
  is the fuller view of the assignment: cover, summary, licence, pin, and the same controls.
- **Open in Room Editor / Table Editor** — a **browser tab**, on the pack's *own* project.

The second one is only available for a pack that is one of **your** projects. Someone else's
published pack cannot be edited here at all; fork its repository to make your own version.

> **Why a browser tab and not an in-app one?** Each mod has its own local file tree, and one
> editor tab owns one tree. The pack is a different mod, so editing it means a second tab on a
> different tree — never a second view of the game you have open. Two tabs on one tree cannot
> both save; see [One mod, one writing
> tab](https://diceytable.com/docs/editor/concepts/editor-urls#one-mod-one-writing-tab).

## Moving content in and out of a pack

Two buttons on the assign inspector move content between your game and a pack. They are two ends
of the same operation, and **both show you exactly what will and will not come along before
anything changes**.

### Extract to a pack

Available only when the row has **no pin** — the button is disabled while a pack is assigned, and
opening a game pins the defaults, so **Remove** the assignment first.

It creates a **new pack project** from your game's own room or table content and leaves your game
unpinned, which means the platform default (Modern Room / Square Table).
Use it when something you built inside a game turns out to be worth publishing on its own.

Some things cannot come with it, and the review step lists every one of them by name with the
reason:

- **Table pieces** never move into a room or table pack. They are your game's, and they stay in
  your game — nothing is deleted.
- A **room** pack cannot carry seats, zones, snap points, prefabs or scripts; a **table** pack
  cannot carry a room, zones, snap points, prefabs or scripts. What it *can* carry — for a room,
  the room model and the environment; for a table, the seat ring — moves across.

**The first step is the new pack's GitHub repository**, exactly as when you create a mod: every
mod lives in its own public repository. The review dialog opens with a **New repository name**
(a slug of the pack's name, e.g. `noir-parlour-room`); if GitHub is not connected yet it shows
**Connect GitHub** and a link to create a free account instead, and the confirm button waits.
Confirming then:

1. creates the public repository,
2. creates the pack project, linked to it,
3. uploads the pack and every file it uses from your game — models, textures and the material
   files that name them — to the repository's `wip` branch.

Your game is only changed once all three have succeeded. If the upload fails, the repository and
pack project are kept and the dialog stays open: confirm again to retry the upload without
creating a second repository.

No pin is written. A pin names an exact **published** version, and your new pack has not been
published yet. Publish it, then assign it from this same row.

### Detach & vendor

The reverse: it copies an assigned pack's content **into** your game and removes the pin, so your
game stops depending on it.

This is the way out when a pack you depend on is unpublished or deleted. A pinned version is
immutable and can never be re-pointed, which is a guarantee — but it also means a pin whose pack
has vanished stays broken until you do something. Vendoring is that something, and it needs
nothing from the pack's author.

It needs the pack's *files*, so it is only available for a pack that is one of your own projects.
If it is not, the button says so rather than sitting there dead.

> **Detaching a table is lossy, and it says so.** The seat ring comes across; the table's model,
> colliders, trigger volumes, play-surface metrics and budget do not — a game scene has nowhere
> to put them, because the assigned table pack is the single owner of the play-surface height.
> Your game falls back to the default table, Square Table, until you assign another table pack.
> Detaching a **room** loses nothing from the document — but with no room pin the game plays in
> the default room, Modern Room.

## Changing the table leaves your seats behind — and offers to fix it

Seat positions are derived from the assigned table's footprint, its ring layout and its seat
count. Changing the table **does not move them for you**; it shows you that they no longer match
and offers to put them back, with a before/after preview rather than a silent rewrite.

The offer lives on the **Player Zones** folder, under **SEAT LAYOUT**, and it names the table the
seats are currently derived against — so a game pinned to a round table with its seats still
spread around a rectangle says so on screen.

Two strengths, and the second is the one for a table that changed *shape*:

- **Move seats onto this table's ring** — the warning that appears on its own. Moves each seat
  and keeps every size, rotation, type and extra zone you authored. Right after a resize.
- **Reset seats to this table's default…** — rebuilds each seat as a fresh seat on the new
  table, including its rotation, its zone size and its name label, and deletes zones beyond the
  first. Right when you swap a square table for a round one, where moving a zone stretched along
  a long edge cannot fix its shape.

Seat colours, custom label text and each seat's first zone type survive both. Objects — card
holders and anything standing in a zone — are never touched by either. See [Player Zones and
Seats](https://diceytable.com/docs/editor/working/zones-and-seats#seat-layout-putting-seats-back-on-the-table).

## Changing the table can leave your pieces floating — and it offers to fix it

Tables are not all the same height. Assign a table whose play surface sits 1.17 ft higher than
the one you laid your board out on, and every piece stays exactly where you put it — which now
means hovering in mid-air. A lower table buries them inside it instead.

Nothing is moved for you. When the two surfaces differ, the Table row shows a preview:

- **the two heights**, and the difference between them;
- **a row per object**, by name, with its height before and after;
- **Apply** and **Cancel** — and Cancel is a real answer.

**The table is not switched until you Apply.** While the preview is open the viewport keeps the
table your pieces are standing on, so a taller table's collider never pushes them around before
you have decided. Apply switches the table and moves the pieces in one step; Cancel keeps the
current table and every position exactly as they were. A table whose play surface is at the same
height is switched straight away, because nothing needs to move.

Applying moves the objects **straight up or down by exactly the height difference**, and nothing
else. No piece is turned, resized, slid sideways, or dropped onto the new surface. A token
0.1 ft above the old table ends up 0.1 ft above the new one; a piece you deliberately left
floating stays floating by the same amount. Your layout is yours — the editor can carry it to a
new height, but it will not redesign it.

Your **seat ring** moves with it: every seat's zone boxes, name label and seat pivot go up or down by
the same height, so the player zones stay on the table. (Zones are footprints - what counts as
inside one never depended on height - but they are drawn where they were authored.)

So do your **snap points** and **table zones** (the deck slot, the discard pile). Both store an
absolute height and are attached to nothing, so a chessboard's 64 square markers left at the old
height would sit inside a taller tabletop. They are decided by the same rules as a piece, below:
one more than 0.1 ft under the old surface, or outside its play area, stays where it is. The
preview does not list them one by one. It adds a summary row for each (`64 snap point(s)`,
`2 table zone(s)`) with the height change.

> **Already switched tables and your snap points are under the top?** Scenes that changed tables
> before snap points were carried along keep the old height. Open the snap-point flyout on the
> transform rail and press **Lift N onto table**. See [The Transform
> Rail](https://diceytable.com/docs/editor/interface/transform-rail#lift-n-onto-table).

### What it leaves alone, and why

Two kinds of object are listed but not moved, because the old table was not holding them up:

- **anything more than 0.1 ft below the old play surface** — floor tokens, a piece under the table;
- **anything outside the old table's play area** — the rectangle the table pack declares. A
  shelf ornament against the wall is *above* the surface too, and moving it with the table
  would send it through the ceiling.

Parented objects are decided as a group: a piece follows whatever its top-level parent does, so
a board and everything standing on it move together or not at all.

> **It cannot see everything, and it says so.** A piece resting on a stand that stands on the
> table looks exactly like a piece resting on the table, and it will be moved with it. Read the
> rows before you press Apply.

If the new table is **smaller**, some pieces will end up past its edge. The preview names them
and moves none of them sideways — repositioning a layout is a decision, not an adjustment.

**Undo (Ctrl+Z)** puts the previous table and the heights back. It does not bring the preview back; re-pick the table
from **Change…** if you want to see it again.

## What the Table row deliberately does not offer

There is **no play-surface height field** on the Table row, and there never will be.

The assigned table pack is the single owner of that number. Every peer reads it from the same
pinned pack at load and uses it to work out where a dragged piece is going. A second place to
set it would be a second answer, and a peer with a different answer sends the table wrong drag
positions — which looks like a physics bug and is not one.

The model, the colliders, the triggers, the play surface and the seat ring all belong to the
pack, and all of them are edited in the [Table Editor](https://diceytable.com/docs/editor/concepts/editor-urls#the-modes).

## Nobody can change these during a session

The room and the table are **fixed by the game mod**. Once a table is running, there is no way to
swap either one — not for a player, not for a spectator, **not for the host**. There is no menu
entry, no keyboard shortcut and no setting, because there is no mechanism underneath one: every
peer derives the room and the table locally from the same pinned mod, and nothing about them is
sent over the network.

Adding a mod mid-session still works and still contributes pieces, prefabs and scripts — but it
can never become the room or table provider. That is decided when the table is created, and if a
mod's room or table is being ignored the mod list says so.

## Collections: which mod supplies the room and the table

Everything above is about **one** mod's assignment. A **collection** is a different thing: a
named bundle of *mod ids* that share one table. It is the only way to put more than one mod on a
table, and it raises a question a single mod never has to answer — if three mods are on the table
and two of them assign a room, whose room is it?

The answer is a field, not a rule of thumb. Open **Mod ▸ Collections…** in the menu bar of the
[Game Editor](https://diceytable.com/docs/editor/concepts/editor-urls#the-modes) and pick a collection; the
**Room & table provider** row names exactly one member. Every other member's room and table are
**ignored**, and the members list says so on each of them.

> DiceyTable never breaks the tie for you — not by list order, not by "the last one added", not
> by which one you assigned most recently. A room chosen by an implementation detail is
> indistinguishable from a broken one, and you would find out from a player.

There are four outcomes, and the row tells you which one you are in:

| Members carrying a room or table | What happens |
|---|---|
| exactly one | it is designated **automatically**, and the row says so — you can still change it |
| two or more, none chosen | **publishing is refused**, and both candidates are named |
| none | the platform defaults, Modern Room and Square Table, are used |
| the designated member is no longer in the collection | **publishing is refused** — it is never re-assigned for you |

That last row is the one worth reading twice. Removing the designated provider from a collection
does not quietly promote another member, however obvious the alternative looks. A published
version is immutable, so a guess made here would be frozen into every table that ever pins this
version.

### Every other member says "room/table ignored"

Each member that is not the provider carries a `room/table ignored` chip, whether or not the
editor can see that it has a room. This is deliberate: a room that is quietly ignored looks
exactly like a room that is broken, and the bug report you get is *"my room doesn't load"*.

While no provider is designated at all, the members are marked `provider unresolved` instead —
nothing has been decided yet, and publishing will not proceed until it has.

### But your **materials** are never ignored

`room/table ignored` means exactly what it says, and no more. It covers the room model, the
environment, the table and the seat ring. It does **not** cover the materials and textures your
pieces are painted with — those travel with the pieces, so a non-provider member's tokens look
the way you authored them.

This matters because material names are not unique across mods. Every new project starts with the
same four seeded materials — **Floor**, **Wall**, **Ceiling** and **Default** — so on a collection
table there are as many different materials called *Default* as there are members. DiceyTable
keeps each member's materials to itself: a piece from member B painted *Default* gets **B's**
Default, never the provider's, even though both are called the same thing and both were seeded
the same way. You do not have to rename anything, and renaming would not have helped anyway.

The one thing a collection does share is the built-in preset library (Felt, Metal, Oak and the
rest). A built-in is the same material everywhere by design, so two members both using Felt are
using one material, not two.

> If a piece renders with its plain shipped look instead of the material you assigned, the
> material is genuinely **missing** from that member — it is never another member's material
> wearing the same name. Check that the piece and the material are in the same mod.

### The designation belongs to the version

The provider is stored on the collection **version**, not on the collection. A published version
can never be re-pointed, so:

- the designation is frozen with the version, exactly like the version's member list;
- a table that pinned version `1.0.0` keeps `1.0.0`'s provider forever;
- changing the provider means **publishing a new version** — a deliberate act, with the same
  blast-radius preview as any other version bump.

### And it is locked when the table is created

The room and the table are resolved **once**, when the table is created, from the mod (or the
pinned collection version) it was created with. Mods added later contribute pieces, prefabs and
scripts and can never change the room or the table — see [Nobody can change these during a
session](https://diceytable.com/docs/editor/workflows/assign-a-room-or-table#nobody-can-change-these-during-a-session) above.

## Related

- [Editor URLs and Modes](https://diceytable.com/docs/editor/concepts/editor-urls) — the four editors and what each owns
- [The Hierarchy Panel](https://diceytable.com/docs/editor/interface/hierarchy#the-tree-changes-shape-in-a-purpose-built-editor) — how the tree differs per mode
- [Player Zones and Seats](https://diceytable.com/docs/editor/working/zones-and-seats) — the seat ring the table's footprint drives
- [Room and Lighting](https://diceytable.com/docs/editor/working/room-and-lighting) — authoring the room itself
