Custom Gameplay Rooms
Custom gameplay rooms are server-authoritative sessions that keep RPGJS players, state synchronization, actions, GUI messages, and session transfer without loading a Tiled map or starting map physics. They fit turn-based battles, card tables, lobbies, matchmaking screens, and other non-spatial game modes. Maps remain unchanged. Useplayer.changeMap() for maps and
player.changeRoom() for custom rooms.
Runtime model
The same API works in standalone and MMORPG modes. In standalone mode, the
server room runs in the browser alongside the client. In MMORPG mode, it runs
only in the server bundle and remains authoritative. Keep reusable features in
separate server and client entry points so server-only code is not imported by
the MMORPG browser bundle.
Define the server room
Create a room class, declare a uniquekind and path, then register it in the
server entry:
/, ?, or #. Duplicate kinds and duplicate paths fail
during bootstrap instead of routing players unpredictably.
RpgGameplayRoom provides:
players, containing synchronizedRpgPlayerinstances;state, a server-authoritative writable signal;id,params, anddescriptor, identifying the resolved room instance;- decorated actions through
@RpgRoomAction(); broadcast(),on(), andoff()for ephemeral messages;- the standard RPGJS database, save, GUI, and player snapshot behavior.
RpgEvent instances. Represent
non-spatial entities in the room’s serializable state, or synchronize a
feature-owned model when a game mode needs more than players.
For a reusable module, export the room class or a rooms array from a dedicated
/server entry point. The application should only install that public result:
Register the client scene
Each custom server kind needs one CanvasEngine scene adapter on the client. The root may be a composed.ce component and may use DOMContainer for HTML UI.
battle-scene.ce:
room and its server-provided
descriptor. When a custom scene is active, RPGJS unmounts the map viewport,
lighting, map entities, prediction, streaming, projectiles, and map physics. GUI
components remain mounted above the custom scene.
Export scenes from a dedicated /client entry point when building a reusable
module. The application then installs only provideClientScenes(battleScenes);
it does not need to know the component tree or socket protocol used internally.
onBeforeEnter runs while the previous scene is still mounted, onEnter runs
after the new room connection opens, onChanges receives synchronized packets,
and onLeave runs before another room replaces the scene.
Use onLeave to remove feature-owned socket listeners and controllers. Use the
CanvasEngine component unmount lifecycle for component-local effects, animation
loops, DOM listeners, and input handlers. RPGJS clears the synchronized room
signals when the scene changes, but it cannot dispose listeners created by the
feature itself.
Public and private state
RpgGameplayRoom.state is synchronized to every connection in the room. Store
only data that every participant and spectator may inspect there. Do not place
hidden hands, enemy skills, private inventory contents, secrets, or authorization
data in the shared state.
Send ephemeral private projections to one player with player.emit(). Store
durable player-owned data in synchronized player properties and use
$syncWithClient: false for server-only properties. Every client command must be
validated again by a @RpgRoomAction() method or another server-side handler;
the custom CanvasEngine scene never owns gameplay authority.
Transfer players
Only the server selects the destination:player.getCurrentRoom() returns the active lobby, map, or custom
room; player.getCurrentMap() is null in a map-independent room.
Store the return map and position in server-owned match or player state before
calling changeRoom(). Returning to spatial gameplay must use changeMap() so
the normal map loading, streaming, movement, and physics lifecycle is restored.
Use the generic hooks when behavior should apply to every room:
canChangeMap, onJoinMap, onLeaveMap, and map hooks continue
to run for maps. The generic room hooks run alongside them.