vimp-engine 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/package.json +34 -0
- package/src/config/authClient.js +47 -0
- package/src/config/clientDefaults.js +113 -0
- package/src/config/hostDefaults.js +79 -0
- package/src/config/lobby.js +104 -0
- package/src/config/master.js +103 -0
- package/src/config/opcodes.js +29 -0
- package/src/config/wsports.js +35 -0
- package/src/host/GameCoreAdapter.js +206 -0
- package/src/host/HostGame.js +857 -0
- package/src/host/host.worker.js +438 -0
- package/src/host/meta/SocketManager.js +431 -0
- package/src/host/meta/core/CommandProcessor.js +99 -0
- package/src/host/meta/core/RoundManager.js +646 -0
- package/src/host/meta/core/VoteCoordinator.js +71 -0
- package/src/host/meta/modules/Panel.js +181 -0
- package/src/host/meta/modules/PlayerDataSync.js +176 -0
- package/src/host/meta/modules/RTTManager.js +168 -0
- package/src/host/meta/modules/Stat.js +294 -0
- package/src/host/meta/modules/TimerManager.js +277 -0
- package/src/host/meta/modules/Vote.js +179 -0
- package/src/host/meta/modules/chat/Chat.js +66 -0
- package/src/host/meta/modules/chat/index.js +1 -0
- package/src/host/meta/modules/chat/systemMessages.js +51 -0
- package/src/host/meta/player/HumanParticipant.js +36 -0
- package/src/host/meta/player/Participant.js +22 -0
- package/src/host/meta/player/ParticipantManager.js +247 -0
- package/src/host/meta/player/ScriptedParticipant.js +19 -0
- package/src/lib/AbstractTimer.js +84 -0
- package/src/lib/Publisher.js +43 -0
- package/src/lib/applyRoomOverrides.js +48 -0
- package/src/lib/buildClientConfig.js +48 -0
- package/src/lib/clientCoreConfig.js +59 -0
- package/src/lib/config.js +83 -0
- package/src/lib/coreConfig.js +60 -0
- package/src/lib/factory.js +21 -0
- package/src/lib/formatters.js +33 -0
- package/src/lib/gamePlugin.js +95 -0
- package/src/lib/jwt.js +103 -0
- package/src/lib/math.js +75 -0
- package/src/lib/rateLimiter.js +40 -0
- package/src/lib/sanitizers.js +17 -0
- package/src/lib/security.js +45 -0
- package/src/lib/validators.js +52 -0
package/README.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# vimp-engine
|
|
2
|
+
|
|
3
|
+
VIMP — движок-приложение: мастер (лобби, WebRTC-сигналинг), P2P-транспорт,
|
|
4
|
+
Worker-хост, мета-модули матча, MVC-каркас клиента. Игровые правила
|
|
5
|
+
(например, `@vimp/tanks`) подключаются динамически через
|
|
6
|
+
[plugin-api](https://github.com/lgick/vimp/blob/main/docs/en/plugin-api.md)
|
|
7
|
+
— этот пакет их не содержит.
|
|
8
|
+
|
|
9
|
+
Документация: https://github.com/lgick/vimp/tree/main/docs/en
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vimp-engine",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "VIMP — движок-приложение (мастер, P2P-транспорт, Worker-хост, мета, MVC-каркас клиента)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
"./lib/*": "./src/lib/*",
|
|
8
|
+
"./config/*": "./src/config/*",
|
|
9
|
+
"./host/*": "./src/host/*"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"src/lib",
|
|
13
|
+
"src/config",
|
|
14
|
+
"src/host"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"dev": "NODE_ENV=development nodemon src/master/main.js -w src/master -w src/lib -w src/config",
|
|
18
|
+
"start": "NODE_ENV=production node --env-file ../../.env src/master/main.js",
|
|
19
|
+
"build": "vite build"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"express": "^5.1.0",
|
|
23
|
+
"uuid": "^13.0.0",
|
|
24
|
+
"vite-express": "^0.21.1",
|
|
25
|
+
"ws": "^8.18.3"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"howler": "^2.2.4",
|
|
29
|
+
"nodemon": "^3.1.10",
|
|
30
|
+
"pixi.js": "^8.14.0",
|
|
31
|
+
"vite": "^7.1.11",
|
|
32
|
+
"vite-plugin-pug": "^0.4.1"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Конфиг клиентского логина лобби (Этап B2). Бандлится в клиентскую сборку,
|
|
2
|
+
// как config/lobby.js — auth-сервис живёт на отдельном домене/порте, поэтому
|
|
3
|
+
// serviceUrl должен быть подставлен под конкретный деплой перед сборкой прода
|
|
4
|
+
// (см. docs/en/auth.md).
|
|
5
|
+
export default {
|
|
6
|
+
// базовый URL центрального auth-сервиса (packages/auth). Дефолт — dev-порт
|
|
7
|
+
// из packages/auth/src/config/auth.js
|
|
8
|
+
serviceUrl: 'http://localhost:3010',
|
|
9
|
+
|
|
10
|
+
// включённые провайдеры (должны совпадать с packages/auth config.oauth)
|
|
11
|
+
providers: ['github'],
|
|
12
|
+
|
|
13
|
+
// ключ localStorage для identity JWT
|
|
14
|
+
tokenStorageKey: 'vimpAuthToken',
|
|
15
|
+
|
|
16
|
+
// issuer, которым central auth-сервис подписывает identity-токен (Этап B3);
|
|
17
|
+
// должен совпадать с packages/auth/src/config/auth.js: jwt.issuer — хост
|
|
18
|
+
// сверяет claim iss токена с этим значением при проверке подписи по /jwks
|
|
19
|
+
issuer: 'vimp-auth',
|
|
20
|
+
|
|
21
|
+
// имя query-параметров, которыми auth-сервис возвращает управление
|
|
22
|
+
// (redirect на returnUrl после /oauth/:provider/callback)
|
|
23
|
+
queryParams: {
|
|
24
|
+
token: 'token',
|
|
25
|
+
pendingToken: 'pendingToken',
|
|
26
|
+
error: 'authError',
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
// DOM-элементы экрана логина/ника (views/includes/lobbyAuth.pug) и бейджа
|
|
30
|
+
// пользователя в лобби (views/includes/lobby.pug)
|
|
31
|
+
elems: {
|
|
32
|
+
containerId: 'lobby-auth',
|
|
33
|
+
loginSectionId: 'lobby-auth-login',
|
|
34
|
+
loginErrorId: 'lobby-auth-login-error',
|
|
35
|
+
nickSectionId: 'lobby-auth-nick',
|
|
36
|
+
nickInputId: 'lobby-auth-nick-input',
|
|
37
|
+
nickErrorId: 'lobby-auth-nick-error',
|
|
38
|
+
nickSubmitId: 'lobby-auth-nick-submit',
|
|
39
|
+
lobbyId: 'lobby',
|
|
40
|
+
userId: 'lobby-user',
|
|
41
|
+
userNickId: 'lobby-user-nick',
|
|
42
|
+
userLogoutId: 'lobby-user-logout',
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
// CSS-класс кнопок провайдеров в lobbyAuth.pug (data-provider на каждой)
|
|
46
|
+
providerButtonClass: 'lobby-auth-provider',
|
|
47
|
+
};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// Движковые дефолты клиентского CONFIG_DATA (порт 0): интерполяция,
|
|
2
|
+
// режимы/служебные клавиши, DOM-структуры движковых модулей, технические
|
|
3
|
+
// сообщения. Игровая половина бывшего config/client.js — src/config/client.js
|
|
4
|
+
// в репозитории игры (например vimp-tanks); merge выполняет buildClientConfig
|
|
5
|
+
// через HostPlugin.buildClientGameConfig.
|
|
6
|
+
export default {
|
|
7
|
+
// ***** interpolation ***** //
|
|
8
|
+
// snapshot-интерполяция: мир рендерится в прошлом (serverNow − delay)
|
|
9
|
+
interpolation: {
|
|
10
|
+
delay: 100, // мс; ~3 кадра при 30 пакетах/сек
|
|
11
|
+
maxFrameAge: 1000, // мс; страховочная очистка старых кадров буфера
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
// ***** modules ***** //
|
|
15
|
+
modules: {
|
|
16
|
+
canvasManager: {
|
|
17
|
+
dynamicCamera: {
|
|
18
|
+
// сила смещения камеры вперед (чем больше, тем дальше смотрит)
|
|
19
|
+
lookAheadFactor: 30,
|
|
20
|
+
|
|
21
|
+
// чувствительность зума (0 - 1)
|
|
22
|
+
// 0.1 - мягкое отдаление
|
|
23
|
+
// 0.5 - агрессивное отдаление
|
|
24
|
+
// 1.0 - экстремально сильная реакция на скорость
|
|
25
|
+
zoomOutFactor: 0.5,
|
|
26
|
+
|
|
27
|
+
// максимально возможное отдаление (от базового масштаба)
|
|
28
|
+
maxZoomOut: 0.6,
|
|
29
|
+
|
|
30
|
+
// плавность изменений позиции камеры (0 - 1)
|
|
31
|
+
smoothnessPosition: 0.008,
|
|
32
|
+
|
|
33
|
+
// плавность изменений зума камеры (0 - 1)
|
|
34
|
+
smoothnessZoom: 0.005,
|
|
35
|
+
|
|
36
|
+
// плавность входящей скорости (0 - 1)
|
|
37
|
+
// (игнорирует мелкие рывки сети, но реагирует на разгон)
|
|
38
|
+
smoothnessVelocity: 0.15,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
controls: {
|
|
43
|
+
modes: {
|
|
44
|
+
67: 'chat', // чат (c)
|
|
45
|
+
77: 'vote', // опрос (m)
|
|
46
|
+
9: 'stat', // статистика (tab)
|
|
47
|
+
},
|
|
48
|
+
cmds: {
|
|
49
|
+
27: 'escape', // отмена (escape)
|
|
50
|
+
13: 'enter', // ввод (enter)
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
chat: {
|
|
55
|
+
elems: {
|
|
56
|
+
chatBox: 'chat-box',
|
|
57
|
+
cmd: 'cmd',
|
|
58
|
+
},
|
|
59
|
+
params: {
|
|
60
|
+
listLimit: 5,
|
|
61
|
+
lineTime: 15000,
|
|
62
|
+
cacheMin: 200,
|
|
63
|
+
cacheMax: 300,
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
panel: {
|
|
68
|
+
// контейнер панели; ячейки генерирует PanelView по схеме игры (fields)
|
|
69
|
+
containerId: 'panel',
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
stat: {
|
|
73
|
+
elems: {
|
|
74
|
+
stat: 'stat',
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
vote: {
|
|
79
|
+
elems: {
|
|
80
|
+
voteId: 'vote',
|
|
81
|
+
titleClass: 'vote-title',
|
|
82
|
+
listClass: 'vote-list',
|
|
83
|
+
navClass: 'vote-nav',
|
|
84
|
+
navActiveClass: 'active',
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
// game information
|
|
90
|
+
gameInform: {
|
|
91
|
+
id: 'game-informer',
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
// technical information
|
|
95
|
+
techInformList: [
|
|
96
|
+
`Server is full! Please wait or try again later.
|
|
97
|
+
Max players: {0}
|
|
98
|
+
You are #{1} in the queue.
|
|
99
|
+
`,
|
|
100
|
+
|
|
101
|
+
'Connection closed due to a new login from another device!',
|
|
102
|
+
|
|
103
|
+
'Loading...',
|
|
104
|
+
|
|
105
|
+
'Kicked for inactivity.',
|
|
106
|
+
|
|
107
|
+
'Connection terminated due to high network latency.',
|
|
108
|
+
|
|
109
|
+
'Connection terminated due to missed network pings.',
|
|
110
|
+
|
|
111
|
+
'Room is full! Max players: {0}. Try again later.',
|
|
112
|
+
],
|
|
113
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// Движковые дефолты хоста: лимиты комнаты, таймеры, RTT/idle-кики,
|
|
2
|
+
// спектаторский keyset (наблюдение — механизм движка). Игровая половина
|
|
3
|
+
// бывшего config/game.js — src/config/game.js в репозитории игры (например
|
|
4
|
+
// vimp-tanks); merge выполняет host.worker.js через HostPlugin.gameConfig.
|
|
5
|
+
export default {
|
|
6
|
+
isDevMode: false, // флаг режима разработки
|
|
7
|
+
|
|
8
|
+
maxPlayers: 30,
|
|
9
|
+
|
|
10
|
+
// максимальная длина сообщения чата (авторитетно на сервере).
|
|
11
|
+
// должно совпадать с maxlength у input#cmd в client/views/includes/chat.pug
|
|
12
|
+
chatMaxLength: 60,
|
|
13
|
+
|
|
14
|
+
// таймеры (ms)
|
|
15
|
+
timers: {
|
|
16
|
+
// время обновления серверного кадра ~120 Гц
|
|
17
|
+
timeStep: 1000 / 120,
|
|
18
|
+
|
|
19
|
+
// частота отправки данных на клиент
|
|
20
|
+
// каждый 2-ой тик - будет 60 пакетов/сек,
|
|
21
|
+
// каждый 4-ый тик - будет 30 пакетов/сек (экономно)
|
|
22
|
+
networkSendRate: 4,
|
|
23
|
+
|
|
24
|
+
// время раунда (2 минуты)
|
|
25
|
+
roundTime: 120000,
|
|
26
|
+
|
|
27
|
+
// время карты (10 минут)
|
|
28
|
+
mapTime: 600000,
|
|
29
|
+
|
|
30
|
+
// границы пользовательских roundTime/mapTime комнаты (клампы в
|
|
31
|
+
// host.worker.js; зеркально min/max в lobby.pug, там в секундах)
|
|
32
|
+
roomTimeMin: 10000,
|
|
33
|
+
roomTimeMax: 3600000,
|
|
34
|
+
|
|
35
|
+
// время голосования (10 секунд)
|
|
36
|
+
voteTime: 10000,
|
|
37
|
+
|
|
38
|
+
// время ожидания для запуска нового голосования (30 секунд)
|
|
39
|
+
timeBlockedVote: 30000,
|
|
40
|
+
|
|
41
|
+
// время возможности сменить команду в начале раунда (10 секунд)
|
|
42
|
+
teamChangeGracePeriod: 10000,
|
|
43
|
+
|
|
44
|
+
// время ожидания запуска нового раунда после завершения текущего (5 секунд)
|
|
45
|
+
roundRestartDelay: 5000,
|
|
46
|
+
|
|
47
|
+
// время ожидания смены карты после голосования (2 секунды)
|
|
48
|
+
mapChangeDelay: 2000,
|
|
49
|
+
|
|
50
|
+
// интервал обновления RTT (round trip time)
|
|
51
|
+
rttPingInterval: 3000,
|
|
52
|
+
|
|
53
|
+
// периодичность проверки на бездействие (30 секунд)
|
|
54
|
+
idleCheckInterval: 30000,
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
rtt: {
|
|
58
|
+
maxMissedPings: 5, // количество пропусков ответа клиента перед киком
|
|
59
|
+
|
|
60
|
+
// сглаженная (EMA) задержка ответа для кика (1 секунда).
|
|
61
|
+
// Порог рассчитан на P2P-хостинг с домашних каналов: реальный RTT
|
|
62
|
+
// 200-300 мс + спайки на смене карты/старте раунда (клиент занят
|
|
63
|
+
// перестройкой полотна) — норма, кикать за это нельзя
|
|
64
|
+
maxLatency: 1000,
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
// порог допустимого бездействия перед киком
|
|
68
|
+
// если null — кик отключен
|
|
69
|
+
idleKickTimeout: {
|
|
70
|
+
player: 120000, // для игрока (2 минуты)
|
|
71
|
+
spectator: null, // для наблюдателя
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
// конфигурация клавиш наблюдателя и неактивного игрока
|
|
75
|
+
spectatorKeys: {
|
|
76
|
+
nextPlayer: 'nextPlayer', // next player (n)
|
|
77
|
+
prevPlayer: 'prevPlayer', // prev player (p)
|
|
78
|
+
},
|
|
79
|
+
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// Конфиг лобби (список серверов + умный пинг). Клиент проходит лобби ДО
|
|
2
|
+
// подключения к хосту, поэтому эти параметры бандлятся в сборку, а не приходят
|
|
3
|
+
// от хоста в CONFIG_DATA (как остальной клиентский конфиг).
|
|
4
|
+
export default {
|
|
5
|
+
// REST-эндпоинт мастера со списком серверов (GET /servers)
|
|
6
|
+
serversUrl: '/servers',
|
|
7
|
+
|
|
8
|
+
// каталог игр мастера (Этап 6.3, GameCatalog): roomDefaults формы создания
|
|
9
|
+
// комнаты и ClientPlugin берутся отсюда вместо статической композиции
|
|
10
|
+
gamesManifestUrl: '/games/manifest.json',
|
|
11
|
+
|
|
12
|
+
// каталог карт мастера, per-game (Этап 6.4): комната хоста стартует на
|
|
13
|
+
// актуальных картах активной игры, недоступность каталога — fallback на
|
|
14
|
+
// карты из бандла
|
|
15
|
+
maps: {
|
|
16
|
+
manifestUrl: gameId => `/games/${gameId}/maps/manifest.json`,
|
|
17
|
+
baseUrl: gameId => `/games/${gameId}/maps`,
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
// манифест конкретной игры (Этап 6.5): эстафета Worker'ов перечитывает его
|
|
21
|
+
// перед свопом — новый Worker должен получить свежий entries.host/wasm
|
|
22
|
+
// (деплой игры мог обновиться независимо от деплоя движка)
|
|
23
|
+
game: {
|
|
24
|
+
manifestUrl: gameId => `/games/${gameId}/manifest.json`,
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
// манифест worker-бандла мастера (Этап 5.2): Worker комнаты создаётся по
|
|
28
|
+
// url из манифеста, расхождение codeVersion при re-register — эстафета
|
|
29
|
+
// Worker'ов; недоступность манифеста — бандловый URL без обновлений кода
|
|
30
|
+
worker: {
|
|
31
|
+
manifestUrl: '/worker/manifest.json',
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
// JWKS central auth-сервиса, проксируемый мастером (Этап B3): Worker хоста
|
|
35
|
+
// фетчит его сам (тот же origin, что и сам Worker) и проверяет подпись
|
|
36
|
+
// identity-токена, не доверяя auth-сервису напрямую из недоверенного хоста
|
|
37
|
+
auth: {
|
|
38
|
+
jwksUrl: '/auth/jwks',
|
|
39
|
+
|
|
40
|
+
// rank/state central auth-сервиса, проксируемые мастером (Этап B4):
|
|
41
|
+
// хост запрашивает их на join своим identity-токеном и синхронизирует
|
|
42
|
+
// обратно по границам раунда/карты (RoundManager)
|
|
43
|
+
rankUrl: '/auth/rank',
|
|
44
|
+
stateUrl: '/auth/state',
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
// переподключение сигнального WS хоста (комната без него выпадает из
|
|
48
|
+
// выдачи мастера): экспоненциальный бэкофф от baseDelay до maxDelay (мс)
|
|
49
|
+
reconnect: {
|
|
50
|
+
baseDelay: 1000,
|
|
51
|
+
maxDelay: 30000,
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
// размер страницы для «Загрузить ещё» (offset/limit к мастеру)
|
|
55
|
+
pageSize: 10,
|
|
56
|
+
|
|
57
|
+
// минимальный интервал повторного пинга одного сервера (мс):
|
|
58
|
+
// защита от спама ping_host при перерисовке/скролле списка
|
|
59
|
+
pingInterval: 5000,
|
|
60
|
+
|
|
61
|
+
// DOM-элементы лобби (из lobby.pug)
|
|
62
|
+
elems: {
|
|
63
|
+
lobbyId: 'lobby',
|
|
64
|
+
listId: 'lobby-list',
|
|
65
|
+
searchId: 'lobby-search',
|
|
66
|
+
moreId: 'lobby-more',
|
|
67
|
+
emptyId: 'lobby-empty',
|
|
68
|
+
nameId: 'lobby-name',
|
|
69
|
+
hostBtnId: 'lobby-host',
|
|
70
|
+
// селектор игры: скрыт, пока в каталоге мастера одна игра (§6, PLAN.md)
|
|
71
|
+
gameId: 'lobby-game',
|
|
72
|
+
// контейнер полей комнаты: генерируются по ключам roomDefaults
|
|
73
|
+
// манифеста активной игры (Д7) — движок не знает игровых полей
|
|
74
|
+
fieldsId: 'lobby-fields',
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
// движковые подсказки генератору формы комнаты (только движковые ключи
|
|
78
|
+
// roomDefaults; игровые поля выводятся из типа значения)
|
|
79
|
+
form: {
|
|
80
|
+
// тайм-ключи движка: в roomDefaults хранятся в мс, в форме — секунды
|
|
81
|
+
secondsKeys: ['roundTime', 'mapTime'],
|
|
82
|
+
// атрибуты числовых полей (в единицах формы)
|
|
83
|
+
attrs: {
|
|
84
|
+
maxPlayers: { min: 1 },
|
|
85
|
+
roundTime: { min: 10, max: 3600 },
|
|
86
|
+
mapTime: { min: 10, max: 3600 },
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
// создание комнаты (хост в этой же вкладке); лимит игроков/время
|
|
91
|
+
// раунда-карты/огонь по своим/карта по умолчанию — из roomDefaults
|
|
92
|
+
// манифеста активной игры (Этап 6.3), не бандлятся здесь
|
|
93
|
+
create: {
|
|
94
|
+
defaultName: 'My Server',
|
|
95
|
+
|
|
96
|
+
// период heartbeat/актуализации комнаты у мастера (мс); должен быть
|
|
97
|
+
// меньше master.host.heartbeatTimeout (30 c), иначе комнату выметет
|
|
98
|
+
heartbeatInterval: 10000,
|
|
99
|
+
|
|
100
|
+
// socketId loopback-соединения хоста-игрока: по нему Worker исключает
|
|
101
|
+
// хоста из kick-политик (его отключение = смерть комнаты для всех)
|
|
102
|
+
hostSocketId: 'local',
|
|
103
|
+
},
|
|
104
|
+
};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
3
|
+
|
|
4
|
+
// корень репозитория — якорь от расположения файла, не от cwd
|
|
5
|
+
const rootDir = path.resolve(fileURLToPath(import.meta.url), '..', '..', '..', '..', '..');
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
name: 'VIMP Master Server',
|
|
9
|
+
protocol: 'https:',
|
|
10
|
+
domain: 'localhost',
|
|
11
|
+
// 3000 — игровой сервер, 3001 — Vite HMR (vite.config.js)
|
|
12
|
+
port: 3002,
|
|
13
|
+
|
|
14
|
+
// сертификаты для локальной разработки — в .certs корня репозитория
|
|
15
|
+
// (в продакшене обычный HTTP за Nginx)
|
|
16
|
+
httpsOptions: {
|
|
17
|
+
key: path.join(rootDir, '.certs', 'key.pem'),
|
|
18
|
+
cert: path.join(rootDir, '.certs', 'cert.pem'),
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
// список игр-плагинов, подключаемых к мастеру (Этап A2 плана разделения):
|
|
22
|
+
// `package` — имя npm-пакета игры, резолвится через node_modules; пакет
|
|
23
|
+
// объявлен зависимостью в корневом package.json репозитория (деплой-уровень),
|
|
24
|
+
// не в packages/engine — vimp-engine остаётся game-agnostic и не тянет
|
|
25
|
+
// конкретную игру за собой (кодревью Этапов A, находка F1). `version` не
|
|
26
|
+
// используется GameCatalog напрямую, это опорная точка для проверки версии
|
|
27
|
+
// при деплое (Этап A4). В проде переопределяется переменной окружения
|
|
28
|
+
// GAMES_MATRIX (JSON), см. master/main.js
|
|
29
|
+
games: [{ id: 'tanks', package: '@vimp/tanks', version: '0.1.0' }],
|
|
30
|
+
|
|
31
|
+
// список серверов (GET /servers)
|
|
32
|
+
servers: {
|
|
33
|
+
// если всего комнат <= порога — региональный фильтр
|
|
34
|
+
// и пагинация отключаются, отдаётся весь список
|
|
35
|
+
regionThreshold: 15,
|
|
36
|
+
defaultLimit: 10, // размер страницы по умолчанию
|
|
37
|
+
maxLimit: 50, // максимальный размер страницы
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
// ограничения регистрируемых комнат
|
|
41
|
+
host: {
|
|
42
|
+
maxNameLength: 30, // длина имени комнаты
|
|
43
|
+
maxPlayersLimit: 8, // целевой размер комнаты (рамка P2P-плана)
|
|
44
|
+
heartbeatTimeout: 30000, // нет heartbeat дольше — комната удаляется
|
|
45
|
+
sweepInterval: 10000, // период проверки протухших комнат
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
// рейтинг хостера комнаты (server-rating этап 2, plan/server-rating/
|
|
49
|
+
// stage_2.md): /like·/unlike гостей комнаты вместо соц-модерации /ban.
|
|
50
|
+
// Дефолт движка для всех игр; auth-сервис хранит и клампит рейтинг
|
|
51
|
+
// персистентно и глобально (нужно и для аннулирования rank/skills, этап 4) —
|
|
52
|
+
// это значение зеркалируется в packages/auth/src/config/auth.js:rating,
|
|
53
|
+
// фактический кламп/blocked считает auth, не мастер
|
|
54
|
+
rating: {
|
|
55
|
+
min: -10,
|
|
56
|
+
max: 10,
|
|
57
|
+
blockAt: -10,
|
|
58
|
+
// период опроса auth за актуальным рейтингом активных хостеров (этап 3,
|
|
59
|
+
// stage_3.md) — держит кэш GET /servers свежим (счёт мог измениться на
|
|
60
|
+
// другом мастере), голос/регистрация на этом мастере обновляют кэш сразу
|
|
61
|
+
refreshInterval: 30000,
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
// заголовки безопасности (гигиена среды, Этап 5.4). CSP на статику/.wasm в
|
|
65
|
+
// проде ставит Nginx (см. docs/deployment.md) — здесь single source of truth
|
|
66
|
+
// политики; мастер применяет её к своим ответам только в проде (в dev CSP
|
|
67
|
+
// сломала бы Vite HMR). WASM требует 'wasm-unsafe-eval', Worker — 'blob:';
|
|
68
|
+
// connect-src data: — PixiJS фетчит тестовый data:-URL для проверки ImageBitmap.
|
|
69
|
+
// authServiceUrl (Этап B2) — домен central auth-сервиса (packages/auth):
|
|
70
|
+
// лобби делает туда прямой fetch (POST /nick), поэтому connect-src должен
|
|
71
|
+
// его разрешать; сам OAuth-редирект (location.href на auth-сервис/провайдера)
|
|
72
|
+
// CSP не ограничивает — это навигация верхнего уровня, не fetch/XHR
|
|
73
|
+
security: {
|
|
74
|
+
authServiceUrl: 'http://localhost:3010',
|
|
75
|
+
csp: authServiceUrl =>
|
|
76
|
+
[
|
|
77
|
+
"default-src 'self'",
|
|
78
|
+
"script-src 'self' 'wasm-unsafe-eval'",
|
|
79
|
+
"worker-src 'self' blob:",
|
|
80
|
+
`connect-src 'self' wss: data: ${authServiceUrl}`,
|
|
81
|
+
"img-src 'self' data: blob:",
|
|
82
|
+
"style-src 'self' 'unsafe-inline'",
|
|
83
|
+
"object-src 'none'",
|
|
84
|
+
"base-uri 'self'",
|
|
85
|
+
"frame-ancestors 'none'",
|
|
86
|
+
].join('; '),
|
|
87
|
+
referrerPolicy: 'no-referrer',
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
// заголовок с регионом хоста от Nginx/CDN (например, CF-IPCountry);
|
|
91
|
+
// выбран вместо geoip-lite — бесплатнее по памяти
|
|
92
|
+
regionHeader: 'x-region',
|
|
93
|
+
|
|
94
|
+
// лимит сигнальных ping-запросов с одного IP (защита от DDOS)
|
|
95
|
+
pingRateLimit: {
|
|
96
|
+
limit: 10,
|
|
97
|
+
windowMs: 1000,
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
// ICE-конфигурация для установки P2P-соединений:
|
|
101
|
+
// STUN обязателен; TURN — опциональный релей по итогам Этапа 0
|
|
102
|
+
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }],
|
|
103
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Разметка бинарного snapshot-протокола (порт SHOT_DATA).
|
|
2
|
+
// Единый источник для хоста (упаковка) и клиента (распаковка) — обе стороны
|
|
3
|
+
// живут в Rust-ядре: packages/engine/core/src/snapshot.rs (pack) и packages/engine/core/src/client/unpack.rs.
|
|
4
|
+
|
|
5
|
+
// версия контракта движок ↔ игра-плагин (GameManifest, HostPlugin,
|
|
6
|
+
// ClientPlugin, Wasm Host ABI — docs/{en,ru}/plugin-api.md);
|
|
7
|
+
// проверяется при загрузке плагинов; ломающие изменения контрактов → +1.
|
|
8
|
+
// Пока номинальная: код движка и игры ещё монолитен (см. PLAN.md)
|
|
9
|
+
export const ENGINE_API_VERSION = 1;
|
|
10
|
+
|
|
11
|
+
// версия формата кадра: первый байт после порта;
|
|
12
|
+
// увеличивать при любом изменении байтовой раскладки в ядре
|
|
13
|
+
// v2: per-user player-блок (gameId, inputSeq, состояние своего танка) — Фаза 5b
|
|
14
|
+
// v3: id автора в событиях оружия (tracers +shooterId, bombs +ownerId) — Фаза 5c
|
|
15
|
+
export const SNAPSHOT_FORMAT_VERSION = 3;
|
|
16
|
+
|
|
17
|
+
// Реестр ключей снапшота (SNAPSHOT_KEYS) — игровая схема: живёт в
|
|
18
|
+
// gameConfig.snapshot игры (например src/config/snapshot.js в vimp-tanks),
|
|
19
|
+
// движок передаёт её ядру (lib/coreConfig.js) и клиенту в CONFIG_DATA
|
|
20
|
+
// (lib/buildClientConfig.js), не зная раскладки.
|
|
21
|
+
|
|
22
|
+
// флаги hot-буфера рендер-тика клиентского ядра
|
|
23
|
+
// (зеркало core/src/client/mod.rs в репозитории игры, например vimp-tanks)
|
|
24
|
+
export const HOT_FLAGS = {
|
|
25
|
+
GAME: 1,
|
|
26
|
+
CAMERA: 2,
|
|
27
|
+
PREDICTED: 4,
|
|
28
|
+
FRAMES: 8,
|
|
29
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
// порты получения данные от сервера
|
|
3
|
+
server: {
|
|
4
|
+
CONFIG_DATA: 0,
|
|
5
|
+
AUTH_DATA: 1,
|
|
6
|
+
AUTH_RESULT: 2,
|
|
7
|
+
MAP_DATA: 3,
|
|
8
|
+
FIRST_SHOT_DATA: 4,
|
|
9
|
+
SHOT_DATA: 5,
|
|
10
|
+
SOUND_DATA: 6,
|
|
11
|
+
GAME_INFORM_DATA: 7,
|
|
12
|
+
TECH_INFORM_DATA: 8,
|
|
13
|
+
MISC: 9,
|
|
14
|
+
PING: 10,
|
|
15
|
+
CLEAR: 11,
|
|
16
|
+
CONSOLE: 12,
|
|
17
|
+
PANEL_DATA: 13,
|
|
18
|
+
STAT_DATA: 14,
|
|
19
|
+
CHAT_DATA: 15,
|
|
20
|
+
VOTE_DATA: 16,
|
|
21
|
+
KEYSET_DATA: 17,
|
|
22
|
+
},
|
|
23
|
+
// порты получения данных от клиента
|
|
24
|
+
client: {
|
|
25
|
+
CONFIG_READY: 0,
|
|
26
|
+
AUTH_RESPONSE: 1,
|
|
27
|
+
MODULES_READY: 2,
|
|
28
|
+
MAP_READY: 3,
|
|
29
|
+
FIRST_SHOT_READY: 4,
|
|
30
|
+
KEYS_DATA: 5,
|
|
31
|
+
CHAT_DATA: 6,
|
|
32
|
+
VOTE_DATA: 7,
|
|
33
|
+
PONG: 8,
|
|
34
|
+
},
|
|
35
|
+
};
|