@rpgjs/client 5.0.0-alpha.9 → 5.0.0-beta.10
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 +37 -0
- package/LICENSE +19 -0
- package/dist/Game/AnimationManager.d.ts +8 -0
- package/dist/Game/AnimationManager.js +35 -0
- package/dist/Game/AnimationManager.js.map +1 -0
- package/dist/Game/AnimationManager.spec.d.ts +1 -0
- package/dist/Game/Event.d.ts +1 -1
- package/dist/Game/Event.js +12 -0
- package/dist/Game/Event.js.map +1 -0
- package/dist/Game/Map.d.ts +31 -2
- package/dist/Game/Map.js +138 -0
- package/dist/Game/Map.js.map +1 -0
- package/dist/Game/Object.d.ts +189 -0
- package/dist/Game/Object.js +255 -0
- package/dist/Game/Object.js.map +1 -0
- package/dist/Game/Player.d.ts +1 -1
- package/dist/Game/Player.js +12 -0
- package/dist/Game/Player.js.map +1 -0
- package/dist/Gui/Gui.d.ts +192 -7
- package/dist/Gui/Gui.js +475 -0
- package/dist/Gui/Gui.js.map +1 -0
- package/dist/Gui/Gui.spec.d.ts +1 -0
- package/dist/Gui/NotificationManager.d.ts +23 -0
- package/dist/Gui/NotificationManager.js +49 -0
- package/dist/Gui/NotificationManager.js.map +1 -0
- package/dist/Resource.d.ts +97 -0
- package/dist/Resource.js +133 -0
- package/dist/Resource.js.map +1 -0
- package/dist/RpgClient.d.ts +295 -13
- package/dist/RpgClientEngine.d.ts +671 -15
- package/dist/RpgClientEngine.js +1442 -0
- package/dist/RpgClientEngine.js.map +1 -0
- package/dist/Sound.d.ts +199 -0
- package/dist/Sound.js +167 -0
- package/dist/Sound.js.map +1 -0
- package/dist/_virtual/_@oxc-project_runtime@0.130.0/helpers/decorate.js +9 -0
- package/dist/_virtual/_@oxc-project_runtime@0.130.0/helpers/decorateMetadata.js +6 -0
- package/dist/components/animations/animation.ce.js +23 -0
- package/dist/components/animations/animation.ce.js.map +1 -0
- package/dist/components/animations/hit.ce.js +64 -0
- package/dist/components/animations/hit.ce.js.map +1 -0
- package/dist/components/animations/index.d.ts +4 -0
- package/dist/components/animations/index.js +11 -0
- package/dist/components/animations/index.js.map +1 -0
- package/dist/components/character.ce.js +572 -0
- package/dist/components/character.ce.js.map +1 -0
- package/dist/components/dynamics/bar.ce.js +96 -0
- package/dist/components/dynamics/bar.ce.js.map +1 -0
- package/dist/components/dynamics/image.ce.js +23 -0
- package/dist/components/dynamics/image.ce.js.map +1 -0
- package/dist/components/dynamics/parse-value.d.ts +4 -0
- package/dist/components/dynamics/parse-value.js +63 -0
- package/dist/components/dynamics/parse-value.js.map +1 -0
- package/dist/components/dynamics/parse-value.spec.d.ts +1 -0
- package/dist/components/dynamics/shape-utils.d.ts +16 -0
- package/dist/components/dynamics/shape-utils.js +73 -0
- package/dist/components/dynamics/shape-utils.js.map +1 -0
- package/dist/components/dynamics/shape-utils.spec.d.ts +1 -0
- package/dist/components/dynamics/shape.ce.js +83 -0
- package/dist/components/dynamics/shape.ce.js.map +1 -0
- package/dist/components/dynamics/text.ce.js +50 -0
- package/dist/components/dynamics/text.ce.js.map +1 -0
- package/dist/components/gui/box.ce.js +26 -0
- package/dist/components/gui/box.ce.js.map +1 -0
- package/dist/components/gui/dialogbox/index.ce.js +198 -0
- package/dist/components/gui/dialogbox/index.ce.js.map +1 -0
- package/dist/components/gui/gameover.ce.js +169 -0
- package/dist/components/gui/gameover.ce.js.map +1 -0
- package/dist/components/gui/hud/hud.ce.js +83 -0
- package/dist/components/gui/hud/hud.ce.js.map +1 -0
- package/dist/components/gui/index.d.ts +15 -3
- package/dist/components/gui/index.js +14 -0
- package/dist/components/gui/menu/equip-menu.ce.js +427 -0
- package/dist/components/gui/menu/equip-menu.ce.js.map +1 -0
- package/dist/components/gui/menu/exit-menu.ce.js +55 -0
- package/dist/components/gui/menu/exit-menu.ce.js.map +1 -0
- package/dist/components/gui/menu/items-menu.ce.js +326 -0
- package/dist/components/gui/menu/items-menu.ce.js.map +1 -0
- package/dist/components/gui/menu/main-menu.ce.js +399 -0
- package/dist/components/gui/menu/main-menu.ce.js.map +1 -0
- package/dist/components/gui/menu/options-menu.ce.js +49 -0
- package/dist/components/gui/menu/options-menu.ce.js.map +1 -0
- package/dist/components/gui/menu/skills-menu.ce.js +102 -0
- package/dist/components/gui/menu/skills-menu.ce.js.map +1 -0
- package/dist/components/gui/mobile/index.d.ts +8 -0
- package/dist/components/gui/mobile/index.js +21 -0
- package/dist/components/gui/mobile/index.js.map +1 -0
- package/dist/components/gui/mobile/mobile.ce.js +79 -0
- package/dist/components/gui/mobile/mobile.ce.js.map +1 -0
- package/dist/components/gui/notification/notification.ce.js +62 -0
- package/dist/components/gui/notification/notification.ce.js.map +1 -0
- package/dist/components/gui/save-load.ce.js +211 -0
- package/dist/components/gui/save-load.ce.js.map +1 -0
- package/dist/components/gui/shop/shop.ce.js +614 -0
- package/dist/components/gui/shop/shop.ce.js.map +1 -0
- package/dist/components/gui/title-screen.ce.js +164 -0
- package/dist/components/gui/title-screen.ce.js.map +1 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +4 -0
- package/dist/components/player-components-utils.d.ts +67 -0
- package/dist/components/player-components-utils.js +162 -0
- package/dist/components/player-components-utils.js.map +1 -0
- package/dist/components/player-components-utils.spec.d.ts +1 -0
- package/dist/components/player-components.ce.js +188 -0
- package/dist/components/player-components.ce.js.map +1 -0
- package/dist/components/prebuilt/hp-bar.ce.js +113 -0
- package/dist/components/prebuilt/hp-bar.ce.js.map +1 -0
- package/dist/components/prebuilt/index.d.ts +19 -0
- package/dist/components/prebuilt/index.js +2 -0
- package/dist/components/prebuilt/light-halo.ce.js +70 -0
- package/dist/components/prebuilt/light-halo.ce.js.map +1 -0
- package/dist/components/scenes/canvas.ce.js +196 -0
- package/dist/components/scenes/canvas.ce.js.map +1 -0
- package/dist/components/scenes/draw-map.ce.js +79 -0
- package/dist/components/scenes/draw-map.ce.js.map +1 -0
- package/dist/components/scenes/event-layer.ce.js +29 -0
- package/dist/components/scenes/event-layer.ce.js.map +1 -0
- package/dist/core/inject.js +18 -0
- package/dist/core/inject.js.map +1 -0
- package/dist/core/setup.js +16 -0
- package/dist/core/setup.js.map +1 -0
- package/dist/decorators/spritesheet.d.ts +1 -0
- package/dist/decorators/spritesheet.js +11 -0
- package/dist/decorators/spritesheet.js.map +1 -0
- package/dist/index.d.ts +16 -1
- package/dist/index.js +45 -14
- package/dist/module.d.ts +43 -4
- package/dist/module.js +179 -0
- package/dist/module.js.map +1 -0
- package/dist/node_modules/.pnpm/@signe_di@3.0.1/node_modules/@signe/di/dist/index.js +167 -0
- package/dist/node_modules/.pnpm/@signe_di@3.0.1/node_modules/@signe/di/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/@signe_reactive@3.0.1/node_modules/@signe/reactive/dist/index.js +239 -0
- package/dist/node_modules/.pnpm/@signe_reactive@3.0.1/node_modules/@signe/reactive/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/@signe_room@3.0.1/node_modules/@signe/room/dist/chunk-EUXUH3YW.js +13 -0
- package/dist/node_modules/.pnpm/@signe_room@3.0.1/node_modules/@signe/room/dist/chunk-EUXUH3YW.js.map +1 -0
- package/dist/node_modules/.pnpm/@signe_room@3.0.1/node_modules/@signe/room/dist/index.js +696 -0
- package/dist/node_modules/.pnpm/@signe_room@3.0.1/node_modules/@signe/room/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/@signe_sync@3.0.1/node_modules/@signe/sync/dist/client/index.js +44 -0
- package/dist/node_modules/.pnpm/@signe_sync@3.0.1/node_modules/@signe/sync/dist/client/index.js.map +1 -0
- package/dist/node_modules/.pnpm/@signe_sync@3.0.1/node_modules/@signe/sync/dist/index.js +241 -0
- package/dist/node_modules/.pnpm/@signe_sync@3.0.1/node_modules/@signe/sync/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/chunk-HAC622V3.js +115 -0
- package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/chunk-HAC622V3.js.map +1 -0
- package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/chunk-S74YV6PU.js +401 -0
- package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/chunk-S74YV6PU.js.map +1 -0
- package/dist/node_modules/.pnpm/partysocket@1.1.3/node_modules/partysocket/dist/index.js +2 -0
- package/dist/node_modules/.pnpm/zod@3.24.2/node_modules/zod/lib/index.js +3756 -0
- package/dist/node_modules/.pnpm/zod@3.24.2/node_modules/zod/lib/index.js.map +1 -0
- package/dist/presets/animation.d.ts +31 -0
- package/dist/presets/animation.js +39 -0
- package/dist/presets/animation.js.map +1 -0
- package/dist/presets/faceset.d.ts +30 -0
- package/dist/presets/faceset.js +51 -0
- package/dist/presets/faceset.js.map +1 -0
- package/dist/presets/icon.d.ts +20 -0
- package/dist/presets/icon.js +15 -0
- package/dist/presets/icon.js.map +1 -0
- package/dist/presets/index.d.ts +123 -0
- package/dist/presets/index.js +17 -0
- package/dist/presets/index.js.map +1 -0
- package/dist/presets/lpc.d.ts +89 -0
- package/dist/presets/lpc.js +98 -0
- package/dist/presets/lpc.js.map +1 -0
- package/dist/presets/rmspritesheet.js +42 -0
- package/dist/presets/rmspritesheet.js.map +1 -0
- package/dist/services/AbstractSocket.d.ts +9 -5
- package/dist/services/AbstractSocket.js +11 -0
- package/dist/services/AbstractSocket.js.map +1 -0
- package/dist/services/keyboardControls.d.ts +15 -0
- package/dist/services/keyboardControls.js +23 -0
- package/dist/services/keyboardControls.js.map +1 -0
- package/dist/services/loadMap.d.ts +6 -0
- package/dist/services/loadMap.js +123 -0
- package/dist/services/loadMap.js.map +1 -0
- package/dist/services/mmorpg.d.ts +21 -9
- package/dist/services/mmorpg.js +136 -0
- package/dist/services/mmorpg.js.map +1 -0
- package/dist/services/save.d.ts +19 -0
- package/dist/services/save.js +77 -0
- package/dist/services/save.js.map +1 -0
- package/dist/services/save.spec.d.ts +1 -0
- package/dist/services/standalone.d.ts +67 -7
- package/dist/services/standalone.js +168 -0
- package/dist/services/standalone.js.map +1 -0
- package/dist/utils/getEntityProp.d.ts +39 -0
- package/dist/utils/getEntityProp.js +53 -0
- package/dist/utils/getEntityProp.js.map +1 -0
- package/dist/utils/getEntityProp.spec.d.ts +1 -0
- package/dist/utils/readPropValue.d.ts +2 -0
- package/dist/utils/readPropValue.js +13 -0
- package/dist/utils/readPropValue.js.map +1 -0
- package/package.json +14 -11
- package/src/Game/AnimationManager.spec.ts +30 -0
- package/src/Game/AnimationManager.ts +33 -0
- package/src/Game/Event.ts +1 -1
- package/src/Game/Map.ts +184 -3
- package/src/Game/Object.ts +409 -14
- package/src/Game/Player.ts +1 -1
- package/src/Gui/Gui.spec.ts +273 -0
- package/src/Gui/Gui.ts +566 -23
- package/src/Gui/NotificationManager.ts +69 -0
- package/src/Resource.ts +149 -0
- package/src/RpgClient.ts +309 -14
- package/src/RpgClientEngine.ts +1790 -63
- package/src/Sound.ts +253 -0
- package/src/components/{effects → animations}/animation.ce +3 -6
- package/src/components/{effects → animations}/index.ts +1 -1
- package/src/components/character.ce +801 -59
- package/src/components/dynamics/bar.ce +87 -0
- package/src/components/dynamics/image.ce +20 -0
- package/src/components/dynamics/parse-value.spec.ts +83 -0
- package/src/components/dynamics/parse-value.ts +154 -0
- package/src/components/dynamics/shape-utils.spec.ts +46 -0
- package/src/components/dynamics/shape-utils.ts +61 -0
- package/src/components/dynamics/shape.ce +89 -0
- package/src/components/dynamics/text.ce +68 -0
- package/src/components/gui/box.ce +17 -0
- package/src/components/gui/dialogbox/index.ce +213 -187
- package/src/components/gui/gameover.ce +158 -0
- package/src/components/gui/hud/hud.ce +61 -0
- package/src/components/gui/index.ts +30 -4
- package/src/components/gui/menu/equip-menu.ce +410 -0
- package/src/components/gui/menu/exit-menu.ce +41 -0
- package/src/components/gui/menu/items-menu.ce +317 -0
- package/src/components/gui/menu/main-menu.ce +294 -0
- package/src/components/gui/menu/options-menu.ce +35 -0
- package/src/components/gui/menu/skills-menu.ce +83 -0
- package/src/components/gui/mobile/index.ts +24 -0
- package/src/components/gui/mobile/mobile.ce +80 -0
- package/src/components/gui/notification/notification.ce +51 -0
- package/src/components/gui/save-load.ce +208 -0
- package/src/components/gui/shop/shop.ce +493 -0
- package/src/components/gui/title-screen.ce +163 -0
- package/src/components/index.ts +3 -0
- package/src/components/player-components-utils.spec.ts +109 -0
- package/src/components/player-components-utils.ts +205 -0
- package/src/components/player-components.ce +221 -0
- package/src/components/prebuilt/hp-bar.ce +255 -0
- package/src/components/prebuilt/index.ts +24 -0
- package/src/components/prebuilt/light-halo.ce +148 -0
- package/src/components/scenes/canvas.ce +185 -21
- package/src/components/scenes/draw-map.ce +55 -21
- package/src/components/scenes/event-layer.ce +8 -2
- package/src/components/scenes/transition.ce +60 -0
- package/src/core/setup.ts +2 -2
- package/src/decorators/spritesheet.ts +8 -0
- package/src/index.ts +17 -2
- package/src/module.ts +132 -10
- package/src/presets/animation.ts +46 -0
- package/src/presets/faceset.ts +60 -0
- package/src/presets/icon.ts +17 -0
- package/src/presets/index.ts +9 -1
- package/src/presets/lpc.ts +108 -0
- package/src/services/AbstractSocket.ts +10 -2
- package/src/services/keyboardControls.ts +20 -0
- package/src/services/loadMap.ts +3 -1
- package/src/services/mmorpg.ts +106 -12
- package/src/services/save.spec.ts +127 -0
- package/src/services/save.ts +103 -0
- package/src/services/standalone.ts +110 -18
- package/src/utils/getEntityProp.spec.ts +96 -0
- package/src/utils/getEntityProp.ts +88 -0
- package/src/utils/readPropValue.ts +16 -0
- package/vite.config.ts +4 -2
- package/dist/Game/EffectManager.d.ts +0 -5
- package/dist/components/effects/index.d.ts +0 -4
- package/dist/index.js.map +0 -1
- package/dist/index10.js +0 -8
- package/dist/index10.js.map +0 -1
- package/dist/index11.js +0 -10
- package/dist/index11.js.map +0 -1
- package/dist/index12.js +0 -8
- package/dist/index12.js.map +0 -1
- package/dist/index13.js +0 -17
- package/dist/index13.js.map +0 -1
- package/dist/index14.js +0 -107
- package/dist/index14.js.map +0 -1
- package/dist/index15.js +0 -50
- package/dist/index15.js.map +0 -1
- package/dist/index16.js +0 -191
- package/dist/index16.js.map +0 -1
- package/dist/index17.js +0 -9
- package/dist/index17.js.map +0 -1
- package/dist/index18.js +0 -387
- package/dist/index18.js.map +0 -1
- package/dist/index19.js +0 -31
- package/dist/index19.js.map +0 -1
- package/dist/index2.js +0 -181
- package/dist/index2.js.map +0 -1
- package/dist/index20.js +0 -24
- package/dist/index20.js.map +0 -1
- package/dist/index21.js +0 -2421
- package/dist/index21.js.map +0 -1
- package/dist/index22.js +0 -114
- package/dist/index22.js.map +0 -1
- package/dist/index23.js +0 -109
- package/dist/index23.js.map +0 -1
- package/dist/index24.js +0 -71
- package/dist/index24.js.map +0 -1
- package/dist/index25.js +0 -21
- package/dist/index25.js.map +0 -1
- package/dist/index26.js +0 -41
- package/dist/index26.js.map +0 -1
- package/dist/index27.js +0 -5
- package/dist/index27.js.map +0 -1
- package/dist/index28.js +0 -322
- package/dist/index28.js.map +0 -1
- package/dist/index29.js +0 -27
- package/dist/index29.js.map +0 -1
- package/dist/index3.js +0 -87
- package/dist/index3.js.map +0 -1
- package/dist/index30.js +0 -11
- package/dist/index30.js.map +0 -1
- package/dist/index31.js +0 -11
- package/dist/index31.js.map +0 -1
- package/dist/index32.js +0 -174
- package/dist/index32.js.map +0 -1
- package/dist/index33.js +0 -501
- package/dist/index33.js.map +0 -1
- package/dist/index34.js +0 -12
- package/dist/index34.js.map +0 -1
- package/dist/index35.js +0 -4403
- package/dist/index35.js.map +0 -1
- package/dist/index36.js +0 -316
- package/dist/index36.js.map +0 -1
- package/dist/index37.js +0 -61
- package/dist/index37.js.map +0 -1
- package/dist/index38.js +0 -20
- package/dist/index38.js.map +0 -1
- package/dist/index39.js +0 -20
- package/dist/index39.js.map +0 -1
- package/dist/index4.js +0 -67
- package/dist/index4.js.map +0 -1
- package/dist/index5.js +0 -16
- package/dist/index5.js.map +0 -1
- package/dist/index6.js +0 -17
- package/dist/index6.js.map +0 -1
- package/dist/index7.js +0 -39
- package/dist/index7.js.map +0 -1
- package/dist/index8.js +0 -108
- package/dist/index8.js.map +0 -1
- package/dist/index9.js +0 -76
- package/dist/index9.js.map +0 -1
- package/src/Game/EffectManager.ts +0 -20
- package/src/components/gui/dialogbox/itemMenu.ce +0 -23
- package/src/components/gui/dialogbox/selection.ce +0 -67
- /package/src/components/{effects → animations}/hit.ce +0 -0
|
@@ -2,6 +2,14 @@ import { Context } from "@signe/di";
|
|
|
2
2
|
|
|
3
3
|
export const WebSocketToken = "websocket";
|
|
4
4
|
|
|
5
|
+
export type SocketQueryValue = string | null | undefined;
|
|
6
|
+
export type SocketQuery = Record<string, SocketQueryValue>;
|
|
7
|
+
export type SocketUpdateProperties = {
|
|
8
|
+
room: string;
|
|
9
|
+
host?: string;
|
|
10
|
+
query?: SocketQuery;
|
|
11
|
+
};
|
|
12
|
+
|
|
5
13
|
export abstract class AbstractWebsocket {
|
|
6
14
|
constructor(protected context: Context) {}
|
|
7
15
|
|
|
@@ -9,6 +17,6 @@ export abstract class AbstractWebsocket {
|
|
|
9
17
|
abstract emit(event: string, data: any): void;
|
|
10
18
|
abstract on(event: string, callback: (data: any) => void): void;
|
|
11
19
|
abstract off(event: string, callback: (data: any) => void): void;
|
|
12
|
-
abstract updateProperties(params:
|
|
13
|
-
abstract reconnect(listeners?: (data: any) => void): void
|
|
20
|
+
abstract updateProperties(params: SocketUpdateProperties): void;
|
|
21
|
+
abstract reconnect(listeners?: (data: any) => void): Promise<void>;
|
|
14
22
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { KeyboardControls } from "canvasengine";
|
|
2
|
+
|
|
3
|
+
export enum Control {
|
|
4
|
+
Action = 'action',
|
|
5
|
+
Attack = 'attack',
|
|
6
|
+
Defense = 'defense',
|
|
7
|
+
Skill = 'skill',
|
|
8
|
+
Back = 'back',
|
|
9
|
+
Up = 1,
|
|
10
|
+
Down = 3,
|
|
11
|
+
Right = 2,
|
|
12
|
+
Left = 4
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function provideKeyboardControls() {
|
|
16
|
+
return {
|
|
17
|
+
provide: 'KeyboardControls',
|
|
18
|
+
useValue: null,
|
|
19
|
+
};
|
|
20
|
+
}
|
package/src/services/loadMap.ts
CHANGED
|
@@ -20,6 +20,8 @@ type MapData = {
|
|
|
20
20
|
height?: number;
|
|
21
21
|
/** Optional map events data (NPCs, interactive objects, etc.) */
|
|
22
22
|
events?: any;
|
|
23
|
+
/** Optional named positions, for example Tiled point objects used by changeMap("map", "name") */
|
|
24
|
+
positions?: Record<string, { x: number; y: number; z?: number }>;
|
|
23
25
|
/** Optional map identifier, defaults to the mapId parameter if not provided */
|
|
24
26
|
id?: string;
|
|
25
27
|
}
|
|
@@ -47,7 +49,7 @@ export class LoadMapService {
|
|
|
47
49
|
|
|
48
50
|
async load(mapId: string) {
|
|
49
51
|
const map = await this.options(mapId.replace('map-', ''))
|
|
50
|
-
await this.updateMapService.update(
|
|
52
|
+
await this.updateMapService.update(map);
|
|
51
53
|
return map;
|
|
52
54
|
}
|
|
53
55
|
}
|
package/src/services/mmorpg.ts
CHANGED
|
@@ -2,18 +2,53 @@ import { Context } from "@signe/di";
|
|
|
2
2
|
import { connectionRoom } from "@signe/sync/client";
|
|
3
3
|
import { RpgGui } from "../Gui/Gui";
|
|
4
4
|
import { RpgClientEngine } from "../RpgClientEngine";
|
|
5
|
-
import { AbstractWebsocket, WebSocketToken } from "./AbstractSocket";
|
|
5
|
+
import { AbstractWebsocket, SocketUpdateProperties, WebSocketToken } from "./AbstractSocket";
|
|
6
6
|
import { UpdateMapService, UpdateMapToken } from "@rpgjs/common";
|
|
7
|
+
import { provideKeyboardControls } from "./keyboardControls";
|
|
8
|
+
import { provideSaveClient } from "./save";
|
|
7
9
|
|
|
8
10
|
interface MmorpgOptions {
|
|
9
11
|
host?: string;
|
|
12
|
+
connectionId?: string;
|
|
13
|
+
connectionIdScope?: "local" | "session" | "ephemeral";
|
|
10
14
|
}
|
|
11
15
|
|
|
12
16
|
class BridgeWebsocket extends AbstractWebsocket {
|
|
13
|
-
|
|
17
|
+
private socket: any;
|
|
18
|
+
private privateId: string;
|
|
19
|
+
private pendingOn: Array<{ event: string; callback: (data: any) => void }> = [];
|
|
20
|
+
private targetRoom = "lobby-1";
|
|
14
21
|
|
|
15
22
|
constructor(protected context: Context, private options: MmorpgOptions = {}) {
|
|
16
23
|
super(context);
|
|
24
|
+
this.privateId = this.resolveConnectionId();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
private resolveConnectionId(): string {
|
|
28
|
+
if (this.options.connectionId) {
|
|
29
|
+
return this.options.connectionId;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const scope = this.options.connectionIdScope ?? "local";
|
|
33
|
+
const key = "rpgjs-user-id";
|
|
34
|
+
|
|
35
|
+
if (scope === "ephemeral") {
|
|
36
|
+
return crypto.randomUUID();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const storage =
|
|
40
|
+
scope === "session"
|
|
41
|
+
? window.sessionStorage
|
|
42
|
+
: window.localStorage;
|
|
43
|
+
|
|
44
|
+
const existing = storage.getItem(key);
|
|
45
|
+
if (existing) {
|
|
46
|
+
return existing;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const id = crypto.randomUUID();
|
|
50
|
+
storage.setItem(key, id);
|
|
51
|
+
return id;
|
|
17
52
|
}
|
|
18
53
|
|
|
19
54
|
async connection(listeners?: (data: any) => void) {
|
|
@@ -22,19 +57,31 @@ class BridgeWebsocket extends AbstractWebsocket {
|
|
|
22
57
|
|
|
23
58
|
}
|
|
24
59
|
const instance = new Room()
|
|
60
|
+
const host = this.options.host || window.location.host;
|
|
25
61
|
this.socket = await connectionRoom({
|
|
26
|
-
host
|
|
27
|
-
room:
|
|
62
|
+
host,
|
|
63
|
+
room: this.targetRoom,
|
|
64
|
+
id: this.privateId,
|
|
65
|
+
query: {
|
|
66
|
+
id: this.privateId,
|
|
67
|
+
},
|
|
28
68
|
}, instance)
|
|
29
69
|
|
|
30
70
|
listeners?.(this.socket)
|
|
71
|
+
this.pendingOn.forEach(({ event, callback }) => this.socket.on(event, callback));
|
|
72
|
+
this.pendingOn = [];
|
|
31
73
|
}
|
|
32
74
|
|
|
33
75
|
on(key: string, callback: (data: any) => void) {
|
|
76
|
+
if (!this.socket) {
|
|
77
|
+
this.pendingOn.push({ event: key, callback });
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
34
80
|
this.socket.on(key, callback);
|
|
35
81
|
}
|
|
36
82
|
|
|
37
83
|
off(event: string, callback: (data: any) => void) {
|
|
84
|
+
if (!this.socket) return;
|
|
38
85
|
this.socket.off(event, callback);
|
|
39
86
|
}
|
|
40
87
|
|
|
@@ -42,25 +89,70 @@ class BridgeWebsocket extends AbstractWebsocket {
|
|
|
42
89
|
this.socket.emit(event, data);
|
|
43
90
|
}
|
|
44
91
|
|
|
45
|
-
updateProperties({ room
|
|
92
|
+
updateProperties({ room, host, query }: SocketUpdateProperties) {
|
|
93
|
+
if (!this.socket?.conn) return;
|
|
94
|
+
this.targetRoom = room;
|
|
46
95
|
this.socket.conn.updateProperties({
|
|
47
|
-
room
|
|
48
|
-
|
|
96
|
+
room,
|
|
97
|
+
id: this.privateId,
|
|
98
|
+
host: host || this.options.host || window.location.host,
|
|
99
|
+
query: {
|
|
100
|
+
...query,
|
|
101
|
+
id: this.privateId,
|
|
102
|
+
},
|
|
49
103
|
})
|
|
50
104
|
}
|
|
51
105
|
|
|
52
|
-
|
|
53
|
-
|
|
106
|
+
private waitForNextOpen(conn: any, timeoutMs = 10000): Promise<void> {
|
|
107
|
+
return new Promise((resolve, reject) => {
|
|
108
|
+
let timeoutId: number | undefined;
|
|
109
|
+
const onOpen = () => {
|
|
110
|
+
cleanup();
|
|
111
|
+
resolve();
|
|
112
|
+
};
|
|
113
|
+
const onError = () => {
|
|
114
|
+
cleanup();
|
|
115
|
+
reject(new Error("WebSocket reconnect failed"));
|
|
116
|
+
};
|
|
117
|
+
const cleanup = () => {
|
|
118
|
+
conn.removeEventListener("open", onOpen);
|
|
119
|
+
conn.removeEventListener("error", onError);
|
|
120
|
+
if (timeoutId !== undefined) {
|
|
121
|
+
window.clearTimeout(timeoutId);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
conn.addEventListener("open", onOpen);
|
|
126
|
+
conn.addEventListener("error", onError);
|
|
127
|
+
timeoutId = window.setTimeout(() => {
|
|
128
|
+
cleanup();
|
|
129
|
+
reject(new Error("WebSocket reconnect timeout"));
|
|
130
|
+
}, timeoutMs);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async reconnect(_listeners?: (data: any) => void): Promise<void> {
|
|
135
|
+
if (!this.socket?.conn) return;
|
|
136
|
+
const conn = this.socket.conn;
|
|
137
|
+
const opened = this.waitForNextOpen(conn);
|
|
138
|
+
conn.reconnect();
|
|
139
|
+
await opened;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
getCurrentRoom(): string {
|
|
143
|
+
return this.targetRoom || this.socket?.conn?.room || "lobby-1";
|
|
54
144
|
}
|
|
55
145
|
}
|
|
56
146
|
|
|
57
147
|
class UpdateMapStandaloneService extends UpdateMapService {
|
|
58
|
-
constructor(protected context: Context, private
|
|
148
|
+
constructor(protected context: Context, private _options: MmorpgOptions) {
|
|
59
149
|
super(context);
|
|
60
150
|
}
|
|
61
151
|
|
|
62
|
-
async update(
|
|
63
|
-
//
|
|
152
|
+
async update(_map: any) {
|
|
153
|
+
// In MMORPG mode, clients are untrusted and must not push map definitions.
|
|
154
|
+
// Map bootstrap/update is handled server-side by @rpgjs/vite.
|
|
155
|
+
return;
|
|
64
156
|
}
|
|
65
157
|
}
|
|
66
158
|
|
|
@@ -74,6 +166,8 @@ export function provideMmorpg(options: MmorpgOptions) {
|
|
|
74
166
|
provide: UpdateMapToken,
|
|
75
167
|
useFactory: (context: Context) => new UpdateMapStandaloneService(context, options),
|
|
76
168
|
},
|
|
169
|
+
provideKeyboardControls(),
|
|
170
|
+
provideSaveClient(),
|
|
77
171
|
RpgGui,
|
|
78
172
|
RpgClientEngine,
|
|
79
173
|
];
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
|
2
|
+
import { SaveClientService, provideSaveClient } from "./save";
|
|
3
|
+
|
|
4
|
+
const createSocket = () => {
|
|
5
|
+
const listeners = new Map<string, (data: any) => void>();
|
|
6
|
+
const emitted: Array<{ event: string; data: any }> = [];
|
|
7
|
+
|
|
8
|
+
return {
|
|
9
|
+
listeners,
|
|
10
|
+
emitted,
|
|
11
|
+
on: vi.fn((event: string, callback: (data: any) => void) => {
|
|
12
|
+
listeners.set(event, callback);
|
|
13
|
+
}),
|
|
14
|
+
off: vi.fn((event: string) => {
|
|
15
|
+
listeners.delete(event);
|
|
16
|
+
}),
|
|
17
|
+
emit: vi.fn((event: string, data: any) => {
|
|
18
|
+
emitted.push({ event, data });
|
|
19
|
+
}),
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const createService = (socket = createSocket()) => {
|
|
24
|
+
const service = Object.create(SaveClientService.prototype) as SaveClientService;
|
|
25
|
+
(service as any).webSocket = socket;
|
|
26
|
+
(service as any).pending = new Map();
|
|
27
|
+
(service as any).requestCounter = 0;
|
|
28
|
+
return { service, socket };
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
describe("SaveClientService", () => {
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
vi.useFakeTimers();
|
|
34
|
+
vi.setSystemTime(new Date("2026-01-02T03:04:05.000Z"));
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
afterEach(() => {
|
|
38
|
+
vi.useRealTimers();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("registers save result listeners idempotently", () => {
|
|
42
|
+
const { service, socket } = createService();
|
|
43
|
+
|
|
44
|
+
service.initialize();
|
|
45
|
+
|
|
46
|
+
expect(socket.off).toHaveBeenCalledTimes(4);
|
|
47
|
+
expect(socket.on).toHaveBeenCalledTimes(4);
|
|
48
|
+
expect([...socket.listeners.keys()]).toEqual([
|
|
49
|
+
"save.list.result",
|
|
50
|
+
"save.save.result",
|
|
51
|
+
"save.load.result",
|
|
52
|
+
"save.error",
|
|
53
|
+
]);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("lists, saves and loads slots through request/response events", async () => {
|
|
57
|
+
const { service, socket } = createService();
|
|
58
|
+
service.initialize();
|
|
59
|
+
|
|
60
|
+
const listPromise = service.listSlots();
|
|
61
|
+
expect(socket.emitted[0]).toEqual({
|
|
62
|
+
event: "save.list",
|
|
63
|
+
data: { requestId: "1767323045000-1" },
|
|
64
|
+
});
|
|
65
|
+
socket.listeners.get("save.list.result")?.({
|
|
66
|
+
requestId: "1767323045000-1",
|
|
67
|
+
slots: [{ id: "slot-a" }],
|
|
68
|
+
});
|
|
69
|
+
await expect(listPromise).resolves.toEqual([{ id: "slot-a" }]);
|
|
70
|
+
|
|
71
|
+
const savePromise = service.saveSlot(2, { map: "forest" } as any);
|
|
72
|
+
expect(socket.emitted[1]).toEqual({
|
|
73
|
+
event: "save.save",
|
|
74
|
+
data: {
|
|
75
|
+
requestId: "1767323045000-2",
|
|
76
|
+
index: 2,
|
|
77
|
+
meta: { map: "forest" },
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
socket.listeners.get("save.save.result")?.({
|
|
81
|
+
requestId: "1767323045000-2",
|
|
82
|
+
index: 2,
|
|
83
|
+
slots: [null, null, { id: "slot-c" }],
|
|
84
|
+
});
|
|
85
|
+
await expect(savePromise).resolves.toEqual([null, null, { id: "slot-c" }]);
|
|
86
|
+
|
|
87
|
+
const loadPromise = service.loadSlot(2);
|
|
88
|
+
expect(socket.emitted[2]).toEqual({
|
|
89
|
+
event: "save.load",
|
|
90
|
+
data: { requestId: "1767323045000-3", index: 2 },
|
|
91
|
+
});
|
|
92
|
+
socket.listeners.get("save.load.result")?.({
|
|
93
|
+
requestId: "1767323045000-3",
|
|
94
|
+
index: 2,
|
|
95
|
+
ok: true,
|
|
96
|
+
});
|
|
97
|
+
await expect(loadPromise).resolves.toBe(true);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test("rejects matching pending requests on save errors and ignores stale responses", async () => {
|
|
101
|
+
const { service, socket } = createService();
|
|
102
|
+
service.initialize();
|
|
103
|
+
|
|
104
|
+
const listPromise = service.listSlots();
|
|
105
|
+
|
|
106
|
+
socket.listeners.get("save.list.result")?.({
|
|
107
|
+
requestId: "unknown",
|
|
108
|
+
slots: [{ id: "stale" }],
|
|
109
|
+
});
|
|
110
|
+
expect((service as any).pending.size).toBe(1);
|
|
111
|
+
|
|
112
|
+
socket.listeners.get("save.error")?.({
|
|
113
|
+
requestId: "1767323045000-1",
|
|
114
|
+
message: "Cannot save",
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
await expect(listPromise).rejects.toThrow("Cannot save");
|
|
118
|
+
expect((service as any).pending.size).toBe(0);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("provides the client save service class", () => {
|
|
122
|
+
expect(provideSaveClient()).toEqual({
|
|
123
|
+
provide: SaveClientService,
|
|
124
|
+
useClass: SaveClientService,
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
});
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { SaveSlotList, SaveSlotMeta } from "@rpgjs/common";
|
|
2
|
+
import { inject } from "../core/inject";
|
|
3
|
+
import { AbstractWebsocket, WebSocketToken } from "./AbstractSocket";
|
|
4
|
+
|
|
5
|
+
export const SaveClientToken = "SaveClientToken";
|
|
6
|
+
|
|
7
|
+
type SaveListResult = {
|
|
8
|
+
requestId: string;
|
|
9
|
+
slots: SaveSlotList;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
type SaveSaveResult = {
|
|
13
|
+
requestId: string;
|
|
14
|
+
index: number;
|
|
15
|
+
slots: SaveSlotList;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
type SaveLoadResult = {
|
|
19
|
+
requestId: string;
|
|
20
|
+
index: number;
|
|
21
|
+
ok: boolean;
|
|
22
|
+
slot?: SaveSlotMeta;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
type SaveErrorResult = {
|
|
26
|
+
requestId: string;
|
|
27
|
+
message: string;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
type PendingRequest = {
|
|
31
|
+
resolve: (value: any) => void;
|
|
32
|
+
reject: (error: Error) => void;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export class SaveClientService {
|
|
36
|
+
private webSocket: AbstractWebsocket = inject(WebSocketToken);
|
|
37
|
+
private pending: Map<string, PendingRequest> = new Map();
|
|
38
|
+
private requestCounter = 0;
|
|
39
|
+
|
|
40
|
+
initialize() {
|
|
41
|
+
const saveListResult = (data: SaveListResult) => this.resolveRequest(data.requestId, data);
|
|
42
|
+
const saveSaveResult = (data: SaveSaveResult) => this.resolveRequest(data.requestId, data);
|
|
43
|
+
const saveLoadResult = (data: SaveLoadResult) => this.resolveRequest(data.requestId, data);
|
|
44
|
+
const saveErrorResult = (data: SaveErrorResult) => this.rejectRequest(data.requestId, data.message);
|
|
45
|
+
this.webSocket.off("save.list.result", saveListResult);
|
|
46
|
+
this.webSocket.off("save.save.result", saveSaveResult);
|
|
47
|
+
this.webSocket.off("save.load.result", saveLoadResult);
|
|
48
|
+
this.webSocket.off("save.error", saveErrorResult);
|
|
49
|
+
this.webSocket.on("save.list.result", saveListResult);
|
|
50
|
+
this.webSocket.on("save.save.result", saveSaveResult);
|
|
51
|
+
this.webSocket.on("save.load.result", saveLoadResult);
|
|
52
|
+
this.webSocket.on("save.error", saveErrorResult);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
listSlots(): Promise<SaveSlotList> {
|
|
56
|
+
return this.request<SaveListResult>("save.list", {}).then((result) => result.slots);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
saveSlot(index: number, meta: SaveSlotMeta = {}): Promise<SaveSlotList> {
|
|
60
|
+
return this.request<SaveSaveResult>("save.save", { index, meta }).then((result) => result.slots);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
loadSlot(index: number): Promise<boolean> {
|
|
64
|
+
return this.request<SaveLoadResult>("save.load", { index }).then((result) => result.ok);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private request<T>(event: string, payload: Record<string, any>): Promise<T> {
|
|
68
|
+
return new Promise((resolve, reject) => {
|
|
69
|
+
if (!this.webSocket) {
|
|
70
|
+
this.initialize();
|
|
71
|
+
}
|
|
72
|
+
const requestId = this.nextRequestId();
|
|
73
|
+
this.pending.set(requestId, { resolve, reject });
|
|
74
|
+
this.webSocket?.emit(event, { requestId, ...payload });
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private resolveRequest(requestId: string, result: any) {
|
|
79
|
+
const pending = this.pending.get(requestId);
|
|
80
|
+
if (!pending) return;
|
|
81
|
+
this.pending.delete(requestId);
|
|
82
|
+
pending.resolve(result);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private rejectRequest(requestId: string, message: string) {
|
|
86
|
+
const pending = this.pending.get(requestId);
|
|
87
|
+
if (!pending) return;
|
|
88
|
+
this.pending.delete(requestId);
|
|
89
|
+
pending.reject(new Error(message));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private nextRequestId(): string {
|
|
93
|
+
this.requestCounter += 1;
|
|
94
|
+
return `${Date.now()}-${this.requestCounter}`;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function provideSaveClient() {
|
|
99
|
+
return {
|
|
100
|
+
provide: SaveClientService,
|
|
101
|
+
useClass: SaveClientService,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
@@ -1,29 +1,55 @@
|
|
|
1
|
-
import { AbstractWebsocket, WebSocketToken } from "./AbstractSocket";
|
|
1
|
+
import { AbstractWebsocket, SocketUpdateProperties, WebSocketToken } from "./AbstractSocket";
|
|
2
2
|
import { ClientIo, ServerIo } from "@signe/room";
|
|
3
3
|
import { Context } from "@signe/di";
|
|
4
4
|
import { RpgClientEngine } from "../RpgClientEngine";
|
|
5
5
|
import { UpdateMapService, UpdateMapToken } from "@rpgjs/common";
|
|
6
6
|
import { LoadMapToken } from "./loadMap";
|
|
7
7
|
import { RpgGui } from "../Gui/Gui";
|
|
8
|
+
import { provideKeyboardControls } from "./keyboardControls";
|
|
9
|
+
import { provideSaveClient } from "./save";
|
|
8
10
|
|
|
9
11
|
type ServerIo = any;
|
|
10
12
|
type ClientIo = any;
|
|
11
13
|
|
|
14
|
+
interface StandaloneOptions {
|
|
15
|
+
env?: Record<string, any>;
|
|
16
|
+
}
|
|
17
|
+
|
|
12
18
|
class BridgeWebsocket extends AbstractWebsocket {
|
|
13
19
|
private room: ServerIo;
|
|
14
20
|
private socket: ClientIo;
|
|
21
|
+
private pendingOn: Array<{ event: string; callback: (data: any) => void }> = [];
|
|
22
|
+
private rooms = {
|
|
23
|
+
partyFn: async (roomId: string) => {
|
|
24
|
+
this.room = new ServerIo(roomId, this.rooms);
|
|
25
|
+
const server = new this.server(this.room)
|
|
26
|
+
await server.onStart();
|
|
27
|
+
await server.subRoom.onStart()
|
|
28
|
+
this.context.set('server', server)
|
|
29
|
+
return server
|
|
30
|
+
},
|
|
31
|
+
env: {}
|
|
32
|
+
}
|
|
33
|
+
private serverInstance: any;
|
|
15
34
|
|
|
16
|
-
constructor(protected context: Context, private server: any) {
|
|
35
|
+
constructor(protected context: Context, private server: any, options: StandaloneOptions = {}) {
|
|
17
36
|
super(context);
|
|
18
37
|
// fake room
|
|
19
|
-
this.
|
|
38
|
+
this.rooms.env = options.env || {};
|
|
39
|
+
this.room = new ServerIo("lobby-1", this.rooms);
|
|
20
40
|
}
|
|
21
41
|
|
|
22
42
|
async connection(listeners?: (data: any) => void) {
|
|
23
|
-
|
|
24
|
-
await
|
|
25
|
-
this.
|
|
26
|
-
this.
|
|
43
|
+
this.serverInstance = new this.server(this.room);
|
|
44
|
+
await this.serverInstance.onStart();
|
|
45
|
+
await this.serverInstance.subRoom.onStart()
|
|
46
|
+
this.context.set('server', this.serverInstance)
|
|
47
|
+
return this._connection(listeners)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private async _connection(listeners?: (data: any) => void) {
|
|
51
|
+
this.serverInstance = this.context.get('server')
|
|
52
|
+
this.socket = new ClientIo(this.serverInstance, 'player-client-id');
|
|
27
53
|
const url = new URL('http://localhost')
|
|
28
54
|
const request = new Request(url.toString(), {
|
|
29
55
|
method: 'GET',
|
|
@@ -32,21 +58,29 @@ class BridgeWebsocket extends AbstractWebsocket {
|
|
|
32
58
|
}
|
|
33
59
|
})
|
|
34
60
|
listeners?.(this.socket)
|
|
35
|
-
await
|
|
61
|
+
await this.serverInstance.onConnect(this.socket.conn as any, { request } as any);
|
|
36
62
|
this.room.clients.set(this.socket.id, this.socket);
|
|
63
|
+
this.pendingOn.forEach(({ event, callback }) => this.socket.addEventListener(event, callback));
|
|
64
|
+
this.pendingOn = [];
|
|
37
65
|
return this.socket
|
|
38
66
|
}
|
|
39
67
|
|
|
40
68
|
on(key: string, callback: (data: any) => void) {
|
|
41
|
-
|
|
69
|
+
const handler = (event) => {
|
|
42
70
|
const object = JSON.parse(event);
|
|
43
71
|
if (object.type === key) {
|
|
44
72
|
callback(object.value);
|
|
45
73
|
}
|
|
46
|
-
}
|
|
74
|
+
};
|
|
75
|
+
if (!this.socket) {
|
|
76
|
+
this.pendingOn.push({ event: "message", callback: handler });
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
this.socket.addEventListener("message", handler);
|
|
47
80
|
}
|
|
48
81
|
|
|
49
82
|
off(event: string, callback: (data: any) => void) {
|
|
83
|
+
if (!this.socket) return;
|
|
50
84
|
this.socket.removeEventListener(event, callback);
|
|
51
85
|
}
|
|
52
86
|
|
|
@@ -57,24 +91,80 @@ class BridgeWebsocket extends AbstractWebsocket {
|
|
|
57
91
|
});
|
|
58
92
|
}
|
|
59
93
|
|
|
60
|
-
|
|
61
|
-
|
|
94
|
+
/**
|
|
95
|
+
* Update underlying connection properties before a reconnect
|
|
96
|
+
*
|
|
97
|
+
* Design
|
|
98
|
+
* - Dynamically register a factory for the requested room to ensure a fresh server instance
|
|
99
|
+
* - Swap the internal ServerIo to target the new room
|
|
100
|
+
*
|
|
101
|
+
* @param params - Properties to update
|
|
102
|
+
* @param params.room - The target room id (e.g. `map-simplemap2`)
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```ts
|
|
106
|
+
* websocket.updateProperties({ room: 'map-simplemap2' })
|
|
107
|
+
* await websocket.reconnect()
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
updateProperties(_params: SocketUpdateProperties) {
|
|
111
|
+
// empty
|
|
62
112
|
}
|
|
63
113
|
|
|
64
|
-
|
|
65
|
-
|
|
114
|
+
/**
|
|
115
|
+
* Reconnect the client to the current Party room
|
|
116
|
+
*
|
|
117
|
+
* Design
|
|
118
|
+
* - Must be called after `updateProperties()` when switching rooms
|
|
119
|
+
* - Rebuilds the client <-> server bridge and re-triggers connection listeners
|
|
120
|
+
*
|
|
121
|
+
* @param listeners - Optional callback to re-bind event handlers on the new socket
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* ```ts
|
|
125
|
+
* websocket.updateProperties({ room: 'map-dungeon' })
|
|
126
|
+
* await websocket.reconnect((socket) => {
|
|
127
|
+
* // re-bind events here
|
|
128
|
+
* })
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
async reconnect(listeners?: (data: any) => void): Promise<void> {
|
|
132
|
+
await this._connection((socket) => {
|
|
66
133
|
listeners?.(socket)
|
|
67
134
|
})
|
|
68
135
|
}
|
|
136
|
+
|
|
137
|
+
getServer() {
|
|
138
|
+
return this.serverInstance
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
getSocket() {
|
|
142
|
+
return this.socket
|
|
143
|
+
}
|
|
69
144
|
}
|
|
70
145
|
|
|
71
146
|
class UpdateMapStandaloneService extends UpdateMapService {
|
|
72
147
|
private server: any;
|
|
73
148
|
|
|
74
|
-
|
|
149
|
+
/**
|
|
150
|
+
* Update the current room map data on the server side
|
|
151
|
+
*
|
|
152
|
+
* Design
|
|
153
|
+
* - Uses the in-memory server instance stored in context (standalone mode)
|
|
154
|
+
* - Builds a local HTTP-like request to the current Party room endpoint
|
|
155
|
+
*
|
|
156
|
+
* @param map - The map payload to apply on the server
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* ```ts
|
|
160
|
+
* await updateMapService.update({ width: 1024, height: 768, events: [] })
|
|
161
|
+
* ```
|
|
162
|
+
*/
|
|
163
|
+
async update(map: any) {
|
|
75
164
|
this.server = this.context.get('server')
|
|
165
|
+
const roomId = this.server?.room?.id ?? 'lobby-1'
|
|
76
166
|
const req = {
|
|
77
|
-
url: `http://localhost/parties/main/${
|
|
167
|
+
url: `http://localhost/parties/main/${roomId}/map/update`,
|
|
78
168
|
method: 'POST',
|
|
79
169
|
headers: new Headers({}),
|
|
80
170
|
json: async () => {
|
|
@@ -85,16 +175,18 @@ class UpdateMapStandaloneService extends UpdateMapService {
|
|
|
85
175
|
}
|
|
86
176
|
}
|
|
87
177
|
|
|
88
|
-
export function provideRpg(server: any) {
|
|
178
|
+
export function provideRpg(server: any, options: StandaloneOptions = {}) {
|
|
89
179
|
return [
|
|
90
180
|
{
|
|
91
181
|
provide: WebSocketToken,
|
|
92
|
-
useFactory: (context: Context) => new BridgeWebsocket(context, server),
|
|
182
|
+
useFactory: (context: Context) => new BridgeWebsocket(context, server, options),
|
|
93
183
|
},
|
|
94
184
|
{
|
|
95
185
|
provide: UpdateMapToken,
|
|
96
186
|
useClass: UpdateMapStandaloneService,
|
|
97
187
|
},
|
|
188
|
+
provideKeyboardControls(),
|
|
189
|
+
provideSaveClient(),
|
|
98
190
|
RpgGui,
|
|
99
191
|
RpgClientEngine,
|
|
100
192
|
];
|