@rpgjs/client 5.0.0-beta.10 → 5.0.0-beta.11
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/CHANGELOG.md +12 -0
- package/dist/Game/ProjectileManager.d.ts +89 -0
- package/dist/Game/ProjectileManager.js +179 -0
- package/dist/Game/ProjectileManager.js.map +1 -0
- package/dist/Game/ProjectileManager.spec.d.ts +1 -0
- package/dist/RpgClient.d.ts +53 -13
- package/dist/RpgClientEngine.d.ts +25 -4
- package/dist/RpgClientEngine.js +197 -48
- package/dist/RpgClientEngine.js.map +1 -1
- package/dist/components/animations/hit.ce.js.map +1 -1
- package/dist/components/character.ce.js +32 -30
- package/dist/components/character.ce.js.map +1 -1
- package/dist/components/dynamics/bar.ce.js +4 -3
- package/dist/components/dynamics/bar.ce.js.map +1 -1
- package/dist/components/dynamics/image.ce.js +2 -1
- package/dist/components/dynamics/image.ce.js.map +1 -1
- package/dist/components/dynamics/shape.ce.js +3 -2
- package/dist/components/dynamics/shape.ce.js.map +1 -1
- package/dist/components/dynamics/text.ce.js +9 -8
- package/dist/components/dynamics/text.ce.js.map +1 -1
- package/dist/components/gui/dialogbox/index.ce.js +3 -2
- package/dist/components/gui/dialogbox/index.ce.js.map +1 -1
- package/dist/components/gui/gameover.ce.js +3 -2
- package/dist/components/gui/gameover.ce.js.map +1 -1
- package/dist/components/gui/hud/hud.ce.js.map +1 -1
- package/dist/components/gui/menu/equip-menu.ce.js +2 -1
- package/dist/components/gui/menu/equip-menu.ce.js.map +1 -1
- package/dist/components/gui/menu/exit-menu.ce.js +2 -1
- package/dist/components/gui/menu/exit-menu.ce.js.map +1 -1
- package/dist/components/gui/menu/items-menu.ce.js +3 -2
- package/dist/components/gui/menu/items-menu.ce.js.map +1 -1
- package/dist/components/gui/menu/main-menu.ce.js +3 -2
- package/dist/components/gui/menu/main-menu.ce.js.map +1 -1
- package/dist/components/gui/menu/options-menu.ce.js.map +1 -1
- package/dist/components/gui/menu/skills-menu.ce.js.map +1 -1
- package/dist/components/gui/mobile/mobile.ce.js.map +1 -1
- package/dist/components/gui/notification/notification.ce.js.map +1 -1
- package/dist/components/gui/save-load.ce.js +2 -1
- package/dist/components/gui/save-load.ce.js.map +1 -1
- package/dist/components/gui/shop/shop.ce.js +3 -2
- package/dist/components/gui/shop/shop.ce.js.map +1 -1
- package/dist/components/gui/title-screen.ce.js +3 -2
- package/dist/components/gui/title-screen.ce.js.map +1 -1
- package/dist/components/index.d.ts +2 -1
- package/dist/components/index.js +1 -0
- package/dist/components/player-components.ce.js +11 -10
- package/dist/components/player-components.ce.js.map +1 -1
- package/dist/components/prebuilt/hp-bar.ce.js +4 -3
- package/dist/components/prebuilt/hp-bar.ce.js.map +1 -1
- package/dist/components/prebuilt/light-halo.ce.js +2 -1
- package/dist/components/prebuilt/light-halo.ce.js.map +1 -1
- package/dist/components/scenes/canvas.ce.js +12 -4
- package/dist/components/scenes/canvas.ce.js.map +1 -1
- package/dist/components/scenes/draw-map.ce.js +6 -3
- package/dist/components/scenes/draw-map.ce.js.map +1 -1
- package/dist/components/scenes/event-layer.ce.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +9 -5
- package/dist/module.js +11 -0
- package/dist/module.js.map +1 -1
- package/dist/services/actionInput.d.ts +12 -0
- package/dist/services/actionInput.js +27 -0
- package/dist/services/actionInput.js.map +1 -0
- package/dist/services/actionInput.spec.d.ts +1 -0
- package/dist/services/mmorpg-connection.d.ts +5 -0
- package/dist/services/mmorpg-connection.js +50 -0
- package/dist/services/mmorpg-connection.js.map +1 -0
- package/dist/services/mmorpg-connection.spec.d.ts +1 -0
- package/dist/services/mmorpg.d.ts +10 -4
- package/dist/services/mmorpg.js +48 -30
- package/dist/services/mmorpg.js.map +1 -1
- package/dist/services/pointerContext.d.ts +11 -0
- package/dist/services/pointerContext.js +48 -0
- package/dist/services/pointerContext.js.map +1 -0
- package/dist/services/pointerContext.spec.d.ts +1 -0
- package/dist/services/standalone-message.d.ts +1 -0
- package/dist/services/standalone-message.js +9 -0
- package/dist/services/standalone-message.js.map +1 -0
- package/dist/services/standalone.js +3 -2
- package/dist/services/standalone.js.map +1 -1
- package/dist/services/standalone.spec.d.ts +1 -0
- package/package.json +7 -7
- package/src/Game/ProjectileManager.spec.ts +338 -0
- package/src/Game/ProjectileManager.ts +324 -0
- package/src/RpgClient.ts +62 -15
- package/src/RpgClientEngine.ts +287 -65
- package/src/components/character.ce +34 -32
- package/src/components/dynamics/bar.ce +4 -3
- package/src/components/dynamics/image.ce +2 -1
- package/src/components/dynamics/shape.ce +3 -2
- package/src/components/dynamics/text.ce +9 -8
- package/src/components/gui/dialogbox/index.ce +3 -2
- package/src/components/gui/gameover.ce +2 -1
- package/src/components/gui/menu/equip-menu.ce +2 -1
- package/src/components/gui/menu/exit-menu.ce +2 -1
- package/src/components/gui/menu/items-menu.ce +3 -2
- package/src/components/gui/menu/main-menu.ce +2 -1
- package/src/components/gui/save-load.ce +2 -1
- package/src/components/gui/shop/shop.ce +3 -2
- package/src/components/gui/title-screen.ce +2 -1
- package/src/components/index.ts +2 -1
- package/src/components/player-components.ce +11 -10
- package/src/components/prebuilt/hp-bar.ce +4 -3
- package/src/components/prebuilt/light-halo.ce +2 -2
- package/src/components/scenes/canvas.ce +10 -2
- package/src/components/scenes/draw-map.ce +17 -3
- package/src/index.ts +3 -0
- package/src/module.ts +13 -0
- package/src/services/actionInput.spec.ts +101 -0
- package/src/services/actionInput.ts +53 -0
- package/src/services/mmorpg-connection.spec.ts +99 -0
- package/src/services/mmorpg-connection.ts +69 -0
- package/src/services/mmorpg.ts +60 -34
- package/src/services/pointerContext.spec.ts +36 -0
- package/src/services/pointerContext.ts +84 -0
- package/src/services/standalone-message.ts +7 -0
- package/src/services/standalone.spec.ts +34 -0
- package/src/services/standalone.ts +3 -2
package/dist/RpgClientEngine.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { inject } from "./core/inject.js";
|
|
2
2
|
import { WebSocketToken } from "./services/AbstractSocket.js";
|
|
3
|
+
import { normalizeActionInput } from "./services/actionInput.js";
|
|
3
4
|
import { getCanMoveValue } from "./utils/readPropValue.js";
|
|
4
5
|
import { SaveClientService } from "./services/save.js";
|
|
5
6
|
import { RpgGui } from "./Gui/Gui.js";
|
|
6
7
|
import __ce_component from "./components/scenes/canvas.ce.js";
|
|
8
|
+
import __ce_component$1 from "./components/scenes/draw-map.ce.js";
|
|
7
9
|
import { LoadMapToken } from "./services/loadMap.js";
|
|
8
10
|
import { RpgSound } from "./Sound.js";
|
|
9
11
|
import { RpgResource } from "./Resource.js";
|
|
@@ -12,25 +14,29 @@ import { RpgClientMap } from "./Game/Map.js";
|
|
|
12
14
|
import { AnimationManager } from "./Game/AnimationManager.js";
|
|
13
15
|
import { GlobalConfigToken } from "./module.js";
|
|
14
16
|
import { PrebuiltComponentAnimations } from "./components/animations/index.js";
|
|
15
|
-
import __ce_component$
|
|
16
|
-
import __ce_component$
|
|
17
|
-
import __ce_component$
|
|
18
|
-
import __ce_component$
|
|
17
|
+
import __ce_component$2 from "./components/dynamics/text.ce.js";
|
|
18
|
+
import __ce_component$3 from "./components/dynamics/bar.ce.js";
|
|
19
|
+
import __ce_component$4 from "./components/dynamics/shape.ce.js";
|
|
20
|
+
import __ce_component$5 from "./components/dynamics/image.ce.js";
|
|
19
21
|
import { NotificationManager } from "./Gui/NotificationManager.js";
|
|
22
|
+
import { ProjectileManager } from "./Game/ProjectileManager.js";
|
|
23
|
+
import { createClientPointerContext } from "./services/pointerContext.js";
|
|
20
24
|
import { Howl, bootstrapCanvas, signal, trigger } from "canvasengine";
|
|
21
|
-
import { Direction, ModulesToken, PredictionController, normalizeLightingState } from "@rpgjs/common";
|
|
25
|
+
import { Direction, ModulesToken, PredictionController, Vector2, normalizeLightingState } from "@rpgjs/common";
|
|
22
26
|
import { BehaviorSubject, combineLatest, filter, lastValueFrom, switchMap, take } from "rxjs";
|
|
23
27
|
import * as PIXI from "pixi.js";
|
|
24
28
|
//#region src/RpgClientEngine.ts
|
|
25
29
|
var RpgClientEngine = class {
|
|
26
30
|
constructor(context) {
|
|
27
31
|
this.context = context;
|
|
32
|
+
this.sceneMapComponent = __ce_component$1;
|
|
28
33
|
this.stopProcessingInput = false;
|
|
29
34
|
this.width = signal("100%");
|
|
30
35
|
this.height = signal("100%");
|
|
31
36
|
this.spritesheets = /* @__PURE__ */ new Map();
|
|
32
37
|
this.sounds = /* @__PURE__ */ new Map();
|
|
33
38
|
this.componentAnimations = [];
|
|
39
|
+
this.pointer = createClientPointerContext();
|
|
34
40
|
this.particleSettings = { emitters: [] };
|
|
35
41
|
this.playerIdSignal = signal(null);
|
|
36
42
|
this.spriteComponentsBehind = signal([]);
|
|
@@ -46,6 +52,7 @@ var RpgClientEngine = class {
|
|
|
46
52
|
this.pendingPredictionFrames = [];
|
|
47
53
|
this.lastClientPhysicsStepAt = 0;
|
|
48
54
|
this.frameOffset = 0;
|
|
55
|
+
this.latestServerTickAt = 0;
|
|
49
56
|
this.rtt = 0;
|
|
50
57
|
this.pingInterval = null;
|
|
51
58
|
this.PING_INTERVAL_MS = 5e3;
|
|
@@ -60,11 +67,13 @@ var RpgClientEngine = class {
|
|
|
60
67
|
this.eventsReceived$ = new BehaviorSubject(false);
|
|
61
68
|
this.sceneResetQueued = false;
|
|
62
69
|
this.tickSubscriptions = [];
|
|
70
|
+
this.pendingSyncPackets = [];
|
|
63
71
|
this.notificationManager = new NotificationManager();
|
|
64
72
|
this.webSocket = inject(WebSocketToken);
|
|
65
73
|
this.guiService = inject(RpgGui);
|
|
66
74
|
this.loadMapService = inject(LoadMapToken);
|
|
67
75
|
this.hooks = inject(ModulesToken);
|
|
76
|
+
this.projectiles = new ProjectileManager(this.hooks, (projectile) => this.predictProjectileImpact(projectile));
|
|
68
77
|
this.globalConfig = inject(GlobalConfigToken);
|
|
69
78
|
if (!this.globalConfig) this.globalConfig = {};
|
|
70
79
|
if (!this.globalConfig.box) this.globalConfig.box = {
|
|
@@ -78,12 +87,12 @@ var RpgClientEngine = class {
|
|
|
78
87
|
id: "animation",
|
|
79
88
|
component: PrebuiltComponentAnimations.Animation
|
|
80
89
|
});
|
|
81
|
-
this.registerSpriteComponent("rpg:text", __ce_component$
|
|
82
|
-
this.registerSpriteComponent("rpg:hpBar", __ce_component$
|
|
83
|
-
this.registerSpriteComponent("rpg:spBar", __ce_component$
|
|
84
|
-
this.registerSpriteComponent("rpg:bar", __ce_component$
|
|
85
|
-
this.registerSpriteComponent("rpg:shape", __ce_component$
|
|
86
|
-
this.registerSpriteComponent("rpg:image", __ce_component$
|
|
90
|
+
this.registerSpriteComponent("rpg:text", __ce_component$2);
|
|
91
|
+
this.registerSpriteComponent("rpg:hpBar", __ce_component$3);
|
|
92
|
+
this.registerSpriteComponent("rpg:spBar", __ce_component$3);
|
|
93
|
+
this.registerSpriteComponent("rpg:bar", __ce_component$3);
|
|
94
|
+
this.registerSpriteComponent("rpg:shape", __ce_component$4);
|
|
95
|
+
this.registerSpriteComponent("rpg:image", __ce_component$5);
|
|
87
96
|
this.predictionEnabled = this.globalConfig?.prediction?.enabled !== false;
|
|
88
97
|
this.initializePredictionController();
|
|
89
98
|
}
|
|
@@ -132,13 +141,26 @@ var RpgClientEngine = class {
|
|
|
132
141
|
this.sceneMap = new RpgClientMap();
|
|
133
142
|
this.sceneMap.configureClientPrediction(this.predictionEnabled);
|
|
134
143
|
this.sceneMap.loadPhysic();
|
|
144
|
+
this.resolveSceneMapComponent();
|
|
145
|
+
inject(SaveClientService).initialize();
|
|
146
|
+
this.initListeners();
|
|
147
|
+
this.guiService._initialize();
|
|
148
|
+
try {
|
|
149
|
+
await this.webSocket.connection();
|
|
150
|
+
} catch (error) {
|
|
151
|
+
this.stopPingPong();
|
|
152
|
+
await this.callConnectError(error);
|
|
153
|
+
throw error;
|
|
154
|
+
}
|
|
135
155
|
this.selector = document.body.querySelector("#rpg");
|
|
136
156
|
const bootstrapOptions = this.globalConfig?.bootstrapCanvasOptions;
|
|
137
157
|
const { app, canvasElement } = await bootstrapCanvas(this.selector, __ce_component, bootstrapOptions);
|
|
138
158
|
this.canvasApp = app;
|
|
139
159
|
this.canvasElement = canvasElement;
|
|
140
160
|
this.renderer = app.renderer;
|
|
161
|
+
this.setupPointerTracking();
|
|
141
162
|
this.tick = canvasElement?.propObservables?.context["tick"].observable;
|
|
163
|
+
this.flushPendingSyncPackets();
|
|
142
164
|
const inputCheckSubscription = this.tick.subscribe(() => {
|
|
143
165
|
if (Date.now() - this.lastInputTime > 100) {
|
|
144
166
|
const player = this.getCurrentPlayer();
|
|
@@ -156,6 +178,7 @@ var RpgClientEngine = class {
|
|
|
156
178
|
this.hooks.callHooks("client-gui-load", this).subscribe();
|
|
157
179
|
this.hooks.callHooks("client-particles-load", this).subscribe();
|
|
158
180
|
this.hooks.callHooks("client-componentAnimations-load", this).subscribe();
|
|
181
|
+
this.hooks.callHooks("client-projectiles-load", this).subscribe();
|
|
159
182
|
this.hooks.callHooks("client-sprite-load", this).subscribe();
|
|
160
183
|
await lastValueFrom(this.hooks.callHooks("client-engine-onStart", this));
|
|
161
184
|
this.resizeHandler = () => {
|
|
@@ -164,6 +187,7 @@ var RpgClientEngine = class {
|
|
|
164
187
|
window.addEventListener("resize", this.resizeHandler);
|
|
165
188
|
const tickSubscription = this.tick.subscribe((tick) => {
|
|
166
189
|
this.stepClientPhysicsTick();
|
|
190
|
+
this.projectiles.step();
|
|
167
191
|
this.flushPendingPredictedStates();
|
|
168
192
|
this.flushPendingMovePath();
|
|
169
193
|
this.hooks.callHooks("client-engine-onStep", this, tick).subscribe();
|
|
@@ -174,12 +198,46 @@ var RpgClientEngine = class {
|
|
|
174
198
|
}
|
|
175
199
|
});
|
|
176
200
|
this.tickSubscriptions.push(tickSubscription);
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
201
|
+
this.startPingPong();
|
|
202
|
+
}
|
|
203
|
+
resolveSceneMapComponent() {
|
|
204
|
+
const components = this.hooks.getHookFunctions("client-sceneMap-component");
|
|
205
|
+
const component = components[components.length - 1];
|
|
206
|
+
if (component) this.sceneMapComponent = component;
|
|
207
|
+
}
|
|
208
|
+
setupPointerTracking() {
|
|
209
|
+
const renderer = this.renderer;
|
|
210
|
+
const canvas = renderer?.canvas ?? renderer?.view ?? this.canvasApp?.canvas;
|
|
211
|
+
if (!canvas || typeof canvas.addEventListener !== "function") return;
|
|
212
|
+
this.pointerCanvas = canvas;
|
|
213
|
+
this.pointerMoveHandler = (event) => {
|
|
214
|
+
const rect = canvas.getBoundingClientRect();
|
|
215
|
+
const screen = {
|
|
216
|
+
x: event.clientX - rect.left,
|
|
217
|
+
y: event.clientY - rect.top
|
|
218
|
+
};
|
|
219
|
+
const viewport = this.findViewportInstance();
|
|
220
|
+
let world = screen;
|
|
221
|
+
if (viewport && typeof viewport.toWorld === "function") {
|
|
222
|
+
const point = viewport.toWorld(screen.x, screen.y);
|
|
223
|
+
world = {
|
|
224
|
+
x: Number(point.x),
|
|
225
|
+
y: Number(point.y)
|
|
226
|
+
};
|
|
227
|
+
} else if (viewport && typeof viewport.toLocal === "function") {
|
|
228
|
+
const point = viewport.toLocal(screen);
|
|
229
|
+
world = {
|
|
230
|
+
x: Number(point.x),
|
|
231
|
+
y: Number(point.y)
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
this.pointer.update(screen, world);
|
|
235
|
+
};
|
|
236
|
+
canvas.addEventListener("pointermove", this.pointerMoveHandler);
|
|
237
|
+
canvas.addEventListener("pointerdown", this.pointerMoveHandler);
|
|
238
|
+
}
|
|
239
|
+
findViewportInstance() {
|
|
240
|
+
return (this.canvasApp?.stage?.children ?? []).find((child) => typeof child?.toWorld === "function" || child?.constructor?.name === "Viewport");
|
|
183
241
|
}
|
|
184
242
|
prepareSyncPayload(data) {
|
|
185
243
|
const payload = { ...data ?? {} };
|
|
@@ -214,41 +272,27 @@ var RpgClientEngine = class {
|
|
|
214
272
|
}
|
|
215
273
|
initListeners() {
|
|
216
274
|
this.webSocket.on("sync", (data) => {
|
|
217
|
-
if (
|
|
218
|
-
this.
|
|
219
|
-
|
|
220
|
-
}
|
|
221
|
-
if (this.sceneResetQueued) {
|
|
222
|
-
this.sceneMap.reset();
|
|
223
|
-
this.sceneMap.loadPhysic();
|
|
224
|
-
this.sceneResetQueued = false;
|
|
225
|
-
}
|
|
226
|
-
this.hooks.callHooks("client-sceneMap-onChanges", this.sceneMap, { partial: data }).subscribe();
|
|
227
|
-
const ack = data?.ack;
|
|
228
|
-
const normalizedAck = ack && typeof ack.frame === "number" ? this.normalizeAckWithSyncState(ack, data) : void 0;
|
|
229
|
-
const payload = this.prepareSyncPayload(data);
|
|
230
|
-
load(this.sceneMap, payload, true);
|
|
231
|
-
if (normalizedAck) this.applyServerAck(normalizedAck);
|
|
232
|
-
for (const playerId in payload.players ?? {}) {
|
|
233
|
-
const player = payload.players[playerId];
|
|
234
|
-
if (!player._param) continue;
|
|
235
|
-
for (const param in player._param) this.sceneMap.players()[playerId]._param()[param] = player._param[param];
|
|
275
|
+
if (!this.tick) {
|
|
276
|
+
this.pendingSyncPackets.push(data);
|
|
277
|
+
return;
|
|
236
278
|
}
|
|
237
|
-
|
|
238
|
-
if (players && Object.keys(players).length > 0) this.playersReceived$.next(true);
|
|
239
|
-
if ((payload.events || this.sceneMap.events()) !== void 0) this.eventsReceived$.next(true);
|
|
279
|
+
this.applySyncPacket(data);
|
|
240
280
|
});
|
|
241
281
|
this.webSocket.on("pong", (data) => {
|
|
242
282
|
const now = Date.now();
|
|
243
283
|
this.rtt = now - data.clientTime;
|
|
244
284
|
const estimatedTicksInFlight = Math.floor(this.rtt / 2 / (1e3 / 60));
|
|
245
285
|
const estimatedServerTickNow = data.serverTick + estimatedTicksInFlight;
|
|
286
|
+
this.updateServerTickEstimate(estimatedServerTickNow, now);
|
|
246
287
|
if (this.inputFrameCounter > 0) this.frameOffset = estimatedServerTickNow - data.clientFrame;
|
|
247
288
|
console.debug(`[Ping/Pong] RTT: ${this.rtt}ms, ServerTick: ${data.serverTick}, FrameOffset: ${this.frameOffset}`);
|
|
248
289
|
});
|
|
249
290
|
this.webSocket.on("changeMap", (data) => {
|
|
250
291
|
this.sceneResetQueued = true;
|
|
292
|
+
this.sceneMap.weatherState.set(null);
|
|
293
|
+
this.sceneMap.lightingState.set(null);
|
|
251
294
|
this.sceneMap.clearLightSpots();
|
|
295
|
+
this.projectiles.clear();
|
|
252
296
|
this.cameraFollowTargetId.set(null);
|
|
253
297
|
const transferToken = typeof data?.transferToken === "string" ? data.transferToken : void 0;
|
|
254
298
|
this.loadScene(data.mapId, transferToken);
|
|
@@ -259,6 +303,21 @@ var RpgClientEngine = class {
|
|
|
259
303
|
const player = object ? this.sceneMap.getObjectById(object) : void 0;
|
|
260
304
|
this.getComponentAnimation(id).displayEffect(params, player || position);
|
|
261
305
|
});
|
|
306
|
+
this.webSocket.on("projectile:spawnBatch", (data) => {
|
|
307
|
+
this.projectiles.spawnBatch(data?.projectiles ?? [], {
|
|
308
|
+
currentServerTick: this.estimateServerTick(),
|
|
309
|
+
tickDurationMs: this.getPhysicsTickDurationMs()
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
this.webSocket.on("projectile:impactBatch", (data) => {
|
|
313
|
+
this.projectiles.impactBatch(data?.impacts ?? []);
|
|
314
|
+
});
|
|
315
|
+
this.webSocket.on("projectile:destroyBatch", (data) => {
|
|
316
|
+
this.projectiles.destroyBatch(data?.projectiles ?? []);
|
|
317
|
+
});
|
|
318
|
+
this.webSocket.on("projectile:clear", () => {
|
|
319
|
+
this.projectiles.clear();
|
|
320
|
+
});
|
|
262
321
|
this.webSocket.on("notification", (data) => {
|
|
263
322
|
this.notificationManager.add(data);
|
|
264
323
|
});
|
|
@@ -346,9 +405,46 @@ var RpgClientEngine = class {
|
|
|
346
405
|
this.stopPingPong();
|
|
347
406
|
});
|
|
348
407
|
this.webSocket.on("error", (error) => {
|
|
349
|
-
this.
|
|
408
|
+
this.callConnectError(error);
|
|
350
409
|
});
|
|
351
410
|
}
|
|
411
|
+
async callConnectError(error) {
|
|
412
|
+
await lastValueFrom(this.hooks.callHooks("client-engine-onConnectError", this, error, this.socket));
|
|
413
|
+
}
|
|
414
|
+
flushPendingSyncPackets() {
|
|
415
|
+
const packets = this.pendingSyncPackets;
|
|
416
|
+
this.pendingSyncPackets = [];
|
|
417
|
+
packets.forEach((packet) => this.applySyncPacket(packet));
|
|
418
|
+
}
|
|
419
|
+
applySyncPacket(data) {
|
|
420
|
+
if (data.pId) {
|
|
421
|
+
this.playerIdSignal.set(data.pId);
|
|
422
|
+
this.playerIdReceived$.next(true);
|
|
423
|
+
}
|
|
424
|
+
if (this.sceneResetQueued) {
|
|
425
|
+
const weatherState = this.sceneMap.weatherState();
|
|
426
|
+
const lightingState = this.sceneMap.lightingState();
|
|
427
|
+
this.sceneMap.reset();
|
|
428
|
+
this.sceneMap.weatherState.set(weatherState);
|
|
429
|
+
this.sceneMap.lightingState.set(lightingState);
|
|
430
|
+
this.sceneMap.loadPhysic();
|
|
431
|
+
this.sceneResetQueued = false;
|
|
432
|
+
}
|
|
433
|
+
this.hooks.callHooks("client-sceneMap-onChanges", this.sceneMap, { partial: data }).subscribe();
|
|
434
|
+
const ack = data?.ack;
|
|
435
|
+
const normalizedAck = ack && typeof ack.frame === "number" ? this.normalizeAckWithSyncState(ack, data) : void 0;
|
|
436
|
+
const payload = this.prepareSyncPayload(data);
|
|
437
|
+
load(this.sceneMap, payload, true);
|
|
438
|
+
if (normalizedAck) this.applyServerAck(normalizedAck);
|
|
439
|
+
for (const playerId in payload.players ?? {}) {
|
|
440
|
+
const player = payload.players[playerId];
|
|
441
|
+
if (!player._param) continue;
|
|
442
|
+
for (const param in player._param) this.sceneMap.players()[playerId]._param()[param] = player._param[param];
|
|
443
|
+
}
|
|
444
|
+
const players = payload.players || this.sceneMap.players();
|
|
445
|
+
if (players && Object.keys(players).length > 0) this.playersReceived$.next(true);
|
|
446
|
+
if ((payload.events || this.sceneMap.events()) !== void 0) this.eventsReceived$.next(true);
|
|
447
|
+
}
|
|
352
448
|
/**
|
|
353
449
|
* Start periodic ping/pong for client-server synchronization
|
|
354
450
|
*
|
|
@@ -425,11 +521,17 @@ var RpgClientEngine = class {
|
|
|
425
521
|
room: mapId,
|
|
426
522
|
query: transferToken ? { transferToken } : void 0
|
|
427
523
|
});
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
524
|
+
try {
|
|
525
|
+
await this.webSocket.reconnect(() => {
|
|
526
|
+
inject(SaveClientService).initialize();
|
|
527
|
+
this.initListeners();
|
|
528
|
+
this.guiService._initialize();
|
|
529
|
+
});
|
|
530
|
+
} catch (error) {
|
|
531
|
+
this.stopPingPong();
|
|
532
|
+
await this.callConnectError(error);
|
|
533
|
+
throw error;
|
|
534
|
+
}
|
|
433
535
|
const res = await this.loadMapService.load(mapId);
|
|
434
536
|
this.sceneMap.data.set(res);
|
|
435
537
|
if (this.playerIdSignal()) this.playerIdReceived$.next(true);
|
|
@@ -858,6 +960,12 @@ var RpgClientEngine = class {
|
|
|
858
960
|
getSpriteComponent(id) {
|
|
859
961
|
return this.spriteComponents.get(id);
|
|
860
962
|
}
|
|
963
|
+
registerProjectileComponent(type, component) {
|
|
964
|
+
return this.projectiles.register(type, component);
|
|
965
|
+
}
|
|
966
|
+
getProjectileComponent(type) {
|
|
967
|
+
return this.projectiles.get(type);
|
|
968
|
+
}
|
|
861
969
|
/**
|
|
862
970
|
* Add a component animation to the engine
|
|
863
971
|
*
|
|
@@ -990,15 +1098,18 @@ var RpgClientEngine = class {
|
|
|
990
1098
|
this.emitMovePacket(input, frame, tick, timestamp, true);
|
|
991
1099
|
this.lastInputTime = Date.now();
|
|
992
1100
|
}
|
|
993
|
-
processAction(
|
|
1101
|
+
processAction(action, data) {
|
|
994
1102
|
if (this.stopProcessingInput) return;
|
|
995
1103
|
const currentPlayer = this.sceneMap.getCurrentPlayer();
|
|
996
1104
|
if (!(!currentPlayer || getCanMoveValue(currentPlayer))) return;
|
|
1105
|
+
const payload = normalizeActionInput(action, data);
|
|
997
1106
|
this.hooks.callHooks("client-engine-onInput", this, {
|
|
998
|
-
input:
|
|
1107
|
+
input: payload.action,
|
|
1108
|
+
action: payload.action,
|
|
1109
|
+
data: payload.data,
|
|
999
1110
|
playerId: this.playerId
|
|
1000
1111
|
}).subscribe();
|
|
1001
|
-
this.webSocket.emit("action",
|
|
1112
|
+
this.webSocket.emit("action", payload);
|
|
1002
1113
|
}
|
|
1003
1114
|
get PIXI() {
|
|
1004
1115
|
return PIXI;
|
|
@@ -1015,6 +1126,37 @@ var RpgClientEngine = class {
|
|
|
1015
1126
|
getPhysicsTick() {
|
|
1016
1127
|
return this.sceneMap?.getTick?.() ?? 0;
|
|
1017
1128
|
}
|
|
1129
|
+
getPhysicsTickDurationMs() {
|
|
1130
|
+
const timeStep = this.sceneMap?.physic?.getWorld?.()?.getTimeStep?.();
|
|
1131
|
+
return typeof timeStep === "number" && Number.isFinite(timeStep) && timeStep > 0 ? timeStep * 1e3 : 1e3 / 60;
|
|
1132
|
+
}
|
|
1133
|
+
updateServerTickEstimate(serverTick, now = Date.now()) {
|
|
1134
|
+
if (typeof serverTick !== "number" || !Number.isFinite(serverTick)) return;
|
|
1135
|
+
this.latestServerTick = serverTick;
|
|
1136
|
+
this.latestServerTickAt = now;
|
|
1137
|
+
}
|
|
1138
|
+
estimateServerTick(now = Date.now()) {
|
|
1139
|
+
if (typeof this.latestServerTick !== "number" || this.latestServerTickAt <= 0) return;
|
|
1140
|
+
const elapsedTicks = Math.max(0, (now - this.latestServerTickAt) / this.getPhysicsTickDurationMs());
|
|
1141
|
+
return this.latestServerTick + elapsedTicks;
|
|
1142
|
+
}
|
|
1143
|
+
predictProjectileImpact(projectile) {
|
|
1144
|
+
if (projectile.predictImpact === false) return null;
|
|
1145
|
+
const sceneMap = this.sceneMap;
|
|
1146
|
+
if (!sceneMap?.physic || !Number.isFinite(projectile.range) || projectile.range <= 0) return null;
|
|
1147
|
+
const origin = projectile.origin;
|
|
1148
|
+
const direction = projectile.direction;
|
|
1149
|
+
if (!origin || !direction || !Number.isFinite(origin.x) || !Number.isFinite(origin.y) || !Number.isFinite(direction.x) || !Number.isFinite(direction.y) || direction.x === 0 && direction.y === 0) return null;
|
|
1150
|
+
const hit = sceneMap.physic.raycast(new Vector2(origin.x, origin.y), new Vector2(direction.x, direction.y), projectile.range, projectile.collisionMask, (entity) => projectile.ignoreOwner === false || !projectile.ownerId || entity.uuid !== projectile.ownerId);
|
|
1151
|
+
if (!hit) return null;
|
|
1152
|
+
return {
|
|
1153
|
+
id: projectile.id,
|
|
1154
|
+
targetId: hit.entity.uuid,
|
|
1155
|
+
x: hit.point.x,
|
|
1156
|
+
y: hit.point.y,
|
|
1157
|
+
distance: hit.distance
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1018
1160
|
ensureCurrentPlayerBody() {
|
|
1019
1161
|
const player = this.sceneMap?.getCurrentPlayer();
|
|
1020
1162
|
const myId = this.playerIdSignal();
|
|
@@ -1281,6 +1423,7 @@ var RpgClientEngine = class {
|
|
|
1281
1423
|
if (sprite && typeof sprite.flash === "function") sprite.flash(options);
|
|
1282
1424
|
}
|
|
1283
1425
|
applyServerAck(ack) {
|
|
1426
|
+
this.updateServerTickEstimate(ack.serverTick);
|
|
1284
1427
|
if (this.predictionEnabled && this.prediction) {
|
|
1285
1428
|
const result = this.prediction.applyServerAck({
|
|
1286
1429
|
frame: ack.frame,
|
|
@@ -1385,6 +1528,12 @@ var RpgClientEngine = class {
|
|
|
1385
1528
|
window.removeEventListener("resize", this.resizeHandler);
|
|
1386
1529
|
this.resizeHandler = void 0;
|
|
1387
1530
|
}
|
|
1531
|
+
if (this.pointerMoveHandler && this.pointerCanvas) {
|
|
1532
|
+
this.pointerCanvas.removeEventListener("pointermove", this.pointerMoveHandler);
|
|
1533
|
+
this.pointerCanvas.removeEventListener("pointerdown", this.pointerMoveHandler);
|
|
1534
|
+
this.pointerMoveHandler = void 0;
|
|
1535
|
+
this.pointerCanvas = void 0;
|
|
1536
|
+
}
|
|
1388
1537
|
const rendererStillExists = this.renderer && typeof this.renderer.destroy === "function";
|
|
1389
1538
|
if (this.canvasApp && typeof this.canvasApp.destroy === "function") {
|
|
1390
1539
|
try {
|