Client API (mp.*, restricted JavaScript)
This is the contract for downloaded client packages. It covers only what is implemented and tested. The generated semantic status is in rage-api-parity.md (docs/compat/rage-api-parity.md). Limits and the reasoning behind them are in native-mp.md (docs/migration/native-mp.md) and page-bridge.md (docs/migration/page-bridge.md).
Client code comes from the server and is untrusted. It runs in a plain V8 isolate, with no Node.js: no fs, process, Buffer, native addons, eval-style code generation, WebAssembly or SharedArrayBuffer. Named game natives and explicitly typed mp.game.invoke share one invoker. Native invocation is powerful local game code, not a security boundary against the game engine: an incorrect signature for an unknown hash can still corrupt engine state. It grants no server authority, unrestricted filesystem/network API or raw JavaScript memory pointer.
Modules and timers
- Global
mp,require('mp')andimport mp from 'mp'are the same API object. Its members are also named exports:import { events, players } from 'mp'. - The extension decides what a file is, and nothing else does (there is no
package.json"type"):
| Extension | Kind |
|---|---|
.mjs | ES module |
.js, .cjs | CommonJS |
.json | data |
- The entry is
index.js,index.cjsorindex.mjs, checked in that order. - CommonJS:
require('./file')resolves.js/.cjs, extensionless names (.js, then.cjs, then/index.js) and explicit.json.require()refuses an.mjsfile. Useimport()instead, which works from CommonJS too.
- ES modules:
importneeds the file's extension. There is no guessing, as in standard ES modules.- A JSON file is imported
with { type: 'json' }; any other import attribute is refused. - Importing CommonJS gives its
module.exportsasdefault. - Cycles work as in any ES module graph.
- Top-level
awaitworks.- An entry that fails while starting does not start. That includes failing after an
awaitthat settles in the same start, such asawait null. - A failure after a longer wait, such as a timer, is logged, and the package keeps running.
- An entry that fails while starting does not start. That includes failing after an
- Containment:
- Only
./and../names inside the same package resolve. Bare names (fs,node:fs), absolute paths, drive letters, backslashes and escapes (%2e%2e) are refused. - Sources are read from the downloaded bundle, never from disk.
- Each package has its own module cache, and a stopped package's cache is dropped.
- Only
- JSON limits: valid UTF-8, at most 64 levels of nesting, and the byte limits below.
requireandimportof one file give the same value. - Limits: at most 256 modules (ES, CommonJS and JSON together), 1 MiB per file and 8 MiB in total.
setTimeout,setInterval,clearTimeoutandclearIntervalare available. They return numeric ids, and a package may have at most 1024 of them.- Each evaluation or callback batch has a 100 ms wall-time budget. A package that exceeds it is retired until it restarts.
mp.events
| Call | Meaning |
|---|---|
add(name, handler), once(name, handler), remove(name, handler?) | Same rules as the server; omitted handler removes this package's handlers for that name. |
add({name: handler, ...}) | Validates the whole map before registration; at most 1024 handlers. |
getAllOf(name) | A detached snapshot of this package's current handlers. |
reset() | Removes this package's events, data handlers and procedure registrations; keeps its key bindings. |
call(name, ...args) | Local handlers in this package. |
callRemote(name, ...args) | Sends an event to the server; it arrives there as (player, ...args). |
callRemoteUnreliable(name, ...args) | The same event on the unreliable channel: it may be lost or overtaken, the server handles it like callRemote. |
addProc(name, handler) | A procedure the server calls with player.callProc; the return value (or promise) is the answer. |
callRemoteProc(name, ...args) | Calls a server procedure; returns a Promise of its answer. |
hasPendingProc(name?) | Checks this resource's outbound pending calls, optionally by checked procedure name. |
cancelPendingProc(name?) | Rejects matching local pending calls with code: "cancelled", releases their quota and returns the count. |
Game facts for this player: playerCreateWaypoint(position) when the map waypoint appears or is moved more than a metre, and playerReachWaypoint(player) when the game clears it while the local player stands within 50 m of it horizontally (clearing it from afar is not "reached"). The waypoint is forgotten with the session. GTA verification is pending.
Procedures follow one contract on both sides (codes, the 10 s timeout, limits, ordering): see server API: Procedures. A package that stops while a server call it claimed is still pending answers failed. When the session ends, its own pending calls reject with disconnected. Local cancellation does not stop remote execution; late answers are ignored. Resetting procedure registrations also releases their registration quota, but does not change another resource's RPCs.
Remote arguments may be null, booleans, finite numbers, strings, nested arrays and plain objects. Other limits: at most 128 items per array or object, depth 8, and 4096 bytes encoded.
Player, vehicle, object and ped wrappers cross as references, both ways and in procedure answers. A reference from the server resolves to this package's own wrapper (mp.vehicles.at(id) and so on), or to null when that entity is not streamed here. A wrapper that is no longer valid is sent as null. Entity variables never hold entities.
Engine events:
| Event | Arguments |
|---|---|
resourceStart / resourceStop | — |
playerReady | — (the local player is in the world) |
playerJoin | remote player, after new membership is committed in the same session; not the initial/reconnect roster or a rename |
sessionEnded | — (before session packages stop) |
render | — (every frame, after timers) |
keyDown / keyUp | virtualKeyCode 1..255 |
browserReady | browser |
browserCreated | owned browser, immediately after its handle is registered |
browserDomReady | owned browser, after browserReady unless that handler destroys/reloads it |
browserLoadingFailed | owned current browser, ready=false; trusted main-frame package load error only |
playerQuit | current remote player before removal; no fabricated quit reason |
entityStreamIn / entityStreamOut | physical entity, after membership commit / before lifetime revocation |
uncaughtException | original exception from owned events/timers, with file/line metadata where available |
unhandledRejection | rejection reason and original promise, after checking handler attachment |
The server cannot send events under these names.
The generated event declarations (sdk/generated/client-events.d.ts) describe 23 additional trusted game-thread dispatch contracts. Their bounded argument translation, wrapper lifetimes, spoof rejection and resource cleanup have portable tests. Registration and fake dispatch do not prove an engine hook fires: seven current GameSession sources are wired, while the remaining combat/vehicle/voice sources still need implementation or live validation. See each event's sourceState in the catalogue; none is live-GTA-certified.
Pools
| Pool | Members |
|---|---|
mp.players | at, atRemoteId, atHandle, exists, forEach, forEachInRange, getClosest, toArray, length, size, local |
mp.vehicles, mp.objects, mp.peds | at, atRemoteId, atHandle, exists, forEach, forEachInRange, getClosest, toArray, length, size |
mp.players.maxStreamed is how many other players the current server streams to this client at once (its max-streamed-players, announced after the welcome); it throws when an older server did not announce it. mp.browsers also has apply, streamed, forEachInStreamRange (every live browser — browsers are not streamed by distance) and atRemoteId (always undefined: browsers never come from the server); position helpers are not provided.
size is the read-only membership-count alias. handle reads the current GTA body handle, or 0 when no body exists; atHandle uses the same host reverse mapping and checks class, session and incarnation. Unknown or ambiguous handles resolve to undefined; stale/forged wrappers cannot revive an entity. These pools do not discover arbitrary GTA entities. Receiver native methods are generated separately for each established physical class.
Client-local entities
const car = mp.vehicles.new('adder', new mp.Vector3(-1037.7, -2738.0, 20.2), {
heading: 90, numberPlate: 'NEXTMP', alpha: 255,
color: [[255, 0, 0], [0, 0, 255]], locked: false, engine: true, dimension: 0,
});
const prop = mp.objects.new('prop_bench_01a', position, { rotation: new mp.Vector3(0, 0, 45), alpha: 200 });
const ped = mp.peds.new('a_m_y_skater_01', position, 180);
car.destroy();
- The model is a hash or a model name (hashed like
mp.game.joaat). - Only the options shown are accepted. An unknown option, a stream-in callback (
mp.peds.new's fourth argument) or a dimension other than 0 is aTypeError, not silently ignored.numberPlateis at most 8 characters, colours are[[r, g, b], [r, g, b]]with 0..255 channels. - The entity joins the same pool as server entities. Its
idis at least0x40000000and itsremoteIdis 65535, as in RAGE MP. It is never sent to the server. handleis 0 until the game creates the body, which happens in a following game frame once the model has loaded. A model the game does not have (IS_MODEL_IN_CDIMAGE), or one that has not loaded within 10 s, leaves the entity without a body and logs a warning.- It belongs to the resource that created it: only that resource can
destroy()it, and it is destroyed when the resource stops and when the session ends.destroy()on a server entity is aTypeError. A resource can hold at most 256.
mp.cameras
const camera = mp.cameras.new('default', new mp.Vector3(1, 2, 3), new mp.Vector3(0, 0, 90), 60);
camera.setActive(true);
mp.game.cam.renderScriptCams(true, false, 0, true, false);
camera.pointAtCoord(4, 5, 6);
// ...
camera.destroy();
mp.game.cam.renderScriptCams(false, false, 0, true, false);
new(name, position?, rotation?, fov?):'default'isDEFAULT_SCRIPTED_CAMERA,'gameplay'returnsmp.cameras.gameplay(getCoord,getRot,getFov,getDirection,isRendering). Without a position it isCREATE_CAM, otherwiseCREATE_CAM_WITH_PARAMS(fov 0..130, default 50).- Camera methods are the
mp.game.camnatives whose first argument is the camera (setActive,setCoord,setRot,setFov,pointAtCoord,pointAtEntity,attachTo,detach,shake,setActiveWithInterpand the rest), called with this camera's handle; the native checks the argument count.getDirection()is computed fromgetRot(2).pointAtispointAtEntity; the depth-of-field setterssetDofFnumberOfLens,setDofFocusDistanceBias,setDofMaxNearInFocusDistanceandsetDofMaxNearInFocusDistanceBlendLevel(also under RAGE MP's spellingsetDofMaxNearInFocuxDistanceBlendLevel) are the same-named natives. idis a resource-local number;atRemoteIdis alwaysundefined(cameras are never networked).streamed/forEachInStreamRangeare the cameras the game is rendering now. Dimensions do not apply:forEachInDimension(0, …)visits all, any other none.mp.game.cam.renderScriptCams(render, ease, easeTime, p3?, p4?, p5?)is provided too: the native is not in the generated table because one NativeDB argument is untyped.- When a resource stops, the client destroys the cameras it created through
mp.game.cam/mp.cameras, and switches back to the gameplay camera if one of them was being rendered.
Range helpers use a finite position and radius 0..10000. getClosest(position) returns the nearest member or undefined; the existing explicit-count form returns an array of at most 4096 members, including when the count is one. That array form is an next:mp overload, not exact RAGE equivalence.
- Players:
id,remoteId,type("player"),valid,nameandisLocal(all read-only).idis the server network id, never a game handle.isStreamedistruewhile the player's body is in this client's world. The local player counts, once spawned.positionis{x, y, z}in that case, andnullotherwise. It is never a stale last-known value.- Positions update every frame, before
renderand before server events are delivered.
- Vehicles, objects, peds:
id,remoteId,valid,model,position,rotation(degrees, as the server sent them) andheading(rotation.z), all read-only.- Read-only
typeis"vehicle","object"or"ped".atRemoteIdis the existing network-ID lookup alias, not a GTA handle lookup. Numeric IDs do not revive stale wrappers. - A member is exactly what the server streamed to this client, so there is no separate
isStreamed. - Vehicle position and rotation update every frame from the interpolated snapshots. Objects and peds show the server's last declaration.
- There is no
dimension, and this is a decision rather than a gap:- The server streams only entities in the player's own dimension, plus global ones. On a client the value could only ever be "mine" or "global".
- Carrying it would add a field to four spawn messages and a new "your dimension changed" message, which is protocol churn for almost no information.
- A mode that needs the player's dimension on the client sends it:
player.call('dimension', [d])fromplayerDimensionChange.
handleis a current GTA entity handle, never a process pointer or permission to revive an entity. Typedentitynative arguments accept a genuine network wrapper and resolve that handle using its current session/incarnation.
- Read-only
- Identity: a wrapper is interned per package. After the entity leaves, reconnects or its id is
reused, the old wrapper answers
valid === false.
mp.keys
bind(keyCode, keyDown, handler)callshandler()when the key goes down (keyDown === true) or up. The code is a virtual-key code from 1 to 255. The handler runs right after the matchingkeyDown/keyUpevent.unbind(keyCode, keyDown, handler?)removes that handler, or every handler for the key and direction, and returns how many it removed.isDown(keyCode)tells whether the key is currently held, as the host reported it.isUp(keyCode)reads the inverse of that same owned host state, with identical validation.
Binds count toward the 1024-handler limit. Which keys reach packages at all (for example, not while chat input is open) is decided by the host, not by the package.
Entity variables
Every player, vehicle, object and ped wrapper has getVariable(name), hasVariable(name) and getVariableNames(), which read the variables the server set (see server API). They are only known for entities this client currently has streamed, and for the local player. Values arrive as null, booleans, numbers, strings, arrays or plain objects.
entity.data is a read-only view of the same variables as fields: player.data.money equals player.getVariable('money'), 'money' in player.data equals hasVariable, and Object.keys(player.data) equals getVariableNames(). Assigning or deleting through it throws TypeError: variables belong to the server.
mp.events.addDataHandler(name, (entity, value, oldValue) => {}) fires when a variable of a known entity changes. undefined stands for "unset". Receiving the full set as an entity streams in is not a change and fires nothing. Data handlers count toward the 1024-handler limit.
Browsers and cursor
browserLoadingFailed comes from the CEF load callback, not page or server messages. Current-navigation tokens are consumed once on the game thread; reload, stop, handle replacement and session changes invalidate queued errors. Aborted navigation, subresource errors and asynchronous creation failure are not included in this partial compatibility contract. Dispatch/ownership tests are portable; actual CEF firing and the Windows source integration are pending the later product certification. This adds no new client ABI or page capability.
mp.browsers.new(url)opens a browser. Itsurlmust lie inside this package's bundle.- A browser has read-only
url,validandready, plusactive,destroy(),reload(),execute(code)andcall(name, ...args). - The page talks back with
mp.trigger(name, ...args). That reaches only the package that owns the browser. browser.reload(ignoreCache?): the optional boolean is accepted; package pages come from the sealed bundle without a browser cache, so both forms reload the same. Reload rejects the browser's pendingcallProccalls.browser.setAllowedEvents(names): from now on only these page events reach the package's handlers (at most 256 names; an empty list allows none). Other browsers are unaffected.browser.callProc(name, ...args)returns a promise for a procedure the page registered withmp.events.addProc(name, handler)(one handler per name, sync or async;mp.events.removeProc(name)removes it). It rejects when the page is not ready yet, the handler throws or does not exist, the result cannot be sent, the browser is destroyed or reloaded, or after 10 s. At most 64 calls per package may wait; arguments and results follow the 4 KiB page-event limit. Calls and answers travel as page events under the reserved__nextmp:prefix, which neither the page (mp.trigger,mp.events.add) nor the package can use; an answer from another browser of the same package is ignored.mp.gui.isGpuRenderingEnabledisfalse: this build renders pages in software (CEF runs withdisable-gpu).mp.gui.cursor.visibleshows the cursor for this package.mp.gui.cursor.show(freeze, state)sets the same state. A visible cursor always takes game input, sofreeze = falsecannot be honoured. The cursor still shows, and the package's log gets one warning.mp.gui.cursor.positiongives[x, y]in pixels of the game window's client area.mp.gui.cursor.normalizedPositiongives the same point as[0..1, 0..1], the coordinatesmp.game.graphics.drawRectand text use.- Hidden cursor: both are
nullwhile no package, browser or menu shows the cursor, because the mouse is steering the camera then. - Clamping: a point outside the window is clamped to its edge.
- Scaling: pixels and window size come from Windows in the same (DPI-aware or virtualised) system, so the normalised value is right at any display scaling.
- Readers: any running package can read them, since the on-screen cursor is not a secret.
- Stopping: a stopped package reads
null.
- Hidden cursor: both are
mp.gui.chat.push(text)(1–160 bytes) adds a system line to this client's own chat. It is never sent to the server.mp.gui.chat.show(shown)andmp.gui.chat.activate(active)are requests owned by the calling package:- Any package can override. The chat stays hidden, or its input locked, while any package asks for it.
- A hidden chat cannot be opened, even when
activateallows it. - Stopping releases. A stopped package's requests are dropped, so a failed mode cannot leave the player without chat.
- Typing is kept. Locking does not close input the player has already opened.
- Hidden lines are kept. Lines that arrive while the chat is hidden appear when it is shown again.
Supported Windows, CEF and live GTA behaviour is not verified yet.
mp.game
mp.game.waitAsync(ms) returns a promise using the existing resource-owned timer. Use integer milliseconds 0..2147483647; zero yields until the next timer tick, not a blocking sleep. Quotas, watchdogs and resource-stop cancellation remain. waitForAsync remains unresolved because the public documentation contradicts itself on timeout behavior. joaat accepts one string or a string array, bounded to 1024 items and 1024 UTF-8 bytes per string; both overloads use the same pure name-hash implementation as the server, not a game call.
The deterministic compatibility catalogue generates 7,414 callable named native methods, including the existing 42 project-witnessed methods, plus 1,709 physical-entity receiver methods. See generated reference (docs/compat/game-native-reference.md) and parity report (docs/compat/rage-api-parity.md). Three existing signature collisions are preserved and explicitly PARTIAL. Unsupported Any, unknown pointers and by-value vector arguments are not guessed. Portable tests exercise every generated path; none is live-GTA-certified by this pass.
Namespaces include graphics, vehicle, ped, player, entity, cam, audio, streaming, weapon, controls, ui and the other established catalogue namespaces. Pinned typing aliases share the same object: pad/controls, hud/ui, misc/gameplay, task/ai, clock/time, physics/rope. Reviewed historical aliases also share objects: worldprobe/shapetest, decisionevent/event, dlc2/dlc, and dlc1/files. NativeDB historical spellings retain the same established hash and signature; for example startScreenEffect and stopScreenEffect are named native bindings.
mp.game.graphics.drawRect(.5, .5, .1, .2, 255, 0, 0, 200, false);
const timer = mp.game.invoke('0x9CD27B0045628463'); // established signature lookup
const output = mp.game.out('float');
mp.game.invoke(0x0123456789ABCDEFn, {args: ['out:float'], returns: 'void'}, output);
// The last hash is illustrative only, not an established GTA native.
invoke(hash, signature?, ...args) accepts a nonzero 64-bit BigInt or exact 0x + 16-hex-digit string. Known hashes can omit the signature. Unknown hashes require explicit safe metadata: {args: [...], returns: ...}. A conflicting explicit signature for a known hash is refused. Scalar types are int, float, bool, hash, string, int64, uint64, entity; return types also include void and vector3. At most 32 arguments. int64 and uint64 require lossless BigInt values, never Number coercion. An entity accepts a live genuine network wrapper or an explicit integer game handle. Wrapper session/incarnation and current stream membership are checked again by the host; stale wrappers are rejected.
Owned outputs use mp.game.out(type, initial?), passed for out:int, out:float, out:bool, out:hash, out:vector3 or out:entity. Their .value is copied back after successful invocation. A plain object cannot impersonate an output container. There are no Any*, string-pointer outputs, arbitrary pointer types or arbitrary JavaScript addresses. Named methods use these same containers; RAGE convenience return-object overloads are not inferred from native pointer signatures alone. Two known animation-data hashes (TASK_SCRIPTED_ANIMATION, PLAY_ENTITY_SCRIPTED_ANIM) are excluded even from explicit invocation: their input pointers cannot fit scalar out slots. An owned struct layout/lifetime adapter is required. This narrow fail-closed policy does not make arbitrary unknown signatures memory-safe or remove the generic unknown-hash escape hatch.
Fourteen receiver getters have independently validated structured results: getMatrix and getQuaternion on the four physical classes, plus Vehicle colour getters getColor, getColours, getCustomPrimaryColour, getCustomSecondaryColour, getExtraColours, and getTyreSmokeColor. They support no-argument convenience and the established typed placeholder form, allocate owned output slots, and return detached records without mutating caller objects. Matrix output matches named RAGE fields even though NativeDB orders forward before right. Only exact named outputs and void-return native contracts qualify; ambiguous success-return/output behavior is not guessed.
Forty-seven additional receiver methods narrow a RAGE any to an established safe native scalar type. These are explicitly PARTIAL, not unrestricted-any compatibility. Native Any, unsupported pointer shapes, arity/type mismatches and ambiguous same-class hashes remain refused. Thirty-nine strict typed variants preserve NativeDB scalar/result types when old RAGE declarations disagree, also PARTIAL. For example player.setAmmoInClip(hash, count) returns a Boolean and rejects Boolean ammunition counts. Exact signatures take precedence over these variants; no missing arguments, optional defaults or numeric/Boolean coercion is invented.
invokeFloat, invokeString and invokeVector3 use the same backend and require the established or explicit signature's result to match. They do not infer an unknown native signature or coerce an incompatible return. Eleven generated read-only engine properties reuse captured receiver methods, so replacing a public method cannot redirect their getter. No setter is guessed.
Common physical Entity native methods use the same callback on Player, Ped, Vehicle and Object when their established receiver/signature semantics match. Ped-specific operations remain on Player/Ped, and declared overrides retain their own native (for example Player getType() is the declared Ped-type native). Generated TypeScript shares PhysicalEntityNatives, PedEntityNatives and PhysicalEntityFacts; virtual Label, Marker, Checkpoint, Pickup and Colshape declarations do not acquire a fake GTA handle. Positioned dist/distSquared reuse the existing common math on current physical wrappers and resource-owned local colshapes. An unstreamed physical wrapper or destroyed local shape is not a valid native receiver. Pool forEachFast/toArrayFast are the same functions as forEach/toArray, not another traversal implementation.
Eighty-two Player-index native methods, including setControl, now resolve the original captured PLAYER_ID through the same typed invoker. They accept only the current genuine local Player, never a server ID, remote ID or ped handle as an engine player index. Remote Players are rejected with an actionable error; these methods are explicitly PARTIAL, not full remote-Player RAGE compatibility. Owned getRgbColour and getParachuteSmokeTrailColor results use the existing output model. Replacing the public playerId function or isLocal property cannot redirect identity resolution.
Player/Ped weapon reads the captured selected-ped-weapon native as a read-only hash. It does not grant loadout authority or guess a setter. Both declarations remain PARTIAL against RAGE's read/write property. Reconnect, incarnation changes and mixed-session snapshots invalidate old wrappers before conversion, including common native methods and these property getters.
player.vehicle calls the captured getVehicleIsIn(false) and resolves only a current, streamed network Vehicle wrapper in the player's session. Missing, unknown, wrong-class or stale handles return undefined, not a fabricated local entity. The getter and resolver cannot be redirected by replacing public methods. This is a partial implementation of the occupied-vehicle contract: local GTA vehicles are not yet wrapped. player.getHealth() remains the explicit raw-native path; no guessed RAGE 0..100 health property normalization was added.
Native hashes are canonical catalogue hashes. A pinned, data-only registration map translates them for GTA 1.0.3889.0; unknown registration hashes pass through to the existing lookup. This is metadata, not a FiveM/RAGE runtime dependency. The actual target-build registration, vector ABI, native results and engine behavior remain live GTA verification gates. Fault containment on Windows reports handler/string-read failures, but cannot make an incorrect unknown signature safe.
mp.game.joaat(name | names[]) computes the game's name hash without calling the game. It gives the same number as the server.
graphics.notify(text) composes the established text/ticker natives with a 256-unit text bound. graphics.requestStreamedTextureDictAsync(name, timeout = 5000) resolves a Boolean using owned timers and a monotonic deadline (0..60000 ms, at most 64 pending requests). It never blocks the game thread and pending polling stops with the resource. Drawing, projection, notification and streaming results remain live-GTA-unverified. streaming.requestAnimDictAsync shares that bounded polling machinery. Its explicit subset is Promise<boolean> with false on timeout, matching the reviewed Wiki return/example but not its contradictory rejection summary. It remains PARTIAL, with resource-stop cleanup and captured native/timer callbacks. graphics.set2dLayer(int32) uses its historical native binding, not a second draw-order helper. The Vector3 world3dToScreen2d helper is not overwritten by a historical five-argument native spelling; the latter remains getScreenCoordFromWorldCoord.
Storage And Local Geometry
mp.storage.data persists bounded typed values under the current server endpoint and resource; sessionData stays in memory. flush() validates both before an atomic write, preserves errors for the caller and skips unchanged data. Clean resource stop also flushes. Limits are 4096 encoded bytes, 128 dictionary keys, depth 8 and 512 nested values; entity references and unsafe values are rejected. This is not unrestricted filesystem access. Corrupt files are not silently overwritten. There is currently no aggregate endpoint-cache eviction or changed-write rate limiter.
mp.colshapes.newCircle/newSphere/newRectangle/newCuboid/newTube reuse the existing geometry engine. Shapes belong to one resource (128 maximum), use revocable monotonically assigned IDs and stop automatically on unload or session replacement. They expose current position, triggered, isPointWithin, destroy, local data/variable reads and pool traversal/range/dimension filters. Enter/exit dispatch follows triggered-state mutation. Only read-only local dimension 0 is supported; remote server shapes are not fabricated. Rectangle and cuboid anchoring follows next:mp's centered geometry and remains a documented compatibility difference.
Virtual Drawings And Pools
mp.labels.new(text, position, options), mp.markers.new(type, position, scale, options), mp.checkpoints.new(type, position, radius, options) and mp.blips.new(sprite, position, options) create resource-owned logical drawings, not physical GTA entities. Marker/Checkpoint use the existing renderers; Label uses the captured bounded world-text helper. Live rendering remains unverified. No hidden objects, fake ENTITY handles or extra native-call backend are allocated.
The shared wrapper provides id, remoteId, valid, type, position, dimension, alpha, dist, distSquared, setAlpha, destroy and local data/variable reads. Marker also has rotation, visible and a four-channel local setColor convenience; Checkpoint has visible. Label has text, RGB color, drawDistance and los. Label text is at most 256 characters, font 0..8, draw distance 0..10000, and LOS must remain false. Unsupported options are refused rather than silently ignored. This is an explicit partial surface, not blanket RAGE inheritance compatibility.
Blip is a map/radar icon drawn by the same renderer as server blips, under its own private IDs. Options: name (at most 127 UTF-8 bytes), scale 0..100, color (game palette index 0..255, not RGB), alpha, shortRange, rotation (degrees, −360..360), visible, dimension: 0. drawDistance is refused: GTA blips have no draw distance here, and accepting it would silently do nothing. Properties sprite, color, scale, name, shortRange, rotation and the methods get/setSprite, get/setColour, getAlpha, setScale, setAsShortRange/isShortRange, get/setRotation, get/setCoords change only this record. Client ABI 28 adds the rest of the renderer's state: setFlashes, setFlashesAlternate, isFlashing, setBright, setShowCone, setAsFriendly, setHighDetail, setAsMissionCreator/isMissionCreator, setShowHeadingIndicator, setRoute (GPS route), setDisplay (0..10, default 2), setPriority (0..255), setFlashInterval/setFlashTimer (ms, 0..20000), showNumberOn (1..255) / hideNumberOn, setSecondaryColour(r, g, b), setNameFromTextFile(label) (a game text label the game localizes), setNameToPlayerName(player) (copies the name now), setPosition(x, y, z) and doesExist(). Methods that need the GTA blip handle (info ids, isOnMinimap, getHudColour, pulse, setFade, setCategory, setRouteColour) are not provided: the handle stays private. mp.blips lists this resource's blips; server blips are not mirrored into it, and no GTA blip handle is given out (atHandle remains missing).
Local drawings support dimension 0 only and own mutable state. Existing streamed server Marker/Checkpoint state is exposed read-only. Their dimension is undefined because it is absent from the current wire record, not inferred as 0. Client scripts cannot destroy or modify server drawings; remote variable reads are unavailable. Local data is not server-synchronized. Network Label, client Pickup and network Blip wrappers remain missing; Pickup is not assumed to be an Object or an ENTITY handle.
Pools share at, atRemoteId, exists, forEach, apply, toArray, length, size, range/dimension traversal and deterministic nearest-array getClosest. streamed and forEachInStreamRange list what the client currently draws, by the renderer's own rule: visible markers and labels within their draw distance of the local player (a marker's 0 means everywhere), every existing checkpoint and blip. Without a local position the distance-based pools stream nothing. maxStreamed exists only on mp.players (the server's announced limit); drawings have none. Fast traversal names are identical callbacks. at uses the resource-local wrapper ID, atRemoteId uses an independent next:mp remote-ID index, and neither accepts a GTA handle as that identity. Missing lookups return undefined. Traversal uses a detached membership snapshot and skips entries destroyed by an earlier callback. Dimension filters do not invent a missing remote dimension.
Creation is bounded to 128 local records per pool; the host additionally bounds combined Marker/Checkpoint records to 128 per resource and 4096 total. Labels and Colshapes each have their own 128-record resource limit. Renderer IDs are private and independent of script IDs and network IDs. Resource stop, failed initialization, watchdog termination and reconnect clean up owned drawings. Session plus incarnation checks prevent reused remote IDs from reviving stale wrappers. No physical inherited native methods are exposed.
Network entityStreamIn runs after view/index commit; entityStreamOut runs before invalidation. View removal is not invented server destruction. Checkpoint enter/exit uses the existing authoritative tube geometry against current local-player coordinates and commits containment state before owned, non-cancellable dispatch. These dispatch paths are portable-tested; game rendering, frame/device behavior and GTA hooks are not live-certified. Remote/CEF event names cannot forge the transition.
mp.raycasting.testPointToPointAsync takes the same arguments but uses the ordinary START_SHAPE_TEST_LOS_PROBE the game computes in its own frame; the promise resolves with the same result (or undefined on a miss) and rejects if the game drops the test or after 5 s. testCapsule is not provided: its result arrives a frame later, and a synchronous answer would be invented.
mp.raycasting.testPointToPoint uses typed synchronous shape-test natives and normalizes hit position/normal/entity through the current host mapping. Portable tests prove translation and result conversion, not actual GTA hit results. Client keys and console logging stay resource-owned; stopping a resource removes its bindings. mp.console.logInfo/logWarning/logError/logFatal accept RAGE MP's optional boolean save and saveAsync; the client log is always written to disk, so both are accepted without changing anything. Console logging does not execute OS or server commands.
Rules:
- Types are strict. Integers are 32-bit and floats must be finite. Hashes take −2³¹…2³²−1 and
come back unsigned. Strings are at most 1024 bytes. A wrong argument throws
TypeErrornaming the function. - Game frame only. Calls work during the game frame, which is the frame event and everything
the client dispatches inside it. Elsewhere the call throws
Error, as it does when the native is missing from the game build. - Text chains work. A string argument stays alive for the next 511 string arguments, so
addTextComponentSubstringPlayerNamefollowed byendTextCommandDisplayTextdraws the text. - Stopping. After
resourceStophas run, calls do nothing. InsideresourceStopthey still work, so a package can restore the radar or HUD it hid.
graphics.drawText(text, [x,y] | [x,y,z], options?) independently composes the typed text natives. Options are font 0..8, RGBA color, scale pair, outline and centre. Text is bounded to 256 UTF-16 units; malformed options are rejected before native effects, and world draw origin is restored even on a later native failure. graphics.world3dToScreen2d({x,y,z}) uses owned float outputs and returns {x,y} or undefined. Both are portable-tested, not live-rendering-verified.
mp.system and mp.console.clear
mp.system.isFocused— the game process owns the foreground window;mp.system.isFullscreen— the game window covers its whole monitor (exclusive or borderless). Both are read-only; reading throws when the client cannot tell, instead of answeringfalse.mp.console.clear()clears the visible F8 console, like the built-inclearcommand. The log file keeps every line.mp.console.reset,mp.console.verbosityandmp.system.notifyare not available: the client has no console settings or Windows notifications to back them.
mp.Vector3
Construct with (x,y,z), a three-number array or an {x,y,z} value. Finite components only. Methods: clone, add, subtract, multiply, divide, negative, dot, cross, length, min, max, unit, angleTo, equals, toArray. Arithmetic returns a new vector, does not mutate its receiver, and accepts vector/scalar arguments where documented in the generated SDK. Angles are radians; a zero vector's unit() is zero, while its angleTo is undefined and throws. Division by zero is refused. Vector instances serialize as their own x/y/z through the existing typed event codec. Entity positions and native vector results remain plain {x,y,z} values. toAngles remains unimplemented because its exact convention has not been established.
mp.voice
Voice status, resource-owned local mute and the push-to-talk key are available through mp.voice; see voice (docs/voice.md) for the exact contract, limits and privacy policy.
mp.voice.setPushToTalkKey(0x56); // V; null releases this resource's request
mp.voice.getPushToTalkKey(); // 0x56
The key only chooses which physical key the player holds. Scripts cannot enable the microphone, choose devices, change volumes or transmit without the player holding that key.
Not yet available
Native signatures excluded by the safety audit, instance-native convenience wrappers, additional client entity pools (dummies, pickups), stream/combat/vehicle hook parity, isolated persistent storage, raycast result wrappers and the remaining helpers/properties in the parity catalogue.