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:
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:
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
-- client
TriggerServerEvent('my_script:server:buy', 'water', 2)-- 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
-- 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')-- 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ę.
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ą:
'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.
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 →