Cookbook
Small examples for common tasks. Every ts server and ts client block on this page is type-checked against sdk/types by the tools test suite (tools/dev/dev.test.cjs). A broken example fails the build, not a reader.
Type-checked is not the same as played. None of these examples has been run in a live GTA V session as part of this documentation. Behaviour inside the game is covered by the owner's live certification, which is a separate step.
The full API reference is docs/api/server.md, docs/api/client.md and docs/api/csharp.md. You can also search everything at once with node tools/nextmp.cjs docs.
A basic server package
packages/gamemode/index.js (or .ts compiled to dist/index.js, see getting started):
mp.events.add('playerJoin', (player) => {
player.outputChatBox(`Welcome, ${player.name}`);
mp.players.broadcast(`${player.name} joined (${mp.players.length} online)`);
});
mp.events.add('playerQuit', (player) => {
mp.players.broadcast(`${player.name} left`);
});
Commands
mp.events.addCommand('pos', (player) => {
const { x, y, z } = player.position;
player.outputChatBox(`${x.toFixed(1)} ${y.toFixed(1)} ${z.toFixed(1)}`);
});
mp.events.addCommand('tp', (player, _full, x, y, z) => {
const target = new mp.Vector3(Number(x), Number(y), Number(z));
player.teleport(target);
});
Events between client and server
The client sends a named event, and the server answers by calling the client.
mp.events.add('scoreboard:show', (rows) => {
mp.console.logInfo(`scoreboard rows: ${JSON.stringify(rows)}`);
});
mp.events.callRemote('scoreboard:request');
// Custom events have no fixed signature, so the player parameter is annotated.
mp.events.add('scoreboard:request', (player: NextMPServer.Player) => {
const rows = mp.players.toArray().map((other) => [other.name, other.ping]);
player.call('scoreboard:show', [rows]);
});
Event payloads are bounded and validated by the server. node tools/admin.cjs events shows the busiest names, the peak sender and why calls were rejected (debugging (docs/developer/debugging.md)).
Limiting what one client can send
Give a handler its own limits when a single player must not flood it. The limits count per player, apply only to calls from clients, and refusals show up in admin events.
mp.events.add('inventory:use', { rateLimit: { calls: 5, seconds: 1 }, maxPayloadBytes: 256 },
(player: NextMPServer.Player, item: unknown) => {
player.outputChatBox(`Used ${String(item)}`);
});
mp.events.addProc('bank:withdraw', { rateLimit: { calls: 1, seconds: 2 } }, () => true);
Procedures (request and answer)
mp.events.addProc('bank:balance', (player) => {
const balance = player.getVariable('balance');
return typeof balance === 'number' ? balance : 0;
});
async function showBalance(): Promise<void> {
try {
const balance = await mp.events.callRemoteProc('bank:balance');
mp.console.logInfo(`balance: ${String(balance)}`);
} catch (error) {
mp.console.logError(`balance unavailable: ${String(error)}`);
}
}
void showBalance();
A procedure that nobody answers rejects with noHandler, and one that takes longer than ten seconds rejects with timeout. Exactly one package answers a given name.
Players and synced variables
mp.events.add('playerReady', (player) => {
player.setVariable('balance', 100);
player.setVariables({ job: 'courier', level: 1 });
player.giveWeapon(mp.joaat('weapon_pistol'), 60, true);
});
Vehicles
mp.events.addCommand('car', (player, _full, model = 'adder') => {
const vehicle = mp.vehicles.new(mp.joaat(model), player.position, player.heading);
player.outputChatBox(`Spawned ${model} (#${vehicle.id})`);
});
Who simulates a vehicle, and why that changed, can be checked with node tools/admin.cjs inspect --kind vehicle --id N (debugging (docs/developer/debugging.md)).
Markers, checkpoints, blips and colshapes
const garage = new mp.Vector3(215.0, -810.0, 30.7);
mp.markers.new(1, garage, 2.0);
mp.blips.new(357, garage, { name: 'Garage' });
const zone = mp.colshapes.newSphere(garage.x, garage.y, garage.z, 3.0);
mp.events.add('playerEnterColshape', (player, shape) => {
if (shape === zone) player.outputChatBox('Press E to open the garage');
});
Client storage
Client packages keep small per-server data through mp.storage. There is no built-in server database: use the database library of your choice inside a server package.
const visits = typeof mp.storage.data.visits === 'number' ? mp.storage.data.visits : 0;
mp.storage.data.visits = visits + 1;
mp.storage.flush();
A browser (in-game UI page)
A browser loads a page from your own client package only (package://). It never loads an arbitrary web address.
const hud = mp.browsers.new('package://hud/index.html');
mp.events.add('hud:money', (amount) => {
hud.execute(`window.setMoney(${Number(amount)})`);
});
C# packages
node tools/nextmp.cjs init my-server --template csharp creates a package that builds against the NextMP.Server reference package. The API and a complete example are in docs/api/csharp.md.
Custom assets (models, maps, DLC packs)
Put streamed files under client_packages/<resource>/stream/ and DLC packs under game_resources/dlcpacks/, then check them before you start the server:
node tools/nextmp.cjs assets validate --manifest assets.json
See assets (docs/developer/assets.md) for the layout, what is checked and how cache invalidation works.
Native plugins
node tools/nextmp.cjs create plugin metrics creates a CMake project that finds the SDK with find_package(NextMPPlugin) and exports nextmp_plugin_v1. See docs/plugins.md (docs/plugins.md).
Unit tests and bots
- Unit tests run your package with
@nextmp/testing, without a server or the game: gamemode testing (docs/developer/testing.md). - Bots drive a real server over the real protocol (no GTA, no browser pages):
bots (
docs/developer/testing.md).