FiveM playerConnecting i deferrals: defer, update, done wyjaśnione
Jak AddEventHandler('playerConnecting') działa z deferal: defer, Wait(0), update, done(reason), karty adaptacyjne, i dlaczego gracze zawieszają się, gdy zapomnisz wezwać done.
Chcesz uruchomić coś, zanim gracz będzie dozwolony: sprawdzenie białej listy, listę banonów, wyszukiwanie w bazie danych, kartę powitalną. Miejsce na to to zdarzenie playerConnecting, a narzędziem są deferrals. Jeśli użyte źle, gracze siedzą na ekranie łączenia na zawsze, więc szczegóły mają znaczenie.
AddEventHandler('playerConnecting', function(name, setKickReason, deferrals)
-- runs when a player starts connecting
end)Jakie są argumenty
name: nazwa gracza.setKickReason(reason): funkcja, która odrzuca połączenie wiadomością. Istnieje dla prostych, natychmiastowych odrzuceń.deferrals: obiekt z funkcjami, które pozwalają ci wstrzymać połączenie i rozmawiać z graczem podczas pracy.
Wewnątrz handlera source jest identyfikatorem połączenia gracza.
Cztery wezwania deferral
AddEventHandler('playerConnecting', function(name, setKickReason, deferrals)
local src = source -- save it now
deferrals.defer() -- 1. pause the connection
Wait(0) -- 2. let the engine register it
deferrals.update('Checking your account...') -- 3. show a message
-- do your checks here
deferrals.done() -- 4. let the player in
end)deferrals.defer()mówi FiveM, aby trzymał gracza, aż się zdecydujesz. Bez tego gracz jest wpuszczany, gdy tylko twój handler się zwróci, nawet jeśli zacząłeś zadanie asynchroniczne.Wait(0)jest potrzebne tuż podefer(). Wezwania doupdate,donelubpresentCardw tym samym takcie codefermogą nie działać.deferrals.update(message)zastępuje tekst, który gracz widzi na ekranie łączenia. Użyj go, aby pokazać postęp.deferrals.done(reason)kończy czekanie. Brak argumentu oznacza, że gracz jest akceptowany. Ciąg znaków oznacza, że są odrzuceni i widzą ten ciąg.
Wskazówka: skopiuj
sourcedo zmiennej lokalnej na samym początek. PoWaitlub żądaniu HTTP, globalnysourcemoże wskazywać na innego gracza.
Odrzucenie gracza
AddEventHandler('playerConnecting', function(name, setKickReason, deferrals)
local src = source
deferrals.defer()
Wait(0)
local license
for _, id in ipairs(GetPlayerIdentifiers(src)) do
if id:sub(1, 8) == 'license:' then license = id break end
end
if not license then
return deferrals.done('No Rockstar license found. Restart the game and try again.')
end
deferrals.done()
end)return deferrals.done(...) kończy handler w tym samym czasie, co uniemożliwia ci wywołanie done dwa razy.
Sprawdzenia asynchroniczne
Odroczenia są przeznaczone do pracy, która zajmuje czas: zapytanie bazy danych lub wywołanie HTTP. Utrzymuj połączenie odroczone i wezwij done w callbacku.
AddEventHandler('playerConnecting', function(name, setKickReason, deferrals)
local src = source
deferrals.defer()
Wait(0)
deferrals.update('Looking you up...')
local license = GetPlayerIdentifierByType(src, 'license')
MySQL.scalar('SELECT 1 FROM whitelist WHERE license = ?', { license }, function(found)
if found then
deferrals.done()
else
deferrals.done('You are not whitelisted.')
end
end)
end)GetPlayerIdentifierByType istnieje w obecnych kompilacjach serwera. Jeśli twój artefakt jest stary, zamiast tego pętluj po GetPlayerIdentifiers, jak powyżej. Aby uzyskać składnię zapytania, zobacz przewodnik oxmysql. Pełny przykład z rolami Discord znajduje się w Discord whitelist dla FiveM.
Typowy błąd: zapomnienie done
Gracze utkwieci na ekranie łączenia prawie zawsze oznaczają, że jedna ścieżka kodu nigdy nie osiąga deferrals.done.
-- Bad: nothing happens when the query returns no row
MySQL.single('SELECT * FROM bans WHERE license = ?', { license }, function(row)
if row then
deferrals.done('You are banned.')
end
end)Jeśli row to nil, gracz jest trzymany na zawsze. Rozwiązaniem jest else:
MySQL.single('SELECT * FROM bans WHERE license = ?', { license }, function(row)
if row then
deferrals.done('You are banned.')
else
deferrals.done()
end
end)Trzy inne sposoby, aby się zaciąć:
- Błąd skryptu wewnątrz handlera przed
done. Handler się zatrzymuje, a gracz czeka. Przeczytaj konsolę. Zobacz czytanie błędu skryptu. - Wywołanie HTTP, które nigdy nie odpowiada. Dodaj limit czasu, na przykład śledząc czas startu i kończąc wiadomością, jeśli nic nie wróciło.
- Dwukrotne wezwanie
donelub wezwanieupdatepodone. Użyjreturn, aby opuścić handler po każdym zakończeniu.
Kilka handlerów naraz
Wiele zasobów słucha playerConnecting, a każdy, który wezwie defer, dodaje do czekania. Gracz jest wpuszczony tylko wtedy, gdy wszystkie się skończą. Powolny lub uszkodzony handler w jednym skrypcie trzyma wszystkich w dół, więc najpierw testuj tylko ten zasób, gdy połączenia się zawieszają.
Karty adaptacyjne
deferrals.presentCard pokazuje formularz lub ekran zasad z Adaptive Card, które jest układem JSON. Gracz może nacisnąć przycisk, a twój callback otrzymuje wynik.
local card = {
type = 'AdaptiveCard',
version = '1.3',
body = {
{ type = 'TextBlock', text = 'Server rules', weight = 'Bolder', size = 'Large' },
{ type = 'TextBlock', text = 'Be respectful and no cheating.', wrap = true },
},
actions = {
{ type = 'Action.Submit', title = 'I agree', data = { accepted = true } },
},
}
AddEventHandler('playerConnecting', function(name, setKickReason, deferrals)
deferrals.defer()
Wait(0)
deferrals.presentCard(json.encode(card), function(data)
if data and data.accepted then
deferrals.done()
else
deferrals.done('You must accept the rules.')
end
end)
end)Utrzymuj karty w prostocie: tekst, obraz i jeden lub dwa przyciski. Callback nadal musi się zakończyć w done.
Lista kontrolna
| Symptom | Naprawa |
|---|---|
| Gracz utkwiłem na łączeniu | Jedna ścieżka nigdy nie wezwie deferrals.done; dodaj brakujący else |
update lub done nie ma efektu |
Wezwij Wait(0) tuż po deferrals.defer() |
| Zły gracz dotkniętym | Najpierw zapisz local src = source |
| Gracz wpuszczony przed końcem sprawdzenia | Zapomnisz deferrals.defer() |
| Odrzucenie z wiadomością | deferrals.done('reason') |
| Zaakceptuj gracza | deferrals.done() bez argumentu |
| Zawieszenie tylko z wieloma skryptami | Handler playerConnecting innego zasobu jest powolny lub uszkodzony |
Szybkie odpowiedzi
Dlaczego potrzebuję Wait(0) po deferrals.defer()?
Odroczenie jest aktywne dopiero po tym, jak silnik przesunął się o jeden takt. Wywołanie deferrals.update lub deferrals.done w tym samym takcie może być zignorowane, dlatego najpierw ustąp raz za pomocą Wait(0).
Jak odrzucić gracza?
Wywołaj deferrals.done('twój powód') z ciągiem znaków. Gracz widzi tekst i nie może wejść. Wywołanie deferrals.done() bez argumentu pozwala mu na połączenie.
Co się dzieje, jeśli nigdy nie wezwę deferrals.done?
Gracz zostaje na ekranie łączenia, dopóki połączenie nie przekroczy limitu czasu. Każda ścieżka kodu w twoim handlerze musi się zakończyć w deferrals.done.

