Split out of #1490, whose other phases have landed (#1496, #1497, #1498, #1499, muchq/muchq.github.io#299, #300, and #1501 retiring the pre-lobby routes). This is the one phase the lobby still lacks.
Private tables
A room is already reachable only by its code, and the plaza is the one room everyone lands in, so "private" means invisible to people in the same room who were not invited. A table created private is listed in RoomState.games only to its own roster; everyone else in the room sees its members as busy but not the table or its id. It is reached by share link — room plus table id, the way the lobby's /games/room/:r/table/:g links already work — and joinGame by id still works for whoever holds the link.
What that costs, and the shape it takes:
- Model.
CreateGame (the one shape both golf and castle use) gains an unlisted: Boolean (a member named private is a C++ keyword the generator would emit verbatim). GameSummary.unlisted tells the roster's own clients to show the badge and the share link. Table.gameId becomes optional: a member at an unlisted table reads to outsiders as at a castle table, id withheld.
- Hub.
GameEntry and the store's GameRow carry the flag, with a games.unlisted boolean NOT NULL DEFAULT false migration so a sibling instance projects it the same way from the row. RoomState becomes a per-viewer projection (RoomStateLocked(room_id, room, viewer_id); StageRoomStateLocked builds one per member) — the real cost of the feature, and where the tests go: an unlisted table lists only to its roster, its members read as busy to the rest, a link-holder's joinGame by id is admitted, and a second instance restored from the store hides it too.
- Site. "Private" at table creation in the lobby panel; the share link copied from the table; presence text "at a castle table" when the id is withheld.
Private rooms
The same idea one level up, and it only matters once the plaza lists rooms at all (it does not today: rooms are by code). A private flag on createRoom rides whatever room directory arrives, if one does. Not in scope until then.
Follow-ups noted along the way
- The site's
/castle room screen folds into /games (a castle deep link is a lobby table link), which deletes the room plumbing useLobby and useCastleGame carry twice.
GolfHub and the :golf_hub target still carry golf's name for the room layer; a mechanical rename (17 files) on its own.
Split out of #1490, whose other phases have landed (#1496, #1497, #1498, #1499, muchq/muchq.github.io#299, #300, and #1501 retiring the pre-lobby routes). This is the one phase the lobby still lacks.
Private tables
A room is already reachable only by its code, and the plaza is the one room everyone lands in, so "private" means invisible to people in the same room who were not invited. A table created private is listed in
RoomState.gamesonly to its own roster; everyone else in the room sees its members as busy but not the table or its id. It is reached by share link — room plus table id, the way the lobby's/games/room/:r/table/:glinks already work — andjoinGameby id still works for whoever holds the link.What that costs, and the shape it takes:
CreateGame(the one shape both golf and castle use) gains anunlisted: Boolean(a member namedprivateis a C++ keyword the generator would emit verbatim).GameSummary.unlistedtells the roster's own clients to show the badge and the share link.Table.gameIdbecomes optional: a member at an unlisted table reads to outsiders as at a castle table, id withheld.GameEntryand the store'sGameRowcarry the flag, with agames.unlisted boolean NOT NULL DEFAULT falsemigration so a sibling instance projects it the same way from the row.RoomStatebecomes a per-viewer projection (RoomStateLocked(room_id, room, viewer_id);StageRoomStateLockedbuilds one per member) — the real cost of the feature, and where the tests go: an unlisted table lists only to its roster, its members read as busy to the rest, a link-holder'sjoinGameby id is admitted, and a second instance restored from the store hides it too.Private rooms
The same idea one level up, and it only matters once the plaza lists rooms at all (it does not today: rooms are by code). A
privateflag oncreateRoomrides whatever room directory arrives, if one does. Not in scope until then.Follow-ups noted along the way
/castleroom screen folds into/games(a castle deep link is a lobby table link), which deletes the room plumbinguseLobbyanduseCastleGamecarry twice.GolfHuband the:golf_hubtarget still carry golf's name for the room layer; a mechanical rename (17 files) on its own.