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.
- package/README.md +9 -0
- package/package.json +34 -0
- package/src/config/authClient.js +47 -0
- package/src/config/clientDefaults.js +113 -0
- package/src/config/hostDefaults.js +79 -0
- package/src/config/lobby.js +104 -0
- package/src/config/master.js +103 -0
- package/src/config/opcodes.js +29 -0
- package/src/config/wsports.js +35 -0
- package/src/host/GameCoreAdapter.js +206 -0
- package/src/host/HostGame.js +857 -0
- package/src/host/host.worker.js +438 -0
- package/src/host/meta/SocketManager.js +431 -0
- package/src/host/meta/core/CommandProcessor.js +99 -0
- package/src/host/meta/core/RoundManager.js +646 -0
- package/src/host/meta/core/VoteCoordinator.js +71 -0
- package/src/host/meta/modules/Panel.js +181 -0
- package/src/host/meta/modules/PlayerDataSync.js +176 -0
- package/src/host/meta/modules/RTTManager.js +168 -0
- package/src/host/meta/modules/Stat.js +294 -0
- package/src/host/meta/modules/TimerManager.js +277 -0
- package/src/host/meta/modules/Vote.js +179 -0
- package/src/host/meta/modules/chat/Chat.js +66 -0
- package/src/host/meta/modules/chat/index.js +1 -0
- package/src/host/meta/modules/chat/systemMessages.js +51 -0
- package/src/host/meta/player/HumanParticipant.js +36 -0
- package/src/host/meta/player/Participant.js +22 -0
- package/src/host/meta/player/ParticipantManager.js +247 -0
- package/src/host/meta/player/ScriptedParticipant.js +19 -0
- package/src/lib/AbstractTimer.js +84 -0
- package/src/lib/Publisher.js +43 -0
- package/src/lib/applyRoomOverrides.js +48 -0
- package/src/lib/buildClientConfig.js +48 -0
- package/src/lib/clientCoreConfig.js +59 -0
- package/src/lib/config.js +83 -0
- package/src/lib/coreConfig.js +60 -0
- package/src/lib/factory.js +21 -0
- package/src/lib/formatters.js +33 -0
- package/src/lib/gamePlugin.js +95 -0
- package/src/lib/jwt.js +103 -0
- package/src/lib/math.js +75 -0
- package/src/lib/rateLimiter.js +40 -0
- package/src/lib/sanitizers.js +17 -0
- package/src/lib/security.js +45 -0
- package/src/lib/validators.js +52 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
// Адаптер Rust-ядра (GameCore) под интерфейс, который потребляют мета-модули
|
|
2
|
+
// хоста (RoundManager, SocketManager) и host-фасад. За поверхностью Game.js
|
|
3
|
+
// (+ упаковка снапшотов) стоит WASM-ядро; события ядра (take_events) несут
|
|
4
|
+
// стандартный движковый словарь (panelSet/panelActive/death/shake/custom,
|
|
5
|
+
// Wasm Host ABI) — адаптер роутит их в мету сам. 'custom' (вне словаря,
|
|
6
|
+
// игровой смысл) уходит в опциональный HostPlugin.onCoreEvent.
|
|
7
|
+
//
|
|
8
|
+
// Различие scripted/человек — по participants.get(gameId).isScripted:
|
|
9
|
+
// scripted-участник создаётся как танк + ИИ-контроллер внутри ядра
|
|
10
|
+
// (spawn_scripted_actor/remove_scripted_actor), человек — только танк
|
|
11
|
+
// (spawn_actor/remove_actor).
|
|
12
|
+
export default class GameCoreAdapter {
|
|
13
|
+
/**
|
|
14
|
+
* @param {GameCore} core - экземпляр WASM-ядра.
|
|
15
|
+
* @param {Object} deps
|
|
16
|
+
* @param {ParticipantManager} deps.participants - реестр участников
|
|
17
|
+
* (различение scripted/человек при спавне и удалении).
|
|
18
|
+
* @param {Function} [deps.onCoreEvent] - игровой обработчик 'custom'
|
|
19
|
+
* событий ядра: (data, services) => void.
|
|
20
|
+
*/
|
|
21
|
+
constructor(core, { participants, onCoreEvent }) {
|
|
22
|
+
this._core = core;
|
|
23
|
+
this._participants = participants;
|
|
24
|
+
this._onCoreEvent = onCoreEvent;
|
|
25
|
+
this._services = {}; // { vimp, panel } — инъекция как у Game.js
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// получает сервисы (аналог Game.injectServices): { vimp, panel }
|
|
29
|
+
injectServices(services) {
|
|
30
|
+
Object.assign(this._services, services);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ***** карта ***** //
|
|
34
|
+
|
|
35
|
+
// создаёт карту в ядре. scaledMapData уже отмасштабирован в JS
|
|
36
|
+
// (RoundManager.scaleMapData) — грузим со scale:1, чтобы ядро не
|
|
37
|
+
// масштабировало повторно (геометрия совпадает с легаси-сервером байт-в-байт)
|
|
38
|
+
createMap(scaledMapData) {
|
|
39
|
+
this._core.load_map(JSON.stringify({ ...scaledMapData, scale: 1 }));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// полная очистка мира (смена карты)
|
|
43
|
+
clear() {
|
|
44
|
+
this._core.clear();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ***** участники ***** //
|
|
48
|
+
|
|
49
|
+
// создаёт танк (Game.createPlayer). Scripted → танк + ИИ в ядре
|
|
50
|
+
createPlayer(gameId, model, name, teamId, data) {
|
|
51
|
+
const [x, y, angle] = data;
|
|
52
|
+
|
|
53
|
+
if (this._isScripted(gameId)) {
|
|
54
|
+
this._core.spawn_scripted_actor(gameId, model, teamId, x, y, angle);
|
|
55
|
+
} else {
|
|
56
|
+
this._core.spawn_actor(gameId, model, teamId, x, y, angle);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// удаляет танк (Game.removePlayer). Scripted → удаляет и ИИ-контроллер
|
|
61
|
+
removePlayer(gameId) {
|
|
62
|
+
if (this._isScripted(gameId)) {
|
|
63
|
+
this._core.remove_scripted_actor(gameId);
|
|
64
|
+
} else {
|
|
65
|
+
this._core.remove_actor(gameId);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// удаляет всех игроков и снаряды, возвращает имена для очистки полотна
|
|
70
|
+
removePlayersAndShots() {
|
|
71
|
+
return JSON.parse(this._core.remove_players_and_shots());
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// имена — мета (ParticipantManager/Stat), ядру не нужны
|
|
75
|
+
changeName() {}
|
|
76
|
+
|
|
77
|
+
// перемещает танк при смене команды/респауне (Game.changePlayerData)
|
|
78
|
+
changePlayerData(gameId, data) {
|
|
79
|
+
const [x, y, angle] = data.respawnData;
|
|
80
|
+
|
|
81
|
+
this._core.reset_actor(gameId, data.teamId, x, y, angle);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ***** ввод ***** //
|
|
85
|
+
|
|
86
|
+
// применяет ввод игрока; seq подтверждается ядром в player-блоке кадра
|
|
87
|
+
applyInput(gameId, seq, action, name) {
|
|
88
|
+
this._core.apply_input(gameId, seq, action, name);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ***** запросы состояния ***** //
|
|
92
|
+
|
|
93
|
+
isAlive(gameId) {
|
|
94
|
+
return this._core.is_alive(gameId);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// координаты танка [x, y]; [] от ядра → [0, 0] (наблюдение за пустотой)
|
|
98
|
+
getPosition(gameId) {
|
|
99
|
+
const pos = this._core.position_of(gameId);
|
|
100
|
+
|
|
101
|
+
return pos.length ? [pos[0], pos[1]] : [0, 0];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// полные данные всех игроков для первого кадра (SocketManager.sendFirstShot)
|
|
105
|
+
getPlayersData() {
|
|
106
|
+
return JSON.parse(this._core.players_data());
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ***** игровой тик ***** //
|
|
110
|
+
|
|
111
|
+
// шаг симуляции (физика + ИИ ботов в ядре) и проекция событий в мету
|
|
112
|
+
updateData(dt) {
|
|
113
|
+
this._core.step(dt);
|
|
114
|
+
this._drainEvents();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// дренирует события ядра и роутит стандартный словарь в мету ({ panel,
|
|
118
|
+
// vimp }); 'custom' (игровой смысл вне словаря) — в HostPlugin.onCoreEvent
|
|
119
|
+
_drainEvents() {
|
|
120
|
+
const events = JSON.parse(this._core.take_events());
|
|
121
|
+
const { panel, vimp } = this._services;
|
|
122
|
+
|
|
123
|
+
for (const event of events) {
|
|
124
|
+
switch (event.type) {
|
|
125
|
+
case 'panelSet':
|
|
126
|
+
panel.updateUser(String(event.id), event.field, event.value, 'set');
|
|
127
|
+
break;
|
|
128
|
+
|
|
129
|
+
case 'panelActive':
|
|
130
|
+
panel.setActiveWeapon(String(event.id), event.field);
|
|
131
|
+
break;
|
|
132
|
+
|
|
133
|
+
case 'death':
|
|
134
|
+
vimp.reportKill(String(event.victim), String(event.killer));
|
|
135
|
+
break;
|
|
136
|
+
|
|
137
|
+
case 'shake':
|
|
138
|
+
vimp.triggerCameraShake(String(event.id), {
|
|
139
|
+
intensity: event.intensity,
|
|
140
|
+
duration: event.duration,
|
|
141
|
+
});
|
|
142
|
+
break;
|
|
143
|
+
|
|
144
|
+
case 'custom':
|
|
145
|
+
this._onCoreEvent?.(event.data, this._services);
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ***** упаковка снапшота ***** //
|
|
152
|
+
|
|
153
|
+
// пакует broadcast-тело кадра (один раз за отправляемый кадр)
|
|
154
|
+
packBody() {
|
|
155
|
+
this._core.pack_body();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// содержал ли последний packBody событийные блоки (трассеры/бомбы/взрывы/
|
|
159
|
+
// удаления) — для классификации канала WebRTC (meta reliable / state)
|
|
160
|
+
bodyHasEvents() {
|
|
161
|
+
return this._core.body_has_events();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Пакует per-user кадр v3 и возвращает его ArrayBuffer.
|
|
166
|
+
* @param {Array|number} camera - [x, y, forceReset?, shake?] или 0.
|
|
167
|
+
* @param {number} serverTime
|
|
168
|
+
* @param {number} seq
|
|
169
|
+
* @param {number|null} playerId - id играющего (null/наблюдатель → без
|
|
170
|
+
* player-блока).
|
|
171
|
+
* @returns {ArrayBuffer}
|
|
172
|
+
*/
|
|
173
|
+
packFrame(camera, serverTime, seq, playerId) {
|
|
174
|
+
const hasCamera = Array.isArray(camera);
|
|
175
|
+
const camX = hasCamera ? camera[0] : 0;
|
|
176
|
+
const camY = hasCamera ? camera[1] : 0;
|
|
177
|
+
const forceReset = hasCamera && camera[2] === true;
|
|
178
|
+
const shake = hasCamera && camera[3] ? camera[3] : undefined;
|
|
179
|
+
|
|
180
|
+
this._core.pack_frame(
|
|
181
|
+
serverTime,
|
|
182
|
+
seq,
|
|
183
|
+
hasCamera,
|
|
184
|
+
camX,
|
|
185
|
+
camY,
|
|
186
|
+
forceReset,
|
|
187
|
+
shake,
|
|
188
|
+
playerId ?? -1,
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
// копия из памяти WASM (target-agnostic: работает и на web, и на nodejs)
|
|
192
|
+
const bytes = this._core.frame_bytes();
|
|
193
|
+
|
|
194
|
+
return bytes.buffer.slice(
|
|
195
|
+
bytes.byteOffset,
|
|
196
|
+
bytes.byteOffset + bytes.byteLength,
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// бот ли участник (спавн/удаление в ядре различаются)
|
|
201
|
+
_isScripted(gameId) {
|
|
202
|
+
const participant = this._participants.get(gameId);
|
|
203
|
+
|
|
204
|
+
return Boolean(participant && participant.isScripted);
|
|
205
|
+
}
|
|
206
|
+
}
|