🔌 04 — Esport e Import
INFO
FiveM permette di esporre funzioni da una risorsa e usarle da altre tramite il sistema
exports. Questo meccanismo è fondamentale per creare librerie riutilizzabili e separare il codice in moduli indipendenti.
| Obiettivo | Dettagli |
|---|---|
| Cosa imparerai | - Esporre funzioni tra risorse con exports- Importare funzioni da altre risorse - Gestire versioning, dipendenze e async readiness |
| Prerequisiti | Completamento file 02 e 03, familiarità con risorse FiveM |
Condividere Funzioni tra Risorse
FiveM permette di esporre funzioni da una risorsa e usarle da altre tramite exports.
flowchart LR R1[Risorsa A\nutility] -->|Esporta| E[exports.utility.function()] R2[Risorsa B\nmain] -->|Importa| E R3[Risorsa C\nadmin] -->|Importa| E E -->|Esegue| F[Funzione in utility]
Esport
Per esporre funzioni da una risorsa, usa exports nel file server.lua o client.lua.
-- utility/server.lua
-- Questa funzione sarà accessibile da altre risorse
local function Saluta(nome)
return "Ciao " .. nome .. "!"
end
local function SommaNumeri(a, b)
return (a or 0) + (b or 0)
end
-- Esponi le funzioni
exports("Saluta", Saluta)
exports("SommaNumeri", SommaNumeri)-- utility/client.lua
local function GetPlayerStatus()
local ped = PlayerPedId()
local salute = GetEntityHealth(ped)
local maxSalute = GetEntityMaxHealth(ped)
local armatura = GetPedArmour(ped)
local pos = GetEntityCoords(ped)
return {
hp = salute,
maxHp = maxSalute,
armour = armatura,
inVehicle = IsPedInAnyVehicle(ped, false),
coords = pos
}
end
exports("GetPlayerStatus", GetPlayerStatus)Pattern con tabella di funzioni
-- utility/client.lua
local Utility = {}
function Utility.GetDistance(p1, p2)
return #(vector3(p1.x, p1.y, p1.z) - vector3(p2.x, p2.y, p2.z))
end
function Utility.Notifica(tipo, testo, durata)
durata = durata or 3000
SetNotificationTextEntry("STRING")
AddTextComponentSubstringPlayerName(testo)
if tipo == "error" then
DrawNotification(true, true)
else
DrawNotification(false, false)
end
end
function Utility.FormatDenaro(importo)
local formatted = tostring(math.floor(importo))
-- Inserisci punti ogni 3 cifre
local k = 1
while k < #formatted do
formatted = formatted:sub(1, -k - 3) .. "." .. formatted:sub(-k - 2)
k = k + 4
end
return "$" .. formatted
end
-- Esportazione iterativa (più pulita per molti exports)
for k, v in pairs(Utility) do
exports(k, v)
endImport
Per importare funzioni da un’altra risorsa, usa exports["nome_risorsa"].
-- main/server.lua
-- Chiamare funzione esportata da "utility"
local messaggio = exports["utility"]:Saluta("Marco")
print(messaggio) --> "Ciao Marco!"
-- Con parametri multipli
local somma = exports["utility"]:SommaNumeri(10, 20)
print(somma) --> 30-- main/client.lua
Citizen.CreateThread(function()
Citizen.Wait(2000) -- aspetta che utility sia pronta
-- Ottieni stato giocatore
local status = exports["utility"]:GetPlayerStatus()
print("HP:", status.hp, "Armatura:", status.armour)
print("In veicolo:", status.inVehicle)
-- Usa utility di distanza
local pos1 = vector3(0, 0, 0)
local pos2 = vector3(10, 10, 0)
local dist = exports["utility"]:GetDistance(pos1, pos2)
print("Distanza:", dist)
end)Sintassi alternativa con dot
-- Entrambi funzionano:
exports["utility"]:Saluta("Marco")
exports.utility.Saluta("Marco") -- equivalente se il nome risorsa non ha caratteri specialiEsempio Completo: Libreria di Utility
fxmanifest.lua
-- utility/fxmanifest.lua
fx_version "cerulean"
game "gta5"
client_scripts {
"client.lua"
}
server_scripts {
"server.lua"
}utility/client.lua
-- utility/client.lua
local Utility = {}
Utility.__version = "1.2.0"
function Utility.Notifica(messaggio, tipo)
tipo = tipo or "info"
-- Mappa colori per tipo
local colori = {
info = { 255, 255, 255 },
success = { 0, 255, 0 },
error = { 255, 0, 0 },
warning = { 255, 255, 0 }
}
local colore = colori[tipo] or colori.info
TriggerEvent("chat:addMessage", {
color = colore,
args = { "UTILITY", messaggio }
})
end
function Utility.DrawText3D(x, y, z, testo, colore, scala)
colore = colore or { 255, 255, 255, 215 }
scala = scala or 0.35
local onScreen, _x, _y = World3dToScreen2d(x, y, z)
if onScreen then
SetTextFont(4)
SetTextProportional(1)
SetTextScale(0.0, scala)
SetTextColour(colore[1], colore[2], colore[3], colore[4] or 215)
SetTextEntry("STRING")
SetTextCentre(true)
AddTextComponentString(testo)
DrawText(_x, _y)
end
end
function Utility.GetClientePronto()
return true
end
-- Esporta tutto
for k, v in pairs(Utility) do
exports(k, v)
end
print("^2[Utility] Libreria caricata (versione " .. Utility.__version .. ")")utility/server.lua
-- utility/server.lua
local Utility = {}
function Utility.Log(messaggio, livello)
livello = livello or "info"
local prefix = {
info = "^3[INFO]",
warn = "^8[WARN]",
error = "^1[ERROR]",
debug = "^5[DEBUG]"
}
print((prefix[livello] or "^3[INFO]") .. " " .. messaggio .. "^7")
end
function Utility.GeneraID()
-- Genera ID alfanumerico semplice
local chars = "abcdefghijklmnopqrstuvwxyz0123456789"
local id = ""
for i = 1, 8 do
id = id .. chars:sub(math.random(1, #chars), math.random(1, #chars))
end
return id
end
function Utility.GetOnlinePlayers()
local players = {}
for _, playerId in ipairs(GetPlayers()) do
table.insert(players, {
id = playerId,
name = GetPlayerName(playerId),
ping = GetPlayerPing(playerId)
})
end
return players
end
for k, v in pairs(Utility) do
exports(k, v)
end
print("^2[Utility Server] Libreria caricata^7")Usare la libreria da un’altra risorsa
-- main/client.lua
Citizen.CreateThread(function()
-- Aspetta che la risorsa utility sia pronta
local pronto = false
while not pronto do
Citizen.Wait(100)
local successo, risultato = pcall(function()
return exports["utility"]:GetClientePronto()
end)
pronto = successo and risultato
end
print("Utility disponibile!")
exports["utility"]:Notifica("Utility collegata!", "success")
end)
RegisterCommand("distance", function(source, args)
if #args ~= 2 then
exports["utility"]:Notifica("Uso: /distance [id1] [id2]", "warning")
return
end
local id1 = tonumber(args[1])
local id2 = tonumber(args[2])
-- Calcola distanza tra due giocatori
local ped1 = GetPlayerPed(id1)
local ped2 = GetPlayerPed(id2)
if ped1 == 0 or ped2 == 0 then
exports["utility"]:Notifica("Giocatore non trovato", "error")
return
end
local coords1 = GetEntityCoords(ped1)
local coords2 = GetEntityCoords(ped2)
local dist = #(vector3(coords1.x, coords1.y, coords1.z) -
vector3(coords2.x, coords2.y, coords2.z))
exports["utility"]:Notifica(
string.format("Distanza: %.1f metri", dist),
"info"
)
end, false)QUESTION
Riflessione: La tua risorsa esporta funzioni con nomi abbastanza specifici da evitare collisioni? Hai considerato l’ordine di avvio e le dipendenze?
Best Practices
Namespace (prefisso)
TIP
Usa sempre un prefisso per i tuoi export. Nomi generici come
GetDataoSaveDatacollidono facilmente con altre risorse. Prefissali col nome della tua risorsa.
Usa il nome della tua risorsa o un prefisso per evitare collisioni:
-- Male: collisione probabile con altre risorse
exports("GetData", GetData)
exports("SaveData", SaveData)
-- Bene: namespaced
exports("myUtils_GetData", GetData)
exports("myUtils_SaveData", SaveData)Versioning
Se la tua libreria cambia, documenta la versione:
-- utility/client.lua
local Utility = {}
Utility.__version = "2.0.0"
Utility.__compat = { "1.x" } -- compatibilità con versioni precedenti
function Utility.__checkVersion(richiesta)
local major = tonumber(richiesta:match("^(%d+)"))
local ourMajor = tonumber(Utility.__version:match("^(%d+)"))
return major == ourMajor
end
exports("__checkVersion", Utility.__checkVersion)
exports("__version", Utility.__version)
-- main/client.lua
if exports["utility"]:__checkVersion("2.x") then
print("Versione compatibile!")
else
print("ATTENZIONE: utility versione " .. exports["utility"]:__version() .. " richiede aggiornamento")
endAsync readiness
WARNING
Le risorse non si avviano in ordine garantito. Se una risorsa chiama
exportssu un’altra non ancora avviata, ottieniattempt to call a nil value. Usa sempre un loop di attesa conpcallprima di usare un export.
Le risorse non si avviano in ordine garantito. Aspetta sempre che l’esport sia pronto:
-- main/client.lua
function AttendiEsport(nomeRisorsa, nomeExport, timeout)
timeout = timeout or 10000
local start = GetGameTimer()
while GetGameTimer() - start < timeout do
local successo, risultato = pcall(function()
return exports[nomeRisorsa][nomeExport](exports[nomeRisorsa])
end)
if successo and risultato ~= nil then
return true
end
Citizen.Wait(100)
end
print("^1ERRORE: Export " .. nomeExport .. " da " .. nomeRisorsa .. " non disponibile^7")
return false
end
-- Uso
Citizen.CreateThread(function()
if AttendiEsport("utility", "GetClientePronto") then
exports["utility"]:Notifica("Tutto pronto!", "success")
end
end)Ordine fxmanifest
Il fxmanifest.lua deve dichiarare le dipendenze:
-- main/fxmanifest.lua
fx_version "cerulean"
game "gta5"
-- utility deve essere avviata PRIMA di main
dependencies {
"utility"
}
client_scripts {
"client.lua"
}
server_scripts {
"server.lua"
}Esport vs Altri Metodi
| Metodo | Scopo | Latenza | Sicurezza |
|---|---|---|---|
exports | Condividere funzioni tra risorse | Bassa | Nome risorsa come namespace |
TriggerEvent / TriggerServerEvent | Comunicazione event-driven | Media (rete) | Filtrabile per risorsa |
Global table (es. ESX = ESX or {}) | Condivisione globale | Bassa | Rischio collisioni |
GetResourceState / polling | Stato risorsa | Alta (polling) | Affidabile |
flowchart TD A[Devo condividere codice tra risorse?] A -->|Sì, funzioni specifiche| B[Usa exports] A -->|Sì, dati/eventi| C[Usa TriggerEvent] A -->|Condivisione globale framework| D[Usa tabella globale] A -->|No| E[Tutto nella stessa risorsa] B --> F[Meglio: namespaced + versionato]
Troubleshooting
| Problema | Causa | Soluzione |
|---|---|---|
attempt to call a nil value | Risorsa esport non avviata | Aggiungi dependencies o attendi col loop |
exports["x"]:y() is not a function | Nome export errato | Verifica spelling, check exports("nome", fn) |
| Export funziona solo intermittente | Race condition all’avvio | Usa loop di attesa con pcall |
| Conflitto tra risorse | Stesso nome export | Usa prefisso unico |
TIP
Prossimo passo: 05 - Config e Database
Torna alla: Indice Generale