@rpgjs/client 5.0.0-beta.15 → 5.0.0-beta.17
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 +20 -0
- package/dist/RpgClientEngine.d.ts +10 -0
- package/dist/RpgClientEngine.js +69 -2
- package/dist/RpgClientEngine.js.map +1 -1
- package/dist/components/character.ce.js +2 -1
- package/dist/components/character.ce.js.map +1 -1
- package/dist/services/AbstractSocket.d.ts +2 -0
- package/dist/services/AbstractSocket.js.map +1 -1
- package/dist/services/mmorpg.d.ts +1 -0
- package/dist/services/mmorpg.js +1 -0
- package/dist/services/mmorpg.js.map +1 -1
- package/dist/services/standalone.d.ts +1 -0
- package/dist/services/standalone.js +1 -0
- package/dist/services/standalone.js.map +1 -1
- package/package.json +4 -4
- package/src/RpgClientEngine.ts +119 -3
- package/src/components/character.ce +2 -1
- package/src/services/AbstractSocket.ts +3 -0
- package/src/services/mmorpg.ts +2 -0
- package/src/services/standalone.spec.ts +20 -0
- package/src/services/standalone.ts +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# @rpgjs/client
|
|
2
2
|
|
|
3
|
+
## 5.0.0-beta.17
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Release the next RPGJS beta while keeping the physics package on its stable release line.
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- @rpgjs/common@5.0.0-beta.16
|
|
10
|
+
- @rpgjs/server@5.0.0-beta.17
|
|
11
|
+
- @rpgjs/ui-css@5.0.0-beta.14
|
|
12
|
+
|
|
13
|
+
## 5.0.0-beta.16
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- Release the next RPGJS beta with terrain rendering performance improvements and a unified server tick loop.
|
|
18
|
+
- Updated dependencies
|
|
19
|
+
- @rpgjs/common@5.0.0-beta.15
|
|
20
|
+
- @rpgjs/server@5.0.0-beta.16
|
|
21
|
+
- @rpgjs/ui-css@5.0.0-beta.13
|
|
22
|
+
|
|
3
23
|
## 5.0.0-beta.15
|
|
4
24
|
|
|
5
25
|
### Patch Changes
|
|
@@ -66,6 +66,9 @@ export declare class RpgClientEngine<T = any> {
|
|
|
66
66
|
private predictionEnabled;
|
|
67
67
|
private prediction?;
|
|
68
68
|
private readonly SERVER_CORRECTION_THRESHOLD;
|
|
69
|
+
private localMovementAuthority;
|
|
70
|
+
private lastLocalMovementInputAt;
|
|
71
|
+
private readonly LOCAL_MOVEMENT_AUTHORITY_ACK_GRACE_MS;
|
|
69
72
|
private inputFrameCounter;
|
|
70
73
|
private pendingPredictionFrames;
|
|
71
74
|
private lastClientPhysicsStepAt;
|
|
@@ -103,6 +106,7 @@ export declare class RpgClientEngine<T = any> {
|
|
|
103
106
|
private i18nService;
|
|
104
107
|
private locale?;
|
|
105
108
|
constructor(context: any);
|
|
109
|
+
private resolveLocalMovementAuthority;
|
|
106
110
|
setLocale(locale: string): void;
|
|
107
111
|
getLocale(): string;
|
|
108
112
|
t(key: string, params?: I18nParams): string;
|
|
@@ -145,11 +149,17 @@ export declare class RpgClientEngine<T = any> {
|
|
|
145
149
|
*/
|
|
146
150
|
setKeyboardControls(controlInstance: any): void;
|
|
147
151
|
start(): Promise<void>;
|
|
152
|
+
private installCanvasResizeGuard;
|
|
153
|
+
private readCanvasResizeTargetSize;
|
|
154
|
+
private readCanvasRendererSize;
|
|
155
|
+
private cancelCanvasResizeFrame;
|
|
148
156
|
private resolveSceneMapComponent;
|
|
149
157
|
private setupPointerTracking;
|
|
150
158
|
updatePointerFromInteractionEvent(event: any): void;
|
|
151
159
|
private findViewportInstance;
|
|
152
160
|
private prepareSyncPayload;
|
|
161
|
+
private shouldPreserveLocalPlayerPosition;
|
|
162
|
+
private shouldKeepLocalPlayerMovement;
|
|
153
163
|
private normalizeAckWithSyncState;
|
|
154
164
|
private initListeners;
|
|
155
165
|
private beginMapTransfer;
|
package/dist/RpgClientEngine.js
CHANGED
|
@@ -111,6 +111,9 @@ var RpgClientEngine = class {
|
|
|
111
111
|
this.gamePause = signal(false);
|
|
112
112
|
this.predictionEnabled = false;
|
|
113
113
|
this.SERVER_CORRECTION_THRESHOLD = 30;
|
|
114
|
+
this.localMovementAuthority = false;
|
|
115
|
+
this.lastLocalMovementInputAt = 0;
|
|
116
|
+
this.LOCAL_MOVEMENT_AUTHORITY_ACK_GRACE_MS = 250;
|
|
114
117
|
this.inputFrameCounter = 0;
|
|
115
118
|
this.pendingPredictionFrames = [];
|
|
116
119
|
this.lastClientPhysicsStepAt = 0;
|
|
@@ -164,8 +167,16 @@ var RpgClientEngine = class {
|
|
|
164
167
|
this.registerSpriteComponent("rpg:shape", __ce_component$4);
|
|
165
168
|
this.registerSpriteComponent("rpg:image", __ce_component$5);
|
|
166
169
|
this.predictionEnabled = this.globalConfig?.prediction?.enabled !== false;
|
|
170
|
+
this.localMovementAuthority = this.resolveLocalMovementAuthority();
|
|
167
171
|
this.initializePredictionController();
|
|
168
172
|
}
|
|
173
|
+
resolveLocalMovementAuthority() {
|
|
174
|
+
const predictionConfig = this.globalConfig?.prediction;
|
|
175
|
+
const configured = this.globalConfig?.movementAuthority ?? predictionConfig?.movementAuthority ?? predictionConfig?.authority ?? predictionConfig?.mode;
|
|
176
|
+
if (configured === "server" || configured === "network" || configured === false) return false;
|
|
177
|
+
if (configured === "client" || configured === "local" || configured === true) return true;
|
|
178
|
+
return this.webSocket.mode === "standalone";
|
|
179
|
+
}
|
|
169
180
|
setLocale(locale) {
|
|
170
181
|
this.locale = locale;
|
|
171
182
|
}
|
|
@@ -240,6 +251,7 @@ var RpgClientEngine = class {
|
|
|
240
251
|
this.selector = document.body.querySelector("#rpg");
|
|
241
252
|
const bootstrapOptions = this.globalConfig?.bootstrapCanvasOptions;
|
|
242
253
|
const { app, canvasElement } = await bootstrapCanvas(this.selector, __ce_component, bootstrapOptions);
|
|
254
|
+
this.installCanvasResizeGuard(app);
|
|
243
255
|
this.canvasApp = app;
|
|
244
256
|
this.canvasElement = canvasElement;
|
|
245
257
|
this.renderer = app.renderer;
|
|
@@ -289,6 +301,45 @@ var RpgClientEngine = class {
|
|
|
289
301
|
this.tickSubscriptions.push(tickSubscription);
|
|
290
302
|
this.startPingPong();
|
|
291
303
|
}
|
|
304
|
+
installCanvasResizeGuard(app) {
|
|
305
|
+
if (!app || typeof app.resize !== "function") return;
|
|
306
|
+
const originalResize = app.resize.bind(app);
|
|
307
|
+
app.resize = () => {
|
|
308
|
+
const targetSize = this.readCanvasResizeTargetSize(app);
|
|
309
|
+
const rendererSize = this.readCanvasRendererSize(app);
|
|
310
|
+
if (targetSize && rendererSize && targetSize.width === rendererSize.width && targetSize.height === rendererSize.height) {
|
|
311
|
+
this.cancelCanvasResizeFrame(app);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
originalResize();
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
readCanvasResizeTargetSize(app) {
|
|
318
|
+
const resizeTarget = app?.resizeTo;
|
|
319
|
+
if (!resizeTarget || typeof window === "undefined") return null;
|
|
320
|
+
const rawWidth = resizeTarget === window ? window.innerWidth : resizeTarget.clientWidth;
|
|
321
|
+
const rawHeight = resizeTarget === window ? window.innerHeight : resizeTarget.clientHeight;
|
|
322
|
+
const width = Math.round(Number(rawWidth));
|
|
323
|
+
const height = Math.round(Number(rawHeight));
|
|
324
|
+
if (!Number.isFinite(width) || !Number.isFinite(height) || width < 0 || height < 0) return null;
|
|
325
|
+
return {
|
|
326
|
+
width,
|
|
327
|
+
height
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
readCanvasRendererSize(app) {
|
|
331
|
+
const screen = app?.renderer?.screen;
|
|
332
|
+
const width = Math.round(Number(screen?.width));
|
|
333
|
+
const height = Math.round(Number(screen?.height));
|
|
334
|
+
if (!Number.isFinite(width) || !Number.isFinite(height)) return null;
|
|
335
|
+
return {
|
|
336
|
+
width,
|
|
337
|
+
height
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
cancelCanvasResizeFrame(app) {
|
|
341
|
+
if (typeof app?._cancelResize === "function") app._cancelResize();
|
|
342
|
+
}
|
|
292
343
|
resolveSceneMapComponent() {
|
|
293
344
|
const components = this.hooks.getHookFunctions("client-sceneMap-component");
|
|
294
345
|
const component = components[components.length - 1];
|
|
@@ -382,7 +433,8 @@ var RpgClientEngine = class {
|
|
|
382
433
|
delete payload.timestamp;
|
|
383
434
|
const myId = this.playerIdSignal();
|
|
384
435
|
const players = payload.players;
|
|
385
|
-
|
|
436
|
+
const localPatch = myId && players ? players[myId] : void 0;
|
|
437
|
+
if (this.shouldPreserveLocalPlayerPosition(localPatch) && myId && players && players[myId]) {
|
|
386
438
|
const localPatch = { ...players[myId] };
|
|
387
439
|
delete localPatch.x;
|
|
388
440
|
delete localPatch.y;
|
|
@@ -395,6 +447,18 @@ var RpgClientEngine = class {
|
|
|
395
447
|
}
|
|
396
448
|
return payload;
|
|
397
449
|
}
|
|
450
|
+
shouldPreserveLocalPlayerPosition(localPatch) {
|
|
451
|
+
if (!localPatch) return false;
|
|
452
|
+
if (this.predictionEnabled && !!this.prediction?.hasPendingInputs()) return true;
|
|
453
|
+
return this.shouldKeepLocalPlayerMovement();
|
|
454
|
+
}
|
|
455
|
+
shouldKeepLocalPlayerMovement() {
|
|
456
|
+
if (!this.localMovementAuthority || this.mapTransitionInProgress) return false;
|
|
457
|
+
const myId = this.playerIdSignal();
|
|
458
|
+
if (!(myId ? this.sceneMap?.players?.()?.[myId] : void 0)) return false;
|
|
459
|
+
if (this.prediction?.hasPendingInputs()) return true;
|
|
460
|
+
return Date.now() - this.lastLocalMovementInputAt <= this.LOCAL_MOVEMENT_AUTHORITY_ACK_GRACE_MS;
|
|
461
|
+
}
|
|
398
462
|
normalizeAckWithSyncState(ack, syncData) {
|
|
399
463
|
const myId = this.playerIdSignal();
|
|
400
464
|
if (!myId) return ack;
|
|
@@ -1321,6 +1385,7 @@ var RpgClientEngine = class {
|
|
|
1321
1385
|
if (timestamp < this.dashLockedUntil) return;
|
|
1322
1386
|
this.dashLockedUntil = timestamp + cooldown;
|
|
1323
1387
|
}
|
|
1388
|
+
this.lastLocalMovementInputAt = timestamp;
|
|
1324
1389
|
let frame;
|
|
1325
1390
|
let tick;
|
|
1326
1391
|
if (this.predictionEnabled && this.prediction) {
|
|
@@ -1693,11 +1758,12 @@ var RpgClientEngine = class {
|
|
|
1693
1758
|
}
|
|
1694
1759
|
applyServerAck(ack) {
|
|
1695
1760
|
this.updateServerTickEstimate(ack.serverTick);
|
|
1761
|
+
const keepLocalMovement = this.shouldKeepLocalPlayerMovement();
|
|
1696
1762
|
if (this.predictionEnabled && this.prediction) {
|
|
1697
1763
|
const result = this.prediction.applyServerAck({
|
|
1698
1764
|
frame: ack.frame,
|
|
1699
1765
|
serverTick: ack.serverTick,
|
|
1700
|
-
state: typeof ack.x === "number" && typeof ack.y === "number" ? {
|
|
1766
|
+
state: !keepLocalMovement && typeof ack.x === "number" && typeof ack.y === "number" ? {
|
|
1701
1767
|
x: ack.x,
|
|
1702
1768
|
y: ack.y,
|
|
1703
1769
|
direction: ack.direction
|
|
@@ -1707,6 +1773,7 @@ var RpgClientEngine = class {
|
|
|
1707
1773
|
return;
|
|
1708
1774
|
}
|
|
1709
1775
|
if (typeof ack.x !== "number" || typeof ack.y !== "number") return;
|
|
1776
|
+
if (keepLocalMovement) return;
|
|
1710
1777
|
const player = this.getCurrentPlayer();
|
|
1711
1778
|
const myId = this.playerIdSignal();
|
|
1712
1779
|
if (!player || !myId) return;
|