Structure
The starter separates client boot, server boot, shared client config, and modules.client.ts
client.ts starts the browser client in MMORPG mode:
localStorage and passes it
to @signe/room as the connection session id. Refreshes and multiple tabs from
the same browser therefore restore the same player session while each WebSocket
keeps its own connection id. Use connectionIdScope: "session" to keep the
session only for one browser tab, or connectionIdScope: "ephemeral" to create a
new player session on each page load:
engine.auth(), send the token with the MMORPG connection
query. RPGJS includes this query on the initial lobby connection and on map-room
reconnects:
server.ts
server.ts creates the game server and registers your providers:
standalone.ts
standalone.ts runs the client and server together for a standalone RPG:
config/config.client.ts
config.client.ts contains the common client setup shared by MMORPG and standalone RPG:
provideClientGlobalConfig()
provideClientGlobalConfig() is the client-side place for shared global configuration.
It can hold built-in options such as keyboardControls, and also any custom object you want
to expose everywhere in the client through dependency injection.
keyboardControls, RPGJS injects the default bindings automatically:
keyboardControls objects are merged with these defaults, so a game can
override one key without redefining every movement/action binding.
The action binding also accepts an object when the action key should send a
custom action input. This keeps the key generic in built-in components while
letting the game decide which action and payload to send.
The dash binding is a movement input. In MMORPG mode the client predicts it
locally through the same movement queue as directional inputs, while the server
validates and acknowledges the dash position.
Custom action inputs
Custom action inputs use the same client-to-server flow whether they come from the configured action key or from code. The client sends a payload shaped like{ action, data }, and the server receives that payload in the player’s
onInput() hook.
"action" input, or Control.Action, automatically triggers
nearby event onAction() hooks. A custom action such as "projectile:shoot"
goes directly to player.onInput() and does not trigger event interactions by
itself.
Do not confuse this player hook with map movement input processing. Custom
actions are sent with client.processAction() and handled in player.onInput();
movement inputs are queued separately by the map and processed by the movement
loop.
You can retrieve this global config anywhere on the client with inject(GlobalConfigToken):