hook.add("client:connect", "bans.check", function(e) if banned[e.ip] then e.reason = "Вы забанены" e:cancel() endend)
Отмена.e:cancel() не пускает игрока. Без e.reason он увидит общую фразу.
Здесь e.player:steamid() возвращает STEAM_ID_PENDING, а
сообщения не доходят. Права и статистику вешай на
client:authorized, приветствия — на client:connected.
true — событие синтезировал модуль (движок его не вызывал), false — настоящий ClientDisconnect
Место, где чистят своё состояние по e.player.id.
e.reason и e.forced приходят из двух разных источников в зависимости от
того, что случилось:
Игрок был в игре и отвалился (кик, бан, таймаут, disconnect в
консоли) — e.forced == false. Причина от движка через ReHLDS-хук
SV_DropClient. Требует ReHLDS: на ванильном HLDS e.reason здесь пустая
строка. Формат — как в логах сервера: "Kicked", "Disconnect by user",
"Timed out" — движок не даёт фиксированный список кодов, только строку.
Коннект отклонил Lua-плагин через client:connect + e:cancel() —
e.forced == true. Причина всегда есть: это тот же текст, что увидел
отказанный игрок (e.reason, выставленный в client:connect, либо
дефолтная фраза, если плагин его не задал). Движок в этом случае
ClientDisconnect вообще не вызывает, client:disconnect подставляется
вместо него, чтобы игрок не “пропадал” молча для тех, кто слушает только
это событие.
Проверяй e.forced, если логика зависит от того, был ли игрок реально в
игре.
hook.add("player:chat", "myplugin.mute", function(e) if muted[e.player.id] then e.player:chat("Ты в муте") e:cancel() endend)
Отмена.e:cancel() проглатывает сообщение — оно не доходит ни до кого. Так работает !команда, и так же чат-менеджер подменяет строку: отменить и разослать свою.