Skip to main content

Client-side scripting

Client scripts run inside each player's game. Use them for UI (CEF), key binds, cameras, drawing, local effects and anything that has to react instantly. The server stays authoritative over players, vehicles and game state.

How it works​

  1. When a player connects, they download client_packages/ from your server (only the files that changed since the last visit).
  2. The client runs client_packages/index.js once the player is in the world.
  3. Client and server talk through events: player.call(...) on the server reaches mp.events.add(...) on the client, and mp.events.callRemote(...) goes the other way.

Client JavaScript runs on V8: arrow functions, classes, let/const, template strings, async/await and promises all work. It is not Node.js: there is no fs, no npm and no network access from scripts. require('./file.js') loads other files from client_packages/.

Layout​

client_packages/
├─ index.js entry point
├─ gamemode/
│ ├─ index.js
│ └─ ui.js
└─ cef/ your web UI
├─ index.html
└─ app.js

A first script​

// client_packages/index.js
require('./gamemode/index.js');
// client_packages/gamemode/index.js
mp.events.add('playerReady', () => {
mp.gui.chat.push('!{#00ff00}Client loaded');
});

// the server calls player.call('welcome', [name])
mp.events.add('welcome', (name) => {
mp.gui.chat.push(`Welcome, ${name}!`);
});

// F5 asks the server for a car
mp.keys.bind(0x74, true, () => {
mp.events.callRemote('requestCar', 'adder');
});
// packages/server/index.js (server side)
mp.events.add('playerJoin', (player) => {
player.spawn(new mp.Vector3(-425.5, 1123.6, 325.8));
player.call('welcome', [player.name]);
});

mp.events.add('requestCar', (player, model) => {
mp.vehicles.new(mp.joaat(model), player.position, { dimension: player.dimension });
});

A CEF interface​

const ui = mp.browsers.new('package://cef/index.html'); // a file inside client_packages
mp.gui.cursor.show(true, true);

// page -> client: in the page, call mp.trigger('ui:buy', 'item', 3)
mp.events.add('ui:buy', (item, amount) => mp.events.callRemote('buy', item, amount));

// client -> page
ui.execute(`window.setMoney(${1500})`);

package:// files are read from the player's encrypted cache; nothing is unpacked to disk.

Next steps​