Parent : [[INDEX-FIVEM]]
FiveM - Node.js CFX Runtime Bridge : exécution JS server-side avec Lua interop
Problème
FiveM supporte nativement Lua server-side, mais certains algorithmes (cryptographie, parsing complexe, intégrations API externes) sont plus faciles à écrire en JavaScript/Node.js.
Solution
Utiliser le runtime Node.js natif de CFX FiveM (node_version) pour exécuter du JS server-side avec accès aux natives CFX via global et bridge Lua/JS via exports.
-- fxmanifest.lua
fx_version 'cerulean'
game 'gta5'
node_version '22'
server_scripts {
'@oxmysql/lib/MySQL.js',
'server/server.js'
}
files {
'web/**/*',
'data/okok_payphone_ring.dat54.rel',
'audiodirectory/okokphone_sounds.awc'
}
data_file 'AUDIO_WAVEPACK' 'audiodirectory'
data_file 'AUDIO_SOUNDDATA' 'data/okok_payphone_ring.dat'
// server.js
const { MySQL } = require('@oxmysql/lib/MySQL.js');
// Accès aux natives CFX via global
global.onNet('phone:sendMessage', (target, message) => {
const src = global.source;
// Appel Lua depuis JS via exports
const result = exports['my_lua_resource'].ValidateMessage(src, message);
if (result.valid) {
MySQL.insert('INSERT INTO phone_messages (sender, receiver, message) VALUES (?, ?, ?)', [
src, target, message
]);
global.emitNet('phone:receiveMessage', target, {
sender: src,
message: message,
timestamp: Date.now()
});
}
});
// Presigned URLs pour uploads (S3/Cloudflare R2)
const generatePresignedUrl = (filename, expiry = 3600) => {
// Logique de signature AWS v4
return `https://cdn.example.com/${filename}?X-Amz-Algorithm=...`;
};
Pourquoi
- Node.js v22 supporte async/await natif, fetch, crypto, streams
- Permet d'utiliser des packages npm (axios, crypto-js, etc.)
- Bridge transparent avec les resources Lua existantes via
exports
Quand l'utiliser
- Intégrations API externes complexes (OAuth, webhooks, uploads)
- Traitement audio/image (FFmpeg, sharp) via child_process
- Algorithmes cryptographiques ou de parsing (JWT, XML, JSON schema)
Quand NE PAS l'utiliser
- Logique simple (event forwarding, CRUD basique) → Lua suffit
- Si la ressource n'a pas besoin de npm packages
Exemples
Correct
// Server JS avec gestion d'erreurs et rate limiting
const rateLimits = new Map();
global.onNet('phone:uploadMedia', async (data) => {
const src = global.source;
const now = Date.now();
if (rateLimits.has(src) && now - rateLimits.get(src) < 5000) {
return global.emitNet('phone:error', src, 'Rate limit');
}
rateLimits.set(src, now);
try {
const url = generatePresignedUrl(data.filename);
global.emitNet('phone:uploadUrl', src, { url, fields: data.fields });
} catch (err) {
console.error('Upload error:', err);
global.emitNet('phone:error', src, 'Upload failed');
}
});
Incorrect
// JS sans gestion d'erreurs → crash du thread Node.js
global.onNet('phone:send', (data) => {
MySQL.insert('INSERT ...', [data.sender, data.message]); // Crash si data undefined
global.emitNet('phone:ok', global.source);
});
Références
- okokPhone/fxmanifest.lua L15-28
- okokPhone/server/server.js
- CFX Docs : Node.js runtime