vimp-engine 0.1.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 +9 -0
  2. package/package.json +34 -0
  3. package/src/config/authClient.js +47 -0
  4. package/src/config/clientDefaults.js +113 -0
  5. package/src/config/hostDefaults.js +79 -0
  6. package/src/config/lobby.js +104 -0
  7. package/src/config/master.js +103 -0
  8. package/src/config/opcodes.js +29 -0
  9. package/src/config/wsports.js +35 -0
  10. package/src/host/GameCoreAdapter.js +206 -0
  11. package/src/host/HostGame.js +857 -0
  12. package/src/host/host.worker.js +438 -0
  13. package/src/host/meta/SocketManager.js +431 -0
  14. package/src/host/meta/core/CommandProcessor.js +99 -0
  15. package/src/host/meta/core/RoundManager.js +646 -0
  16. package/src/host/meta/core/VoteCoordinator.js +71 -0
  17. package/src/host/meta/modules/Panel.js +181 -0
  18. package/src/host/meta/modules/PlayerDataSync.js +176 -0
  19. package/src/host/meta/modules/RTTManager.js +168 -0
  20. package/src/host/meta/modules/Stat.js +294 -0
  21. package/src/host/meta/modules/TimerManager.js +277 -0
  22. package/src/host/meta/modules/Vote.js +179 -0
  23. package/src/host/meta/modules/chat/Chat.js +66 -0
  24. package/src/host/meta/modules/chat/index.js +1 -0
  25. package/src/host/meta/modules/chat/systemMessages.js +51 -0
  26. package/src/host/meta/player/HumanParticipant.js +36 -0
  27. package/src/host/meta/player/Participant.js +22 -0
  28. package/src/host/meta/player/ParticipantManager.js +247 -0
  29. package/src/host/meta/player/ScriptedParticipant.js +19 -0
  30. package/src/lib/AbstractTimer.js +84 -0
  31. package/src/lib/Publisher.js +43 -0
  32. package/src/lib/applyRoomOverrides.js +48 -0
  33. package/src/lib/buildClientConfig.js +48 -0
  34. package/src/lib/clientCoreConfig.js +59 -0
  35. package/src/lib/config.js +83 -0
  36. package/src/lib/coreConfig.js +60 -0
  37. package/src/lib/factory.js +21 -0
  38. package/src/lib/formatters.js +33 -0
  39. package/src/lib/gamePlugin.js +95 -0
  40. package/src/lib/jwt.js +103 -0
  41. package/src/lib/math.js +75 -0
  42. package/src/lib/rateLimiter.js +40 -0
  43. package/src/lib/sanitizers.js +17 -0
  44. package/src/lib/security.js +45 -0
  45. package/src/lib/validators.js +52 -0
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Линейная интерполяция
3
+ * @param {number} a
4
+ * @param {number} b
5
+ * @param {number} t - значение от 0 до 1
6
+ */
7
+ export const lerp = (a, b, t) => a + (b - a) * t;
8
+
9
+ /**
10
+ * Интерполяция угла в радианах по кратчайшему пути
11
+ * @param {number} a
12
+ * @param {number} b
13
+ * @param {number} t - значение от 0 до 1
14
+ */
15
+ export const lerpAngle = (a, b, t) => {
16
+ let diff = b - a;
17
+
18
+ while (diff > Math.PI) {
19
+ diff -= Math.PI * 2;
20
+ }
21
+
22
+ while (diff < -Math.PI) {
23
+ diff += Math.PI * 2;
24
+ }
25
+
26
+ return a + diff * t;
27
+ };
28
+
29
+ /**
30
+ * Ограничивает число в диапазоне [min, max]
31
+ * @param {number} value
32
+ * @param {number} min
33
+ * @param {number} max
34
+ * @returns {number}
35
+ */
36
+ export const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
37
+
38
+ /**
39
+ * Перевод градусов в радианы
40
+ * @param {number} degrees
41
+ * @returns {number}
42
+ */
43
+ export const degToRad = degrees => degrees * (Math.PI / 180);
44
+
45
+ /**
46
+ * Перевод радиан в градусы
47
+ * @param {number} radians
48
+ * @returns {number}
49
+ */
50
+ export const radToDeg = radians => radians * (180 / Math.PI);
51
+
52
+ /**
53
+ * Нормализует угол в радианах к диапазону [-PI, PI]
54
+ * @param {number} angle
55
+ * @returns {number}
56
+ */
57
+ export const normalizeAngle = angle => {
58
+ while (angle > Math.PI) {
59
+ angle -= Math.PI * 2;
60
+ }
61
+
62
+ while (angle < -Math.PI) {
63
+ angle += Math.PI * 2;
64
+ }
65
+
66
+ return angle;
67
+ };
68
+
69
+ /**
70
+ * Случайное число в диапазоне [min, max)
71
+ * @param {number} min
72
+ * @param {number} max
73
+ * @returns {number}
74
+ */
75
+ export const randomRange = (min, max) => Math.random() * (max - min) + min;
@@ -0,0 +1,40 @@
1
+ // Rate limiter с фиксированным окном: не более `limit` событий
2
+ // на ключ (например, IP) за `windowMs` миллисекунд.
3
+ export default class RateLimiter {
4
+ constructor({ limit, windowMs }) {
5
+ this._limit = limit;
6
+ this._windowMs = windowMs;
7
+ this._buckets = new Map(); // key -> { count, windowStart }
8
+ }
9
+
10
+ // регистрирует событие; false — лимит для ключа исчерпан
11
+ consume(key, now = Date.now()) {
12
+ const bucket = this._buckets.get(key);
13
+
14
+ if (!bucket || now - bucket.windowStart >= this._windowMs) {
15
+ this._buckets.set(key, { count: 1, windowStart: now });
16
+ return true;
17
+ }
18
+
19
+ if (bucket.count >= this._limit) {
20
+ return false;
21
+ }
22
+
23
+ bucket.count += 1;
24
+
25
+ return true;
26
+ }
27
+
28
+ // удаляет истёкшие окна (периодическая уборка памяти)
29
+ sweep(now = Date.now()) {
30
+ for (const [key, bucket] of this._buckets) {
31
+ if (now - bucket.windowStart >= this._windowMs) {
32
+ this._buckets.delete(key);
33
+ }
34
+ }
35
+ }
36
+
37
+ clear() {
38
+ this._buckets.clear();
39
+ }
40
+ }
@@ -0,0 +1,17 @@
1
+ // Управляющие символы (переводы строк, табы и т.п.) ломают однострочный чат.
2
+ // eslint-disable-next-line no-control-regex
3
+ const controlCharsRegExp = /[\x00-\x1f\x7f]/g;
4
+
5
+ /**
6
+ * Убирает из сообщения управляющие символы. Не XSS-защита — экранирование
7
+ * на выводе (`textContent`/`setAttribute`).
8
+ * @param {string} message
9
+ * @returns {string} - Очищенная строка (пустая, если на входе не строка).
10
+ */
11
+ export const sanitizeMessage = message => {
12
+ if (typeof message !== 'string') {
13
+ return '';
14
+ }
15
+
16
+ return message.replace(controlCharsRegExp, '');
17
+ };
@@ -0,0 +1,45 @@
1
+ import config from './config.js';
2
+
3
+ // создаёт проверку origin для заданных protocol/domain/port
4
+ // (используется и игровым сервером, и мастер-сервером)
5
+ const createOriginValidator = function ({ protocol, domain, port }) {
6
+ return function (requestOrigin, cb) {
7
+ const isProduction = process.env.NODE_ENV === 'production';
8
+
9
+ const allowedOrigins = [
10
+ // origin'ы для локальной разработки
11
+ `${protocol}//localhost:${port}`,
12
+ `${protocol}//127.0.0.1:${port}`,
13
+ ];
14
+
15
+ if (isProduction) {
16
+ // в продакшене разрешен только настоящий домен по стандартному HTTPS
17
+ // порт не требуется, так как браузер не добавляет его
18
+ // для стандартного порта 443
19
+ allowedOrigins.push(`https://${domain}`);
20
+ }
21
+
22
+ let err = null;
23
+
24
+ if (!allowedOrigins.includes(requestOrigin)) {
25
+ err = `Blocked connection from invalid origin: ${requestOrigin}`;
26
+ }
27
+
28
+ process.nextTick(() => cb(err));
29
+ };
30
+ };
31
+
32
+ const origin = function (requestOrigin, cb) {
33
+ const validator = createOriginValidator({
34
+ protocol: config.get('server:protocol'),
35
+ domain: config.get('server:domain'),
36
+ port: config.get('server:port'),
37
+ });
38
+
39
+ validator(requestOrigin, cb);
40
+ };
41
+
42
+ export default {
43
+ origin,
44
+ createOriginValidator,
45
+ };
@@ -0,0 +1,52 @@
1
+ const NAME_REGEXP = new RegExp('^[a-zA-Z]([\\w\\s#]{0,13})[\\w]{1}$');
2
+
3
+ /**
4
+ * Проверяет, является ли строка валидным именем пользователя.
5
+ * @param {string} name - Имя для проверки.
6
+ * @returns {boolean} - true, если имя валидно, иначе false.
7
+ */
8
+ export const isValidName = name =>
9
+ typeof name === 'string' && NAME_REGEXP.test(name);
10
+
11
+ // движковые правила валидации; игровые (например isValidModel)
12
+ // инжектируются третьим аргументом validateAuth (authSchema игры)
13
+ const validationRules = {
14
+ isValidName,
15
+ };
16
+
17
+ /**
18
+ * Валидирует объект с данными для авторизации.
19
+ * @param {object} data - Объект с данными для проверки.
20
+ * @param {Array} authParams - Массив правил для валидации
21
+ * @param {Object} [validators] - Игровые валидаторы (имя → функция),
22
+ * дополняют движковые validationRules.
23
+ * @returns {Array|undefined} - Массив ошибок или undefined.
24
+ */
25
+ export const validateAuth = (data, authParams, validators = {}) => {
26
+ const rules = { ...validationRules, ...validators };
27
+ const errors = [];
28
+
29
+ for (const { name, options } of authParams) {
30
+ if (!(name in data)) {
31
+ return [{ name, error: `Property is missing` }];
32
+ }
33
+
34
+ const value = data[name];
35
+
36
+ if (typeof value !== 'string') {
37
+ return [{ name, error: `Property must be a string` }];
38
+ }
39
+
40
+ if (options?.validator) {
41
+ const validatorFn = rules[options.validator];
42
+
43
+ if (validatorFn && !validatorFn(value)) {
44
+ errors.push({ name, error: 'not valid' });
45
+ } else if (!validatorFn) {
46
+ console.warn(`Validator function '${options.validator}' not found.`);
47
+ }
48
+ }
49
+ }
50
+
51
+ return errors.length ? errors : undefined;
52
+ };