vimp-engine 0.35.6 → 0.37.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/bin/vimp-sim.js +19 -1
- package/core/Cargo.toml +1 -1
- package/package.json +1 -1
- package/src/client/components/controller/Controls.js +5 -0
- package/src/client/components/controller/Lobby.js +4 -4
- package/src/client/components/controller/RoomMenu.js +43 -0
- package/src/client/components/controller/Vote.js +18 -2
- package/src/client/components/model/Chat.js +19 -2
- package/src/client/components/model/ClientReports.js +6 -2
- package/src/client/components/model/Controls.js +27 -0
- package/src/client/components/model/Games.js +5 -1
- package/src/client/components/model/Lobby.js +32 -24
- package/src/client/components/model/LobbyAuth.js +20 -4
- package/src/client/components/model/RoomMenu.js +121 -0
- package/src/client/components/model/Stat.js +2 -1
- package/src/client/components/model/Vote.js +122 -4
- package/src/client/components/view/Auth.js +20 -10
- package/src/client/components/view/ClientReports.js +16 -5
- package/src/client/components/view/Games.js +39 -14
- package/src/client/components/view/Lobby.js +90 -43
- package/src/client/components/view/LobbyAuth.js +13 -7
- package/src/client/components/view/Panel.js +1 -2
- package/src/client/components/view/RoomMenu.js +69 -0
- package/src/client/debug.js +24 -1
- package/src/client/lib/FpsMeter.js +46 -0
- package/src/client/lib/JoinRetry.js +73 -0
- package/src/client/lib/diagnostics.js +28 -5
- package/src/client/lib/hostCaps.js +82 -0
- package/src/client/lib/hostUnloadGuard.js +108 -0
- package/src/client/lib/hostVoteCommand.js +92 -0
- package/src/client/lib/pageReload.js +29 -0
- package/src/client/lib/roomLink.js +257 -0
- package/src/client/lib/signalingErrors.js +74 -0
- package/src/client/main.js +676 -451
- package/src/client/network/HiddenHostHealthLog.js +76 -0
- package/src/client/network/HostConnectionManager.js +62 -8
- package/src/client/network/HostController.js +376 -23
- package/src/client/network/HostHealthPolicy.js +307 -0
- package/src/client/network/HostHealthReporter.js +54 -0
- package/src/client/network/HostPrewarm.js +235 -0
- package/src/client/network/LoopbackTransport.js +16 -1
- package/src/client/network/PlannedHandoff.js +366 -0
- package/src/client/network/Promotion.js +420 -0
- package/src/client/network/RoomPeersReporter.js +81 -0
- package/src/client/network/SessionSupervisor.js +730 -0
- package/src/client/network/SignalingClient.js +183 -30
- package/src/client/network/StandbyReceiver.js +287 -0
- package/src/client/network/StandbySender.js +319 -0
- package/src/client/network/TokenHandoffTimer.js +81 -0
- package/src/client/network/WebRtcManager.js +338 -11
- package/src/client/network/policyClose.js +32 -2
- package/src/client/network/standbyChunks.js +185 -0
- package/src/client/session/GuestSession.js +428 -0
- package/src/client/session/HandoffFlow.js +399 -0
- package/src/client/session/HostRole.js +708 -0
- package/src/client/session/Membership.js +179 -0
- package/src/client/session/PromotionFlow.js +325 -0
- package/src/client/session/RouteBoot.js +432 -0
- package/src/client/session/StandbyRole.js +195 -0
- package/src/client/session/hostRoomPrep.js +152 -0
- package/src/client/session/roomContext.js +32 -0
- package/src/client/style.css +83 -22
- package/src/client/views/gameShell.js +7 -0
- package/src/client/views/includes/informer.pug +4 -0
- package/src/client/views/includes/lobby.pug +0 -3
- package/src/client/views/includes/roomMenu.pug +9 -0
- package/src/client/views/index.pug +1 -0
- package/src/config/chatMessages.js +45 -0
- package/src/config/closeCodes.js +1 -1
- package/src/config/env.js +37 -0
- package/src/config/hostDefaults.js +24 -0
- package/src/config/lobby.js +154 -3
- package/src/config/master.js +104 -21
- package/src/config/wsports.js +8 -0
- package/src/devtools/RecordingSocketManager.js +3 -2
- package/src/devtools/ScenarioRunner.js +123 -8
- package/src/devtools/VirtualClient.js +20 -0
- package/src/devtools/contract/format.js +4 -1
- package/src/devtools/contract/index.js +1 -2
- package/src/devtools/contract/rules/a2-package-scripts.js +1 -3
- package/src/devtools/contract/rules/b8-system-messages.js +2 -2
- package/src/devtools/contract/rules/b9-vote-names.js +25 -1
- package/src/devtools/contract/rules/c11-stat-mode.js +4 -1
- package/src/devtools/contract/rules/c6-stat-columns.js +1 -1
- package/src/devtools/contract/rules/c7-key-sets.js +8 -4
- package/src/devtools/contract/rules/d3-snapshot-interp.js +2 -1
- package/src/devtools/contract/rules/e1-sound-pairs.js +3 -1
- package/src/devtools/contract/rules/e4-map-layers.js +13 -2
- package/src/devtools/contract/rules/e5-map-radar-walls.js +7 -1
- package/src/devtools/contract/rules/index.js +38 -5
- package/src/devtools/invariants.js +10 -4
- package/src/devtools/pluginLoader.js +1 -4
- package/src/devtools/report.js +17 -1
- package/src/host/DebugRecorder.js +6 -1
- package/src/host/GameCoreAdapter.js +45 -0
- package/src/host/HostGame.js +928 -59
- package/src/host/PortMachine.js +268 -20
- package/src/host/host.worker.js +222 -16
- package/src/host/meta/SocketManager.js +20 -0
- package/src/host/meta/core/RoundManager.js +89 -14
- package/src/host/meta/modules/Accolades.js +4 -1
- package/src/host/meta/modules/Panel.js +30 -0
- package/src/host/meta/modules/PlayerDataSync.js +255 -46
- package/src/host/meta/modules/RTTManager.js +34 -0
- package/src/host/meta/modules/TimerManager.js +215 -18
- package/src/host/meta/modules/chat/systemMessages.js +29 -3
- package/src/host/meta/player/HumanParticipant.js +10 -0
- package/src/host/meta/player/ParticipantManager.js +98 -5
- package/src/lib/AbstractTimer.js +63 -1
- package/src/lib/Publisher.js +26 -0
- package/src/lib/capabilities.js +5 -0
- package/src/lib/checkpointCodec.js +125 -0
- package/src/lib/clientIp.js +9 -2
- package/src/lib/createHostRuntime.js +95 -6
- package/src/lib/formOptions.js +3 -1
- package/src/lib/gameConfigView.js +10 -0
- package/src/lib/gameId.js +13 -0
- package/src/lib/jwt.js +3 -1
- package/src/lib/loadGamePackage.js +2 -2
- package/src/lib/resumeKey.js +38 -0
- package/src/lib/roomId.js +39 -0
- package/src/lib/roomSettings.js +59 -0
- package/src/lib/validators.js +7 -0
- package/tests/fixtures/generations/gen-api3/host/ScriptedManager.js +2 -3
- package/tests/fixtures/generations/gen-api3/host/fakeCore.js +31 -5
- package/tests/fixtures/generations/gen-api3/manifest.json +1 -3
- package/tests/fixtures/generations/gen-api4/host/ScriptedManager.js +2 -3
- package/tests/fixtures/generations/gen-api4/host/fakeCore.js +31 -5
- package/tests/fixtures/generations/gen-api4/manifest.json +1 -3
- package/tests/fixtures/miniGame/config/client.js +0 -20
- package/tests/fixtures/miniGame/host/ScriptedManager.js +2 -3
- package/tests/fixtures/miniGame/host/fakeCore.js +31 -5
package/src/host/PortMachine.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Хендшейк-машина клиента, вынутая из host.worker.js: автомат по клиентским
|
|
2
|
-
// портам 0–
|
|
2
|
+
// портам 0–10 (см. docs/en/network.md). Изоморфна — ни self, ни postMessage,
|
|
3
3
|
// ни DOM: всё, что знает про транспорт, приходит через makeSocket. Поэтому её
|
|
4
4
|
// крутит и Worker браузерного хоста, и inline-хост standalone SDK, и
|
|
5
5
|
// Node-процесс dedicated-сервера: копий автомата быть не должно, иначе они
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
import wsports from '../config/wsports.js';
|
|
9
9
|
import closeCodes from '../config/closeCodes.js';
|
|
10
|
+
import clock from '../lib/clock.js';
|
|
11
|
+
import { resumeKeysEqual } from '../lib/resumeKey.js';
|
|
10
12
|
import { resolveValidator, validateAuth } from '../lib/validators.js';
|
|
11
13
|
|
|
12
14
|
// PC (client ports): порты получения данных от клиента
|
|
@@ -19,8 +21,24 @@ const PC_KEYS_DATA = wsports.client.KEYS_DATA;
|
|
|
19
21
|
const PC_CHAT_DATA = wsports.client.CHAT_DATA;
|
|
20
22
|
const PC_VOTE_DATA = wsports.client.VOTE_DATA;
|
|
21
23
|
const PC_PONG = wsports.client.PONG;
|
|
24
|
+
const PC_RESUME_REQUEST = wsports.client.RESUME_REQUEST;
|
|
25
|
+
const PC_LEAVE = wsports.client.LEAVE;
|
|
22
26
|
|
|
23
|
-
const PORT_COUNT =
|
|
27
|
+
const PORT_COUNT = 11;
|
|
28
|
+
|
|
29
|
+
// версии формата RESUME_REQUEST, которые понимает хост
|
|
30
|
+
const RESUME_VERSION = 1;
|
|
31
|
+
|
|
32
|
+
// порты игрового состояния (после MODULES_READY, после restore и resume)
|
|
33
|
+
const IN_GAME_PORTS = [
|
|
34
|
+
PC_MAP_READY,
|
|
35
|
+
PC_FIRST_SHOT_READY,
|
|
36
|
+
PC_KEYS_DATA,
|
|
37
|
+
PC_CHAT_DATA,
|
|
38
|
+
PC_VOTE_DATA,
|
|
39
|
+
PC_PONG,
|
|
40
|
+
PC_LEAVE,
|
|
41
|
+
];
|
|
24
42
|
|
|
25
43
|
export default class PortMachine {
|
|
26
44
|
/**
|
|
@@ -31,9 +49,27 @@ export default class PortMachine {
|
|
|
31
49
|
* @param {Object} deps.authSchema - hostPlugin.authSchema.
|
|
32
50
|
* @param {Function} deps.makeSocket - (socketId) => { send, sendBinary, close }.
|
|
33
51
|
* @param {Object} deps.identity - стратегия идентичности (./identity.js).
|
|
52
|
+
* @param {number} [deps.resumeGraceMs] - сколько держать место участника,
|
|
53
|
+
* чей транспорт оборвался (host-migration этап 4); 0 — снимать сразу
|
|
54
|
+
* (dedicated, standalone: поведение до этапа 4).
|
|
55
|
+
* @param {number} [deps.resumeRequestTimeoutMs] - сколько ждать
|
|
56
|
+
* RESUME_REQUEST от возобновляющего соединения.
|
|
34
57
|
*/
|
|
35
|
-
constructor({
|
|
58
|
+
constructor({
|
|
59
|
+
host,
|
|
60
|
+
socketManager,
|
|
61
|
+
clientCfg,
|
|
62
|
+
authSchema,
|
|
63
|
+
makeSocket,
|
|
64
|
+
identity,
|
|
65
|
+
resumeGraceMs = 0,
|
|
66
|
+
resumeRequestTimeoutMs = 5000,
|
|
67
|
+
}) {
|
|
36
68
|
this._host = host;
|
|
69
|
+
this._resumeGraceMs = resumeGraceMs > 0 ? resumeGraceMs : 0;
|
|
70
|
+
this._resumeRequestTimeoutMs = resumeRequestTimeoutMs;
|
|
71
|
+
// gameId → таймер истечения ожидания отсоединённого участника
|
|
72
|
+
this._graceTimers = new Map();
|
|
37
73
|
this._socketManager = socketManager;
|
|
38
74
|
this._clientCfg = clientCfg;
|
|
39
75
|
this._authSchema = authSchema;
|
|
@@ -72,14 +108,23 @@ export default class PortMachine {
|
|
|
72
108
|
/**
|
|
73
109
|
* Новое подключение клиента: регистрация сокета и старт хендшейка.
|
|
74
110
|
* @param {string} socketId
|
|
111
|
+
* @param {Object} [opts]
|
|
112
|
+
* @param {boolean} [opts.resume] - переподключение к матчу: CONFIG_DATA
|
|
113
|
+
* не шлётся, ждём RESUME_REQUEST (только при включённом resumeGraceMs).
|
|
75
114
|
*/
|
|
76
|
-
connect(socketId) {
|
|
115
|
+
connect(socketId, { resume = false } = {}) {
|
|
77
116
|
if (this._clients.has(socketId)) {
|
|
78
117
|
return;
|
|
79
118
|
}
|
|
80
119
|
|
|
81
120
|
this._socketManager.addUser(socketId, this._makeSocket(socketId));
|
|
82
121
|
|
|
122
|
+
// место уже занято участником — полнота комнаты его не касается
|
|
123
|
+
if (resume && this._resumeGraceMs > 0) {
|
|
124
|
+
this._connectForResume(socketId);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
83
128
|
// комната заполнена (люди + боты) — отказ без порт-машины
|
|
84
129
|
// (очереди ожидания легаси-сервера в P2P-комнате нет)
|
|
85
130
|
if (this._host.isFull) {
|
|
@@ -103,6 +148,51 @@ export default class PortMachine {
|
|
|
103
148
|
this._socketManager.sendConfig(socketId, this._clientCfg);
|
|
104
149
|
}
|
|
105
150
|
|
|
151
|
+
// соединение ждёт RESUME_REQUEST; молчание дольше таймаута — закрыть
|
|
152
|
+
_connectForResume(socketId) {
|
|
153
|
+
const state = {
|
|
154
|
+
gameId: undefined,
|
|
155
|
+
enabled: new Array(PORT_COUNT).fill(false),
|
|
156
|
+
resumeTimer: null,
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
this._clients.set(socketId, state);
|
|
160
|
+
state.methods = this._buildPortMethods(socketId, state);
|
|
161
|
+
state.enabled[PC_RESUME_REQUEST] = true;
|
|
162
|
+
|
|
163
|
+
state.resumeTimer = clock.setTimeout(() => {
|
|
164
|
+
state.resumeTimer = null;
|
|
165
|
+
this._dropConnection(socketId, closeCodes.handshakeTimeout);
|
|
166
|
+
}, this._resumeRequestTimeoutMs);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// закрывает соединение без участника (отказ возобновления, таймаут)
|
|
170
|
+
_dropConnection(socketId, code) {
|
|
171
|
+
const state = this._clients.get(socketId);
|
|
172
|
+
|
|
173
|
+
if (!state) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
this._clearResumeTimer(state);
|
|
178
|
+
this._socketManager.close(socketId, code);
|
|
179
|
+
this._socketManager.removeUser(socketId);
|
|
180
|
+
this._clients.delete(socketId);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
_clearResumeTimer(state) {
|
|
184
|
+
if (state.resumeTimer) {
|
|
185
|
+
clock.clearTimeout(state.resumeTimer);
|
|
186
|
+
state.resumeTimer = null;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
_enableInGame(state) {
|
|
191
|
+
for (const port of IN_GAME_PORTS) {
|
|
192
|
+
state.enabled[port] = true;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
106
196
|
/**
|
|
107
197
|
* Подключение участника, уже восстановленного в HostGame (эстафета
|
|
108
198
|
* Worker'ов, Этап 5.2): порт-машина поднимается сразу в игровом
|
|
@@ -123,12 +213,7 @@ export default class PortMachine {
|
|
|
123
213
|
};
|
|
124
214
|
|
|
125
215
|
state.methods = this._buildPortMethods(socketId, state);
|
|
126
|
-
state
|
|
127
|
-
state.enabled[PC_FIRST_SHOT_READY] = true;
|
|
128
|
-
state.enabled[PC_KEYS_DATA] = true;
|
|
129
|
-
state.enabled[PC_CHAT_DATA] = true;
|
|
130
|
-
state.enabled[PC_VOTE_DATA] = true;
|
|
131
|
-
state.enabled[PC_PONG] = true;
|
|
216
|
+
this._enableInGame(state);
|
|
132
217
|
|
|
133
218
|
this._clients.set(socketId, state);
|
|
134
219
|
}
|
|
@@ -169,13 +254,156 @@ export default class PortMachine {
|
|
|
169
254
|
return;
|
|
170
255
|
}
|
|
171
256
|
|
|
257
|
+
this._clearResumeTimer(state);
|
|
172
258
|
this._socketManager.removeUser(socketId);
|
|
259
|
+
this._clients.delete(socketId);
|
|
173
260
|
|
|
174
|
-
if (state.gameId
|
|
175
|
-
|
|
261
|
+
if (state.gameId === undefined) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// обрыв без LEAVE (host-migration этап 4): место держится resumeGraceMs,
|
|
266
|
+
// если участнику есть куда вернуться (он вошёл в матч и получил ключ)
|
|
267
|
+
if (this._resumeGraceMs > 0 && this._host.detachUser(state.gameId)) {
|
|
268
|
+
this._startGrace(state.gameId);
|
|
269
|
+
return;
|
|
176
270
|
}
|
|
177
271
|
|
|
272
|
+
this._host.removeUser(state.gameId);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// ожидание возврата отсоединённого участника; не дождались — снять
|
|
276
|
+
_startGrace(gameId) {
|
|
277
|
+
this._stopGrace(gameId);
|
|
278
|
+
|
|
279
|
+
this._graceTimers.set(
|
|
280
|
+
gameId,
|
|
281
|
+
clock.setTimeout(() => {
|
|
282
|
+
this._graceTimers.delete(gameId);
|
|
283
|
+
|
|
284
|
+
if (this._host.isDetached(gameId)) {
|
|
285
|
+
this._host.removeUser(gameId);
|
|
286
|
+
}
|
|
287
|
+
}, this._resumeGraceMs),
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Ожидание возврата людей, поднятых из контрольной точки отсоединёнными
|
|
293
|
+
* (host-migration этап 7.4): у них не было обрыва на этом хосте, и
|
|
294
|
+
* таймер им никто не заводил — не вернувшийся занимал бы слот вечно.
|
|
295
|
+
* Отсчёт — со старта восстановленного матча.
|
|
296
|
+
* @param {Iterable<number>} gameIds
|
|
297
|
+
*/
|
|
298
|
+
startGraceFor(gameIds) {
|
|
299
|
+
for (const gameId of gameIds) {
|
|
300
|
+
if (!this._host.isDetached(gameId) || this._graceTimers.has(gameId)) {
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (this._resumeGraceMs > 0) {
|
|
305
|
+
this._startGrace(gameId);
|
|
306
|
+
} else {
|
|
307
|
+
this._host.removeUser(gameId);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
_stopGrace(gameId) {
|
|
313
|
+
const timer = this._graceTimers.get(gameId);
|
|
314
|
+
|
|
315
|
+
if (timer) {
|
|
316
|
+
clock.clearTimeout(timer);
|
|
317
|
+
this._graceTimers.delete(gameId);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// RESUME_REQUEST { v, gameId, resumeKey, token }: проверки по порядку —
|
|
322
|
+
// версия формата, место (существует, ключ совпал), личность по токену
|
|
323
|
+
_resume(socketId, state, data) {
|
|
324
|
+
const reject = reason => {
|
|
325
|
+
if (!this._clients.has(socketId)) {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
this._socketManager.sendResumeResult(socketId, { ok: false, reason });
|
|
330
|
+
this._dropConnection(socketId);
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
if (!data || typeof data !== 'object' || data.v !== RESUME_VERSION) {
|
|
334
|
+
reject('version');
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const { gameId } = data;
|
|
339
|
+
const target = this._host.getResumeTarget(gameId);
|
|
340
|
+
|
|
341
|
+
// неизвестный участник и чужой ключ неразличимы снаружи: перебором
|
|
342
|
+
// gameId нельзя узнать, чьё место существует
|
|
343
|
+
if (!target || !resumeKeysEqual(data.resumeKey, target.resumeKey)) {
|
|
344
|
+
reject('unknown');
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
this._identity
|
|
349
|
+
.resolve(data, socketId)
|
|
350
|
+
.then(name => {
|
|
351
|
+
// клиент отключился, пока проверялась личность
|
|
352
|
+
if (!this._clients.has(socketId)) {
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (name !== target.identityName) {
|
|
357
|
+
reject('auth');
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// место могли снять, пока проверялась личность (истекло ожидание)
|
|
362
|
+
const current = this._host.getResumeTarget(gameId);
|
|
363
|
+
|
|
364
|
+
if (!current || !resumeKeysEqual(data.resumeKey, current.resumeKey)) {
|
|
365
|
+
reject('unknown');
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
this._takeOver(gameId, socketId);
|
|
370
|
+
this._clearResumeTimer(state);
|
|
371
|
+
this._stopGrace(gameId);
|
|
372
|
+
|
|
373
|
+
state.gameId = gameId;
|
|
374
|
+
state.enabled[PC_RESUME_REQUEST] = false;
|
|
375
|
+
this._enableInGame(state);
|
|
376
|
+
|
|
377
|
+
this._host.resumeUser(gameId, socketId, data.token);
|
|
378
|
+
})
|
|
379
|
+
.catch(() => reject('auth'));
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// перехват полуоткрытой сессии: участник ещё числится за прежним
|
|
383
|
+
// соединением (обрыв, которого хост не заметил) — закрыть его, не снимая
|
|
384
|
+
// участника; его поздний disconnect уже ничего не найдёт
|
|
385
|
+
_takeOver(gameId, socketId) {
|
|
386
|
+
for (const [otherId, other] of this._clients) {
|
|
387
|
+
if (otherId !== socketId && other.gameId === gameId) {
|
|
388
|
+
this._clients.delete(otherId);
|
|
389
|
+
this._clearResumeTimer(other);
|
|
390
|
+
this._socketManager.close(otherId);
|
|
391
|
+
this._socketManager.removeUser(otherId);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// LEAVE: игрок уходит сам — снять сразу, без ожидания возврата
|
|
397
|
+
_leave(socketId, state) {
|
|
398
|
+
const { gameId } = state;
|
|
399
|
+
|
|
400
|
+
this._socketManager.close(socketId);
|
|
401
|
+
this._socketManager.removeUser(socketId);
|
|
178
402
|
this._clients.delete(socketId);
|
|
403
|
+
|
|
404
|
+
if (gameId !== undefined) {
|
|
405
|
+
this._host.removeUser(gameId);
|
|
406
|
+
}
|
|
179
407
|
}
|
|
180
408
|
|
|
181
409
|
/**
|
|
@@ -270,12 +498,7 @@ export default class PortMachine {
|
|
|
270
498
|
// 2: modules ready
|
|
271
499
|
() => {
|
|
272
500
|
state.enabled[PC_MODULES_READY] = false;
|
|
273
|
-
state
|
|
274
|
-
state.enabled[PC_FIRST_SHOT_READY] = true;
|
|
275
|
-
state.enabled[PC_KEYS_DATA] = true;
|
|
276
|
-
state.enabled[PC_CHAT_DATA] = true;
|
|
277
|
-
state.enabled[PC_VOTE_DATA] = true;
|
|
278
|
-
state.enabled[PC_PONG] = true;
|
|
501
|
+
this._enableInGame(state);
|
|
279
502
|
|
|
280
503
|
host.sendMap(state.gameId);
|
|
281
504
|
},
|
|
@@ -283,8 +506,24 @@ export default class PortMachine {
|
|
|
283
506
|
// 3: map ready
|
|
284
507
|
() => host.mapReady(state.gameId),
|
|
285
508
|
|
|
286
|
-
// 4: first shot ready
|
|
287
|
-
|
|
509
|
+
// 4: first shot ready. Участник вошёл в матч — с этого момента ему
|
|
510
|
+
// есть куда вернуться после обрыва: секрет места (SESSION_DATA)
|
|
511
|
+
() => {
|
|
512
|
+
host.firstShotReady(state.gameId);
|
|
513
|
+
|
|
514
|
+
if (this._resumeGraceMs > 0) {
|
|
515
|
+
const resumeKey = host.issueResumeKey(state.gameId);
|
|
516
|
+
|
|
517
|
+
// gameId — клиенту для RESUME_REQUEST: наблюдатель свой id
|
|
518
|
+
// больше ниоткуда не узнаёт
|
|
519
|
+
if (resumeKey) {
|
|
520
|
+
socketManager.sendSessionData(socketId, {
|
|
521
|
+
resumeKey,
|
|
522
|
+
gameId: state.gameId,
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
},
|
|
288
527
|
|
|
289
528
|
// 5: keys data ('seq:action:name'; указатель — 'seq:aim:x:y:flags')
|
|
290
529
|
keyEventString => {
|
|
@@ -305,6 +544,15 @@ export default class PortMachine {
|
|
|
305
544
|
|
|
306
545
|
// 8: pong
|
|
307
546
|
pingId => host.updateRTT(state.gameId, pingId),
|
|
547
|
+
|
|
548
|
+
// 9: resume request — попытка одна на соединение
|
|
549
|
+
data => {
|
|
550
|
+
state.enabled[PC_RESUME_REQUEST] = false;
|
|
551
|
+
this._resume(socketId, state, data);
|
|
552
|
+
},
|
|
553
|
+
|
|
554
|
+
// 10: leave
|
|
555
|
+
() => this._leave(socketId, state),
|
|
308
556
|
];
|
|
309
557
|
}
|
|
310
558
|
}
|