Învață programare
Prima ta resursă FiveM în Lua
Creezi o resursă FiveM cu fxmanifest.lua, un script de server și unul de client, o comandă și evenimente între cele două părți.
FiveM este o modificare pentru GTA V care îți permite să rulezi servere cu reguli proprii, mai ales de roleplay. Tot ce adaugi pe un astfel de server vine sub formă de „resurse”: foldere cu scripturi, mașini, hărți sau meniuri. Scripturile se scriu în Lua, un limbaj mic și ușor de citit.
Presupunem că ai citit „Ce este programarea” și ai pregătit calculatorul cu „Mediu de lucru pe Windows”. Lua nu trebuie instalat pe calculatorul tău: codul rulează în serverul și în clientul FiveM. Testarea se face deci pe un server FiveM.
Lua în cinci minute
- Variabilele se declară cu local: local nume = "Alex". Fără local, variabila devine globală, ceea ce provoacă greșeli greu de găsit.
- Textul se lipește cu operatorul format din două puncte consecutive (scris ..): "Salut, " .. nume. Lungimea unui tabel se ia cu #.
- Condițiile: if x > 5 then ... elseif ... else ... end. Diferit se scrie ~=, iar „nu” este not.
- Funcțiile: function aduna(a, b) return a + b end. Se pot da și ca valoare, de pildă ca parametru.
- Tabelele sunt singura structură: listă, dicționar sau obiect. local jucator = { nume = "Alex", xp = 10 }. Într-o listă, numărătoarea începe de la 1, nu de la 0.
- nil înseamnă „nimic”. Dacă încerci să folosești un nil ca pe un tabel, scriptul dă eroare.
- Comentariile încep cu două cratime: -- text.
Server și client: două lumi. O resursă FiveM rulează în două locuri. Scripturile de server rulează o singură dată, pe mașina serverului: ele știu cine e conectat, țin datele și iau deciziile. Scripturile de client rulează pe calculatorul fiecărui jucător: desenează pe ecran, citesc tastele și comenzile. Cele două părți vorbesc prin evenimente.
Regula de aur: serverul este autoritatea. Tot ce ține de bani, obiecte, permisiuni se hotărăște pe server; clientul doar cere și afișează.
Anatomia unei resurse
O resursă este un folder în directorul resources al serverului. Numele folderului este numele resursei. Înăuntru are cel puțin un fișier fxmanifest.lua, care spune FiveM ce scripturi să încarce.
resources/
salut_fivem/
fxmanifest.lua
server.lua
client.luafxmanifest.lua
fx_version 'cerulean'
game 'gta5'
author 'Numele tău'
description 'Prima mea resursă'
version '1.0.0'
server_script 'server.lua'
client_script 'client.lua'- fx_version stabilește generația de funcții a manifestului. Valoarea cerulean este cea folosită și în resursele de exemplu DominosLand.
- game spune pentru ce joc este resursa; pentru GTA V este gta5.
- server_script și client_script indică fișierele de încărcat. Pentru mai multe fișiere folosești server_scripts { ... } și client_scripts { ... }.
- author, description și version sunt informative.
Scriptul de server
Primul script afișează un mesaj când pornește resursa, răspunde la un eveniment venit de la client și are o comandă pentru consola serverului.
AddEventHandler('onResourceStart', function(resource)
if resource == GetCurrentResourceName() then
print('[salut] resursa a pornit')
end
end)
RegisterNetEvent('salut:cerSalut')
AddEventHandler('salut:cerSalut', function()
local src = source
local nume = GetPlayerName(src) or 'necunoscut'
print('[salut] cerere de la ' .. nume .. ' (id ' .. src .. ')')
TriggerClientEvent('salut:raspuns', src, 'Salut, ' .. nume .. '!')
end)
RegisterCommand('jucatori', function(source, args, rawCommand)
local total = #GetPlayers()
if source == 0 then
print('Jucători conectați: ' .. total)
else
TriggerClientEvent('chat:addMessage', source, { args = { 'Server', 'Jucători conectați: ' .. total } })
end
end, false)- onResourceStart este un eveniment încorporat. Primește numele resursei pornite, de aceea îl comparăm cu GetCurrentResourceName().
- RegisterNetEvent declară un eveniment care poate fi chemat din rețea. AddEventHandler spune ce funcție rulează când vine.
- source este o variabilă specială, disponibilă în handler pe server: ID-ul jucătorului care a trimis evenimentul. Salvează-l imediat într-un local (local src = source), pentru că el poate dispărea dacă folosești funcții asincrone.
- GetPlayerName(id) întoarce numele jucătorului. GetPlayers() întoarce lista celor conectați.
- RegisterCommand: când un jucător (sau consola) scrie comanda, rulează funcția. Pe server, source este 0 dacă a scris-o consola serverului. Ultimul argument, false, înseamnă că oricine o poate folosi; true ar cere permisiune.
Scriptul de client
RegisterCommand('salut', function()
TriggerServerEvent('salut:cerSalut')
end, false)
RegisterNetEvent('salut:raspuns')
AddEventHandler('salut:raspuns', function(mesaj)
TriggerEvent('chat:addMessage', {
color = { 0, 200, 0 },
args = { 'Salut', mesaj }
})
end)
RegisterCommand('pozitie', function()
local ped = PlayerPedId()
local c = GetEntityCoords(ped)
local text = string.format('x=%.1f y=%.1f z=%.1f', c.x, c.y, c.z)
TriggerEvent('chat:addMessage', { args = { 'Poziție', text } })
end, false)- Pe client, comanda /salut trimite un eveniment la server cu TriggerServerEvent. Serverul îi răspunde cu TriggerClientEvent, iar clientul afișează în chat.
- PlayerPedId() este personajul tău din joc, iar GetEntityCoords îi dă poziția ca vector cu x, y, z.
- chat:addMessage este un eveniment al resursei chat, inclusă de obicei în serverele FiveM. Aceasta e metoda simplă de a scrie în chat de la client.
- Pe client, print scrie în consola F8 a jocului, nu în cea a serverului.
Cum trimiți evenimente între server și client
- Client către server: TriggerServerEvent("nume", argumente). Pe server, ai nevoie de RegisterNetEvent("nume") și de AddEventHandler.
- Server către un client: TriggerClientEvent("nume", idJucator, argumente). Cu -1 în loc de id, evenimentul ajunge la toți jucătorii.
- În aceeași parte (doar client sau doar server): TriggerEvent("nume", argumente), fără RegisterNetEvent.
- Dă evenimentelor numele cu prefix propriu, de exemplu salut:cerSalut, ca să nu se calce cu alte resurse.
Pornirea resursei: ensure. Serverul pornește o resursă doar dacă este cerută în server.cfg. Adaugă o linie cu numele folderului:
ensure salut_fivemÎn fișierul server.cfg de exemplu din repository-ul DominosLand, resursele de bază se pornesc exact așa (ensure chat, ensure spawnmanager și așa mai departe), iar altele sunt lăsate comentate cu # până când le activezi.
- Copiază folderul salut_fivem în folderul resources al serverului.
- Adaugă ensure salut_fivem în server.cfg.
- Pornește serverul sau, dacă rulează deja, scrie în consolă refresh și apoi ensure salut_fivem. Prima citește din nou lista de resurse, a doua pornește (sau repornește) resursa.
- În consolă ar trebui să vezi [salut] resursa a pornit.
- Intră în joc, deschide chatul și scrie /salut. Apoi încearcă /pozitie.
Acest ghid nu a putut rula codul Lua în momentul scrierii. Scripturile folosesc doar funcții standard FiveM, dar verifică-le pe un server de test. Dacă scrii o greșeală, consola serverului sau F8 arată numele fișierului și linia.
Securitate: nu ai încredere în client
Orice eveniment declarat cu RegisterNetEvent poate fi chemat de un jucător rău intenționat, cu argumente inventate. Un client modificat poate trimite oricând evenimentul tău, de oricâte ori și cu orice valoare.
- Nu face evenimente de tipul „dă-mi 1000 de bani”. Clientul poate cere o acțiune, dar serverul decide dacă o permite.
- Verifică tipul și intervalul argumentelor pe server: tonumber, mai mare ca zero, mai mic ca un maximum rezonabil. Verificarea făcută doar în clientul tău nu ajută, pentru că poate fi ocolită.
- Verifică permisiunile pe server cu IsPlayerAceAllowed pentru comenzile de admin, nu doar cu un if pe client.
- Folosește source-ul oferit de FiveM, nu un ID trimis de client ca argument.
- Limitează cât de des poate fi chemat un eveniment (de exemplu o pauză de câteva secunde pe jucător).
RegisterNetEvent('salut:trimiteScor')
AddEventHandler('salut:trimiteScor', function(scor)
local src = source
scor = tonumber(scor)
if not scor or scor < 0 or scor > 100 then
print('[salut] scor invalid de la ' .. GetPlayerName(src))
return
end
print('[salut] ' .. GetPlayerName(src) .. ' are scorul ' .. scor)
end)Cum sunt organizate resursele de exemplu DominosLand
În repository-ul DominosLand există, în services/game-assets, foldere de resurse FiveM cu prefixul dominos-, de exemplu dominos-money, dominos-jobs și dominos-menus. Fiecare are fxmanifest.lua cu fx_version cerulean și game gta5.
Să luăm dominos-money. Manifestul încarcă config.lua ca shared_scripts (fișier citit atât de server, cât și de client), server/main.lua și server/admin.lua ca scripturi de server și client/main.lua ca script de client. Mai declară și exports, funcții pe care alte resurse le pot apela (getBalance, addMoney, transferMoney ș.a.).
- config.lua ține setările într-un tabel global Config, ca să le schimbi fără să umbli în cod.
- Evenimentele au prefix propriu, de exemplu dominos_money:updateBalance, exact cum ți-am recomandat mai sus.
- Comenzile pentru administratori din server/admin.lua verifică permisiunea cu IsPlayerAceAllowed înainte să facă ceva, iar argumentele sunt verificate cu tonumber și cu o comparație cu zero.
- Datele se salvează într-un fișier JSON din folderul resursei (data/balances.json), cu LoadResourceFile și SaveResourceFile.
- README.md-ul resursei explică comenzile jucătorilor și ale adminilor.
Citește aceste fișiere ca să vezi cum arată o resursă mai mare, dar nu le copia fără să înțelegi ce face fiecare linie.
Pe un server FiveM din GamePanel. GamePanel are FiveM în catalogul de jocuri pe care le poți instala printr-un nod (la crearea unui server alegi „Alt joc”). Consola live a panoului are pentru FiveM butoane rapide pentru status, refresh, ensure și restart, deci comenzile din acest ghid se pot scrie acolo.
Pagina Mods a panoului arată pentru FiveM că resursele merg în folderul resources, ca folder cu fxmanifest.lua. Managerul de fișiere al panoului îți permite să listezi, să citești, să salvezi, să ștergi și să descarci fișiere, deci poți edita server.cfg și fișierele .lua direct din panou. Pentru a urca un întreg folder de resursă pe server, folosește metoda pe care ți-o oferă găzduirea ta.
Nu pune niciodată parole sau chei într-un script de client: orice jucător le poate descărca. Cheile și parolele de bază de date țin-le doar în configurația serverului.
Pașii următori
- Salvează date pentru fiecare jucător într-un fișier JSON, ca în exemplul DominosLand.
- Învață exporturile (exports), ca resursele tale să poată folosi funcțiile unele altora.
- Citește documentația FiveM despre native și despre interfețe NUI (HTML în joc).
- Adaugă un meniu simplu și controlează-l cu RegisterKeyMapping.