vimp-engine 0.39.0 → 0.41.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 (62) 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/controller/Vote.js +26 -0
  7. package/src/client/components/model/Controls.js +9 -1
  8. package/src/client/components/model/RoomMenu.js +1 -1
  9. package/src/client/components/model/Vote.js +202 -0
  10. package/src/client/components/view/Auth.js +3 -3
  11. package/src/client/components/view/Panel.js +176 -6
  12. package/src/client/components/view/Voice.js +6 -1
  13. package/src/client/components/view/Vote.js +71 -2
  14. package/src/client/lib/formBuilder.js +7 -6
  15. package/src/client/lib/hostVoteCommand.js +2 -2
  16. package/src/client/lib/pickActiveGame.js +3 -12
  17. package/src/client/lib/retryOnNetworkError.js +0 -9
  18. package/src/client/main.js +47 -90
  19. package/src/client/network/SessionSupervisor.js +14 -14
  20. package/src/client/network/WebRtcManager.js +1 -2
  21. package/src/client/network/policyClose.js +10 -77
  22. package/src/client/style.css +85 -10
  23. package/src/client/views/gameShell.js +2 -4
  24. package/src/client/views/includes/auth.pug +1 -4
  25. package/src/client/views/includes/lobby.pug +3 -6
  26. package/src/client/voice/MicCapture.js +3 -1
  27. package/src/config/abiOps.js +42 -0
  28. package/src/config/closeCodes.js +7 -9
  29. package/src/config/gameCodes.js +1 -1
  30. package/src/config/hostDefaults.js +2 -2
  31. package/src/config/lobby.js +3 -4
  32. package/src/config/menuName.js +3 -0
  33. package/src/config/wsports.js +1 -1
  34. package/src/devtools/ScenarioRunner.js +1 -1
  35. package/src/devtools/builtinScenario.js +2 -2
  36. package/src/devtools/contract/rules/a2-package-scripts.js +1 -1
  37. package/src/devtools/contract/rules/a4-vite-config.js +1 -1
  38. package/src/devtools/contract/rules/c4-component-dependencies.js +1 -1
  39. package/src/devtools/invariants.js +1 -1
  40. package/src/devtools/pluginLoader.js +2 -2
  41. package/src/host/GameCoreAdapter.js +83 -4
  42. package/src/host/HostGame.js +93 -17
  43. package/src/host/PortMachine.js +4 -21
  44. package/src/host/commandFailure.js +2 -2
  45. package/src/host/identity.js +3 -3
  46. package/src/host/meta/core/CommandProcessor.js +2 -2
  47. package/src/host/meta/core/RoundManager.js +8 -1
  48. package/src/host/meta/core/VoteCoordinator.js +56 -1
  49. package/src/host/meta/modules/Accolades.js +1 -1
  50. package/src/host/meta/modules/TimerManager.js +16 -2
  51. package/src/host/meta/modules/Vote.js +65 -0
  52. package/src/host/meta/player/ParticipantManager.js +19 -3
  53. package/src/lib/capabilities.js +17 -0
  54. package/src/lib/createHostRuntime.js +1 -1
  55. package/src/lib/gameConfigView.js +7 -0
  56. package/src/lib/loadGamePackage.js +7 -9
  57. package/src/lib/offlinePlayerData.js +1 -1
  58. package/src/lib/validators.js +2 -2
  59. package/src/lib/voicePacket.js +1 -1
  60. package/src/standalone/index.js +3 -2
  61. package/src/client/lib/footerLink.js +0 -30
  62. package/src/client/network/WebSocketTransport.js +0 -78
@@ -6,13 +6,11 @@ import { ENGINE_CAPABILITIES } from './capabilities.js';
6
6
 
7
7
  // Загрузка пакета игры в Node по его собранному dist/ (Этап 4 плана
8
8
  // standalone-sdk). В браузере плагин грузится по URL из GameManifest мастера;
9
- // в Node URL-ов нет — оба Node-контура (headless-runner devtools/pluginLoader.js
10
- // и dedicated-сервер) читают манифест с диска и импортируют половины плагина
11
- // как обычные модули. Модуль живёт в lib/, а не в devtools/: прод-сервер не
12
- // должен зависеть от отладочного контура (граница из CLAUDE.md).
9
+ // в Node URL-ов нет — headless-runner (devtools/pluginLoader.js) читает
10
+ // манифест с диска и импортирует половины плагина как обычные модули.
13
11
  //
14
12
  // entries.wasmNode — node-сборка ядра игры (конвенция core/pkg-node,
15
- // docs.vimp.lgick.dev/en/packaging). Для боевой игры она обязательна: без ядра
13
+ // VIMP docs: packaging.md). Для боевой игры она обязательна: без ядра
16
14
  // авторитетную симуляцию крутить нечем.
17
15
 
18
16
  /**
@@ -33,7 +31,7 @@ export async function loadGamePackage(
33
31
  { core = null, client = 'lazy' } = {},
34
32
  ) {
35
33
  // манифест под нестандартным именем нужен headless-прогону (--game
36
- // <path>/manifest-variant.json); dedicated-сервер всегда даёт каталог
34
+ // <path>/manifest-variant.json)
37
35
  const manifestPath = distDir.endsWith('.json')
38
36
  ? path.resolve(distDir)
39
37
  : path.join(path.resolve(distDir), 'manifest.json');
@@ -58,8 +56,8 @@ export async function loadGamePackage(
58
56
  );
59
57
 
60
58
  // Клиентская половина в Node нужна только виртуальным клиентам
61
- // headless-прогона; dedicated-сервер её не касается вовсе (в браузер она
62
- // едет статикой, и pixi.js там резолвится import map'ом). Грузить её
59
+ // headless-прогона (в браузер она едет статикой, и pixi.js там
60
+ // резолвится import map'ом). Грузить её
63
61
  // безусловно значило бы требовать от игры самодостаточной клиентской
64
62
  // сборки — а правило контракта A1 держит pixi.js внешним намеренно.
65
63
  //
@@ -160,7 +158,7 @@ function assertPluginMatchesManifest(manifest, plugins) {
160
158
  // лобби честно отвергается, а в solo-режиме тихо недоигрывает.
161
159
  //
162
160
  // Здесь это ПРЕДУПРЕЖДЕНИЕ, а не отказ, и разница принципиальная. Node-путь
163
- // (dedicated, vimp-sim, inline host) читает авторитетный манифест, и его
161
+ // (vimp-sim, inline host) читает авторитетный манифест, и его
164
162
  // совместимость уже проверена выше (checkPluginCompatibility): рассинхрон с
165
163
  // половинами ничего здесь не ломает. Бросить значило бы отвергнуть пакет,
166
164
  // который грузился раньше, — прямое нарушение И4 плана
@@ -1,5 +1,5 @@
1
1
  // Профиль участника в контуре без мастера и auth-сервиса (headless-прогон,
2
- // standalone SDK, dedicated-сервер). Это не заглушка ради тишины: пустой
2
+ // standalone SDK). Это не заглушка ради тишины: пустой
3
3
  // профиль и есть корректное состояние такого матча, а настоящий fetch по
4
4
  // относительному URL вне браузерной вкладки просто не разрешается.
5
5
 
@@ -24,8 +24,8 @@ export const isValidNick = nick =>
24
24
  // потолок длины поля формы, когда maxlength в дескрипторе не объявлен.
25
25
  // regExp игры исполняется на хосте против строки, которую прислал клиент, и
26
26
  // катастрофический паттерн вроде "(a+)+b" превращает три десятка символов в
27
- // минуты заблокированного event loop — а хост это либо Worker с авторитетным
28
- // матчем (замирает комната целиком), либо процесс dedicated. 256 — с запасом
27
+ // минуты заблокированного event loop — а хост это Worker с авторитетным
28
+ // матчем (замирает комната целиком). 256 — с запасом
29
29
  // на любое поле auth-формы (ник, имя модели): длиннее не бывает у формы и не
30
30
  // нужно тому, кто её обошёл
31
31
  const MAX_FIELD_LENGTH = 256;
@@ -7,7 +7,7 @@ import {
7
7
  } from '../config/voiceProtocol.js';
8
8
 
9
9
  // Кодек голосового пакета (раскладка — config/voiceProtocol.js). Изоморфный:
10
- // им пользуются клиент и ретранслятор вкладки хоста (dedicated голоса не имеет).
10
+ // им пользуются клиент и ретранслятор вкладки хоста.
11
11
 
12
12
  const isByte = value => Number.isInteger(value) && value >= 0 && value <= 0xff;
13
13
 
@@ -44,7 +44,7 @@ import { ensureGameShell } from '../client/views/gameShell.js';
44
44
  * @param {boolean} [options.devMode] - room.isDevMode: рекордер и хостовый
45
45
  * CONSOLE-лог.
46
46
  * @param {string|null} [options.reportUrl] - Адрес приёма журнала клиентских
47
- * ошибок (`POST`, формат — docs.vimp.lgick.dev/en/standalone). По
47
+ * ошибок (`POST`, формат — VIMP docs: standalone.md). По
48
48
  * умолчанию `null`: в solo нет бокса, и отчёты не отправляются.
49
49
  * @returns {Promise<Object>} `{ stop() }` — останов матча.
50
50
  */
@@ -71,7 +71,8 @@ export async function startStandaloneGame({
71
71
  // возможность, которой в этой сборке движка нет, иначе всплыла бы где-то
72
72
  // в середине хендшейка непрозрачной ошибкой. Мастера тут нет, а значит нет
73
73
  // и GameManifest с его `requires` — источником служат сами половины
74
- // плагина (необязательное поле `requires`, docs.vimp.lgick.dev/en/plugin-api)
74
+ // плагина (необязательное поле `requires`,
75
+ // VIMP docs: plugin-api.md § "requires and engine capabilities")
75
76
  // либо опция вызова, если встраивающий знает лучше. Одна проверка вместо
76
77
  // двух: сообщение адресовано разработчику встраивания, делить его по
77
78
  // половинам незачем
@@ -1,30 +0,0 @@
1
- // Ссылка на документацию в футере формы входа игры. Показывается только в
2
- // dev-сборке: разработчик игры при локальном запуске видит, где читать
3
- // документацию, а игрок на проде — нет. Футер лобби несёт ту же ссылку
4
- // статичной разметкой (lobby.pug). Тот же адрес — литералом в lobby.pug и в
5
- // `homepage` пакета; совпадение сторожит tests/client/footerLink.test.js.
6
- export const DOCS_URL = 'https://docs.vimp.lgick.dev';
7
-
8
- /**
9
- * @param {HTMLAnchorElement|null} anchor - якорь футера
10
- * @param {boolean} enabled - true только в dev-сборке
11
- */
12
- export function renderDocsLink(anchor, enabled) {
13
- if (!anchor) {
14
- return;
15
- }
16
-
17
- if (!enabled) {
18
- anchor.textContent = '';
19
- anchor.removeAttribute('href');
20
- anchor.removeAttribute('target');
21
- anchor.removeAttribute('rel');
22
-
23
- return;
24
- }
25
-
26
- anchor.textContent = 'docs';
27
- anchor.href = DOCS_URL;
28
- anchor.target = '_blank';
29
- anchor.rel = 'noopener';
30
- }
@@ -1,78 +0,0 @@
1
- import Publisher from '../../lib/Publisher.js';
2
-
3
- // Третий транспорт клиента (Этап 2 плана standalone-sdk): прямой WebSocket к
4
- // dedicated-серверу. Интерфейсный близнец WebRtcManager и LoopbackTransport —
5
- // publisher с 'message'/'close', connect/send/close, — поэтому клиентский код
6
- // про него не знает.
7
- //
8
- // Отличия от WebRTC, которые надо держать в голове:
9
- // разделения meta/state тут нет, флаг reliable игнорируется. Значит PING/PONG
10
- // идут надёжным каналом (замер RTT становится замером TCP-пути), а бэкпрешер
11
- // вместо дропа позиционных кадров живёт на серверной стороне.
12
-
13
- // WebSocket.OPEN: константа класса, а не инстанса — у фейкового сокета в
14
- // тестах её может не быть вовсе
15
- const WS_OPEN = 1;
16
-
17
- export default class WebSocketTransport {
18
- /**
19
- * @param {string} url - Адрес игрового WebSocket.
20
- * @param {Object} [options]
21
- * @param {Function} [options.socketFactory] - Фабрика сокета (тесты).
22
- */
23
- constructor(url, { socketFactory = u => new WebSocket(u) } = {}) {
24
- this._url = url;
25
- this._socketFactory = socketFactory;
26
- this._ws = null;
27
- this._closed = false;
28
-
29
- this.publisher = new Publisher();
30
- }
31
-
32
- connect() {
33
- this._ws = this._socketFactory(this._url);
34
-
35
- // обязательно: клиент различает кадр снапшота и JSON-порт по
36
- // `data instanceof ArrayBuffer`, а дефолтный браузерный WS отдаёт Blob
37
- this._ws.binaryType = 'arraybuffer';
38
-
39
- this._ws.onopen = () => this.publisher.emit('open');
40
- this._ws.onmessage = event => this.publisher.emit('message', event.data);
41
- // код закрытия уезжает подписчику: политические отказы сервера (лимит
42
- // подключений, таймаут хендшейка) клиент лечить перезагрузкой не должен —
43
- // см. handleDisconnect в main.js
44
- this._ws.onclose = event => this._emitClose(event?.code);
45
- // ошибка сокета всегда сопровождается close — отдельного события наружу
46
- // не даём, иначе клиент погасил бы матч дважды
47
- this._ws.onerror = () => {};
48
- }
49
-
50
- /**
51
- * @param {string|ArrayBuffer} data - Кадр.
52
- * @param {boolean} [reliable] - Игнорируется: у WebSocket нет уровней
53
- * надёжности, всё едет надёжно и по порядку.
54
- */
55
- send(data) {
56
- if (this._ws && this._ws.readyState === WS_OPEN) {
57
- this._ws.send(data);
58
- }
59
- }
60
-
61
- close() {
62
- if (this._closed) {
63
- return;
64
- }
65
-
66
- this._ws?.close();
67
- this._emitClose();
68
- }
69
-
70
- _emitClose(code) {
71
- if (this._closed) {
72
- return;
73
- }
74
-
75
- this._closed = true;
76
- this.publisher.emit('close', code);
77
- }
78
- }