vimp-engine 0.14.4 → 0.16.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/core/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "vimp-engine-core"
3
- version = "0.8.3"
3
+ version = "0.8.4"
4
4
  edition = "2024"
5
5
  description = "VIMP — движковый каркас симуляции (физика, карта, снапшот-фрейминг, интерполяция/предикт/raycast-примитивы, нав-утилиты). Без wasm-bindgen — обёртки для WASM-ABI собирает game-crate."
6
6
  license = "MIT"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vimp-engine",
3
- "version": "0.14.4",
3
+ "version": "0.16.0",
4
4
  "description": "VIMP — движок-приложение (мастер, P2P-транспорт, Worker-хост, мета, MVC-каркас клиента)",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -31,21 +31,14 @@ export default class AuthModel {
31
31
  }
32
32
 
33
33
  // обновление данных
34
- // если value невалиден, возвращается текущий value
34
+ // формат поля проверяет formBuilder.collectFormErrors перед сабмитом —
35
+ // единственный контур валидации формы (раньше здесь была вторая,
36
+ // параллельная проверка по options.regExp, которая молча зануляла
37
+ // значение и падала на строке из JSON: у неё нет .test)
35
38
  update(data) {
36
- const { name } = data;
37
- const { regExp } = this._options[name] ?? {};
38
- let { value } = data;
39
-
40
- if (regExp) {
41
- if (regExp.test(value)) {
42
- this._data[name] = value;
43
- } else {
44
- this._data[name] = value = '';
45
- }
46
- } else {
47
- this._data[name] = value;
48
- }
39
+ const { name, value } = data;
40
+
41
+ this._data[name] = value;
49
42
 
50
43
  this.publisher.emit('form', { name, value });
51
44
  }
@@ -1,5 +1,5 @@
1
1
  import Publisher from '../../../lib/Publisher.js';
2
- import { buildForm, reportFormValidity } from '../../lib/formBuilder.js';
2
+ import { bindLiveErrors, buildForm, renderFormErrors } from '../../lib/formBuilder.js';
3
3
 
4
4
  // Singleton AuthView
5
5
 
@@ -24,19 +24,29 @@ export default class AuthView {
24
24
  this._enter = document.getElementById(elems.enterId);
25
25
  this._fieldsContainer = document.getElementById(elems.fieldsId);
26
26
  this._fields = new Map();
27
+ this._descriptors = [];
27
28
 
28
29
  this.publisher = new Publisher();
29
30
 
30
31
  this._renderTexts(elems, texts);
31
32
  this._renderFields(elems, params);
32
33
 
33
- // форма заполнена; нативная проверка (pattern/required) сервер всё
34
- // равно валидирует своими validators (renderError), это лишь UX-фильтр
35
- // до отправки
34
+ // ошибки видны по ходу правки (см. bindLiveErrors), а не по клику:
35
+ // до сабмита по тронутым полям, после — по всей форме
36
+ this._liveErrors = bindLiveErrors(this._fieldsContainer, this._error, () => ({
37
+ descriptors: this._descriptors,
38
+ fields: this._fields,
39
+ }));
40
+
41
+ // форма заполнена; клиентская проверка (required/regExp/min/max) —
42
+ // сервер всё равно валидирует своими validators (renderError), это лишь
43
+ // UX-фильтр до отправки
36
44
  this._enter.onclick = () => {
37
- if (reportFormValidity(this._fieldsContainer)) {
38
- authView.publisher.emit('enter');
45
+ if (this._liveErrors.arm().length) {
46
+ return;
39
47
  }
48
+
49
+ authView.publisher.emit('enter');
40
50
  };
41
51
 
42
52
  this._mPublic.on('form', 'renderData', this);
@@ -65,6 +75,11 @@ export default class AuthView {
65
75
  ...descriptorRest,
66
76
  }));
67
77
 
78
+ this._descriptors = descriptors;
79
+ // params сюда приходят чужие (PS_AUTH_DATA, тот же массив main.js
80
+ // отдаёт следом в AuthCtrl.init) и остаются нетронутыми: значение поля
81
+ // с единственным вариантом закрепляет main.js (resolveForcedValue) до
82
+ // разветвления на solo-путь, а не эта форма
68
83
  this._fields = buildForm(descriptors, container, {}, ({ name, value }) => {
69
84
  this.publisher.emit('input', { name, value });
70
85
  });
@@ -146,22 +161,17 @@ export default class AuthView {
146
161
  renderData(data) {
147
162
  const { name, value } = data;
148
163
 
149
- this._error.textContent = '';
164
+ // блок ошибок ведёт bindLiveErrors — он перевалидирует форму по 'input',
165
+ // то есть по ходу правки. Чистка, стоявшая здесь, висела на событии
166
+ // модели, а оно приходит из 'change' (у text-инпута — blur): игрок,
167
+ // перейдя из починенного поля в следующее, терял ошибки полей, которых
168
+ // ещё не касался
150
169
  this._fields.get(name)?.setValue(value);
151
170
  }
152
171
 
153
- // отображает ошибки
172
+ // отображает серверные ошибки (PS_AUTH_DATA/validators) — тот же рендер,
173
+ // что и у клиентской collectFormErrors (formBuilder.renderFormErrors)
154
174
  renderError(data) {
155
- this._error.textContent = '';
156
-
157
- data.forEach(item => {
158
- const name = item.name.toUpperCase();
159
- const err = item.error;
160
- const line = document.createElement('div');
161
-
162
- line.textContent = err ? `${name}: ${err}` : `${name} is not correctly!`;
163
-
164
- this._error.appendChild(line);
165
- });
175
+ renderFormErrors(this._error, data);
166
176
  }
167
177
  }
@@ -1,4 +1,5 @@
1
1
  import Publisher from '../../../lib/Publisher.js';
2
+ import { ENGINE_VERSION } from '../../lib/engineVersion.js';
2
3
 
3
4
  // Singleton LobbyView
4
5
 
@@ -34,6 +35,14 @@ export default class LobbyView {
34
35
  this._leaderboardTotal = document.getElementById(elems.leaderboardTotalId);
35
36
  this._myPlacement = document.getElementById(elems.myPlacementId);
36
37
 
38
+ // футер лобби: строка статична на всё время жизни вкладки, поэтому
39
+ // пишется здесь, а не через модель и publisher
40
+ const version = document.getElementById(elems.versionId);
41
+
42
+ if (version) {
43
+ version.textContent = `vimp-engine ${ENGINE_VERSION}`;
44
+ }
45
+
37
46
  // заголовок игры для "<TITLE> TOP-N" (SVG-ориентир) — задаётся controller'ом
38
47
  // при выборе игры (сама модель не хранит title манифеста)
39
48
  this._gameTitle = '';
@@ -0,0 +1,41 @@
1
+ import { resolveForcedValue } from './formBuilder.js';
2
+
3
+ // Значения полей auth-формы до её постройки (PS_AUTH_DATA.params).
4
+ // Вынесено из socketMethods[PS_AUTH_DATA] (main.js), чтобы этот код был
5
+ // накрыт тестом: solo-путь (boot.autoAuth) отвечает хосту вообще без формы и
6
+ // берёт значения ровно отсюда, то есть ошибка здесь не видна ни в одном
7
+ // тесте формы.
8
+
9
+ /**
10
+ * Правит `param.value` на месте: сначала память клиента
11
+ * (`localStorage[storage]`), затем схема. Порядок важен — поле с
12
+ * единственным вариантом форма не показывает и править не даёт, поэтому
13
+ * устаревший `localStorage` от версии игры, где вариантов было больше,
14
+ * обязан быть перекрыт: иначе он уедет на хост и получит отказ от
15
+ * игровых validators, а поправить его игроку нечем.
16
+ *
17
+ * Мутация, а не копия: тот же массив main.js отдаёт следом в
18
+ * `AuthCtrl.init` (оттуда он попадает в `AuthModel._data`) и в
19
+ * `defaultsFrom(params)` на solo-пути.
20
+ *
21
+ * @param {Array<Object>} params - PS_AUTH_DATA.params.
22
+ * @param {Object} [storage] - Хранилище значений, по умолчанию localStorage.
23
+ * @returns {Array<Object>} Тот же массив.
24
+ */
25
+ export function normalizeAuthParams(params, storage = localStorage) {
26
+ params.forEach(param => {
27
+ const descriptor = param.options ?? {};
28
+
29
+ if (descriptor.storage) {
30
+ param.value = storage[descriptor.storage] || param.value || '';
31
+ }
32
+
33
+ const forced = resolveForcedValue(descriptor);
34
+
35
+ if (forced !== undefined) {
36
+ param.value = forced;
37
+ }
38
+ });
39
+
40
+ return params;
41
+ }
@@ -0,0 +1,11 @@
1
+ // Версия npm-пакета движка, запечённая в бандл на сборке: сети для неё нет
2
+ // (у мастера нет version-эндпоинта), а лобби обязано показывать версию того
3
+ // кода, который сейчас исполняется, а не того, что развёрнут на сервере.
4
+ //
5
+ // Модуль лежит в client/lib, а не в src/config: config/* импортирует мастер
6
+ // под Node, а сюда тянется package.json пакета — он нужен только браузерному
7
+ // бандлу (в npm-тарболле vimp-engine package.json есть всегда, поэтому
8
+ // standalone SDK собирается у игры тем же импортом).
9
+ import pkg from '../../../package.json' with { type: 'json' };
10
+
11
+ export const ENGINE_VERSION = pkg.version;