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
| Category | Event | Status |
|---|---|---|
| Browser | browserCreated | ✅ |
| Browser | browserDomReady | ✅ |
| Browser | browserLoadingFailed | ✅ |
| Checkpoints | playerEnterCheckpoint | ✅ |
| Checkpoints | playerExitCheckpoint | ✅ |
| Colshapes | playerEnterColshape | ✅ |
| Colshapes | playerExitColshape | ✅ |
| Console | consoleCommand | ✅ |
| Common | click | ✅ |
| Common | playerChat | ✅ |
| Common | playerCommand | ✅ |
| Common | playerDeath | ✅ |
| Common | playerJoin | ✅ |
| Common | playerQuit | ✅ |
| Common | playerReady | ✅ |
| Common | playerResurrect | ✅ |
| Common | playerRuleTriggered | ⚠️ dispatchable, nothing raises it |
| Common | playerSpawn | ✅ |
| Common | playerWeaponShot | ✅ |
| Common | addDataHandler | ✅ (a function, not an event) |
| Common | dummyEntityCreated | ⚠️ local dummies only |
| Common | dummyEntityDestroyed | ⚠️ local dummies only |
| Common | entityControllerChange | ✅ |
| Common | replayEditorRequest | ❌ needs a pause-menu item hook |
| Common | pauseMenuItemSelect | ❌ needs a pause-menu item hook |
| Common | pauseMenuItemChange | ❌ needs a pause-menu item hook |
| Damage | explosion | ✅ (no sourcePlayer) |
| Damage | incomingDamage | ✅ |
| Damage | projectile | ⚠️ local player's own only |
| Damage | outgoingDamage | ✅ cancellable |
| Damage | meleeActionDamage | ✅ cancellable |
| Vehicles | playerStartEnterVehicle | ✅ |
| Vehicles | playerEnterVehicle | ✅ |
| Vehicles | playerLeaveVehicle | ✅ |
| Voice chat | playerStartTalking | ✅ |
| Voice chat | playerStopTalking | ✅ |
| Streaming | entityStreamIn | ✅ |
| Streaming | entityStreamOut | ✅ |
| Graphics | render | ✅ |
| Waypoint | playerCreateWaypoint | ✅ |
| Waypoint | playerReachWaypoint | ✅ |
| Misc | uncaughtException | ✅ cancellable |
| Misc | unhandledRejection | ✅ 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 pixelsupOrDown— the string"up"or"down"leftOrRight— the string"left"or"right"relativeX/relativeY— the same position normalised to0..1worldPosition—Vector3where 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, or0
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;
});