Running a full RAGE:MP clientside (CEF UI + client JS)
Alongside the server, SYNC MP runs an unmodified RAGE:MP clientside — the browser-based UI
(CEF), the client_packages JavaScript entry (index.js / a webpack main.js bundle), and the
mp.* client API the gamemode expects. A large production clientside (a 1+ MB webpack bundle,
1000+ event handlers, dozens of CEF views) loads and its interface renders in-game with no source
changes.
How it works
The SYNC MP client is a DLL injected into GTA5.exe. It provides:
- A V8 JavaScript runtime that runs the gamemode's downloaded
client_packagesentry, exposing the RAGE:MPmp.*client surface (mp.events,mp.game.*,mp.players,mp.vehicles,mp.gui,mp.browsers, timers, promises, …). - A CEF (Chromium) browser rendered off-screen and composited into the game frame, driving the
gamemode's HTML/Svelte/React UI (
package://interface/…). - A native invoker (ScriptHookV-style) so
mp.game.invoke(hash, …)and the generatedmp.game.<namespace>.<fn>(…)wrappers call real GTA natives.
The client downloads the server's client_packages on connect (with a manifest diff so only changed
files transfer), runs the entry, and the gamemode registers its handlers and opens its CEF views.
The client mp.* API
The full RAGE:MP client mp.* surface is provided. Highlights:
mp.events—add/call/callRemote/callRemoteProc/addProc/addDataHandler(per-key synced-data handlers). The raw registry is also exposed asmp._eventsso frameworks that register viamp._events.addwork.mp.game.*— the full generated native table, grouped into RAGE:MP namespaces (mp.game.graphics,mp.game.ui,mp.game.controls,mp.game.gameplay,mp.game.cam,mp.game.streaming,mp.game.player,mp.game.entity, …). GTA's raw native namespaces are remapped to RAGE:MP names (HUD → ui,PAD → controls,CAMERA → cam/graphics,MISC → gameplay/system,CLOCK → time, …), and out-pointer natives (e.g.mp.game.graphics.getScreenResolution()) return their results.- Entity pools —
mp.players/mp.vehicles/mp.peds/mp.objects/mp.blips/mp.markers/mp.checkpoints/mp.colshapes/mp.labels/mp.browsers/mp.camerasare iterable (forEach/at/atRemoteId/atHandle/exists/toArray/length) and have the RAGE:MP constructors (mp.colshapes.newSphere/newCircle/newTube/newRectangle/newCuboid,mp.cameras.new(...), …). - Entity objects —
mp.players.localand remote players/peds carry the RAGE:MP method set (freezePosition,setAlpha,setVisible,setHeading,setComponentVariation,setRelationshipGroupHash,hasClearLosTo,getHeading/getArmour/getHealth/getCoords/getModel/getVehicle, …). Spawned entities frommp.peds.new/mp.objects.newcarry the common ped/object methods (setPropIndex,clearProp,setLodDist, …). mp.gui—mp.gui.chat(push/clear/activate/show),mp.gui.cursor(.show(...)and the.visibleproperty), andmp.gui.emmit(js)— the general "run this JS in the browser" bridge the gamemode uses to drive its UI (window.router.setView(...),window.FadeScreen(...),window.events.callEvent(...)).mp.browsers.new(url)— opens the CEF view.package://interface/<page>.htmlresolves against the downloadedclient_packages; externalhttp(s)://URLs are kept off the main UI browser.
The CEF UI bridge
Inside a CEF page the gamemode uses a window.mp object (window.mp.trigger, window.mp.events,
window.mp.invoke) to talk back to the client. That bridge is available to your interface pages;
messages route to the client's event system, and the client drives the page with mp.gui.emmit(...)
(client → page) which executes JS in the browser.
When the mouse cursor is shown (mp.gui.cursor.visible = true) the CEF overlay takes input focus, so
keyboard and mouse reach the page (login forms, menus) — matching RAGE:MP, where a visible cursor
means the UI has focus.
Serving your interface
Your interface files live under client_packages/interface/. A page that loads its bundle with a
plain tag —
<link rel="stylesheet" href="build/bundle.css">
<script src="build/bundle.js"></script>
— loads reliably from the downloaded package. Pages that fetch their bundle from a remote CDN
will only work if that CDN is reachable from the player; ship the built bundle with your
client_packages for an offline-capable UI.
The join → UI-ready flow
The client loads the world, spawns, and sends CLIENT_READY; the server then fires the gamemode's
playerJoin, which drives the client into its UI (auth / character / spawn). Server → client events
fire reliably even under a heavy clientside load, and a join event that arrives before the
clientside finished registering its handlers is deferred and replayed once the entry has run —
so the gamemode's first UI event (e.g. the login screen trigger) is never dropped to a race.
Debugging the CEF UI
Set the client marker file next to the client (ALTV_CEF_DEVTOOLS) or the env var
ALTV_CEF_DEVTOOLS=1 to open Chromium DevTools on http://localhost:9222. From a Chromium browser
on the same machine, http://localhost:9222/json/list lists the inspectable pages; open the page's
inspector to see its console, DOM, and network — the fastest way to tell whether your UI bundle
mounted, errored, or is waiting on an event.
Known limitations (in progress)
- A
mp.game.<namespace>.<fn>that maps to a native not present in the running table is a hard error, so the client only exposes wrappers for real natives; RAGE:MP-aliased names not covered by the generator are added as needed. - Native network session features (a real
objMgrforCNetObjPedcloning) are not up, so remote peds animate via a velocity/move-blend drive rather than the engine's net blender. - Some UI flows that depend on gamemode-specific browser objects being registered in a particular
order can log non-fatal
window.<x> is not a functionmessages until that object exists; these do not block the interface.