WebSocket - это протокол для двусторонней связи между сервером и клиентом в реальном времени. В отличие от стандартного механизма Topic в BYOND, WebSocket позволяет:
- Мгновенно отправлять сообщения в обе стороны
- Держать постоянное соединение вместо постоянных запросов
- Снизить нагрузку на сервер за счёт отсутствия HTTP-оверхеда
- Улучшить отзывчивость интерфейсов TGUI
Механизм Topic в BYOND работает через HTTP-подобные запросы. Каждый клик в TGUI - это новый запрос. С WebSocket соединение устанавливается один раз, и дальше данные летают туда-сюда без задержек.
graph LR
subgraph Клиент
Browser[Browser / TGUI]
end
subgraph BYOND Сервер
SSws[SSws подсистема]
Native[Нативная библиотека libz]
SSws --> Native
end
Browser <-->|WebSocket| Native
- SSws - подсистема DM, управляет сервером
- Нативная библиотека (libz.dll / liblibz.so) - обрабатывает низкоуровневую работу с сокетами
- TGUI Window - привязывается к соединению для получения сообщений
Все параметры задаются в TOML-конфиге в секции [ws]:
| Параметр | Тип | По умолчанию | Описание |
|---|---|---|---|
secure |
bool | false | Использовать WSS (требует прокси) |
host |
string | - | Публичный адрес для WSS (например, "ss13.ru:3508") |
port |
number | 0 | Порт сервера. 0 = автовыбор |
| Параметр | По умолчанию | Описание |
|---|---|---|
max_connections |
256 | Максимум одновременных соединений |
max_connections_per_ip |
5 | Соединений с одного IP (0 = без лимита) |
| Параметр | По умолчанию | Описание |
|---|---|---|
handshake_timeout_ms |
5000 | Время на рукопожатие WebSocket |
idle_timeout_ms |
300000 | Таймаут неактивности (5 минут) |
ping_interval_ms |
30000 | Интервал отправки ping |
pong_timeout_ms |
10000 | Ожидание pong после ping |
initial_message_timeout_ms |
1000 | Время на первое сообщение |
afk_timeout_ms |
0 | AFK-таймаут (0 = выключен) |
| Параметр | По умолчанию | Описание |
|---|---|---|
max_message_size |
1048576 | Макс. размер сообщения (1 МБ) |
max_frame_size |
1048576 | Макс. размер фрейма (1 МБ) |
max_handshake_size |
8192 | Макс. размер HTTP-хендшейка |
| Параметр | По умолчанию | Описание |
|---|---|---|
rate_limit_messages_per_sec |
25 | Сообщений в секунду |
rate_limit_bytes_per_sec |
1048576 | Байт в секунду |
| Параметр | По умолчанию | Описание |
|---|---|---|
log |
false | Логировать все сообщения |
sequenceDiagram
participant C as Клиент
participant S as Сервер
C->>S: TCP Connect
C->>S: WebSocket Handshake
S-->>C: HTTP 101 Switching Protocols
C->>S: JSON: {token, type: "tgui/connect", payload}
Note over S: OnWSText() - валидация токена
Note over S: Z_WS_TIE() - привязка к окну
loop Обмен данными
C->>S: Сообщения от клиента
Note over S: __on_ws_text()
S-->>C: Ответы и события
end
C->>S: Close / Disconnect
Note over S: __on_ws_disconnected()
Ключевая концепция: соединение можно привязать к объекту.
// Привязать соединение conn_id к объекту window
Z_WS_TIE(conn_id, window, "on_text_proc", "on_binary_proc", "on_disconnect_proc")После привязки:
- Все текстовые сообщения вызывают
window.on_text_proc() - Бинарные сообщения вызывают
window.on_binary_proc() - При отключении вызывается
window.on_disconnect_proc()
// Отправить текст
Z_WS_SEND(conn_id, "Hello, client!")
// Отправить JSON
Z_WS_SEND(conn_id, json_encode(list("type" = "update", "data" = mydata)))
// Отправить бинарные данные (список байтов)
Z_WS_SEND(conn_id, list(0x48, 0x65, 0x6C, 0x6C, 0x6F))// Принудительно отключить клиента
Z_WS_DISCONNECT(conn_id)Важно: Нельзя вызывать
Z_WS_DISCONNECTвнутри колбэковon_text/on_binary. Вместо этого вернитеFALSEиз колбэка - соединение закроется автоматически.
SIGNATURE;CKEY;GAME_ID;WORLD_TIME
Пример:
a1b2c3d4e5f6...;someplayerckey;abc123;12345
sequenceDiagram
participant C as Клиент
participant S as Сервер
C->>S: 1. Запрашивает токен (HTTP/Topic)
Note over S: SSws.issue_token(ckey)
S-->>C: 2. Токен
C->>S: 3. Подключается к WebSocket
C->>S: 4. Отправляет токен в первом сообщении
Note over S: 5. SSws.verify_token()<br/>Проверка подписи, времени, game_id
S-->>C: 6. Соединение привязывается к клиенту
- Секретный ключ (
GLOB.ws_secret) генерируется при первом запросе токена - Используется криптографически стойкий HMAC-SHA256
- Токен действует только 60 секунд
- Токен привязан к конкретной игре (
game_id)
WebSocket заменяет механизм Topic для TGUI-окон.
/proc/tgui_WSConnect(list/content, addr, conn_id, client/client)Эта процедура вызывается когда клиент отправляет:
{
"token": "...",
"type": "tgui/connect",
"payload": {
"window_id": "1"
}
}Затем, если токен корректный - соединение "связывается" с соответствующим объектом окна, и все последующие сообщения пойдут напрямую к нему:
/datum/tgui_window/proc/__on_ws_text(content, addr, conn_id)