> ## Documentation Index
> Fetch the complete documentation index at: https://cs-lua.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# hook — Подключение

> Порядок: `client:connect` → `client:authorized` → `client:connected`

# Подключение

Порядок: `client:connect` → `client:authorized` → `client:connected`.

<h2 id="connect">
  client:connect
</h2>

Игрок стучится на сервер; его ещё можно не пустить.

```lua theme={null}
hook.add("client:connect", id, function(e)
	...
end)
```

<h3 id="connect-поля-события">
  Поля события
</h3>

| поле       | тип    |                                            |
| ---------- | ------ | ------------------------------------------ |
| `e.player` | player | кого касается событие                      |
| `e.name`   | string | ник                                        |
| `e.ip`     | string | адрес                                      |
| `e.reason` | string | запись: текст отказа, который увидит игрок |

### Пример

```lua theme={null}
hook.add("client:connect", "bans.check", function(e)
	if banned[e.ip] then
		e.reason = "Вы забанены"
		e:cancel()
	end
end)
```

**Отмена.** `e:cancel()` не пускает игрока. Без `e.reason` он увидит общую фразу.

<Warning>
  Здесь `e.player:steamid()` возвращает `STEAM_ID_PENDING`, а
  сообщения не доходят. Права и статистику вешай на
  `client:authorized`, приветствия — на `client:connected`.
</Warning>

<h2 id="disconnect">
  client:disconnect
</h2>

Игрок отключился — уже был в игре, или его коннект отклонил Lua-плагин через
`client:connect`.

```lua theme={null}
hook.add("client:disconnect", id, function(e)
	...
end)
```

<h3 id="disconnect-поля-события">
  Поля события
</h3>

| поле       | тип     |                                                                                                      |
| ---------- | ------- | ---------------------------------------------------------------------------------------------------- |
| `e.player` | player  | кого касается событие                                                                                |
| `e.name`   | string  | ник — сам объект уже пустеет                                                                         |
| `e.reason` | string  | причина отключения, см. ниже                                                                         |
| `e.forced` | boolean | `true` — событие синтезировал модуль (движок его не вызывал), `false` — настоящий `ClientDisconnect` |

Место, где чистят своё состояние по `e.player.id`.

<Warning>
  `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`, если логика зависит от того, был ли игрок реально в
  игре.
</Warning>

<h2 id="authorized">
  client:authorized
</h2>

Steam ответил, steamid наконец известен.

```lua theme={null}
hook.add("client:authorized", id, function(e)
	...
end)
```

<h3 id="authorized-поля-события">
  Поля события
</h3>

| поле        | тип    |                       |
| ----------- | ------ | --------------------- |
| `e.player`  | player | кого касается событие |
| `e.steamid` | string | настоящий authid      |

Срабатывает один раз за подключение. Всё, что завязано на steamid — права, статистика — начинается здесь.

<h2 id="connected">
  client:connected
</h2>

Игрок в игре, сообщения до него доходят.

```lua theme={null}
hook.add("client:connected", id, function(e)
	...
end)
```

<h3 id="connected-поля-события">
  Поля события
</h3>

| поле       | тип    |                       |
| ---------- | ------ | --------------------- |
| `e.player` | player | кого касается событие |

Место для приветствий и первого HUD.

<h2 id="chat">
  player:chat
</h2>

Игрок написал в чат.

```lua theme={null}
hook.add("player:chat", id, function(e)
	...
end)
```

<h3 id="chat-поля-события">
  Поля события
</h3>

| поле       | тип     |                             |
| ---------- | ------- | --------------------------- |
| `e.player` | player  | кого касается событие       |
| `e.text`   | string  | что он написал              |
| `e.team`   | boolean | сообщение ушло в `say_team` |

### Пример

```lua theme={null}
hook.add("player:chat", "myplugin.mute", function(e)
	if muted[e.player.id] then
		e.player:chat("Ты в муте")
		e:cancel()
	end
end)
```

**Отмена.** `e:cancel()` проглатывает сообщение — оно не доходит ни до кого. Так работает `!команда`, и так же чат-менеджер подменяет строку: отменить и разослать свою.

<h2 id="select">
  menu:select
</h2>

Игрок нажал клавишу в меню, открытом из Lua.

```lua theme={null}
hook.add("menu:select", id, function(e)
	...
end)
```

<h3 id="select-поля-события">
  Поля события
</h3>

| поле       | тип    |                       |
| ---------- | ------ | --------------------- |
| `e.player` | player | кого касается событие |
| `e.key`    | number | номер клавиши, 1..10  |

Обычно не нужно: [`menu`](../menu/index.md) разбирает это сам.
