Jeux Tarifs Pro Infrastructure Guides Blog Partenariat À propos

Créer un script RedM en Lua : le tutoriel complet (natives RDR3, événements, VORP)

Apprenez à écrire des scripts RedM pour Red Dead Redemption 2 : fxmanifest rdr3, client et serveur, natives appelées par leur code, texte à l'écran, touches, événements réseau, base de données, sécurité, avec des exemples complets.

4 min de lecture Mis à jour le 21 octobre 2026

Ce tutoriel vous apprend à écrire vos propres ressources RedM en Lua. Les bases sont les mêmes que sur FiveM (client, serveur, événements, commandes), avec une particularité : beaucoup de natives de Red Dead Redemption 2 n'ont pas encore de nom et s'appellent par leur code. Chaque notion est illustrée par un exemple complet.

Client et serveur

CôtéOù le code s'exécuteÀ quoi il sert
ClientSur le PC de chaque joueurAfficher, lire les touches, agir sur le personnage, le cheval, le monde
ServeurSur votre serveurGarder les données, décider, vérifier, parler à la base de données

Règle d'or : le client peut être modifié par un tricheur, le serveur non. L'argent, les objets et les droits se décident côté serveur.

Étape 1 : la ressource minimale

Créez un dossier ma-ressource dans resources/ :

fxmanifest.lua
fx_version 'cerulean'
game 'rdr3'
rdr3_warning 'I acknowledge that this is a prerelease build of RedM, and I am aware my resources *will* become incompatible once RedM ships.'
lua54 'yes'

name 'ma-ressource'
version '1.0.0'

shared_script 'config.lua'
client_script 'client.lua'
server_script 'server.lua'
config.lua
Config = {}
Config.Saloon = vector3(-313.0, 805.0, 118.98)   -- saloon de Valentine
client.lua
print('Client chargé')
server.lua
print('Serveur chargé')

Ajoutez ensure ma-ressource au server.cfg et redémarrez. Pendant le développement, refresh puis ensure ma-ressource dans la Live Console de txAdmin rechargent la ressource sans couper le serveur.

rdr3_warning

La phrase doit être copiée exactement, sans traduction ni modification, sinon la ressource ne démarre pas.

Étape 2 : une première commande

client.lua
RegisterCommand('position', function()
  local ped = PlayerPedId()
  local c = GetEntityCoords(ped)
  print(('vector3(%.2f, %.2f, %.2f), cap %.2f'):format(c.x, c.y, c.z, GetEntityHeading(ped)))
  TriggerEvent('chat:addMessage', { args = { 'Position', ('%.2f, %.2f, %.2f'):format(c.x, c.y, c.z) } })
end, false)

Le dernier argument false ouvre la commande à tous. Avec true, elle est réservée aux joueurs qui ont le droit ACE command.position.

Étape 3 : les natives de Red Dead Redemption 2

Les natives sont les fonctions du jeu. Sur RedM, il en existe deux sortes :

La référence complète, avec les noms trouvés par la communauté, est sur rdr3natives.com et dans la documentation de VORP.

client.lua
-- Native nommée : soigner le joueur
SetEntityHealth(PlayerPedId(), GetEntityMaxHealth(PlayerPedId()))

-- Native appelée par son code : donner une tenue aléatoire à un PNJ ou un cheval
-- (0x283978A15512B2FE = _SET_RANDOM_OUTFIT_VARIATION)
Citizen.InvokeNative(0x283978A15512B2FE, ped, true)
Commentez vos codes

Notez toujours le nom de la native en commentaire à côté de son code : dans six mois, 0x283978A15512B2FE ne vous dira plus rien.

Natives très utilisées sur RedM :

Code ou nomRôle
PlayerPedId()Le personnage du joueur
GetEntityCoords(entite)Position d'une entité
SetEntityCoords(entite, x, y, z)Téléporter
NetworkResurrectLocalPlayer(x, y, z, cap, true, false)Réanimer le joueur
NetworkOverrideClockTime(h, m, s)Imposer l'heure
0x283978A15512B2FETenue aléatoire d'un PNJ ou d'un cheval
0x028F76B6E78246EBMettre un personnage en selle sur une monture
0x554D9D53F696D002Créer un repère (blip) à des coordonnées
0x9CB1A1623062F402Donner un nom à un repère

Étape 4 : écrire à l'écran

Les fonctions de texte de GTA V n'existent pas sur RedM. On crée une chaîne avec CreateVarString puis on l'affiche avec DisplayText.

client.lua
local function texteEcran(texte, x, y, taille)
  SetTextScale(taille or 0.4, taille or 0.4)
  SetTextColor(255, 255, 255, 230)
  SetTextCentre(true)
  SetTextDropshadow(1, 0, 0, 0, 255)
  DisplayText(CreateVarString(10, 'LITERAL_STRING', texte), x, y)
end

local function texte3D(coords, texte)
  local visible, x, y = GetScreenCoordFromWorldCoord(coords.x, coords.y, coords.z)
  if visible then texteEcran(texte, x, y, 0.35) end
end

Étape 5 : un point d'interaction avec une touche

Sur RedM, les touches se désignent par un code (hash). Quelques touches courantes :

ToucheCode
E0xCEFD9220
G0x760A9C6F
R0xE30CD707
Entrée0xC7B5340A
Retour arrière0x156F7119
client.lua
local TOUCHE_E = 0xCEFD9220

CreateThread(function()
  while true do
    local attente = 1000
    local distance = #(GetEntityCoords(PlayerPedId()) - Config.Saloon)

    if distance < 15.0 then
      attente = 0
      if distance < 2.0 then
        texte3D(Config.Saloon + vector3(0.0, 0.0, 1.0), 'Appuyez sur E pour commander un verre')
        if IsControlJustReleased(0, TOUCHE_E) then
          TriggerServerEvent('ma-ressource:commander')
        end
      end
    end

    Wait(attente)
  end
end)
Les performances

La boucle ne tourne à chaque image (Wait(0)) que lorsque le joueur est proche. Loin, elle ne vérifie qu'une fois par seconde. Vérifiez le coût de vos ressources avec resmon 1 dans la console F8.

Étape 6 : les événements réseau

server.lua
RegisterNetEvent('ma-ressource:commander', function()
  local src = source

  -- Vérifier côté serveur que le joueur est bien au comptoir (OneSync)
  local ped = GetPlayerPed(src)
  if #(GetEntityCoords(ped) - Config.Saloon) > 4.0 then return end

  TriggerClientEvent('chat:addMessage', src, { args = { 'Saloon', 'Le barman vous sert un whisky.' } })
  TriggerClientEvent('ma-ressource:boire', src)
end)
client.lua
RegisterNetEvent('ma-ressource:boire', function()
  -- Petite animation : on récupère un peu de santé
  local ped = PlayerPedId()
  SetEntityHealth(ped, math.min(GetEntityHealth(ped) + 25, GetEntityMaxHealth(ped)))
end)
FonctionSens
TriggerServerEvent('nom', ...)Client vers serveur
TriggerClientEvent('nom', joueur, ...)Serveur vers un joueur (-1 pour tous)
RegisterNetEvent('nom', fonction)Écouter un événement réseau

Étape 7 : faire apparaître un cheval

Un exemple complet qui combine chargement de modèle, natives nommées et natives par code.

client.lua
RegisterCommand('cheval', function(_, args)
  local modele = args[1] or 'A_C_Horse_Arabian_White'
  local hash = joaat(modele)
  if not IsModelValid(hash) then
    return TriggerEvent('chat:addMessage', { args = { 'Écurie', 'Modèle inconnu : ' .. modele } })
  end

  RequestModel(hash)
  local limite = GetGameTimer() + 5000
  while not HasModelLoaded(hash) and GetGameTimer() < limite do Wait(10) end
  if not HasModelLoaded(hash) then return end

  local ped = PlayerPedId()
  local pos = GetOffsetFromEntityInWorldCoords(ped, 0.0, 3.0, 0.0)
  local cheval = CreatePed(hash, pos.x, pos.y, pos.z, GetEntityHeading(ped), true, true, false, false)

  Citizen.InvokeNative(0x283978A15512B2FE, cheval, true)          -- _SET_RANDOM_OUTFIT_VARIATION : selle et apparence
  Citizen.InvokeNative(0x028F76B6E78246EB, ped, cheval, -1)        -- _SET_PED_ONTO_MOUNT : le joueur monte en selle
  SetModelAsNoLongerNeeded(hash)
end, true)

Quelques modèles de chevaux : A_C_Horse_Arabian_White, A_C_Horse_Turkoman_Gold, A_C_Horse_Thoroughbred_DappleGrey, A_C_Horse_MissouriFoxTrotter_SilverDapplePinto, A_C_Horse_Mustang_GoldenDun.

Étape 8 : la base de données

Déclarez oxmysql dans le manifeste :

fxmanifest.lua
fx_version 'cerulean'
game 'rdr3'
rdr3_warning 'I acknowledge that this is a prerelease build of RedM, and I am aware my resources *will* become incompatible once RedM ships.'
lua54 'yes'

dependency 'oxmysql'
client_script 'client.lua'
server_scripts { '@oxmysql/lib/MySQL.lua', 'server.lua' }
table.sql
CREATE TABLE IF NOT EXISTS primes (
  identifier VARCHAR(64) PRIMARY KEY,
  nom VARCHAR(64) NOT NULL,
  montant INT NOT NULL DEFAULT 0
);
server.lua
-- /prime [id] [montant] : met une prime sur la tête d'un joueur (admin)
RegisterCommand('prime', function(src, args)
  local cible, montant = tonumber(args[1]), tonumber(args[2])
  if not cible or not montant or montant <= 0 or not GetPlayerName(cible) then return end

  local license = GetPlayerIdentifierByType(cible, 'license')
  MySQL.insert.await('INSERT INTO primes (identifier, nom, montant) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE montant = montant + VALUES(montant), nom = VALUES(nom)',
    { license, GetPlayerName(cible), montant })

  TriggerClientEvent('chat:addMessage', -1, {
    color = { 200, 60, 40 },
    args = { 'Avis de recherche', ('%s est recherché : %d $ de récompense'):format(GetPlayerName(cible), montant) },
  })
end, true)

-- /primes : les 5 plus grosses primes
RegisterCommand('primes', function(src)
  local rows = MySQL.query.await('SELECT nom, montant FROM primes ORDER BY montant DESC LIMIT 5')
  for i, r in ipairs(rows) do
    local ligne = ('%d. %s : %d $'):format(i, r.nom, r.montant)
    if src == 0 then print(ligne) else TriggerClientEvent('chat:addMessage', src, { args = { 'Primes', ligne } }) end
  end
end, false)

Étape 9 : utiliser VORP

Sur un serveur VORP, l'argent, l'inventaire et les métiers passent par l'API de VORP plutôt que par votre propre base.

server.lua
local VORPcore = exports.vorp_core:GetCore()

RegisterNetEvent('ma-ressource:commander', function()
  local src = source
  local user = VORPcore.getUser(src)
  if not user then return end
  local perso = user.getUsedCharacter

  local PRIX = 2.0
  if perso.money < PRIX then
    return VORPcore.NotifyRightTip(src, "Vous n'avez pas assez d'argent", 4000)
  end

  perso.removeCurrency(0, PRIX)               -- 0 = dollars, 1 = or
  VORPcore.NotifyRightTip(src, ('Whisky servi pour %.2f $'):format(PRIX), 4000)
  TriggerClientEvent('ma-ressource:boire', src)
end)

Tout le détail (installation, objets, métiers, inventaire) est dans le guide Installer VORP Core sur RedM.

Bonnes pratiques

Et ensuite ?

Des scripts complets prêts à copier vous attendent dans la bibliothèque de scripts RedM.


EsoterHost n'est ni approuvé, ni sponsorisé, ni affilié à Rockstar Games ou à Cfx.re. RedM et Red Dead Redemption 2 sont des marques de leurs propriétaires.

Ce guide vous a-t-il aidé ? Une question ? Ouvrez un ticket

Vous ne trouvez pas votre réponse ?

Notre équipe répond à vos tickets 7j/7, directement depuis votre espace client.