vimp-engine 0.39.0 → 0.40.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 (45) hide show
  1. package/README.md +2 -2
  2. package/bin/vimp-sim.js +2 -2
  3. package/core/Cargo.toml +2 -2
  4. package/package.json +3 -4
  5. package/src/client/boot.js +11 -87
  6. package/src/client/components/model/RoomMenu.js +1 -1
  7. package/src/client/components/view/Auth.js +3 -3
  8. package/src/client/lib/formBuilder.js +7 -6
  9. package/src/client/lib/hostVoteCommand.js +2 -2
  10. package/src/client/lib/pickActiveGame.js +3 -12
  11. package/src/client/lib/retryOnNetworkError.js +0 -9
  12. package/src/client/main.js +43 -90
  13. package/src/client/network/SessionSupervisor.js +14 -14
  14. package/src/client/network/WebRtcManager.js +1 -2
  15. package/src/client/network/policyClose.js +10 -77
  16. package/src/client/style.css +4 -6
  17. package/src/client/views/gameShell.js +2 -4
  18. package/src/client/views/includes/auth.pug +1 -4
  19. package/src/client/views/includes/lobby.pug +3 -6
  20. package/src/config/closeCodes.js +7 -9
  21. package/src/config/gameCodes.js +1 -1
  22. package/src/config/hostDefaults.js +2 -2
  23. package/src/config/lobby.js +3 -4
  24. package/src/config/wsports.js +1 -1
  25. package/src/devtools/ScenarioRunner.js +1 -1
  26. package/src/devtools/builtinScenario.js +2 -2
  27. package/src/devtools/contract/rules/a2-package-scripts.js +1 -1
  28. package/src/devtools/contract/rules/a4-vite-config.js +1 -1
  29. package/src/devtools/contract/rules/c4-component-dependencies.js +1 -1
  30. package/src/devtools/invariants.js +1 -1
  31. package/src/devtools/pluginLoader.js +2 -2
  32. package/src/host/HostGame.js +4 -4
  33. package/src/host/PortMachine.js +4 -21
  34. package/src/host/commandFailure.js +2 -2
  35. package/src/host/identity.js +3 -3
  36. package/src/host/meta/core/CommandProcessor.js +2 -2
  37. package/src/host/meta/modules/Accolades.js +1 -1
  38. package/src/lib/createHostRuntime.js +1 -1
  39. package/src/lib/loadGamePackage.js +7 -9
  40. package/src/lib/offlinePlayerData.js +1 -1
  41. package/src/lib/validators.js +2 -2
  42. package/src/lib/voicePacket.js +1 -1
  43. package/src/standalone/index.js +3 -2
  44. package/src/client/lib/footerLink.js +0 -30
  45. package/src/client/network/WebSocketTransport.js +0 -78
@@ -52,8 +52,8 @@ export default class SessionSupervisor {
52
52
  * @param {Object} opts
53
53
  * @param {Function} opts.onMessage - входящее сообщение текущего
54
54
  * транспорта (строка или ArrayBuffer).
55
- * @param {Function} opts.onTerminal - (closeCode) сессия закрыта
56
- * окончательно: путь прежнего handleDisconnect.
55
+ * @param {Function} opts.onTerminal - сессия закрыта окончательно
56
+ * (handleDisconnect).
57
57
  * @param {Function} [opts.onVoice] - (data) голосовой пакет текущего
58
58
  * транспорта в состоянии inGame (бинарный, без разбора).
59
59
  * @param {Function} [opts.onStateChange] - (state, prev).
@@ -63,10 +63,10 @@ export default class SessionSupervisor {
63
63
  * матче (до переподключения): свидетельство мастеру (host_unreachable).
64
64
  * @param {Function} [opts.onColdRestart] - новый хост поднял матч заново
65
65
  * (host_changed cold): возобновлять нечего, только чистый вход.
66
- * @param {Function} [opts.isTerminalClose] - (closeCode) закрытие по
67
- * политике или кику: возвращаться некуда.
66
+ * @param {Function} [opts.isTerminalClose] - () закрытие после
67
+ * терминальной причины (кик, полная комната): возвращаться некуда.
68
68
  * @param {Object|null} [opts.reconnect] - null — переподключения нет
69
- * (solo, dedicated, вкладка-хост на loopback). Иначе:
69
+ * (solo, вкладка-хост на loopback). Иначе:
70
70
  * { createTransport() → новый транспорт с resume (уже connect'нутый),
71
71
  * getToken() → identity-токен для RESUME_REQUEST }.
72
72
  * @param {Object} [opts.timing] - { reconnectWindowMs, reconnectBaseDelayMs,
@@ -493,7 +493,7 @@ export default class SessionSupervisor {
493
493
  const handlers = {
494
494
  message: data => this._handleMessage(transport, data),
495
495
  voice: data => this._handleVoice(transport, data),
496
- close: closeCode => this._handleClose(transport, closeCode),
496
+ close: () => this._handleClose(transport),
497
497
  open: () => this._handleOpen(transport),
498
498
  };
499
499
 
@@ -573,7 +573,7 @@ export default class SessionSupervisor {
573
573
  }
574
574
  }
575
575
 
576
- _handleClose(transport, closeCode) {
576
+ _handleClose(transport) {
577
577
  if (transport !== this._transport) {
578
578
  return;
579
579
  }
@@ -590,7 +590,7 @@ export default class SessionSupervisor {
590
590
  // транспорт, выданный resumeWith без фабрики попыток: повторять нечем
591
591
  if (this._reconnect === null) {
592
592
  if (!this._failRoleResume('closed')) {
593
- this._terminate(closeCode);
593
+ this._terminate();
594
594
  }
595
595
 
596
596
  return;
@@ -600,23 +600,23 @@ export default class SessionSupervisor {
600
600
  return;
601
601
  }
602
602
 
603
- if (this._canResume(closeCode)) {
603
+ if (this._canResume()) {
604
604
  this._startReconnect();
605
605
  return;
606
606
  }
607
607
 
608
- this._terminate(closeCode);
608
+ this._terminate();
609
609
  }
610
610
 
611
611
  // нетерминальным бывает только обрыв посреди матча, у которого есть место
612
612
  // для возврата и кому возвращаться
613
- _canResume(closeCode) {
613
+ _canResume() {
614
614
  return (
615
615
  this._reconnect !== null &&
616
616
  !this._userLeft &&
617
617
  this._state === S.inGame &&
618
618
  this._session !== null &&
619
- !this._isTerminalClose(closeCode)
619
+ !this._isTerminalClose()
620
620
  );
621
621
  }
622
622
 
@@ -728,7 +728,7 @@ export default class SessionSupervisor {
728
728
 
729
729
  // ***** завершение ***** //
730
730
 
731
- _terminate(closeCode) {
731
+ _terminate() {
732
732
  if (this._state === S.closed) {
733
733
  return;
734
734
  }
@@ -738,7 +738,7 @@ export default class SessionSupervisor {
738
738
  this._clearMigrationTimer();
739
739
  this._onRoleResumeFailed = null;
740
740
  this._setState(S.closed);
741
- this._onTerminal(closeCode);
741
+ this._onTerminal();
742
742
  }
743
743
 
744
744
  _setState(state) {
@@ -493,8 +493,7 @@ export default class WebRtcManager {
493
493
  channel.onmessage = event => this.publisher.emit('voice', event.data);
494
494
  }
495
495
 
496
- // кода закрытия у data channel нет: 'close' эмитится без аргумента, как
497
- // и раньше (код приходит только от WebSocket-транспорта dedicated)
496
+ // кода закрытия у data channel нет: 'close' эмитится без аргумента
498
497
  _emitClose() {
499
498
  if (this._closed) {
500
499
  return;
@@ -1,84 +1,17 @@
1
- // Отказы по политике: коды закрытия, которыми dedicated-сервер отбивает
2
- // соединение (`src/dedicated/main.js` — invalidOrigin, handshakeTimeout,
3
- // tooManyConnections) и которыми порт-машина отбивает полную комнату
4
- // (`src/host/PortMachine.js` — roomFull). Числа берутся из общей карты
5
- // `src/config/closeCodes.js`, чтобы серверные контуры и клиент не разъехались
6
- // молча; тест модуля требует явного решения по каждому коду карты.
7
- //
8
- // Клиенту от кода нужны два разных ответа, и совпадают они не всегда, поэтому
9
- // знание разведено на два экспорта (review-4.md, R4-2):
10
- //
11
- // - перезагружаться ли. Ни на одном из этих кодов перезагрузка не помогает:
12
- // лимит частоты подключений съел бы ей же очередное соединение, таймаут
13
- // хендшейка запустился бы заново, origin страницы не меняется вовсе, а
14
- // слот в полной комнате перезагрузка не освобождает. Брошенная вкладка
15
- // перезагружалась бы вечно, а ждущий слот игрок за 30 витков сам упёрся бы
16
- // в лимит подключений и получил бы чужую причину отказа;
17
- // - какой текст показать. Тексты запасные: `handleDisconnect` берёт их
18
- // только тогда, когда сервер причину не прислал сам (`terminalInformShown`
19
- // — `client/main.js`). Полная комната, например, приезжает с причиной от
20
- // порт-машины (TECH_INFORM перед `close`), и она всегда побеждает —
21
- // запись 4006 ниже отработает, только если тот кадр не доехал. Тексты
22
- // здесь, а не в `techInformList`: список переопределяет игра, и движковый
23
- // индекс дал бы у неё «Unknown error».
24
-
25
- import closeCodes from '../../config/closeCodes.js';
26
-
27
- const {
28
- invalidOrigin,
29
- roomFull,
30
- handshakeTimeout,
31
- tooManyConnections,
32
- kickIdle,
33
- kickForMaxLatency,
34
- kickForMissedPings,
35
- } = closeCodes;
36
-
37
- const NO_RELOAD_CLOSE_CODES = new Set([
38
- invalidOrigin,
39
- roomFull,
40
- handshakeTimeout,
41
- tooManyConnections,
42
- ]);
43
-
44
- export const POLICY_CLOSE_INFORMS = {
45
- [invalidOrigin]:
46
- 'This page is not allowed by the game server. Check the address you opened.',
47
- [roomFull]: 'The room is full. Try again later.',
48
- [handshakeTimeout]:
49
- 'Idle connection closed before the match started. Reload the page when you are ready to play.',
50
- [tooManyConnections]:
51
- 'Too many connections from your address. Wait a minute and reload the page.',
52
- };
53
-
54
- /**
55
- * @param {number} [closeCode] - Код закрытия транспорта. Его отдаёт только
56
- * WebSocket-транспорт (dedicated); в P2P и solo кода нет вовсе.
57
- * @returns {boolean} Уместна ли перезагрузка страницы после разрыва.
58
- */
59
- export function shouldReloadAfterClose(closeCode) {
60
- return !NO_RELOAD_CLOSE_CODES.has(closeCode);
61
- }
62
-
63
- // Кики хоста (host/HostGame.js). В P2P кода закрытия нет вовсе — причина
64
- // приезжает только TECH_INFORM-ключом перед закрытием (индексы
65
- // TECH_CODES из host/meta/SocketManager.js = индексы techInformList);
66
- // в dedicated есть и код. Лобби-режим уводит кикнутого на главную без
67
- // перезагрузки (client/lib/roomLink.js decideExitRoute): быстрая игра по
68
- // hash вернула бы его в ту же комнату
69
- const KICK_CLOSE_CODES = new Set([
70
- kickIdle,
71
- kickForMaxLatency,
72
- kickForMissedPings,
73
- ]);
74
-
1
+ // Кики хоста (host/HostGame.js). Кода закрытия у транспортов клиента нет —
2
+ // причина приезжает только TECH_INFORM-ключом перед закрытием (индексы
3
+ // TECH_CODES из host/meta/SocketManager.js = индексы techInformList).
4
+ // Лобби-режим уводит кикнутого на главную без перезагрузки
5
+ // (client/lib/roomLink.js decideExitRoute): быстрая игра по hash вернула бы
6
+ // его в ту же комнату.
7
+
8
+ // kickIdle, kickForMaxLatency, kickForMissedPings
75
9
  export const KICK_TECH_KEYS = new Set([3, 4, 5]);
76
10
 
77
11
  /**
78
- * @param {number} [closeCode] - Код закрытия транспорта (только WebSocket).
79
12
  * @param {number|null} [techKey] - Последний терминальный TECH_INFORM-ключ.
80
13
  * @returns {boolean} Закрыто ли соединение киком хоста.
81
14
  */
82
- export function isKickClose(closeCode, techKey) {
83
- return KICK_CLOSE_CODES.has(closeCode) || KICK_TECH_KEYS.has(techKey);
15
+ export function isKickClose(techKey) {
16
+ return KICK_TECH_KEYS.has(techKey);
84
17
  }
@@ -726,8 +726,8 @@ body.hide-cursor {
726
726
  display: block;
727
727
  }
728
728
 
729
- /* футеры лобби и формы входа: одна и та же полоса из трёх ячеек —
730
- ссылка на пакет, его версия, копирайт. Оба заполняет JS, поэтому вид
729
+ /* футеры лобби (Terms/Privacy | версия движка | копирайт) и формы входа
730
+ (версия игры | копирайт): одна полоса, оба заполняет JS, поэтому вид
731
731
  держится в одном месте (#lobby-footer лежит в секции lobby ниже только
732
732
  разметкой) */
733
733
  #auth-link,
@@ -761,9 +761,8 @@ body.hide-cursor {
761
761
  padding-left: 10px;
762
762
  }
763
763
 
764
- /* три равные ячейки: середину держит не space-between, а сама равность —
765
- любая из крайних бывает пустой (у пакета игры может не быть метаданных),
766
- и при space-between версия съезжала бы с центра */
764
+ /* равные ячейки: версия игры у левого края, копирайт у правого; пустая
765
+ версия (у пакета игры может не быть метаданных) не сдвигает копирайт */
767
766
  #auth-link p {
768
767
  margin: 0;
769
768
  flex: 1;
@@ -781,7 +780,6 @@ body.hide-cursor {
781
780
  text-align: left;
782
781
  }
783
782
 
784
- #auth-version,
785
783
  #lobby-version {
786
784
  text-align: center;
787
785
  }
@@ -55,9 +55,7 @@ const SHELL = [
55
55
  tag: 'div',
56
56
  id: 'auth-link',
57
57
  children: [
58
- // версия пакета ИГРЫ (из манифеста) и ссылка на документацию
59
- // (только в dev-сборке) — заполняет main.js по AUTH_DATA
60
- { tag: 'p', children: [{ tag: 'a', id: 'auth-docs-link' }] },
58
+ // версия пакета ИГРЫ (из манифеста) — заполняет main.js по AUTH_DATA
61
59
  { tag: 'p', id: 'auth-version' },
62
60
  { tag: 'p', text: `© ${new Date().getFullYear()} VIMP` },
63
61
  ],
@@ -67,7 +65,7 @@ const SHELL = [
67
65
  ],
68
66
  },
69
67
  { tag: 'div', id: 'game-informer' },
70
- // оверлей переподключения (host-migration этап 4): в solo/dedicated не
68
+ // оверлей переподключения (host-migration этап 4): в solo не
71
69
  // показывается, но разметка общая с informer.pug
72
70
  {
73
71
  tag: 'div',
@@ -16,9 +16,6 @@ div#auth
16
16
 
17
17
  div#auth-link
18
18
  //- версия пакета ИГРЫ (мастер кладёт её в манифест, читая package.json
19
- //- пакета) и ссылка на документацию (только в dev-сборке) —
20
- //- проставляет main.js по AUTH_DATA; без данных ячейки остаются пустыми
21
- p
22
- a#auth-docs-link
19
+ //- пакета) — проставляет main.js по AUTH_DATA; без данных ячейка пустая
23
20
  p#auth-version
24
21
  p © #{new Date().getFullYear()} VIMP
@@ -32,8 +32,7 @@ div#lobby(style='display:none')
32
32
  label.form-label(for='lobby-game')= 'Select game'
33
33
  select#lobby-game.field-select
34
34
  //- поля настроек комнаты генерируются по явной схеме roomForm
35
- //- манифеста активной игры (docs.vimp.lgick.dev/en/plugin-api "Form
36
- //- schema")
35
+ //- манифеста активной игры (VIMP docs: plugin-api.md § "Form schema")
37
36
  div#lobby-fields
38
37
  //- ошибки формы (formBuilder.renderFormErrors) и отказ загрузки
39
38
  //- плагина выбранной игры — строкой в форме, а не в #tech-informer:
@@ -67,15 +66,13 @@ div#lobby(style='display:none')
67
66
  span#leaderboard-total
68
67
  ol#lobby-leaderboard-list
69
68
  p#lobby-my-placement
70
- //- футер: ссылка на документацию статична, версию пакета движка
71
- //- проставляет LobbyView (engineVersion.js)
69
+ //- футер: Terms/Privacy, версия пакета движка (проставляет LobbyView,
70
+ //- engineVersion.js), копирайт
72
71
  div#lobby-footer
73
72
  p
74
73
  a#lobby-terms-link(target='_blank', rel='noopener') Terms of Service
75
74
  | ·
76
75
  |
77
76
  a#lobby-privacy-link(target='_blank', rel='noopener') Privacy Policy
78
- p
79
- a#lobby-link(href='https://docs.vimp.lgick.dev', target='_blank', rel='noopener') docs
80
77
  p#lobby-version
81
78
  p © #{new Date().getFullYear()} VIMP
@@ -1,18 +1,16 @@
1
- // коды закрытия транспорта — общий контракт серверных контуров (мастер,
2
- // dedicated, Worker-хост) и клиента, тот же приём, что в gameCodes.js:
3
- // значение не должно расходиться между ними молча. Отказы политики — те, что
4
- // клиент не лечит перезагрузкой, — перечислены в
5
- // `client/network/policyClose.js`, и тест там требует явного решения по
6
- // каждому коду из этой карты
1
+ // коды закрытия транспорта — общий контракт сигналинга мастера и
2
+ // Worker-хоста, тот же приём, что в gameCodes.js: значение не должно
3
+ // расходиться между ними молча. Игровой транспорт клиента кода не передаёт
4
+ // (у data channel его нет): причину кика или отказа клиент узнаёт по
5
+ // TECH_INFORM перед закрытием
7
6
  export default {
8
7
  staleHost: 4000, // master/SignalingServer.js — сигнальный WS хоста протух
9
- invalidOrigin: 4001, // master/SignalingServer.js, dedicated/main.js
8
+ invalidOrigin: 4001, // master/SignalingServer.js
10
9
  // 4002 retired (server rating removed) — номер не переиспользуется
11
10
  kickForMaxLatency: 4003, // host/HostGame.js
12
11
  kickForMissedPings: 4004, // host/HostGame.js
13
12
  kickIdle: 4005, // host/HostGame.js
14
13
  roomFull: 4006, // host/PortMachine.js
15
14
  // 4007 свободен
16
- handshakeTimeout: 4008, // dedicated/main.js
17
- tooManyConnections: 4009, // dedicated/main.js
15
+ handshakeTimeout: 4008, // host/PortMachine.js — нет RESUME_REQUEST вовремя
18
16
  };
@@ -1,5 +1,5 @@
1
1
  // коды игровых информ-сообщений (порт GAME_INFORM_DATA, см.
2
- // docs.vimp.lgick.dev/en/snapshot) — общий контракт хоста (SocketManager) и
2
+ // VIMP docs: snapshot.md) — общий контракт хоста (SocketManager) и
3
3
  // клиента (main.js): значение не должно расходиться между ними молча
4
4
  export default {
5
5
  winnerTeam: [0],
@@ -66,8 +66,8 @@ export default {
66
66
 
67
67
  // возобновление сессии (host-migration этап 4). Сколько хост держит место
68
68
  // участника, чей транспорт оборвался без LEAVE: слот занят, актор в мире,
69
- // клавиши отпущены. Включает его только Worker лобби — dedicated и
70
- // standalone поднимают PortMachine без этой опции (0 — снять сразу)
69
+ // клавиши отпущены. Включает его только Worker лобби — standalone
70
+ // поднимает PortMachine без этой опции (0 — снять сразу)
71
71
  resumeGraceMs: 20000,
72
72
 
73
73
  // сколько возобновляющее соединение может молчать до RESUME_REQUEST
@@ -50,9 +50,8 @@ export default {
50
50
  // клиент берёт не из assetsBase, а по отдельному URL, и версионность
51
51
  // каталога обязана доехать и сюда, иначе комната на застейдженной версии
52
52
  // играла бы на картах одобренной. `mapsBase` проставляет мастер при
53
- // ребейзе версионного каталога; его отсутствие (dev, standalone,
54
- // dedicated, старый мастер) — законный случай, тогда работает прежний
55
- // путь по id
53
+ // ребейзе версионного каталога; его отсутствие (dev, standalone, старый
54
+ // мастер) — законный случай, тогда работает путь по id
56
55
  maps: {
57
56
  manifestUrl: manifest =>
58
57
  `${manifest.mapsBase ?? `/games/${manifest.id}/maps`}/manifest.json`,
@@ -305,7 +304,7 @@ export default {
305
304
  debugReportUrl: '/debug/report',
306
305
 
307
306
  // журнал клиентских ошибок: приём на том же боксе,
308
- // что раздал страницу, — лобби-мастер или dedicated
307
+ // что раздал страницу
309
308
  clientReportUrl: '/client-reports',
310
309
 
311
310
  // размер страницы для «Загрузить ещё» (offset/limit к мастеру)
@@ -1,4 +1,4 @@
1
- // Номера портов JSON-канала (docs.vimp.lgick.dev/en/snapshot). Реестр
1
+ // Номера портов JSON-канала (VIMP docs: snapshot.md). Реестр
2
2
  // append-only, как и остальные словари плагинной поверхности (этап 3 плана
3
3
  // plugin-forward-compat): номер НИКОГДА не переиспользуется и не
4
4
  // перенумеровывается — уже опубликованная сборка игры шлёт и слушает те
@@ -39,7 +39,7 @@ const RECORDED_PORTS = {
39
39
  const FLUSHING_METHODS = new Set(['sendClear', 'sendMap', 'sendFirstShot']);
40
40
 
41
41
  // В headless-контуре нет ни мастера, ни auth-сервиса — участник стартует с
42
- // пустым профилем (lib/offlinePlayerData.js, общее со standalone/dedicated).
42
+ // пустым профилем (lib/offlinePlayerData.js, общее со standalone).
43
43
  const emptyProfileFetch = offlinePlayerData();
44
44
 
45
45
  /**
@@ -13,7 +13,7 @@ import { isFixture } from './pluginLoader.js';
13
13
  // Ввод начинается на 40-м тике, а не сразу: кадр спавна с force_reset
14
14
  // приходит примерно на interpolation.delay (движковый дефолт — 100 мс, тут
15
15
  // ~3× запаса) позже входа и чистит удержанные клавиши предиктора
16
- // (docs.vimp.lgick.dev/en/debugging) — встроенный сценарий не должен
16
+ // (VIMP docs: debugging.md) — встроенный сценарий не должен
17
17
  // демонстрировать ровно ту ловушку, от которой предостерегает документация
18
18
  const INPUT_DOWN_TICK = 40;
19
19
  const INPUT_UP_TICK = 100;
@@ -26,7 +26,7 @@ const FIXTURE_UNUSED_KEYS = ['e1'];
26
26
  const firstKey = dict => Object.keys(dict ?? {})[0];
27
27
 
28
28
  // Клавиша для смоука обязана быть удерживаемой: по конвенции playerKeys
29
- // (docs.vimp.lgick.dev/en/plugin-api) `type: 1` — триггер, у которого 'up'
29
+ // (VIMP docs: plugin-api.md) `type: 1` — триггер, у которого 'up'
30
30
  // игнорируется, и «нажал — отпустил» не даёт ни удержания, ни движения.
31
31
  // Порядок объявления в playerKeys контрактом не задан, поэтому берём первую
32
32
  // без type; если игра объявила одни триггеры — первую любую, смоук всё равно
@@ -1,6 +1,6 @@
1
1
  import { ERROR, skip, verdict } from '../result.js';
2
2
 
3
- // Набор npm-скриптов пакета игры (docs.vimp.lgick.dev/en/packaging). Это не
3
+ // Набор npm-скриптов пакета игры (VIMP docs: packaging.md). Это не
4
4
  // вкусовое требование: инструкции движка и CI сгенерированной игры зовут их по
5
5
  // имени, а отсутствующий core:build:node тихо оставляет headless-прогон без
6
6
  // ядра.
@@ -3,7 +3,7 @@ import { ERROR, skip, verdict } from '../result.js';
3
3
  // vite.config.js проверяется как текст, а не импортом: конфиг —
4
4
  // функция от mode и вне Vite не исполняется. Каждый пункт здесь ловит
5
5
  // молчаливую поломку сборки, а не стилистику
6
- // (docs.vimp.lgick.dev/en/packaging): без preserveEntrySignatures
6
+ // (VIMP docs: packaging.md): без preserveEntrySignatures
7
7
  // default-экспорт плагина вытряхивает tree-shaking, а build.lib всегда инлайнит
8
8
  // ассеты.
9
9
  const REQUIRED = [
@@ -4,7 +4,7 @@ import { SERVICES } from '../../../config/clientServices.js';
4
4
  // Движковый пул сервисов держит реестр (config/clientServices.js), но пул
5
5
  // ими не исчерпывается: игра доливает туда свои через
6
6
  // ClientPlugin.hooks.services(core) — например геометрию предсказанной
7
- // динамики карты (docs.vimp.lgick.dev/en/plugin-api «hooks.services»).
7
+ // динамики карты (VIMP docs: plugin-api.md § "Services").
8
8
  // Незнакомое имя не ошибка для движка: part получает undefined и рисует пустоту
9
9
  // — карта без assetsBase выглядит как чистый холст без единой строки в консоли,
10
10
  // ради этого правило и существует. Правило остаётся ERROR: оно работает на
@@ -484,7 +484,7 @@ function predictionDrift({ clients, scenario }) {
484
484
  // Компоненты player-блока — игровая раскладка, движок знает только их
485
485
  // порядок, поэтому нарушение адресуется индексом. Исключение — уровень 0
486
486
  // (source 'camera'): там движок сравнивает ровно мировые x/y, и называть их
487
- // «#0/#1» значит прятать причину (см. docs.vimp.lgick.dev/en/debugging).
487
+ // «#0/#1» значит прятать причину (см. VIMP docs: debugging.md).
488
488
  const CAMERA_COMPONENTS = ['x', 'y'];
489
489
 
490
490
  function formatDivergence(record) {
@@ -12,7 +12,7 @@ import { loadGamePackage } from '../lib/loadGamePackage.js';
12
12
  // 3. ничего — фикстура miniGame из этого репозитория.
13
13
  //
14
14
  // entries.wasmNode — новое ОПЦИОНАЛЬНОЕ поле манифеста: конвенция
15
- // core/pkg-node/ уже описана в docs.vimp.lgick.dev/en/packaging, не хватало
15
+ // core/pkg-node/ уже описана в VIMP docs: packaging.md, не хватало
16
16
  // только пути в манифесте. Его отсутствие не ошибка — просто игра не
17
17
  // прогоняется headless своим настоящим ядром.
18
18
 
@@ -51,7 +51,7 @@ export async function loadGameForSim({ game = null, core = null } = {}) {
51
51
  }
52
52
 
53
53
  // путь --game указывает либо на пакет игры, либо прямо на манифест; сама
54
- // загрузка общая с dedicated-сервером (lib/loadGamePackage.js).
54
+ // загрузка — lib/loadGamePackage.js.
55
55
  // client: true — прогону виртуальные клиенты обязательны, и отказ на
56
56
  // загрузке пакета лучше отказа посреди сценария
57
57
  async function loadFromManifest(game, core) {
@@ -858,9 +858,9 @@ export default class HostGame {
858
858
 
859
859
  /**
860
860
  * Публичный teardown матча: останов таймеров, финальная синхронизация
861
- * профилей и снятие всех участников. Во вкладке матч умирает вместе с
862
- * Worker'ом, а долгоживущему процессу (dedicated-сервер) нужен graceful
863
- * shutdown — иначе таймеры держат процесс, а rank/state теряются.
861
+ * профилей и снятие всех участников. Его зовут Worker при закрытии
862
+ * комнаты и inline-хост solo при останове матча: без него таймеры
863
+ * переживают матч, а rank/state теряются.
864
864
  * @returns {Promise} Завершение финальной синхронизации профилей.
865
865
  */
866
866
  async destroy() {
@@ -2052,7 +2052,7 @@ export default class HostGame {
2052
2052
  };
2053
2053
  }
2054
2054
 
2055
- // есть ли у участника identity-токен: в standalone и dedicated личность
2055
+ // есть ли у участника identity-токен: в standalone личность
2056
2056
  // гостевая, и обновлять там нечего
2057
2057
  hasToken(gameId) {
2058
2058
  const token = this._participants.get(gameId)?.token;
@@ -1,10 +1,9 @@
1
1
  // Хендшейк-машина клиента, вынутая из host.worker.js: автомат по клиентским
2
- // портам (таблица портов — docs.vimp.lgick.dev/en/snapshot). Изоморфна — ни
2
+ // портам (таблица портов — VIMP docs: snapshot.md). Изоморфна — ни
3
3
  // self, ни postMessage, ни DOM: всё, что знает про транспорт, приходит через
4
4
  // makeSocket. Поэтому её крутит и Worker браузерного хоста, и inline-хост
5
- // standalone SDK, и Node-процесс dedicated-сервера: копий автомата быть не
6
- // должно, иначе они разъедутся ровно так, как разъезжались бы копии
7
- // createHostRuntime.
5
+ // standalone SDK, и headless-прогон: копий автомата быть не должно, иначе
6
+ // они разъедутся ровно так, как разъезжались бы копии createHostRuntime.
8
7
 
9
8
  import wsports from '../config/wsports.js';
10
9
  import closeCodes from '../config/closeCodes.js';
@@ -57,7 +56,7 @@ export default class PortMachine {
57
56
  * @param {Object} deps.identity - стратегия идентичности (./identity.js).
58
57
  * @param {number} [deps.resumeGraceMs] - сколько держать место участника,
59
58
  * чей транспорт оборвался (host-migration этап 4); 0 — снимать сразу
60
- * (dedicated, standalone: поведение до этапа 4).
59
+ * (standalone).
61
60
  * @param {number} [deps.resumeRequestTimeoutMs] - сколько ждать
62
61
  * RESUME_REQUEST от возобновляющего соединения.
63
62
  */
@@ -466,22 +465,6 @@ export default class PortMachine {
466
465
  }
467
466
  }
468
467
 
469
- /**
470
- * @param {string} socketId
471
- * @returns {boolean} Есть ли порт-машина у этого соединения.
472
- */
473
- has(socketId) {
474
- return this._clients.has(socketId);
475
- }
476
-
477
- /**
478
- * @param {string} socketId
479
- * @returns {boolean} Дошёл ли клиент до созданного участника матча.
480
- */
481
- hasParticipant(socketId) {
482
- return this._clients.get(socketId)?.gameId !== undefined;
483
- }
484
-
485
468
  /**
486
469
  * @returns {Iterator<string>} socketId живых соединений (для
487
470
  * host.completeHandoff(new Set(...))).
@@ -1,7 +1,7 @@
1
1
  // Отчёт о сбое чат-команды игры для приёмника диагностики
2
2
  // (HostGame.setDiagnosticSink): сообщение в одну строку и стек без текста
3
- // ошибки. Один отчёт на все среды — Worker (журнал платформы), dedicated
4
- // (лог процесса), standalone (консоль вкладки). Чистые функции, без DOM и
3
+ // ошибки. Один отчёт на все среды — Worker (журнал платформы), standalone
4
+ // (консоль вкладки), headless-прогон (лог процесса). Чистые функции, без DOM и
5
5
  // Node API: модуль работает в Worker'е
6
6
 
7
7
  // символы, подделывающие строки лога: управляющие (перевод строки, ESC),
@@ -1,7 +1,7 @@
1
1
  // Стратегии идентичности хоста: как порт-машина (PortMachine.js) узнаёт,
2
2
  // под каким ником участник входит в комнату. Прод-лобби доверяет только
3
- // подписанному identity-токену центрального auth-сервиса; standalone- и
4
- // dedicated-контуры мастера не имеют вовсе и пускают гостей по нику из формы.
3
+ // подписанному identity-токену центрального auth-сервиса; standalone-контур
4
+ // мастера не имеет вовсе и пускает гостей по нику из формы.
5
5
  //
6
6
  // Контракт стратегии:
7
7
  // { params: Array, errorField: string, resolve(data, socketId): Promise<string> }
@@ -61,7 +61,7 @@ export function createTokenIdentity({ jwksUrl, issuer }) {
61
61
  }
62
62
 
63
63
  /**
64
- * Гостевой вход (standalone/dedicated): ник — поле формы, которое хост
64
+ * Гостевой вход (standalone): ник — поле формы, которое хост
65
65
  * валидирует сам движковым isValidName, без кода игры.
66
66
  *
67
67
  * Ограничение (принимается осознанно): гостевые ники не уникальны и не
@@ -51,8 +51,8 @@ class CommandProcessor {
51
51
  return;
52
52
  }
53
53
 
54
- // в Worker'е сбой стал бы необработанным reject'ом, в dedicated-процессе —
55
- // uncaughtException, то есть падением матча всех игроков
54
+ // в Worker'е сбой стал бы необработанным reject'ом, то есть падением
55
+ // матча всех игроков
56
56
  try {
57
57
  const result = handler(this._ctx, gameId, arr);
58
58
 
@@ -234,7 +234,7 @@ export default class Accolades {
234
234
  // payload.nick, и PortMachine передаёт в createUser именно его), а не
235
235
  // то, что игрок написал в форме.
236
236
  //
237
- // Меняет это в гостевом контуре (standalone/dedicated), где ник —
237
+ // Меняет это в гостевом контуре (standalone), где ник —
238
238
  // поле формы и «не защищён от подмены» по прямому признанию
239
239
  // createGuestIdentity. Токена у гостя нет, и сопоставление по нику
240
240
  // выдало бы ему чужую корону — стоит она ровно того, что значит.
@@ -31,7 +31,7 @@ import HostGame from '../host/HostGame.js';
31
31
  * прогону (сценарий подкручивает networkSendRate и таймеры).
32
32
  * @param {boolean} [options.voiceRelay] - У контура есть ретранслятор голоса
33
33
  * на транспортном уровне (браузерный хост лобби); без него связь
34
- * выключается, даже если игра её объявила (standalone, headless, dedicated).
34
+ * выключается, даже если игра её объявила (standalone, headless).
35
35
  * @returns {Promise<Object>} { hostPlugin, game, seed, core, clientCfg,
36
36
  * socketManager, host }.
37
37
  */