vimp-engine 0.35.6 → 0.36.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.
Files changed (130) hide show
  1. package/bin/vimp-sim.js +19 -1
  2. package/core/Cargo.toml +1 -1
  3. package/package.json +1 -1
  4. package/src/client/components/controller/Controls.js +5 -0
  5. package/src/client/components/controller/Lobby.js +4 -4
  6. package/src/client/components/controller/RoomMenu.js +43 -0
  7. package/src/client/components/controller/Vote.js +18 -2
  8. package/src/client/components/model/ClientReports.js +6 -2
  9. package/src/client/components/model/Controls.js +27 -0
  10. package/src/client/components/model/Games.js +5 -1
  11. package/src/client/components/model/Lobby.js +32 -24
  12. package/src/client/components/model/LobbyAuth.js +20 -4
  13. package/src/client/components/model/RoomMenu.js +121 -0
  14. package/src/client/components/model/Stat.js +2 -1
  15. package/src/client/components/model/Vote.js +122 -4
  16. package/src/client/components/view/Auth.js +20 -10
  17. package/src/client/components/view/ClientReports.js +16 -5
  18. package/src/client/components/view/Games.js +39 -14
  19. package/src/client/components/view/Lobby.js +90 -43
  20. package/src/client/components/view/LobbyAuth.js +13 -7
  21. package/src/client/components/view/Panel.js +1 -2
  22. package/src/client/components/view/RoomMenu.js +69 -0
  23. package/src/client/debug.js +24 -1
  24. package/src/client/lib/FpsMeter.js +46 -0
  25. package/src/client/lib/JoinRetry.js +73 -0
  26. package/src/client/lib/diagnostics.js +28 -5
  27. package/src/client/lib/hostCaps.js +82 -0
  28. package/src/client/lib/hostUnloadGuard.js +108 -0
  29. package/src/client/lib/hostVoteCommand.js +92 -0
  30. package/src/client/lib/pageReload.js +29 -0
  31. package/src/client/lib/roomLink.js +257 -0
  32. package/src/client/lib/signalingErrors.js +74 -0
  33. package/src/client/main.js +674 -451
  34. package/src/client/network/HiddenHostHealthLog.js +76 -0
  35. package/src/client/network/HostConnectionManager.js +62 -8
  36. package/src/client/network/HostController.js +376 -23
  37. package/src/client/network/HostHealthPolicy.js +307 -0
  38. package/src/client/network/HostHealthReporter.js +54 -0
  39. package/src/client/network/HostPrewarm.js +235 -0
  40. package/src/client/network/LoopbackTransport.js +16 -1
  41. package/src/client/network/PlannedHandoff.js +366 -0
  42. package/src/client/network/Promotion.js +420 -0
  43. package/src/client/network/RoomPeersReporter.js +81 -0
  44. package/src/client/network/SessionSupervisor.js +730 -0
  45. package/src/client/network/SignalingClient.js +183 -30
  46. package/src/client/network/StandbyReceiver.js +287 -0
  47. package/src/client/network/StandbySender.js +319 -0
  48. package/src/client/network/TokenHandoffTimer.js +81 -0
  49. package/src/client/network/WebRtcManager.js +338 -11
  50. package/src/client/network/policyClose.js +32 -2
  51. package/src/client/network/standbyChunks.js +185 -0
  52. package/src/client/session/GuestSession.js +428 -0
  53. package/src/client/session/HandoffFlow.js +399 -0
  54. package/src/client/session/HostRole.js +708 -0
  55. package/src/client/session/Membership.js +179 -0
  56. package/src/client/session/PromotionFlow.js +325 -0
  57. package/src/client/session/RouteBoot.js +432 -0
  58. package/src/client/session/StandbyRole.js +195 -0
  59. package/src/client/session/hostRoomPrep.js +152 -0
  60. package/src/client/session/roomContext.js +32 -0
  61. package/src/client/style.css +83 -22
  62. package/src/client/views/gameShell.js +7 -0
  63. package/src/client/views/includes/informer.pug +4 -0
  64. package/src/client/views/includes/lobby.pug +0 -3
  65. package/src/client/views/includes/roomMenu.pug +9 -0
  66. package/src/client/views/index.pug +1 -0
  67. package/src/config/closeCodes.js +1 -1
  68. package/src/config/env.js +37 -0
  69. package/src/config/hostDefaults.js +24 -0
  70. package/src/config/lobby.js +154 -3
  71. package/src/config/master.js +104 -21
  72. package/src/config/wsports.js +8 -0
  73. package/src/devtools/RecordingSocketManager.js +3 -2
  74. package/src/devtools/ScenarioRunner.js +123 -8
  75. package/src/devtools/VirtualClient.js +20 -0
  76. package/src/devtools/contract/format.js +4 -1
  77. package/src/devtools/contract/index.js +1 -2
  78. package/src/devtools/contract/rules/a2-package-scripts.js +1 -3
  79. package/src/devtools/contract/rules/b8-system-messages.js +1 -1
  80. package/src/devtools/contract/rules/b9-vote-names.js +25 -1
  81. package/src/devtools/contract/rules/c11-stat-mode.js +4 -1
  82. package/src/devtools/contract/rules/c6-stat-columns.js +1 -1
  83. package/src/devtools/contract/rules/c7-key-sets.js +8 -4
  84. package/src/devtools/contract/rules/d3-snapshot-interp.js +2 -1
  85. package/src/devtools/contract/rules/e1-sound-pairs.js +3 -1
  86. package/src/devtools/contract/rules/e4-map-layers.js +13 -2
  87. package/src/devtools/contract/rules/e5-map-radar-walls.js +7 -1
  88. package/src/devtools/contract/rules/index.js +38 -5
  89. package/src/devtools/invariants.js +10 -4
  90. package/src/devtools/pluginLoader.js +1 -4
  91. package/src/devtools/report.js +17 -1
  92. package/src/host/DebugRecorder.js +6 -1
  93. package/src/host/GameCoreAdapter.js +45 -0
  94. package/src/host/HostGame.js +928 -59
  95. package/src/host/PortMachine.js +268 -20
  96. package/src/host/host.worker.js +222 -16
  97. package/src/host/meta/SocketManager.js +20 -0
  98. package/src/host/meta/core/RoundManager.js +89 -14
  99. package/src/host/meta/modules/Accolades.js +4 -1
  100. package/src/host/meta/modules/Panel.js +30 -0
  101. package/src/host/meta/modules/PlayerDataSync.js +255 -46
  102. package/src/host/meta/modules/RTTManager.js +34 -0
  103. package/src/host/meta/modules/TimerManager.js +215 -18
  104. package/src/host/meta/modules/chat/systemMessages.js +22 -0
  105. package/src/host/meta/player/HumanParticipant.js +10 -0
  106. package/src/host/meta/player/ParticipantManager.js +98 -5
  107. package/src/lib/AbstractTimer.js +63 -1
  108. package/src/lib/Publisher.js +26 -0
  109. package/src/lib/capabilities.js +5 -0
  110. package/src/lib/checkpointCodec.js +125 -0
  111. package/src/lib/clientIp.js +9 -2
  112. package/src/lib/createHostRuntime.js +95 -6
  113. package/src/lib/formOptions.js +3 -1
  114. package/src/lib/gameConfigView.js +10 -0
  115. package/src/lib/gameId.js +13 -0
  116. package/src/lib/jwt.js +3 -1
  117. package/src/lib/loadGamePackage.js +2 -2
  118. package/src/lib/resumeKey.js +38 -0
  119. package/src/lib/roomId.js +39 -0
  120. package/src/lib/roomSettings.js +59 -0
  121. package/src/lib/validators.js +7 -0
  122. package/tests/fixtures/generations/gen-api3/host/ScriptedManager.js +2 -3
  123. package/tests/fixtures/generations/gen-api3/host/fakeCore.js +31 -5
  124. package/tests/fixtures/generations/gen-api3/manifest.json +1 -3
  125. package/tests/fixtures/generations/gen-api4/host/ScriptedManager.js +2 -3
  126. package/tests/fixtures/generations/gen-api4/host/fakeCore.js +31 -5
  127. package/tests/fixtures/generations/gen-api4/manifest.json +1 -3
  128. package/tests/fixtures/miniGame/config/client.js +15 -0
  129. package/tests/fixtures/miniGame/host/ScriptedManager.js +2 -3
  130. package/tests/fixtures/miniGame/host/fakeCore.js +31 -5
@@ -0,0 +1,76 @@
1
+ // Сводка здоровья хоста за эпизод скрытой вкладки (host-migration этап
2
+ // 9a): браузер троттлит Worker скрытой вкладки (этап 0), и в журнал
3
+ // клиентских ошибок уходит, насколько просел матч, — одна запись на эпизод,
4
+ // а не на каждую секунду (журнал дедуплицирует по коду и хранит details
5
+ // только первой записи).
6
+ //
7
+ // Эпизод закрывается возвратом видимости, потерей роли хоста или
8
+ // выгрузкой страницы (flush). Следующие эпизоды той же сессии журнал
9
+ // склеивает с первым: растёт счётчик, details остаются первого — осознанно.
10
+ // Эпизод без единой метрики (матч стоял, скрытие короче окна цикла) не
11
+ // пишется. Без DOM: видимость сообщает владелец.
12
+
13
+ export const HIDDEN_HEALTH_CODE = 'engine.host.hiddenHealth';
14
+
15
+ export default class HiddenHostHealthLog {
16
+ /**
17
+ * @param {Object} options
18
+ * @param {Function} options.warn - (code, summary) запись в журнал.
19
+ * @param {boolean} [options.hidden] - вкладка скрыта в момент создания.
20
+ * @param {Function} [options.now] - монотонные часы, мс.
21
+ */
22
+ constructor({ warn, hidden = false, now = () => performance.now() }) {
23
+ this._warn = warn;
24
+ this._now = now;
25
+ this._episode = null;
26
+
27
+ this.setHidden(hidden);
28
+ }
29
+
30
+ setHidden(hidden) {
31
+ if (hidden && !this._episode) {
32
+ this._episode = {
33
+ startedAt: this._now(),
34
+ samples: 0,
35
+ minTickRate: Infinity,
36
+ maxGapMs: 0,
37
+ lostMs: 0,
38
+ };
39
+ } else if (!hidden) {
40
+ this.flush();
41
+ }
42
+ }
43
+
44
+ // метрика Worker'а (HostController.onHealth): в зачёт только скрытой вкладки
45
+ add(health) {
46
+ const episode = this._episode;
47
+
48
+ if (!episode || !health) {
49
+ return;
50
+ }
51
+
52
+ episode.samples += 1;
53
+ episode.minTickRate = Math.min(episode.minTickRate, health.tickRate);
54
+ episode.maxGapMs = Math.max(episode.maxGapMs, health.maxGapMs);
55
+ episode.lostMs += health.lostMs;
56
+ }
57
+
58
+ // закрыть эпизод (если идёт) и записать сводку
59
+ flush() {
60
+ const episode = this._episode;
61
+
62
+ this._episode = null;
63
+
64
+ if (!episode || episode.samples === 0) {
65
+ return;
66
+ }
67
+
68
+ this._warn(HIDDEN_HEALTH_CODE, {
69
+ hiddenMs: Math.round(this._now() - episode.startedAt),
70
+ samples: episode.samples,
71
+ minTickRate: episode.minTickRate,
72
+ maxGapMs: episode.maxGapMs,
73
+ lostMs: episode.lostMs,
74
+ });
75
+ }
76
+ }
@@ -26,7 +26,8 @@ export default class HostConnectionManager {
26
26
  this._threshold = opts.backpressureThreshold ?? 262144; // 256 КБ
27
27
  this._onPeersChange = opts.onPeersChange;
28
28
 
29
- this._peers = new Map(); // clientId → { pc, meta, state, openCount }
29
+ // clientId → { pc, meta, state, openCount, memberId }
30
+ this._peers = new Map();
30
31
 
31
32
  signaling.publisher.on('webrtc_offer', 'onOffer', this);
32
33
  signaling.publisher.on('ice_candidate', 'onRemoteCandidate', this);
@@ -35,19 +36,39 @@ export default class HostConnectionManager {
35
36
 
36
37
  // приём SDP-оффера клиента: создаёт peer, отвечает answer
37
38
  async onOffer(msg) {
38
- const { clientId, sdp } = msg;
39
+ // memberId — id вкладки гостя (переживает реконнект сигналинга);
40
+ // понадобится назначению преемника (host-migration, этап 6)
41
+ const { clientId, sdp, memberId = null } = msg;
42
+ // переподключение гостя к матчу (host-migration этап 4): Worker не
43
+ // начнёт хендшейк, а будет ждать RESUME_REQUEST
44
+ const resume = msg.resume === true;
39
45
 
40
46
  if (this._peers.has(clientId)) {
41
- return;
47
+ // гость с тем же сигнальным id переподключается: для него прежнее
48
+ // соединение уже мертво, а у нас ещё полуоткрыто — снять его
49
+ // (Worker отсоединит участника и дождётся возобновления)
50
+ if (!resume) {
51
+ return;
52
+ }
53
+
54
+ this._closePeer(clientId);
42
55
  }
43
56
 
44
57
  const pc = this._peerFactory({ iceServers: this._iceServers });
45
- const peer = { pc, meta: null, state: null, openCount: 0 };
58
+ const peer = {
59
+ pc,
60
+ meta: null,
61
+ state: null,
62
+ openCount: 0,
63
+ memberId,
64
+ resume,
65
+ };
46
66
 
47
67
  this._peers.set(clientId, peer);
48
68
 
49
69
  // каналы создаёт offerer — ловим их здесь
50
- pc.ondatachannel = event => this._wireChannel(clientId, peer, event.channel);
70
+ pc.ondatachannel = event =>
71
+ this._wireChannel(clientId, peer, event.channel);
51
72
 
52
73
  pc.onicecandidate = event => {
53
74
  if (event.candidate) {
@@ -123,11 +144,14 @@ export default class HostConnectionManager {
123
144
  peer.openCount += 1;
124
145
 
125
146
  // оба канала открыты — поднимаем соединение клиента в Worker'е
126
- // (Worker сразу шлёт CONFIG_DATA по meta)
147
+ // (Worker сразу шлёт CONFIG_DATA по meta, а при resume ждёт
148
+ // RESUME_REQUEST)
127
149
  if (peer.openCount === 2) {
128
150
  this._controller.open(clientId, {
129
- onMessage: (payload, reliable) => this._deliver(peer, payload, reliable),
151
+ onMessage: (payload, reliable) =>
152
+ this._deliver(peer, payload, reliable),
130
153
  onClose: () => this._closePeer(clientId),
154
+ resume: peer.resume,
131
155
  });
132
156
 
133
157
  this._onPeersChange?.(this._peers.size);
@@ -182,7 +206,37 @@ export default class HostConnectionManager {
182
206
  this._onPeersChange?.(this._peers.size);
183
207
  }
184
208
 
185
- // число активных пиров (для currentPlayers у мастера)
209
+ // RTCPeerConnection гостя по memberId (канал standby преемнику,
210
+ // host-migration этап 6): только пир с открытыми meta/state; null — нет
211
+ peerConnectionOf(memberId) {
212
+ if (!memberId) {
213
+ return null;
214
+ }
215
+
216
+ for (const peer of this._peers.values()) {
217
+ if (peer.memberId === memberId && peer.openCount === 2) {
218
+ return peer.pc;
219
+ }
220
+ }
221
+
222
+ return null;
223
+ }
224
+
225
+ // memberId гостей с открытыми meta и state — отчёт room_peers мастеру
226
+ // (кого хост действительно видит)
227
+ connectedMemberIds() {
228
+ const ids = [];
229
+
230
+ for (const peer of this._peers.values()) {
231
+ if (peer.openCount === 2 && peer.memberId) {
232
+ ids.push(peer.memberId);
233
+ }
234
+ }
235
+
236
+ return ids;
237
+ }
238
+
239
+ // число активных пиров
186
240
  get peerCount() {
187
241
  return this._peers.size;
188
242
  }