Skip to main content

Client-side Javascript Events

Client events fire inside client_packages/, running on V8 (full ES2020+ — async/await, arrow functions, classes and template literals are all available).

Register a handler the RAGE:MP way:

mp.events.add('playerSpawn', () => {
mp.gui.chat.push('spawned');
});

Some events are cancellable: return true from the handler and the default behaviour is suppressed. Every cancellable event is marked below. Returning anything else (including a truthy object) does nothing — the check is strictly === true.


Status at a glance​

CategoryEventStatus
BrowserbrowserCreated✅
BrowserbrowserDomReady✅
BrowserbrowserLoadingFailed✅
CheckpointsplayerEnterCheckpoint✅
CheckpointsplayerExitCheckpoint✅
ColshapesplayerEnterColshape✅
ColshapesplayerExitColshape✅
ConsoleconsoleCommand✅
Commonclick✅
CommonplayerChat✅
CommonplayerCommand✅
CommonplayerDeath✅
CommonplayerJoin✅
CommonplayerQuit✅
CommonplayerReady✅
CommonplayerResurrect✅
CommonplayerRuleTriggered⚠️ dispatchable, nothing raises it
CommonplayerSpawn✅
CommonplayerWeaponShot✅
CommonaddDataHandler✅ (a function, not an event)
CommondummyEntityCreated⚠️ local dummies only
CommondummyEntityDestroyed⚠️ local dummies only
CommonentityControllerChange✅
CommonreplayEditorRequest❌ needs a pause-menu item hook
CommonpauseMenuItemSelect❌ needs a pause-menu item hook
CommonpauseMenuItemChange❌ needs a pause-menu item hook
Damageexplosion✅ (no sourcePlayer)
DamageincomingDamage✅
Damageprojectile⚠️ local player's own only
DamageoutgoingDamage✅ cancellable
DamagemeleeActionDamage✅ cancellable
VehiclesplayerStartEnterVehicle✅
VehiclesplayerEnterVehicle✅
VehiclesplayerLeaveVehicle✅
Voice chatplayerStartTalking✅
Voice chatplayerStopTalking✅
StreamingentityStreamIn✅
StreamingentityStreamOut✅
Graphicsrender✅
WaypointplayerCreateWaypoint✅
WaypointplayerReachWaypoint✅
MiscuncaughtException✅ cancellable
MiscunhandledRejection✅ cancellable

Browser​

browserCreated(browser)​

Fired once for each browser made with mp.browsers.new / mp.browsers.newHeadless. It is raised on the frame after the call returns, so the variable you assigned the browser to already exists by the time your handler runs.

mp.events.add('browserCreated', (browser) => {
mp.gui.chat.push('browser ready: ' + browser.url);
});

Created browsers are also placed in the mp.browsers pool, so mp.browsers.forEach(...), .length and .at(...) see them (this is what rage-rpc uses to broadcast to pages).

browserDomReady()​

The page finished loading. Not raised for the client's own F11 console browser.

browserLoadingFailed(url, code)​

The page failed to load. code is the CEF error code.


Checkpoints​

Checkpoints are created client-side with:

const cp = mp.checkpoints.new(1, new mp.Vector3(x, y, z), 3.0, {
direction: new mp.Vector3(0, 0, 0),
color: [255, 128, 0, 120],
visible: true,
dimension: 0
});

This makes a real GTA checkpoint (you see the marker) and a collider that raises:

playerEnterCheckpoint(checkpoint) / playerExitCheckpoint(checkpoint)​

Cylindrical test: radius in 2D plus a matching vertical band. Only collides while the local player's dimension matches the checkpoint's.

mp.events.add('playerEnterCheckpoint', (checkpoint) => {
mp.gui.chat.push('entered checkpoint ' + checkpoint.id);
checkpoint.destroy();
});

Colshapes​

All six RAGE:MP shapes are supported and use real geometry:

mp.colshapes.newSphere(x, y, z, radius, dimension);
mp.colshapes.newCircle(x, y, radius, dimension); // 2D, ignores z
mp.colshapes.newTube(x, y, z, radius, height, dimension);
mp.colshapes.newRectangle(x, y, width, height, dimension); // 2D AABB
mp.colshapes.newCuboid(x, y, z, width, depth, height, dimension);
mp.colshapes.newPolygon(vertices, minZ, maxZ, dimension); // even-odd test + z band

playerEnterColshape(shape) / playerExitColshape(shape)​

Tested once per frame against the local player's position. A shape only collides with a player in the same dimension (default 0). shape.isPointWithin(position) runs the same test on demand.

const zone = mp.colshapes.newSphere(0, 0, 72, 15.0);
mp.events.add('playerEnterColshape', (shape) => {
if (shape === zone) mp.gui.chat.push('you are in the zone');
});

Console​

consoleCommand(command)​

A line typed into the F11 console's prompt and submitted with Enter. command is the raw text (control characters stripped, capped at 255 characters). Arrow Up/Down walk the command history.

mp.events.add('consoleCommand', (command) => {
if (command === 'pos') mp.console.logInfo(JSON.stringify(mp.players.local.position));
});

The channel is one-way by design: the console can hand the gamemode what the player typed, but the gamemode cannot read, drive or close the console.


Common​

click(absoluteX, absoluteY, upOrDown, leftOrRight, relativeX, relativeY, worldPosition, hitEntity)​

Raised on every mouse-button edge while the cursor is visible (mp.gui.cursor.show(...)) and the game window has focus. Without the cursor gate every shot fired would also be a click, since the left button is the attack control.

  • absoluteX / absoluteY — cursor position in pixels
  • upOrDown — the string "up" or "down"
  • leftOrRight — the string "left" or "right"
  • relativeX / relativeY — the same position normalised to 0..1
  • worldPosition — Vector3 where a ray through the cursor hits the world (the far end of a 1000 m ray if it hits nothing)
  • hitEntity — the game handle the ray hit, or 0
mp.events.add('click', (x, y, upOrDown, leftOrRight, rx, ry, worldPosition, hitEntity) => {
if (upOrDown === 'down' && leftOrRight === 'left')
mp.gui.chat.push(`clicked ${worldPosition.x.toFixed(1)}, ${worldPosition.y.toFixed(1)}`);
});

playerChat(text) / playerCommand(command)​

The local player submitted a chat line. A line starting with / becomes playerCommand with the slash stripped; anything else becomes playerChat.

playerDeath(player, reason, killer)​

See playerDeath. reason is the cause-of-death weapon hash, killer the killing entity handle.

playerJoin(player) / playerQuit(player)​

Another player appeared in / disappeared from this client's world. Never raised for the local player. Because the server only tells a client about players it can see, "left the server" and "left your stream range" are the same message on the wire — playerQuit is raised right after entityStreamOut for that player.

playerReady()​

The local player is in the world and mp.players.local is usable. Fired once.

playerResurrect() / playerSpawn()​

Raised when the local player comes back alive. playerSpawn also fires once at playerReady.

playerRuleTriggered(rule, counter)​

⚠️ Dispatchable but never raised by the platform. In RAGE:MP this comes from the built-in anti-cheat; there is no equivalent rule engine here. A handler you register will fire if your server raises the event by name (player.call('playerRuleTriggered', [rule, counter])), which is the supported way to use it today.

playerWeaponShot(targetPosition, targetEntity)​

The local player fired. targetPosition is the last weapon impact coordinate.

mp.events.addDataHandler(key, handler)​

Not an event — a registration function. handler(entity, newValue, oldValue) runs whenever a synced variable named key changes on any entity. removeDataHandler and hasDataHandler exist too.

dummyEntityCreated(dummyType, dummy) / dummyEntityDestroyed(dummyType, dummy)​

⚠️ Local dummies only. mp.dummies.new(type, data), mp.dummies.forEachByType(type, cb) and dummy.destroy() work client-side and raise these two events. Server-replicated dummies are not implemented yet; when they are, they will feed the same two events and gamemode code will not change.

entityControllerChange(entity, newController)​

The client that owns an entity changed. Everything here is server-authoritative except a vehicle being driven — the driver's client runs its physics — so this is raised when a vehicle's driver changes. newController is null when the vehicle goes driverless.

mp.events.add('entityControllerChange', (entity, newController) => {
mp.console.logInfo(`${entity.type} -> ${newController ? newController.name : 'Nobody'}`);
});

replayEditorRequest(), pauseMenuItemSelect(itemHash), pauseMenuItemChange(itemHash, value, oldValue)​

❌ Not implemented. All three need a hook into the pause menu's own item dispatch. The client hooks the pause menu's labels and tab list (CPauseMenuHooks) but not its item callbacks, and the script natives this build exposes only report whether the menu is open — never which item was picked. Guessing an itemHash would be worse than not raising the event.


Damage​

explosion(sourcePlayer, type, position)​

An explosion went off within 200 m of the local player. type is the GTA explosion tag (0–82), position is located to roughly a metre.

sourcePlayer is always null: attributing an explosion to a player needs the network event RAGE:MP hooks, which is not available here, and a guess would be worse than nothing.

incomingDamage(sourceEntity, sourcePlayer, targetEntity, weapon, boneIndex, damage)​

The local player lost health. sourceEntity / sourcePlayer / weapon are not resolvable per-frame without an attacker hook, so they are null/0; boneIndex and damage are real.

projectile(sourcePlayer, weaponHash, ammoType, position, direction)​

⚠️ The local player's own projectiles only. Raised when this client throws or launches one (grenade, sticky, molotov, RPG, grenade launcher, firework, …). sourcePlayer is always the local player; position is the launch point and direction the ped's forward vector.

Other players' projectiles are not reported: the natives available here can say only whether a projectile is inside a box — no weapon, no owner, no direction — so every argument but position would be fabricated.

outgoingDamage(sourceEntity, targetEntity, sourcePlayer, weapon, boneIndex, damage) — cancellable​

Damage this client dealt to a streamed player, ped or vehicle.

mp.events.add('outgoingDamage', (sourceEntity, targetEntity, sourcePlayer, weapon, boneIndex, damage) => {
if (targetEntity.type === 'player' && boneIndex === 20) return true; // no headshot damage
});

How cancelling works here: GTA has already applied the damage by the time we can see it, so returning true writes the target's pre-hit health back. The target ends the frame undamaged, which is the same observable result — but it cannot undo a one-shot kill's ragdoll.

meleeActionDamage(source, target, weaponHash, damage, isCritical) — cancellable​

Raised alongside outgoingDamage when the equipped weapon is unarmed or a melee weapon. isCritical is true for an instant stealth kill.

mp.events.add('meleeActionDamage', (source, target, weaponHash, damage, isCritical) => {
if (isCritical) return true; // no instant stealth kills
});

Vehicles​

playerStartEnterVehicle(vehicle, seat)​

The local player began entering a vehicle.

playerEnterVehicle(vehicle, seat) / playerLeaveVehicle(vehicle, seat)​

Seat changes fire a leave followed by an enter. vehicle is the local game handle (RAGE:MP client scripts expect that); the matching server-side events carry the server vehicle id.


Voice chat​

playerStartTalking(player) / playerStopTalking(player)​

A speaker became audible or went quiet. Also raised for the local player, driven by the transmit gate (mp.voiceChat.muted / your push-to-talk key) rather than the microphone level, so one spoken sentence is one start and one stop.


Streaming​

entityStreamIn(entity) / entityStreamOut(entity)​

A remote entity entered or left the local streaming radius. entity is { id, type, remoteId }.


Graphics​

render(nametags)​

Every frame, on the script thread — natives called here are valid. nametags is an array of [player, screenX, screenY, distance] entries (empty when nametags are disabled).

Keep handlers cheap: this runs 60 times a second and fans out to every registered handler.


Waypoint​

playerCreateWaypoint() / playerReachWaypoint()​

The map waypoint became active / stopped being active.


Misc​

uncaughtException(exception) — cancellable​

Every clientside fault in gamemode code is offered here first: a throwing event handler, timer callback, RPC proc or key bind, and a module that fails to compile or run. Return true and the default report is suppressed.

mp.events.add('uncaughtException', (exception) => {
mp.console.logError(`[uncaughtException]: ${exception}`);
return true;
});

A throw inside an uncaughtException handler is not fed back into it — that would recurse until the stack died.

unhandledRejection(promise, error) — cancellable​

A promise rejected with no .catch(). Dispatched from the frame pump rather than from inside V8's rejection callback, and withdrawn if a handler is attached later, so const p = doAsync(); …; p.catch(...) is not reported.

mp.events.add('unhandledRejection', (promise, error) => {
mp.console.logError(`[unhandledRejection]: ${error}`);
return true;
});