Eventi FiveM: RegisterNetEvent, TriggerServerEvent, TriggerClientEvent

Come funzionano gli eventi client e server di FiveM: RegisterNetEvent, AddEventHandler, TriggerServerEvent, TriggerClientEvent con -1, la variabile source e la denominazione degli eventi.

La maggior parte dei bug degli script tra il gioco e il server si riducono agli eventi: un handler che non viene mai eseguito, un evento inviato a nessuno, o un source che punta al giocatore sbagliato.

Questo articolo spiega come funzionano gli eventi di rete, come registrarli e attivarli in entrambe le direzioni, e come nominarli in modo che due script non collidano mai.

I due lati

Una risorsa FiveM può avere codice su client (una copia per giocatore) e sul server (una copia). Comunicano attraverso eventi:

Da A Funzione
Client Server TriggerServerEvent
Server Un client TriggerClientEvent(name, playerId, ...)
Server Tutti i client TriggerClientEvent(name, -1, ...)
Stesso lato Stesso lato TriggerEvent

Gli argomenti sono serializzati, quindi puoi inviare numeri, stringhe, booleani e tabelle. Non puoi inviare funzioni. Gli handle delle entità sono locali per ogni macchina, quindi invia invece un id di rete (NetworkGetNetworkIdFromEntity).

Registrazione di un handler

Un handler è una funzione che viene eseguita quando l'evento arriva. Per un evento che proviene dalla rete devi anche marcarlo come consentito:

lua
RegisterNetEvent('my_script:client:notify', function(message)
    print(message)
end)

Questo è il modulo breve: RegisterNetEvent con la funzione come secondo argomento registra l'evento e allega il handler. Il modulo lungo fa lo stesso in due passaggi:

lua
RegisterNetEvent('my_script:client:notify')
AddEventHandler('my_script:client:notify', function(message)
    print(message)
end)

Se usi solo AddEventHandler, il handler funziona per le chiamate TriggerEvent locali, ma un TriggerServerEvent o TriggerClientEvent dall'altro lato viene ignorato. Questa è la ragione più comune per un evento che non fa nulla.

Client a server

lua
-- client
TriggerServerEvent('my_script:server:buy', 'water', 2)
lua
-- server
RegisterNetEvent('my_script:server:buy', function(item, amount)
    local src = source
    print(('player %s wants %s x %s'):format(src, amount, item))
end)

Sul server, il globale source contiene l'id del giocatore che ha attivato l'evento. Il client non lo invia, quindi non può essere falsificato da un client che cambia i suoi argomenti.

Server a client

lua
-- server: to one player
TriggerClientEvent('my_script:client:notify', src, 'Purchase done')

-- server: to everyone
TriggerClientEvent('my_script:client:notify', -1, 'Server restart in 5 minutes')
lua
-- client
RegisterNetEvent('my_script:client:notify', function(message)
    print(message)
end)

Il secondo argomento di TriggerClientEvent è sempre il bersaglio. Se lo dimentichi, il tuo primo argomento di dati viene letto come id del giocatore e l'evento non va da nessuna parte.

Salva source prima di Wait

source non è un valore che possiedi, è un globale che il runtime imposta per l'evento che sta girando. Se il tuo handler chiama Wait, o chiama qualcosa che cede come MySQL.query.await, un altro evento potrebbe essere eseguito nel frattempo e source cambia.

lua
RegisterNetEvent('my_script:server:buy', function(item, amount)
    local src = source            -- copy it first

    Wait(500)                     -- or any await
    print(src)                    -- still the right player
    print(source)                 -- may be someone else now
end)

Fai local src = source la prima riga di ogni handler del server e usa src dopo. Lo stesso vale dentro un callback che passi a un'altra funzione.

Denominazione degli eventi

Gli eventi sono globali a tutto il server: qualsiasi risorsa può attivare qualsiasi nome di evento. Se due script registrano entrambi buy o server:buy, entrambi gli handler vengono eseguiti, e un giocatore può attivarli per nome.

Anteponi ogni evento con la risorsa e il lato:

lua
'my_script:server:buy'
'my_script:client:updateJob'

Non riutilizzare i nomi degli eventi di altri script, e non denominare il tuo evento qualcosa di generico come giveMoney. Un nome leggibile rende anche ovvio nella console quale script ha inviato cosa.

Non fidarti mai del client

Qualsiasi cosa arrivi attraverso TriggerServerEvent può essere attivata da un cheater con qualsiasi argomento, in qualsiasi momento. Controlla sul server:

  • che il giocatore sia autorizzato a farlo (lavoro, distanza, cooldown),
  • che gli importi e i nomi degli oggetti siano validi,
  • che tu non prenda mai il prezzo o la ricompensa dagli argomenti.
lua
RegisterNetEvent('my_script:server:buy', function(item, amount)
    local src = source
    if type(amount) ~= 'number' or amount < 1 or amount > 10 then return end
    -- look the price up on the server, do not read it from the client
end)

L'elenco completo è in protezione degli eventi del server. Per un risultato che deve tornare al chiamante, un callback è più pulito di due eventi: vedi callback del server su ESX, QBCore e ox_lib.

Eventi grandi o frequenti

Ogni evento è un messaggio di rete. Non attivarne uno ad ogni frame, e non inviare una tabella molto grande a ogni giocatore. Se la console del server stampa informazioni sugli eventi affidabili che traboccano, stai inviando troppo, troppo velocemente: vedi overflow di evento di rete affidabile.

Checklist

Sintomo Soluzione
L'handler non viene mai eseguito dall'altro lato Usa RegisterNetEvent, non solo AddEventHandler
TriggerClientEvent non raggiunge nessuno Passa un id del giocatore, o -1, come secondo argomento
source è nil o il giocatore sbagliato local src = source come prima riga, prima di qualsiasi Wait
Due script reagiscono allo stesso evento Anteponi i nomi con la risorsa: my_script:server:buy
Il client può darsi oggetti o soldi Convalida sul server e cerca lì i prezzi
Spam di eventi nella console o lag Invia meno, meno spesso: vedi overflow di evento di rete affidabile

Risposte rapide

Qual è la differenza tra RegisterNetEvent e AddEventHandler?

RegisterNetEvent permette all'evento di essere attivato dall'altro lato della rete. AddEventHandler allega solo una funzione. Un handler per un evento di rete ha bisogno di entrambi, o RegisterNetEvent con la funzione come secondo argomento.

Come invio un evento a ogni giocatore?

Sul server, TriggerClientEvent('my_script:client:thing', -1, data). -1 significa tutti i client connessi.

Perché source è sbagliato nel mio evento server?

source è un globale che contiene il giocatore che ha attivato l'evento corrente. Se chiami Wait e un altro evento funziona nel frattempo, cambia. Copialo in una variabile locale prima: local src = source.

Script senza questo problema

Advanced BoostingBoosting di veicoli dal tablet: contratti dalla classe D alla S+, crew e coda in tempo reale.Vedi script →Drug Dealer AppSpaccio di strada come app per lb-phone: zone, acquirenti NPC, livelli e allerte alla polizia.Vedi script →Shop CreatorCrea un negozio in meno di un minuto — proprietari, dipendenti, casseforti e rapine inclusi.Vedi script →

Continua a leggere