Zdarzenia FiveM: RegisterNetEvent, TriggerServerEvent, TriggerClientEvent

Jak działają zdarzenia klienta i serwera FiveM: RegisterNetEvent, AddEventHandler, TriggerServerEvent, TriggerClientEvent z -1, zmienna source i nazewnictwo zdarzeń.

Większość bugów skryptów między grą a serwerem sprowadza się do zdarzeń: handler, który nigdy się nie uruchomia, zdarzenie wysłane do nikogo, lub source, który wskazuje na złego gracza.

Ten artykuł wyjaśnia, jak działają zdarzenia sieciowe, jak je rejestrować i wyzwalać w obu kierunkach, i jak je nazewać, aby dwa skrypty nigdy się nie zderzyły.

Obie strony

Zasób FiveM może mieć kod na kliencie (jedna kopia na gracza) i na serwerze (jedna kopia). Rozmawiam przez zdarzenia:

Z Do Funkcja
Klient Serwer TriggerServerEvent
Serwer Jeden klient TriggerClientEvent(name, playerId, ...)
Serwer Wszyscy klienci TriggerClientEvent(name, -1, ...)
Ta sama strona Ta sama strona TriggerEvent

Argumenty są serializowane, więc możesz wysyłać liczby, ciągi, wartości logiczne i tabele. Nie możesz wysyłać funkcji. Uchwyty encji są lokalne dla każdej maszyny, więc wysyłaj zamiast tego identyfikator sieci (NetworkGetNetworkIdFromEntity).

Rejestrowanie handlera

Handler to funkcja, która uruchamia się, gdy zdarzenie dotrze. Dla zdarzenia, które pochodzi z sieci, musisz również je oznaczyć jako dozwolone:

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

To jest krótka forma: RegisterNetEvent z funkcją jako drugi argument rejestruje zdarzenie i dołącza handler. Długa forma robi to samo w dwóch krokach:

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

Jeśli używasz tylko AddEventHandler, handler działa dla lokalnych wezwań TriggerEvent, ale TriggerServerEvent lub TriggerClientEvent z drugiej strony jest ignorowany. To jest najczęstszy powód zdarzenia, które nic nie robi.

Klient do serwera

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)

Na serwerze, globala source trzyma id gracza, który wyzwolił zdarzenie. Klient go nie wysyła, więc nie może być sfałszowany przez klienta zmieniającego jego argumenty.

Serwer do klienta

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)

Drugi argument TriggerClientEvent jest zawsze celem. Jeśli go zapomnisz, twój pierwszy argument danych jest odczytywany jako id gracza i zdarzenie idzie donikąd.

Zapisz source przed Wait

source to nie wartość, którą posiadasz, to globala, którą runtime ustawia dla zdarzenia, które działa. Jeśli twój handler wezwie Wait, lub wezwie coś, co ma opóźnienie, takie jak MySQL.query.await, inne zdarzenie może uruchomić się tymczasem i source zmienia się.

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)

Zrób local src = source pierwszym wierszem każdego handlera serwera i używaj src po tym. To samo dotyczy wewnątrz callback, którą przekazujesz innej funkcji.

Nazewnictwo zdarzeń

Zdarzenia są globalne dla całego serwera: każdy zasób może wyzwolić każdą nazwę zdarzenia. Jeśli dwa skrypty rejestrują buy lub server:buy, oba handlowcy uruchamiają się, a gracz może je wyzwolić po nazwie.

Poprzedzaj każde zdarzenie zasobem i stroną:

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

Nie ponownie używaj nazw zdarzeń innych skryptów i nie nazwenictwo twojego zdarzenia coś ogólnego, takiego jak giveMoney. Czytelna nazwa także sprawia, że oczywiste jest w konsoli, który skrypt wysłał co.

Nigdy nie ufaj klientowi

Coś, co przechodzi przez TriggerServerEvent może być uruchomiane przez oszusta z dowolnymi argumentami, w dowolnym momencie. Sprawdzaj na serwerze:

  • czy gracz ma prawo to robić (praca, dystans, cooldown),
  • że kwoty i nazwy przedmiotów są ważne,
  • że nigdy nie bierz ceny lub nagrody z argumentów.
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)

Pełna lista znajduje się w securing server events. Dla wyniku, który musi wrócić do dzwoniącego, callback jest czystszy niż dwa zdarzenia: patrz server callbacks on ESX, QBCore and ox_lib.

Duże lub częste zdarzenia

Każde zdarzenie to wiadomość sieciowa. Nie wyzwalaj jednego każdej klatki i nie wysyłaj bardzo dużej tabeli do każdego gracza. Jeśli konsola serwera drukuje informacje o przepełnieniu niezawodnych zdarzeń, wysyłasz zbyt wiele, zbyt szybko: patrz reliable network event overflow.

Lista kontrolna

Objaw Naprawa
Handler nigdy się nie uruchomia z drugiej strony Używaj RegisterNetEvent, nie tylko AddEventHandler
TriggerClientEvent nie dociera do nikogo Podaj id gracza, lub -1, jako drugi argument
source to nil lub zły gracz local src = source jako pierwsza linia, przed każdym Wait
Dwa skrypty reagują na to samo zdarzenie Poprzedzaj nazwy zasobem: my_script:server:buy
Klient może dać sobie przedmioty lub pieniądze Waliduj na serwerze i szukaj tam ceny
Spam zdarzeń w konsoli lub lag Wysyłaj mniej, rzadziej: patrz reliable network event overflow

Szybkie odpowiedzi

Jaka jest różnica między RegisterNetEvent i AddEventHandler?

RegisterNetEvent pozwala zdarzeniu być wyzwolonym z drugiej strony sieci. AddEventHandler tylko przywiązuje funkcję. Handler dla zdarzenia sieciowego potrzebuje obu, lub RegisterNetEvent z funkcją jako jego drugi argument.

Jak wysłać zdarzenie do każdego gracza?

Na serwerze, TriggerClientEvent('my_script:client:thing', -1, data). -1 oznacza wszystkich podłączonych klientów.

Dlaczego source jest zły w moim zdarzeniu serwera?

source to globala, która trzyma gracza, który wyzwolił bieżące zdarzenie. Jeśli wezwiesz Wait i inne zdarzenie uruchomi się tymczasem, zmienia się. Najpierw skopiuj go do lokalnego: local src = source.

Skrypty bez tego problemu

Advanced BoostingBoosting pojazdów z tabletu: kontrakty od klasy D do S+, ekipy i kolejka na żywo.Zobacz skrypt →Drug Dealer AppUliczna sprzedaż jako aplikacja lb-phone: strefy, kupcy NPC, poziomy i alerty dla policji.Zobacz skrypt →Shop CreatorZbuduj sklep w niecałą minutę — właściciele, pracownicy, sejfy i napady w zestawie.Zobacz skrypt →

Czytaj dalej