Skip to main content

Game SDK

The API inside a game’s iframe. Where the JavaScript SDK is about the headset around a window, this one is about the session a game is part of: the shared room, the state everyone sees, and the actions your page submits to the server for judgement.

The guide

Registering a game, writing the rules and the lobby lifecycle

Loading it

Copy public/sdk/game.js out of the built RoadVR resource into your own game and load it before your application. The source copy is at ui/public/sdk/game.js, with game.d.ts beside it.
ui/index.html
This one is copied rather than loaded from cfx-nui-roadvr, unlike the app SDK. A game bundles its own build, and connect() negotiates the version explicitly, so a copy that has fallen behind says so instead of failing quietly.

RoadVrGame.connect()

Negotiates API version 1 and resolves to the connection. It rejects when the host answers with a different version, and it times out when nothing answers at all — which is what happens if you open the page outside RoadVR.
object
The connection: state, on, send, pause, leave.
An iframe cannot claim a different game id. RoadVR resolves the registered frame from the actual message source, not from anything the page sends.

game.state

room becomes null after departure or closure. me is the local participant’s public RoadVR account tag.
Read game.state once after connect() for the opening picture, then keep up through the state event. After a reopened view this is the only correct source — module globals from the previous iframe are gone.

The Room snapshot

string
The six-letter code players read out to each other to join.
number
Increases on every lobby and game change. An older update can never replace a newer accepted snapshot.
string
The account tag of the player who may start the game. It moves to another present player if the host goes away.
string
A public RoadVR account tag — never a framework identifier and never a FiveM source id.
object | false
The player’s saved Character Studio look, or false when unavailable. See the character module.
object
Whatever your onStart and onAction handlers put there. Public to every participant.

game.on()

Every on() returns its own unsubscribe function.
Unsubscribe in close and release your renderer there. Closing and reopening the view builds a new iframe, so anything left running belongs to a document that is already gone.

game.send()

Submits an action for the server to judge. It resolves after the server has accepted it, and rejects with an Error whose code says why it did not.
string
required
The action name your onAction handler switches on.
object
Plain data, held to the same content limits as the state itself.

The error codes

On stale, RoadVR asks for a fresh snapshot by itself — but the rejected action is not replayed. Decide in your page whether the move still makes sense against the new state, then send it again.A custom reason from your handler falls back to a generic message in the shared lobby, so put player-facing wording in your own page.
RoadVR serializes local requests and rate limits mutating server actions to one per 100 ms per source, across all games. Do not push animation frames or continuous positions through send(). Render locally; submit meaningful actions.

game.pause() and game.leave()

Both also exist as RoadVR-owned controls outside your iframe, so a page needs them only when it wants its own button.
Pause is local: the other players carry on. Leave releases the seat immediately, which is the difference between it and RoadVR’s Back to lobby — that one closes the view but keeps membership.

The frame you are given

string
The host appends the player’s interface language to your page URL, or adds it to the query string you already have. Use it for your own strings with English as the fallback — RoadVR does not translate a game’s own text.
reserved
RoadVR’s controls sit there. Your iframe gets the rest. Size the renderer to the iframe’s actual dimensions rather than to the viewport.
key
Opens RoadVR’s pause sheet from inside the iframe. Do not bind it yourself.

The character module

character.js renders the original RoadVR character, so a game does not need a character system of its own.
{ root, head, update, dispose }
Accepts false and falls back to the default appearance.
void
Call from your render loop with elapsed seconds. options takes { moving, reducedMotion, putt }; moving is a 0–1 intensity driving the idle/walk blend.
void
Call before replacing a look or removing a player. It removes the root and releases the geometry and materials it owns.

Orientation

The model is Z-up, faces negative Y, and has its feet at Z = 0.
Move and rotate root to place the character. Pause your render loop with the game and while the document is hidden.
The module also exports normalizeAppearance, DEFAULT_APPEARANCE, CHOICES, COLORS and PRESETS. CharacterAppearance describes the version-1 hair, outfit, accessories, colours and body/face proportions in character.d.ts, which game.d.ts re-exports — so keep the two declaration files together when you copy them.
character.js is an ES module with imports from three and three/addons. Bundle it with your game’s own Three.js dependency; the example is built against Three.js 0.185.1. It is separate from the classic game.js connection script and makes no backend requests of its own.

What the Game SDK cannot do

Version 1 is one shared public state, judged on one server. There is no per-player private channel, no persistent score, no cross-server session, no distribution catalog and no shared world-space rendering — a game draws in its own iframe, not into the room around the player.

Custom Games

Registration, rules and the lobby

JavaScript SDK

The headset API, for apps and widgets

Client API

The Lua half

Discord Support

Questions about integrating