@rpgjs/client 5.0.0-beta.25 → 5.0.0-beta.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/dist/Game/Map.d.ts +1 -0
  3. package/dist/Game/Map.js +5 -0
  4. package/dist/Game/Map.js.map +1 -1
  5. package/dist/Game/Object.js.map +1 -1
  6. package/dist/Gui/Gui.d.ts +2 -2
  7. package/dist/Gui/Gui.js.map +1 -1
  8. package/dist/RpgClient.d.ts +2 -1
  9. package/dist/RpgClientEngine.d.ts +76 -0
  10. package/dist/RpgClientEngine.js +59 -4
  11. package/dist/RpgClientEngine.js.map +1 -1
  12. package/dist/components/scenes/draw-map.ce.js +5 -2
  13. package/dist/components/scenes/draw-map.ce.js.map +1 -1
  14. package/dist/components/scenes/weather-lifecycle-compat.d.ts +0 -0
  15. package/dist/components/scenes/weather-lifecycle-compat.js +11 -0
  16. package/dist/components/scenes/weather-lifecycle-compat.js.map +1 -0
  17. package/dist/components/scenes/weather-tick-lifecycle.d.ts +17 -0
  18. package/dist/components/scenes/weather-tick-lifecycle.js +44 -0
  19. package/dist/components/scenes/weather-tick-lifecycle.js.map +1 -0
  20. package/dist/components/scenes/weather-tick-lifecycle.spec.d.ts +1 -0
  21. package/dist/core/inject.d.ts +4 -4
  22. package/dist/core/inject.js.map +1 -1
  23. package/dist/core/setup.d.ts +3 -3
  24. package/dist/core/setup.js +1 -1
  25. package/dist/core/setup.js.map +1 -1
  26. package/dist/core/setup.spec.d.ts +1 -0
  27. package/dist/i18n.d.ts +2 -1
  28. package/dist/i18n.js +1 -0
  29. package/dist/i18n.js.map +1 -1
  30. package/dist/index.d.ts +3 -1
  31. package/dist/index.js +3 -2
  32. package/dist/module.d.ts +2 -2
  33. package/dist/module.js.map +1 -1
  34. package/dist/public-api-types.spec.d.ts +1 -0
  35. package/dist/services/AbstractSocket.d.ts +3 -3
  36. package/dist/services/AbstractSocket.js.map +1 -1
  37. package/dist/services/loadMap.d.ts +13 -9
  38. package/dist/services/loadMap.js +4 -3
  39. package/dist/services/loadMap.js.map +1 -1
  40. package/dist/services/mapStreaming.d.ts +71 -0
  41. package/dist/services/mapStreaming.js +210 -0
  42. package/dist/services/mapStreaming.js.map +1 -0
  43. package/dist/services/mapStreaming.spec.d.ts +1 -0
  44. package/dist/services/mmorpg.d.ts +10 -26
  45. package/dist/services/mmorpg.js +2 -2
  46. package/dist/services/mmorpg.js.map +1 -1
  47. package/dist/services/standalone.d.ts +2 -98
  48. package/dist/services/standalone.js.map +1 -1
  49. package/package.json +3 -3
  50. package/src/Game/Map.ts +7 -0
  51. package/src/Game/Object.spec.ts +1 -1
  52. package/src/Game/Object.ts +4 -4
  53. package/src/Gui/Gui.ts +3 -3
  54. package/src/RpgClient.ts +2 -1
  55. package/src/RpgClientEngine.ts +147 -6
  56. package/src/components/scenes/draw-map.ce +4 -1
  57. package/src/components/scenes/weather-lifecycle-compat.ts +8 -0
  58. package/src/components/scenes/weather-tick-lifecycle.spec.ts +60 -0
  59. package/src/components/scenes/weather-tick-lifecycle.ts +69 -0
  60. package/src/core/inject.ts +6 -5
  61. package/src/core/setup.spec.ts +35 -0
  62. package/src/core/setup.ts +8 -6
  63. package/src/i18n.spec.ts +2 -1
  64. package/src/i18n.ts +1 -0
  65. package/src/index.ts +14 -1
  66. package/src/module.ts +2 -2
  67. package/src/public-api-types.spec.ts +19 -0
  68. package/src/services/AbstractSocket.ts +2 -2
  69. package/src/services/loadMap.ts +16 -9
  70. package/src/services/mapStreaming.spec.ts +240 -0
  71. package/src/services/mapStreaming.ts +277 -0
  72. package/src/services/mmorpg.ts +21 -9
  73. package/src/services/standalone.ts +4 -5
@@ -1,4 +1,5 @@
1
1
  import { inject } from "../../core/inject.js";
2
+ import "./weather-lifecycle-compat.js";
2
3
  import { RpgClientEngine } from "../../RpgClientEngine.js";
3
4
  import { Container, computed, cond, h, loop, useDefineEmits, useDefineProps, useProps } from "canvasengine";
4
5
  import { Weather } from "@canvasengine/presets";
@@ -12,6 +13,8 @@ function component($$props) {
12
13
  const componentAnimations = engine.componentAnimations;
13
14
  const projectiles = engine.projectiles.current;
14
15
  const map = engine.sceneMap?.data;
16
+ const emptySceneData = { params: {} };
17
+ const emptySceneParams = {};
15
18
  const sceneComponent = computed(() => map()?.component);
16
19
  const weather = engine.sceneMap.weather;
17
20
  const backgroundMusic = computed(() => {
@@ -73,8 +76,8 @@ function component($$props) {
73
76
  cond(computed(() => backgroundMusic()), () => h(Container, { sound: computed(() => backgroundMusic()) })),
74
77
  cond(computed(() => backgroundAmbientSound()), () => h(Container, { sound: computed(() => backgroundAmbientSound()) })),
75
78
  h(Container, null, cond(computed(() => map() && sceneComponent()), () => h(sceneComponent(), {
76
- data: computed(() => map().data),
77
- params: computed(() => map().params)
79
+ data: computed(() => map()?.data ?? emptySceneData),
80
+ params: computed(() => map()?.params ?? emptySceneParams)
78
81
  }))),
79
82
  loop(children, (child) => h(child)),
80
83
  loop(componentAnimations, (componentAnimation) => h(Container, null, loop(componentAnimation.current, (animation) => h(componentAnimation.component, animation)))),
@@ -1 +1 @@
1
- {"version":3,"file":"draw-map.ce.js","names":[],"sources":["../../../src/components/scenes/draw-map.ce"],"sourcesContent":["<Container shake={shakeConfig} freeze={engine.gamePause}>\n @if (backgroundMusic()) {\n <Container sound={backgroundMusic()} />\n }\n\n @if (backgroundAmbientSound()) {\n <Container sound={backgroundAmbientSound()} />\n }\n\n <Container>\n @if (map() && sceneComponent()) {\n <sceneComponent() data={map().data} params={map().params} />\n }\n </Container>\n\n @for (child of children) {\n <child />\n }\n\n @for (componentAnimation of componentAnimations) {\n <Container>\n @for (animation of componentAnimation.current) {\n <componentAnimation.component ...animation />\n }\n </Container>\n }\n\n <Container sortableChildren={true}>\n @for (projectile of projectiles() ; track projectile.props.id) {\n <projectile.component ...projectile.props />\n }\n </Container>\n\n @if (weatherProps()) {\n <Weather ...weatherProps() />\n }\n</Container>\n\n<script>\n import { computed, effect } from 'canvasengine'\n import { inject } from \"../../core/inject\";\n import { RpgClientEngine } from \"../../RpgClientEngine\";\n import { Weather } from '@canvasengine/presets'\n\n const { children } = defineProps()\n const engine = inject(RpgClientEngine);\n const componentAnimations = engine.componentAnimations\n const projectiles = engine.projectiles.current\n const map = engine.sceneMap?.data\n const sceneComponent = computed(() => map()?.component)\n const weather = engine.sceneMap.weather\n const backgroundMusic = computed(() => {\n const src = map()?.params?.backgroundMusic\n return src ? { src, autoplay: true, loop: true } : undefined\n })\n const backgroundAmbientSound = computed(() => {\n const src = map()?.params?.backgroundAmbientSound\n return src ? { src, autoplay: true, loop: true } : undefined\n })\n\n const shakeConfig = {\n trigger: engine.mapShakeTrigger,\n intensity: 10,\n duration: 500,\n frequency: 10,\n direction: 'both'\n }\n\n const weatherProps = computed(() => {\n const state = weather?.()\n if (!state) {\n return null\n }\n const validEffects = ['rain', 'snow', 'fog', 'cloud']\n if (!validEffects.includes(state.effect)) {\n return null\n }\n const params = state.params ?? {}\n return {\n effect: state.effect,\n speed: params.speed,\n windDirection: params.windDirection,\n windStrength: params.windStrength,\n density: params.density,\n maxDrops: params.maxDrops,\n height: params.height,\n scale: params.scale,\n sunIntensity: params.sunIntensity,\n sunAngle: params.sunAngle,\n raySpread: params.raySpread,\n rayTwinkle: params.rayTwinkle,\n rayTwinkleSpeed: params.rayTwinkleSpeed,\n zIndex: params.zIndex ?? 1000,\n alpha: params.alpha,\n blendMode: params.blendMode,\n }\n })\n</script>\n"],"mappings":";;;;;AASG,SAAS,UAAA,SAAA;CACM,SAAc,OAAI;CAC7B,MAAA,cAAqB,eAAc,OAAQ;CAC9C,eAAA,OAAA;CACA,MAAS,EAAA,aAAA,YAAA;;CAEb,MAAM,sBAAqB,OAAA;CAC3B,MAAK,cAAO,OAAA,YAAA;CACZ,MAAE,MAAA,OAAA,UAAA;;CAEF,MAAM,UAAE,OAAA,SAAsB;CAC9B,MAAK,kBAAS,eAAA;EACV,MAAM,MAAE,IAAA,GAAU,QAAG;EACrB,OAAK,MAAA;GAAA;GAAA,UAAmB;GAAY,MAAC;EAAU,IAAC,KAAA;CACpD,CAAC;CACD,MAAM,yBAAS,eAAA;EACb,MAAA,MAAA,IAAA,GAAA,QAAA;;;;;;CAEF,CAAC;CACD,MAAK,cAAe;EAChB,SAAG,OAAW;EACd,WAAA;EACA,UAAS;;EAEV,WAAI;CACP;CACA,MAAE,eAAA,eAAA;EACA,MAAA,QAAS,UAAA;cAEJ,OAAA;EAGH,IAAA,CAAA;GADsB;GAAQ;GAAK;GAAO;EACjC,EAAA,SAAkB,MAAM,MAAM,GACvC,OAAS;EAET,MAAM,SAAE,MAAa,UAAA,CAAW;EAChC,OAAM;GACN,QAAM,MAAA;GACN,OAAM,OAAY;GAClB,eAAmB,OAAA;GACnB,cAAM,OAAiB;GACvB,SAAa,OAAG;GAChB,UAAM,OAAe;GACnB,QAAU,OAAO;GACjB,OAAO,OAAQ;GAChB,cAAA,OAAA;GACD,UAAM,OAAA;GACJ,WAAY,OAAO;GACnB,YAAc,OAAM;GACrB,iBAAA,OAAA;;GAED,OAAM,OAAY;GAChB,WAAS,OAAO;EAClB;CACJ,CAAC;CAEK,OADa,EAAA,WAAA;EAAA,OAAA;EAAA,QAAA,OAAA;CAAA,GAAA;EAAA,KAAA,eAAA,gBAAA,CAAA,SAAA,EAAA,WAAA,EAAA,OAAA,eAAA,gBAAA,CAAA,EAAA,CAAA,CAAA;EAAA,KAAA,eAAA,uBAAA,CAAA,SAAA,EAAA,WAAA,EAAA,OAAA,eAAA,uBAAA,CAAA,EAAA,CAAA,CAAA;EAAA,EAAA,WAAA,MAAA,KAAA,eAAA,IAAA,KAAA,eAAA,CAAA,SAAA,EAAA,eAAA,GAAA;GAAA,MAAA,eAAA,IAAA,EAAA,IAAA;GAAA,QAAA,eAAA,IAAA,EAAA,MAAA;EAAA,CAAA,CAAA,CAAA;EAAA,KAAA,WAAA,UAAA,EAAA,KAAA,CAAA;EAAA,KAAA,sBAAA,uBAAA,EAAA,WAAA,MAAA,KAAA,mBAAA,UAAA,cAAA,EAAA,mBAAA,WAAA,SAAA,CAAA,CAAA,CAAA;EAAA,EAAA,WAAA,EAAA,kBAAA,KAAA,GAAA,KAAA,eAAA,YAAA,CAAA,IAAA,eAAA,EAAA,WAAA,WAAA,WAAA,KAAA,GAAA,EAAA,QAAA,eAAA,WAAA,MAAA,GAAA,CAAA,CAAA;EAAA,KAAA,eAAA,aAAA,CAAA,SAAA,EAAA,SAAA,aAAA,CAAA,CAAA;CAAA,CACD;AACd;AAEA,IAAM,iBAAe"}
1
+ {"version":3,"file":"draw-map.ce.js","names":[],"sources":["../../../src/components/scenes/draw-map.ce"],"sourcesContent":["<Container shake={shakeConfig} freeze={engine.gamePause}>\n @if (backgroundMusic()) {\n <Container sound={backgroundMusic()} />\n }\n\n @if (backgroundAmbientSound()) {\n <Container sound={backgroundAmbientSound()} />\n }\n\n <Container>\n @if (map() && sceneComponent()) {\n <sceneComponent() data={map()?.data ?? emptySceneData} params={map()?.params ?? emptySceneParams} />\n }\n </Container>\n\n @for (child of children) {\n <child />\n }\n\n @for (componentAnimation of componentAnimations) {\n <Container>\n @for (animation of componentAnimation.current) {\n <componentAnimation.component ...animation />\n }\n </Container>\n }\n\n <Container sortableChildren={true}>\n @for (projectile of projectiles() ; track projectile.props.id) {\n <projectile.component ...projectile.props />\n }\n </Container>\n\n @if (weatherProps()) {\n <Weather ...weatherProps() />\n }\n</Container>\n\n<script>\n import { computed, effect } from 'canvasengine'\n import './weather-lifecycle-compat'\n import { inject } from \"../../core/inject\";\n import { RpgClientEngine } from \"../../RpgClientEngine\";\n import { Weather } from '@canvasengine/presets'\n\n const { children } = defineProps()\n const engine = inject(RpgClientEngine);\n const componentAnimations = engine.componentAnimations\n const projectiles = engine.projectiles.current\n const map = engine.sceneMap?.data\n const emptySceneData = { params: {} }\n const emptySceneParams = {}\n const sceneComponent = computed(() => map()?.component)\n const weather = engine.sceneMap.weather\n const backgroundMusic = computed(() => {\n const src = map()?.params?.backgroundMusic\n return src ? { src, autoplay: true, loop: true } : undefined\n })\n const backgroundAmbientSound = computed(() => {\n const src = map()?.params?.backgroundAmbientSound\n return src ? { src, autoplay: true, loop: true } : undefined\n })\n\n const shakeConfig = {\n trigger: engine.mapShakeTrigger,\n intensity: 10,\n duration: 500,\n frequency: 10,\n direction: 'both'\n }\n\n const weatherProps = computed(() => {\n const state = weather?.()\n if (!state) {\n return null\n }\n const validEffects = ['rain', 'snow', 'fog', 'cloud']\n if (!validEffects.includes(state.effect)) {\n return null\n }\n const params = state.params ?? {}\n return {\n effect: state.effect,\n speed: params.speed,\n windDirection: params.windDirection,\n windStrength: params.windStrength,\n density: params.density,\n maxDrops: params.maxDrops,\n height: params.height,\n scale: params.scale,\n sunIntensity: params.sunIntensity,\n sunAngle: params.sunAngle,\n raySpread: params.raySpread,\n rayTwinkle: params.rayTwinkle,\n rayTwinkleSpeed: params.rayTwinkleSpeed,\n zIndex: params.zIndex ?? 1000,\n alpha: params.alpha,\n blendMode: params.blendMode,\n }\n })\n</script>\n"],"mappings":";;;;;;AAUK,SAAU,UAAG,SAAgB;CACX,SAAO,OAAO;CACjC,MAAA,cAAA,eAAA,OAAA;CACS,eAAA,OAAA;;CAEb,MAAM,SAAQ,OAAG,eAAU;CAC3B,MAAK,sBAAO,OAAA;CACZ,MAAE,cAAA,OAAA,YAAA;;CAEF,MAAM,iBAAE,EAAA,QAAsB,CAAA,EAAA;CAC9B,MAAK,mBAAS,CAAA;CACd,MAAM,iBAAgB,eAAG,IAAA,GAAmB,SAAS;CACrD,MAAM,UAAG,OAAA,SAAmB;CAC5B,MAAM,kBAAA,eAAA;EACF,MAAE,MAAS,IAAA,GAAA,QAAA;EACb,OAAA,MAAA;GAAA;GAAA,UAAA;GAAA,MAAA;EAAA,IAAA,KAAA;;CAEF,MAAG,yBAA4B,eAAK;EAChC,MAAM,MAAA,IAAU,GAAG,QAAC;EACpB,OAAG,MAAU;GAAC;GAAA,UAAa;GAAA,MAAW;EAAO,IAAA,KAAA;CACjD,CAAC;CACD,MAAI,cAAS;;EAEV,WAAI;EACH,UAAU;EACZ,WAAA;EACA,WAAS;;CAEX,MAAC,eAAM,eAAA;EACH,MAAM,QAAG,UAAU;EACnB,IAAA,CAAA,OACA,OAAS;;GAEU;GAAO;GAAY;GAAC;4BAEvC,OAAQ;EAER,MAAM,SAAA,MAAA,UAAsB,CAAA;EAC5B,OAAM;GACN,QAAY,MAAM;GAClB,OAAM,OAAA;GACN,eAAM,OAAoB;GAC1B,cAAM,OAAiB;GACvB,SAAa,OAAG;GAChB,UAAM,OAAe;GACnB,QAAU,OAAO;GACjB,OAAO,OAAQ;GAChB,cAAA,OAAA;GACD,UAAM,OAAA;GACJ,WAAY,OAAO;GACnB,YAAc,OAAM;GACrB,iBAAA,OAAA;;GAED,OAAM,OAAY;GAChB,WAAS,OAAO;EAClB;CACJ,CAAC;CAEK,OADa,EAAA,WAAA;EAAA,OAAA;EAAA,QAAA,OAAA;CAAA,GAAA;EAAA,KAAA,eAAA,gBAAA,CAAA,SAAA,EAAA,WAAA,EAAA,OAAA,eAAA,gBAAA,CAAA,EAAA,CAAA,CAAA;EAAA,KAAA,eAAA,uBAAA,CAAA,SAAA,EAAA,WAAA,EAAA,OAAA,eAAA,uBAAA,CAAA,EAAA,CAAA,CAAA;EAAA,EAAA,WAAA,MAAA,KAAA,eAAA,IAAA,KAAA,eAAA,CAAA,SAAA,EAAA,eAAA,GAAA;GAAA,MAAA,eAAA,IAAA,GAAA,QAAA,cAAA;GAAA,QAAA,eAAA,IAAA,GAAA,UAAA,gBAAA;EAAA,CAAA,CAAA,CAAA;EAAA,KAAA,WAAA,UAAA,EAAA,KAAA,CAAA;EAAA,KAAA,sBAAA,uBAAA,EAAA,WAAA,MAAA,KAAA,mBAAA,UAAA,cAAA,EAAA,mBAAA,WAAA,SAAA,CAAA,CAAA,CAAA;EAAA,EAAA,WAAA,EAAA,kBAAA,KAAA,GAAA,KAAA,eAAA,YAAA,CAAA,IAAA,eAAA,EAAA,WAAA,WAAA,WAAA,KAAA,GAAA,EAAA,QAAA,eAAA,WAAA,MAAA,GAAA,CAAA,CAAA;EAAA,KAAA,eAAA,aAAA,CAAA,SAAA,EAAA,SAAA,aAAA,CAAA,CAAA;CAAA,CACD;AACd;AAEA,IAAM,iBAAe"}
@@ -0,0 +1,11 @@
1
+ import { patchWeatherTickLifecycle } from "./weather-tick-lifecycle.js";
2
+ import { createComponent } from "canvasengine";
3
+ import "@canvasengine/presets";
4
+ //#region src/components/scenes/weather-lifecycle-compat.ts
5
+ for (const tag of ["RainTextureLayer", "RainImpactLayer"]) {
6
+ const probe = createComponent(tag);
7
+ patchWeatherTickLifecycle(Object.getPrototypeOf(probe.componentInstance));
8
+ }
9
+ //#endregion
10
+
11
+ //# sourceMappingURL=weather-lifecycle-compat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"weather-lifecycle-compat.js","names":[],"sources":["../../../src/components/scenes/weather-lifecycle-compat.ts"],"sourcesContent":["import \"@canvasengine/presets\";\nimport { createComponent } from \"canvasengine\";\nimport { patchWeatherTickLifecycle } from \"./weather-tick-lifecycle\";\n\nfor (const tag of [\"RainTextureLayer\", \"RainImpactLayer\"]) {\n const probe = createComponent(tag);\n patchWeatherTickLifecycle(Object.getPrototypeOf(probe.componentInstance));\n}\n"],"mappings":";;;;AAIA,KAAK,MAAM,OAAO,CAAC,oBAAoB,iBAAiB,GAAG;CACzD,MAAM,QAAQ,gBAAgB,GAAG;CACjC,0BAA0B,OAAO,eAAe,MAAM,iBAAiB,CAAC;AAC1E"}
@@ -0,0 +1,17 @@
1
+ declare const PATCHED: unique symbol;
2
+ type TickSubscriptionPrototype = {
3
+ [PATCHED]?: boolean;
4
+ onMount?: (...args: any[]) => unknown;
5
+ onDestroy?: (...args: any[]) => unknown;
6
+ };
7
+ /**
8
+ * CanvasEngine presets 2.0.0 can finish mounting a rain layer after that layer
9
+ * has already been destroyed. The late mount then leaves a tick subscription
10
+ * operating on a destroyed Pixi object. It can also overwrite an existing
11
+ * subscription when mounts overlap.
12
+ *
13
+ * Keep this compatibility patch local to the affected preset prototypes. It
14
+ * can be removed once the preset owns the same lifecycle guarantees.
15
+ */
16
+ export declare function patchWeatherTickLifecycle(prototype: TickSubscriptionPrototype): void;
17
+ export {};
@@ -0,0 +1,44 @@
1
+ //#region src/components/scenes/weather-tick-lifecycle.ts
2
+ var PATCHED = Symbol.for("@rpgjs/client/weather-tick-lifecycle-patched");
3
+ var DISPOSED = Symbol("weather-tick-lifecycle-disposed");
4
+ var MOUNT_QUEUE = Symbol("weather-tick-lifecycle-mount-queue");
5
+ function stopTickSubscription(instance) {
6
+ instance.tickSubscription?.unsubscribe?.();
7
+ instance.tickSubscription = void 0;
8
+ }
9
+ /**
10
+ * CanvasEngine presets 2.0.0 can finish mounting a rain layer after that layer
11
+ * has already been destroyed. The late mount then leaves a tick subscription
12
+ * operating on a destroyed Pixi object. It can also overwrite an existing
13
+ * subscription when mounts overlap.
14
+ *
15
+ * Keep this compatibility patch local to the affected preset prototypes. It
16
+ * can be removed once the preset owns the same lifecycle guarantees.
17
+ */
18
+ function patchWeatherTickLifecycle(prototype) {
19
+ if (!prototype || prototype[PATCHED]) return;
20
+ const originalOnMount = prototype.onMount;
21
+ const originalOnDestroy = prototype.onDestroy;
22
+ if (!originalOnMount || !originalOnDestroy) return;
23
+ Object.defineProperty(prototype, PATCHED, { value: true });
24
+ prototype.onMount = function(...args) {
25
+ const mount = async () => {
26
+ if (this[DISPOSED] || this.destroyed) return;
27
+ stopTickSubscription(this);
28
+ await originalOnMount.apply(this, args);
29
+ if (this[DISPOSED] || this.destroyed) stopTickSubscription(this);
30
+ };
31
+ const pendingMount = (this[MOUNT_QUEUE] ?? Promise.resolve()).catch(() => void 0).then(mount);
32
+ this[MOUNT_QUEUE] = pendingMount;
33
+ return pendingMount;
34
+ };
35
+ prototype.onDestroy = function(...args) {
36
+ this[DISPOSED] = true;
37
+ stopTickSubscription(this);
38
+ return originalOnDestroy.apply(this, args);
39
+ };
40
+ }
41
+ //#endregion
42
+ export { patchWeatherTickLifecycle };
43
+
44
+ //# sourceMappingURL=weather-tick-lifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"weather-tick-lifecycle.js","names":[],"sources":["../../../src/components/scenes/weather-tick-lifecycle.ts"],"sourcesContent":["const PATCHED = Symbol.for(\"@rpgjs/client/weather-tick-lifecycle-patched\");\nconst DISPOSED = Symbol(\"weather-tick-lifecycle-disposed\");\nconst MOUNT_QUEUE = Symbol(\"weather-tick-lifecycle-mount-queue\");\n\ntype TickSubscription = {\n unsubscribe?: () => void;\n};\n\ntype TickSubscriptionInstance = {\n destroyed?: boolean;\n tickSubscription?: TickSubscription;\n [DISPOSED]?: boolean;\n [MOUNT_QUEUE]?: Promise<void>;\n};\n\ntype TickSubscriptionPrototype = {\n [PATCHED]?: boolean;\n onMount?: (...args: any[]) => unknown;\n onDestroy?: (...args: any[]) => unknown;\n};\n\nfunction stopTickSubscription(instance: TickSubscriptionInstance) {\n instance.tickSubscription?.unsubscribe?.();\n instance.tickSubscription = undefined;\n}\n\n/**\n * CanvasEngine presets 2.0.0 can finish mounting a rain layer after that layer\n * has already been destroyed. The late mount then leaves a tick subscription\n * operating on a destroyed Pixi object. It can also overwrite an existing\n * subscription when mounts overlap.\n *\n * Keep this compatibility patch local to the affected preset prototypes. It\n * can be removed once the preset owns the same lifecycle guarantees.\n */\nexport function patchWeatherTickLifecycle(prototype: TickSubscriptionPrototype) {\n if (!prototype || prototype[PATCHED]) return;\n\n const originalOnMount = prototype.onMount;\n const originalOnDestroy = prototype.onDestroy;\n if (!originalOnMount || !originalOnDestroy) return;\n\n Object.defineProperty(prototype, PATCHED, { value: true });\n\n prototype.onMount = function (this: TickSubscriptionInstance, ...args: any[]) {\n const mount = async () => {\n if (this[DISPOSED] || this.destroyed) return;\n\n stopTickSubscription(this);\n await originalOnMount.apply(this, args);\n\n if (this[DISPOSED] || this.destroyed) {\n stopTickSubscription(this);\n }\n };\n\n const pendingMount = (this[MOUNT_QUEUE] ?? Promise.resolve())\n .catch(() => undefined)\n .then(mount);\n this[MOUNT_QUEUE] = pendingMount;\n return pendingMount;\n };\n\n prototype.onDestroy = function (this: TickSubscriptionInstance, ...args: any[]) {\n this[DISPOSED] = true;\n stopTickSubscription(this);\n return originalOnDestroy.apply(this, args);\n };\n}\n"],"mappings":";AAAA,IAAM,UAAU,OAAO,IAAI,8CAA8C;AACzE,IAAM,WAAW,OAAO,iCAAiC;AACzD,IAAM,cAAc,OAAO,oCAAoC;AAmB/D,SAAS,qBAAqB,UAAoC;CAChE,SAAS,kBAAkB,cAAc;CACzC,SAAS,mBAAmB,KAAA;AAC9B;;;;;;;;;;AAWA,SAAgB,0BAA0B,WAAsC;CAC9E,IAAI,CAAC,aAAa,UAAU,UAAU;CAEtC,MAAM,kBAAkB,UAAU;CAClC,MAAM,oBAAoB,UAAU;CACpC,IAAI,CAAC,mBAAmB,CAAC,mBAAmB;CAE5C,OAAO,eAAe,WAAW,SAAS,EAAE,OAAO,KAAK,CAAC;CAEzD,UAAU,UAAU,SAA0C,GAAG,MAAa;EAC5E,MAAM,QAAQ,YAAY;GACxB,IAAI,KAAK,aAAa,KAAK,WAAW;GAEtC,qBAAqB,IAAI;GACzB,MAAM,gBAAgB,MAAM,MAAM,IAAI;GAEtC,IAAI,KAAK,aAAa,KAAK,WACzB,qBAAqB,IAAI;EAE7B;EAEA,MAAM,gBAAgB,KAAK,gBAAgB,QAAQ,QAAQ,GACxD,YAAY,KAAA,CAAS,EACrB,KAAK,KAAK;EACb,KAAK,eAAe;EACpB,OAAO;CACT;CAEA,UAAU,YAAY,SAA0C,GAAG,MAAa;EAC9E,KAAK,YAAY;EACjB,qBAAqB,IAAI;EACzB,OAAO,kBAAkB,MAAM,MAAM,IAAI;CAC3C;AACF"}
@@ -1,5 +1,5 @@
1
- import { Context } from '@signe/di';
2
- export declare let context: Context | null;
3
- export declare function inject<T>(service: (new (...args: any[]) => T) | string, _context?: Context): T;
4
- export declare function setInject(_context: Context): void;
1
+ import { RpgContext } from '@rpgjs/common';
2
+ export declare let context: RpgContext | null;
3
+ export declare function inject<T>(service: (new (...args: any[]) => T) | string, _context?: RpgContext): T;
4
+ export declare function setInject(_context: RpgContext): void;
5
5
  export declare function clearInject(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"inject.js","names":[],"sources":["../../src/core/inject.ts"],"sourcesContent":["import { Context, inject as injector } from \"@signe/di\";\n\nexport let context: Context | null = null\n\nexport function inject<T>(service: (new (...args: any[]) => T) | string, _context?: Context): T {\n const c = _context ?? context\n if (!c) throw new Error(\"Context is not set. use setInject() to set the context\");\n return injector(c, service);\n}\n\nexport function setInject(_context: Context) {\n context = _context;\n}\n\nexport function clearInject() {\n context = null\n}"],"mappings":";;AAEA,IAAW,UAA0B;AAErC,SAAgB,OAAU,SAA+C,UAAuB;CAC5F,MAAM,IAAI,YAAY;CACtB,IAAI,CAAC,GAAG,MAAM,IAAI,MAAM,wDAAwD;CAChF,OAAO,SAAS,GAAG,OAAO;AAC9B;AAEA,SAAgB,UAAU,UAAmB;CACzC,UAAU;AACd;AAEA,SAAgB,cAAc;CAC1B,UAAU;AACd"}
1
+ {"version":3,"file":"inject.js","names":[],"sources":["../../src/core/inject.ts"],"sourcesContent":["import { Context, inject as injector } from \"@signe/di\";\nimport type { RpgContext } from \"@rpgjs/common\";\n\nexport let context: RpgContext | null = null\n\nexport function inject<T>(service: (new (...args: any[]) => T) | string, _context?: RpgContext): T {\n const c = _context ?? context\n if (!c) throw new Error(\"Context is not set. use setInject() to set the context\");\n return injector(c as Context, service);\n}\n\nexport function setInject(_context: RpgContext) {\n context = _context;\n}\n\nexport function clearInject() {\n context = null\n}\n"],"mappings":";;AAGA,IAAW,UAA6B;AAExC,SAAgB,OAAU,SAA+C,UAA0B;CAC/F,MAAM,IAAI,YAAY;CACtB,IAAI,CAAC,GAAG,MAAM,IAAI,MAAM,wDAAwD;CAChF,OAAO,SAAS,GAAc,OAAO;AACzC;AAEA,SAAgB,UAAU,UAAsB;CAC5C,UAAU;AACd;AAEA,SAAgB,cAAc;CAC1B,UAAU;AACd"}
@@ -1,6 +1,6 @@
1
- import { Context, Providers } from '@signe/di';
1
+ import { RpgContext, RpgProviders } from '@rpgjs/common';
2
2
  interface SetupOptions {
3
- providers: Providers;
3
+ providers: RpgProviders;
4
4
  }
5
- export declare function startGame(options: SetupOptions): Promise<Context<any, any, Record<string, any>>>;
5
+ export declare function startGame(options: SetupOptions): Promise<RpgContext>;
6
6
  export {};
@@ -6,7 +6,7 @@ async function startGame(options) {
6
6
  const context = new Context();
7
7
  context["side"] = "client";
8
8
  setInject(context);
9
- await injector(context, options.providers);
9
+ await injector(context, options.providers.flat(Infinity));
10
10
  await inject(context, RpgClientEngine).start();
11
11
  return context;
12
12
  }
@@ -1 +1 @@
1
- {"version":3,"file":"setup.js","names":[],"sources":["../../src/core/setup.ts"],"sourcesContent":["import { Context, FactoryProvider, findProvider, findProviders, inject, injector, Providers } from \"@signe/di\";\nimport { RpgClientEngine } from \"../RpgClientEngine\";\nimport { setInject } from \"./inject\";\n\ninterface SetupOptions {\n providers: Providers;\n}\n\n\nexport async function startGame(options: SetupOptions) {\n const context = new Context();\n context['side'] = 'client'\n setInject(context);\n\n await injector(context, options.providers);\n\n const engine = inject<RpgClientEngine>(context, RpgClientEngine);\n await engine.start();\n return context;\n}\n"],"mappings":";;;;AASA,eAAsB,UAAU,SAAuB;CACrD,MAAM,UAAU,IAAI,QAAQ;CAC5B,QAAQ,UAAU;CAClB,UAAU,OAAO;CAEjB,MAAM,SAAS,SAAS,QAAQ,SAAS;CAGzC,MADe,OAAwB,SAAS,eAC1C,EAAO,MAAM;CACnB,OAAO;AACT"}
1
+ {"version":3,"file":"setup.js","names":[],"sources":["../../src/core/setup.ts"],"sourcesContent":["import { Context, inject, injector, type Providers } from \"@signe/di\";\nimport type { RpgContext, RpgProviders } from \"@rpgjs/common\";\nimport { RpgClientEngine } from \"../RpgClientEngine\";\nimport { setInject } from \"./inject\";\n\ninterface SetupOptions {\n providers: RpgProviders;\n}\n\n\nexport async function startGame(options: SetupOptions): Promise<RpgContext> {\n const context = new Context();\n context['side'] = 'client'\n setInject(context as unknown as RpgContext);\n\n const providers = (options.providers as unknown[]).flat(Infinity) as Providers;\n await injector(context, providers);\n\n const engine = inject<RpgClientEngine>(context, RpgClientEngine);\n await engine.start();\n return context as unknown as RpgContext;\n}\n"],"mappings":";;;;AAUA,eAAsB,UAAU,SAA4C;CAC1E,MAAM,UAAU,IAAI,QAAQ;CAC5B,QAAQ,UAAU;CAClB,UAAU,OAAgC;CAG1C,MAAM,SAAS,SADI,QAAQ,UAAwB,KAAK,QAChC,CAAS;CAGjC,MADe,OAAwB,SAAS,eAC1C,EAAO,MAAM;CACnB,OAAO;AACT"}
@@ -0,0 +1 @@
1
+ export {};
package/dist/i18n.d.ts CHANGED
@@ -50,6 +50,7 @@ export declare const RpgClientBuiltinI18n: {
50
50
  "rpg.input.error.min-length": string;
51
51
  "rpg.input.error.max-length": string;
52
52
  "rpg.input.error.email": string;
53
+ "rpg.transition.loading": string;
53
54
  "rpg.shop.default-message": string;
54
55
  "rpg.shop.choose-action": string;
55
56
  "rpg.shop.buy": string;
@@ -63,4 +64,4 @@ export declare const RpgClientBuiltinI18n: {
63
64
  "rpg.shop.total": string;
64
65
  };
65
66
  };
66
- export declare function provideI18n(config?: I18nConfig): import('@signe/di').FactoryProvider;
67
+ export declare function provideI18n(config?: I18nConfig): import('@rpgjs/common').RpgFactoryProvider<import('@rpgjs/common').I18nService>;
package/dist/i18n.js CHANGED
@@ -50,6 +50,7 @@ var RpgClientBuiltinI18n = { en: {
50
50
  "rpg.input.error.min-length": "Enter at least {minLength} characters.",
51
51
  "rpg.input.error.max-length": "Enter no more than {maxLength} characters.",
52
52
  "rpg.input.error.email": "Enter a valid email address.",
53
+ "rpg.transition.loading": "Loading area…",
53
54
  "rpg.shop.default-message": "Welcome to my shop!",
54
55
  "rpg.shop.choose-action": "Choose an action",
55
56
  "rpg.shop.buy": "Buy",
package/dist/i18n.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"i18n.js","names":[],"sources":["../src/i18n.ts"],"sourcesContent":["import { createI18nProvider, type I18nConfig } from \"@rpgjs/common\";\n\nexport const RpgClientBuiltinI18n = {\n en: {\n \"rpg.menu.title\": \"Menu\",\n \"rpg.menu.status\": \"Status\",\n \"rpg.menu.level\": \"Level\",\n \"rpg.menu.gold\": \"Gold\",\n \"rpg.menu.parameters\": \"Parameters\",\n \"rpg.menu.items\": \"Items\",\n \"rpg.menu.skills\": \"Skills\",\n \"rpg.menu.equip\": \"Equip\",\n \"rpg.menu.options\": \"Options\",\n \"rpg.menu.save\": \"Save\",\n \"rpg.menu.exit\": \"Exit\",\n \"rpg.menu.weapons\": \"Weapons\",\n \"rpg.menu.armor\": \"Armor\",\n \"rpg.menu.use\": \"Use\",\n \"rpg.menu.cancel\": \"Cancel\",\n \"rpg.menu.unequip\": \"Unequip\",\n \"rpg.menu.remove-equipment\": \"Remove the current equipment\",\n \"rpg.menu.empty\": \"Empty\",\n \"rpg.menu.leave-game\": \"Leave the game?\",\n \"rpg.menu.exit-help\": \"Press Action to confirm or Escape to go back.\",\n \"rpg.menu.options-help\": \"Configure your preferences here.\",\n \"rpg.save.title\": \"Save Game\",\n \"rpg.save.subtitle\": \"Choose a slot to overwrite or create.\",\n \"rpg.load.title\": \"Load Game\",\n \"rpg.load.subtitle\": \"Select a slot to load your progress.\",\n \"rpg.save.auto\": \"Auto Save\",\n \"rpg.save.slot\": \"Slot {index}\",\n \"rpg.save.empty-slot\": \"Empty Slot\",\n \"rpg.save.level\": \"Level\",\n \"rpg.save.exp\": \"Exp\",\n \"rpg.save.map\": \"Map\",\n \"rpg.save.date\": \"Date\",\n \"rpg.title.default\": \"RPG\",\n \"rpg.title.start\": \"Start\",\n \"rpg.title.load\": \"Load\",\n \"rpg.gameover.title\": \"Game Over\",\n \"rpg.gameover.title-screen\": \"Title Screen\",\n \"rpg.gameover.load-game\": \"Load Game\",\n \"rpg.input.confirm\": \"Confirm\",\n \"rpg.input.cancel\": \"Cancel\",\n \"rpg.input.error.invalid\": \"Invalid value.\",\n \"rpg.input.error.required\": \"A value is required.\",\n \"rpg.input.error.number\": \"Enter a valid number.\",\n \"rpg.input.error.min\": \"The value must be at least {min}.\",\n \"rpg.input.error.max\": \"The value must be at most {max}.\",\n \"rpg.input.error.step\": \"The value must use increments of {step}.\",\n \"rpg.input.error.min-length\": \"Enter at least {minLength} characters.\",\n \"rpg.input.error.max-length\": \"Enter no more than {maxLength} characters.\",\n \"rpg.input.error.email\": \"Enter a valid email address.\",\n \"rpg.shop.default-message\": \"Welcome to my shop!\",\n \"rpg.shop.choose-action\": \"Choose an action\",\n \"rpg.shop.buy\": \"Buy\",\n \"rpg.shop.sell\": \"Sell\",\n \"rpg.shop.back\": \"Back\",\n \"rpg.shop.equipped\": \"Equipped\",\n \"rpg.shop.already-equipped\": \"Already equipped\",\n \"rpg.shop.qty\": \"Qty\",\n \"rpg.shop.available\": \"Available\",\n \"rpg.shop.quantity\": \"Quantity\",\n \"rpg.shop.total\": \"Total\",\n },\n};\n\nexport function provideI18n(config: I18nConfig = {}) {\n return createI18nProvider(config);\n}\n"],"mappings":";;AAEA,IAAa,uBAAuB,EAClC,IAAI;CACF,kBAAkB;CAClB,mBAAmB;CACnB,kBAAkB;CAClB,iBAAiB;CACjB,uBAAuB;CACvB,kBAAkB;CAClB,mBAAmB;CACnB,kBAAkB;CAClB,oBAAoB;CACpB,iBAAiB;CACjB,iBAAiB;CACjB,oBAAoB;CACpB,kBAAkB;CAClB,gBAAgB;CAChB,mBAAmB;CACnB,oBAAoB;CACpB,6BAA6B;CAC7B,kBAAkB;CAClB,uBAAuB;CACvB,sBAAsB;CACtB,yBAAyB;CACzB,kBAAkB;CAClB,qBAAqB;CACrB,kBAAkB;CAClB,qBAAqB;CACrB,iBAAiB;CACjB,iBAAiB;CACjB,uBAAuB;CACvB,kBAAkB;CAClB,gBAAgB;CAChB,gBAAgB;CAChB,iBAAiB;CACjB,qBAAqB;CACrB,mBAAmB;CACnB,kBAAkB;CAClB,sBAAsB;CACtB,6BAA6B;CAC7B,0BAA0B;CAC1B,qBAAqB;CACrB,oBAAoB;CACpB,2BAA2B;CAC3B,4BAA4B;CAC5B,0BAA0B;CAC1B,uBAAuB;CACvB,uBAAuB;CACvB,wBAAwB;CACxB,8BAA8B;CAC9B,8BAA8B;CAC9B,yBAAyB;CACzB,4BAA4B;CAC5B,0BAA0B;CAC1B,gBAAgB;CAChB,iBAAiB;CACjB,iBAAiB;CACjB,qBAAqB;CACrB,6BAA6B;CAC7B,gBAAgB;CAChB,sBAAsB;CACtB,qBAAqB;CACrB,kBAAkB;AACpB,EACF;AAEA,SAAgB,YAAY,SAAqB,CAAC,GAAG;CACnD,OAAO,mBAAmB,MAAM;AAClC"}
1
+ {"version":3,"file":"i18n.js","names":[],"sources":["../src/i18n.ts"],"sourcesContent":["import { createI18nProvider, type I18nConfig } from \"@rpgjs/common\";\n\nexport const RpgClientBuiltinI18n = {\n en: {\n \"rpg.menu.title\": \"Menu\",\n \"rpg.menu.status\": \"Status\",\n \"rpg.menu.level\": \"Level\",\n \"rpg.menu.gold\": \"Gold\",\n \"rpg.menu.parameters\": \"Parameters\",\n \"rpg.menu.items\": \"Items\",\n \"rpg.menu.skills\": \"Skills\",\n \"rpg.menu.equip\": \"Equip\",\n \"rpg.menu.options\": \"Options\",\n \"rpg.menu.save\": \"Save\",\n \"rpg.menu.exit\": \"Exit\",\n \"rpg.menu.weapons\": \"Weapons\",\n \"rpg.menu.armor\": \"Armor\",\n \"rpg.menu.use\": \"Use\",\n \"rpg.menu.cancel\": \"Cancel\",\n \"rpg.menu.unequip\": \"Unequip\",\n \"rpg.menu.remove-equipment\": \"Remove the current equipment\",\n \"rpg.menu.empty\": \"Empty\",\n \"rpg.menu.leave-game\": \"Leave the game?\",\n \"rpg.menu.exit-help\": \"Press Action to confirm or Escape to go back.\",\n \"rpg.menu.options-help\": \"Configure your preferences here.\",\n \"rpg.save.title\": \"Save Game\",\n \"rpg.save.subtitle\": \"Choose a slot to overwrite or create.\",\n \"rpg.load.title\": \"Load Game\",\n \"rpg.load.subtitle\": \"Select a slot to load your progress.\",\n \"rpg.save.auto\": \"Auto Save\",\n \"rpg.save.slot\": \"Slot {index}\",\n \"rpg.save.empty-slot\": \"Empty Slot\",\n \"rpg.save.level\": \"Level\",\n \"rpg.save.exp\": \"Exp\",\n \"rpg.save.map\": \"Map\",\n \"rpg.save.date\": \"Date\",\n \"rpg.title.default\": \"RPG\",\n \"rpg.title.start\": \"Start\",\n \"rpg.title.load\": \"Load\",\n \"rpg.gameover.title\": \"Game Over\",\n \"rpg.gameover.title-screen\": \"Title Screen\",\n \"rpg.gameover.load-game\": \"Load Game\",\n \"rpg.input.confirm\": \"Confirm\",\n \"rpg.input.cancel\": \"Cancel\",\n \"rpg.input.error.invalid\": \"Invalid value.\",\n \"rpg.input.error.required\": \"A value is required.\",\n \"rpg.input.error.number\": \"Enter a valid number.\",\n \"rpg.input.error.min\": \"The value must be at least {min}.\",\n \"rpg.input.error.max\": \"The value must be at most {max}.\",\n \"rpg.input.error.step\": \"The value must use increments of {step}.\",\n \"rpg.input.error.min-length\": \"Enter at least {minLength} characters.\",\n \"rpg.input.error.max-length\": \"Enter no more than {maxLength} characters.\",\n \"rpg.input.error.email\": \"Enter a valid email address.\",\n \"rpg.transition.loading\": \"Loading area…\",\n \"rpg.shop.default-message\": \"Welcome to my shop!\",\n \"rpg.shop.choose-action\": \"Choose an action\",\n \"rpg.shop.buy\": \"Buy\",\n \"rpg.shop.sell\": \"Sell\",\n \"rpg.shop.back\": \"Back\",\n \"rpg.shop.equipped\": \"Equipped\",\n \"rpg.shop.already-equipped\": \"Already equipped\",\n \"rpg.shop.qty\": \"Qty\",\n \"rpg.shop.available\": \"Available\",\n \"rpg.shop.quantity\": \"Quantity\",\n \"rpg.shop.total\": \"Total\",\n },\n};\n\nexport function provideI18n(config: I18nConfig = {}) {\n return createI18nProvider(config);\n}\n"],"mappings":";;AAEA,IAAa,uBAAuB,EAClC,IAAI;CACF,kBAAkB;CAClB,mBAAmB;CACnB,kBAAkB;CAClB,iBAAiB;CACjB,uBAAuB;CACvB,kBAAkB;CAClB,mBAAmB;CACnB,kBAAkB;CAClB,oBAAoB;CACpB,iBAAiB;CACjB,iBAAiB;CACjB,oBAAoB;CACpB,kBAAkB;CAClB,gBAAgB;CAChB,mBAAmB;CACnB,oBAAoB;CACpB,6BAA6B;CAC7B,kBAAkB;CAClB,uBAAuB;CACvB,sBAAsB;CACtB,yBAAyB;CACzB,kBAAkB;CAClB,qBAAqB;CACrB,kBAAkB;CAClB,qBAAqB;CACrB,iBAAiB;CACjB,iBAAiB;CACjB,uBAAuB;CACvB,kBAAkB;CAClB,gBAAgB;CAChB,gBAAgB;CAChB,iBAAiB;CACjB,qBAAqB;CACrB,mBAAmB;CACnB,kBAAkB;CAClB,sBAAsB;CACtB,6BAA6B;CAC7B,0BAA0B;CAC1B,qBAAqB;CACrB,oBAAoB;CACpB,2BAA2B;CAC3B,4BAA4B;CAC5B,0BAA0B;CAC1B,uBAAuB;CACvB,uBAAuB;CACvB,wBAAwB;CACxB,8BAA8B;CAC9B,8BAA8B;CAC9B,yBAAyB;CACzB,0BAA0B;CAC1B,4BAA4B;CAC5B,0BAA0B;CAC1B,gBAAgB;CAChB,iBAAiB;CACjB,iBAAiB;CACjB,qBAAqB;CACrB,6BAA6B;CAC7B,gBAAgB;CAChB,sBAAsB;CACtB,qBAAqB;CACrB,kBAAkB;AACpB,EACF;AAEA,SAAgB,YAAY,SAAqB,CAAC,GAAG;CACnD,OAAO,mBAAmB,MAAM;AAClC"}
package/dist/index.d.ts CHANGED
@@ -7,6 +7,7 @@ export * from './core/setup';
7
7
  export * from './core/inject';
8
8
  export * from './services/loadMap';
9
9
  export * from './services/actionInput';
10
+ export * from './services/mapStreaming';
10
11
  export * from './services/pointerContext';
11
12
  export * from './services/interactions';
12
13
  export * from './module';
@@ -21,9 +22,10 @@ export * from './Sound';
21
22
  export * from './Resource';
22
23
  export * from './decorators/spritesheet';
23
24
  export * from './utils/getEntityProp';
24
- export { Context } from '@signe/di';
25
25
  export { KeyboardControls, Input } from 'canvasengine';
26
26
  export { Control } from './services/keyboardControls';
27
+ export { defineModule } from '@rpgjs/common';
28
+ export type { RpgClassProvider, RpgContext, RpgExistingProvider, RpgFactoryProvider, RpgProvider, RpgProviders, RpgProviderToken, RpgReadableSignal, RpgValueProvider, RpgWritableSignal, } from '@rpgjs/common';
27
29
  export { RpgClientObject } from './Game/Object';
28
30
  export { RpgClientPlayer } from './Game/Player';
29
31
  export { RpgClientEvent } from './Game/Event';
package/dist/index.js CHANGED
@@ -1,4 +1,3 @@
1
- import { Context } from "./node_modules/.pnpm/@signe_di@3.1.0/node_modules/@signe/di/dist/index.js";
2
1
  import { clearInject, context, inject, setInject } from "./core/inject.js";
3
2
  import { AbstractWebsocket, WebSocketToken } from "./services/AbstractSocket.js";
4
3
  import { getKeyboardControlBind, isKeyboardActionConfig, keyboardEventMatchesBind, normalizeActionInput, resolveKeyboardActionInput, resolveKeyboardDirectionInput } from "./services/actionInput.js";
@@ -41,6 +40,7 @@ import { Control } from "./services/keyboardControls.js";
41
40
  import { provideRpg } from "./services/standalone.js";
42
41
  import { BridgeWebsocket, provideMmorpg } from "./services/mmorpg.js";
43
42
  import { startGame } from "./core/setup.js";
43
+ import { MapStreamClientController, provideClientMapStreaming } from "./services/mapStreaming.js";
44
44
  import __ce_component$7 from "./components/prebuilt/hp-bar.ce.js";
45
45
  import __ce_component$12 from "./components/prebuilt/light-halo.ce.js";
46
46
  import "./components/prebuilt/index.js";
@@ -51,4 +51,5 @@ import "./components/index.js";
51
51
  import { Spritesheet } from "./decorators/spritesheet.js";
52
52
  import { withMobile } from "./components/gui/mobile/index.js";
53
53
  import { Input, KeyboardControls } from "canvasengine";
54
- export { AbstractWebsocket, __ce_component as BoxComponent, BridgeWebsocket, __ce_component$1 as CharacterComponent, ClientVisualRegistry, Context, Control, __ce_component$2 as DialogboxComponent, __ce_component$3 as EquipMenuComponent, __ce_component$4 as EventLayerComponent, __ce_component$5 as ExitMenuComponent, __ce_component$6 as GameoverComponent, GlobalConfigToken, __ce_component$7 as HpBar, __ce_component$8 as HudComponent, Input, __ce_component$9 as InputComponent, __ce_component$10 as InputFieldComponent, __ce_component$11 as ItemsMenuComponent, KeyboardControls, __ce_component$12 as LightHalo, LoadMapService, LoadMapToken, __ce_component$13 as MainMenuComponent, __ce_component$14 as NotificationComponent, __ce_component$15 as OptionsMenuComponent, PrebuiltComponentAnimations, Presets, ProjectileManager, RpgClientBuiltinI18n, RpgClientEngine, RpgClientEvent, RpgClientInteractions, RpgClientObject, RpgClientPlayer, RpgGui, RpgResource, RpgSound, SaveClientService, SaveClientToken, __ce_component$16 as SaveLoadComponent, __ce_component$17 as SceneMap, __ce_component$18 as ShopComponent, __ce_component$19 as SkillsMenuComponent, Sound, Spritesheet, __ce_component$20 as TitleScreenComponent, WebSocketToken, clearInject, context, createClientPointerContext, dragToTile, draggable, getEntityProp, getKeyboardControlBind, getSoundMetadata, hoverPopover, inject, isKeyboardActionConfig, keyboardEventMatchesBind, normalizeActionInput, provideClientGlobalConfig, provideClientModules, provideGlobalConfig, provideI18n, provideLoadMap, provideMmorpg, provideRpg, provideSaveClient, resolveKeyboardActionInput, resolveKeyboardDirectionInput, selectable, setInject, startGame, withMobile };
54
+ import { defineModule } from "@rpgjs/common";
55
+ export { AbstractWebsocket, __ce_component as BoxComponent, BridgeWebsocket, __ce_component$1 as CharacterComponent, ClientVisualRegistry, Control, __ce_component$2 as DialogboxComponent, __ce_component$3 as EquipMenuComponent, __ce_component$4 as EventLayerComponent, __ce_component$5 as ExitMenuComponent, __ce_component$6 as GameoverComponent, GlobalConfigToken, __ce_component$7 as HpBar, __ce_component$8 as HudComponent, Input, __ce_component$9 as InputComponent, __ce_component$10 as InputFieldComponent, __ce_component$11 as ItemsMenuComponent, KeyboardControls, __ce_component$12 as LightHalo, LoadMapService, LoadMapToken, __ce_component$13 as MainMenuComponent, MapStreamClientController, __ce_component$14 as NotificationComponent, __ce_component$15 as OptionsMenuComponent, PrebuiltComponentAnimations, Presets, ProjectileManager, RpgClientBuiltinI18n, RpgClientEngine, RpgClientEvent, RpgClientInteractions, RpgClientObject, RpgClientPlayer, RpgGui, RpgResource, RpgSound, SaveClientService, SaveClientToken, __ce_component$16 as SaveLoadComponent, __ce_component$17 as SceneMap, __ce_component$18 as ShopComponent, __ce_component$19 as SkillsMenuComponent, Sound, Spritesheet, __ce_component$20 as TitleScreenComponent, WebSocketToken, clearInject, context, createClientPointerContext, defineModule, dragToTile, draggable, getEntityProp, getKeyboardControlBind, getSoundMetadata, hoverPopover, inject, isKeyboardActionConfig, keyboardEventMatchesBind, normalizeActionInput, provideClientGlobalConfig, provideClientMapStreaming, provideClientModules, provideGlobalConfig, provideI18n, provideLoadMap, provideMmorpg, provideRpg, provideSaveClient, resolveKeyboardActionInput, resolveKeyboardDirectionInput, selectable, setInject, startGame, withMobile };
package/dist/module.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { FactoryProvider } from '@signe/di';
1
+ import { RpgFactoryProvider } from '@rpgjs/common';
2
2
  import { RpgClient } from './RpgClient';
3
3
  /**
4
4
  * Type for client modules that can be either:
@@ -41,7 +41,7 @@ export type RpgClientModule = RpgClient | (new () => any);
41
41
  * provideClientModules([MyClientModule])
42
42
  * ```
43
43
  */
44
- export declare function provideClientModules(modules: RpgClientModule[]): FactoryProvider;
44
+ export declare function provideClientModules(modules: RpgClientModule[]): RpgFactoryProvider;
45
45
  export declare const GlobalConfigToken = "GlobalConfigToken";
46
46
  export declare function provideGlobalConfig(config: any): {
47
47
  provide: string;
@@ -1 +1 @@
1
- {"version":3,"file":"module.js","names":[],"sources":["../src/module.ts"],"sourcesContent":["import { findModules, provideModules, registerI18nMessages } from \"@rpgjs/common\";\nimport { FactoryProvider } from \"@signe/di\";\nimport { RpgClientEngine } from \"./RpgClientEngine\";\nimport { RpgClient } from \"./RpgClient\";\nimport { inject } from \"@signe/di\";\nimport { RpgGui } from \"./Gui/Gui\";\nimport { getSoundMetadata } from \"./Sound\";\n\n/**\n * Type for client modules that can be either:\n * - An object implementing RpgClient interface\n * - A class decorated with @RpgModule decorator\n */\nexport type RpgClientModule = RpgClient | (new () => any);\n\n/**\n * Provides client modules configuration to Dependency Injection\n * \n * This function accepts an array of client modules that can be either:\n * - Objects implementing the RpgClient interface\n * - Classes decorated with the @RpgModule decorator (which will be instantiated)\n * \n * @param modules - Array of client modules (objects or classes)\n * @returns FactoryProvider configuration for DI\n * @example\n * ```ts\n * // Using an object\n * provideClientModules([\n * {\n * engine: {\n * onConnected(engine) {\n * console.log('Client connected')\n * }\n * }\n * }\n * ])\n * \n * // Using a decorated class\n * @RpgModule<RpgClient>({\n * engine: {\n * onStart(engine) {\n * console.log('Client started')\n * }\n * }\n * })\n * class MyClientModule {}\n * \n * provideClientModules([MyClientModule])\n * ```\n */\nexport function provideClientModules(modules: RpgClientModule[]): FactoryProvider {\n return provideModules(modules, \"client\", (modules, context) => {\n const mainModuleClient = findModules(context, 'Client')\n modules = [...mainModuleClient, ...modules]\n modules = modules.map((module) => {\n // If module is a class (constructor function), instantiate it\n // The RpgModule decorator adds properties to the prototype, which will be accessible via the instance\n if (typeof module === 'function') {\n const instance = new module() as any;\n // Copy all enumerable properties (including from prototype) to a plain object\n const moduleObj: any = {};\n for (const key in instance) {\n moduleObj[key] = instance[key];\n }\n module = moduleObj;\n }\n if ('client' in module) {\n module = module.client as any;\n }\n if (module.i18n) {\n registerI18nMessages(context, module.i18n, \"client-module\", 10);\n }\n if (module.spritesheets) {\n const spritesheets = [...module.spritesheets];\n module.spritesheets = {\n load: (engine: RpgClientEngine) => {\n spritesheets.forEach((spritesheet) => {\n engine.addSpriteSheet(spritesheet);\n });\n },\n };\n }\n if (module.spritesheetResolver) {\n const resolver = module.spritesheetResolver;\n module.spritesheetResolver = {\n load: (engine: RpgClientEngine) => {\n engine.setSpritesheetResolver(resolver);\n },\n };\n }\n if (module.sounds) {\n const sounds = [...module.sounds];\n module.sounds = {\n load: (engine: RpgClientEngine) => {\n sounds.forEach((sound) => {\n // Check if it's a class decorated with @Sound\n if (typeof sound === 'function' || (sound && sound.constructor && sound.constructor !== Object)) {\n const metadata = getSoundMetadata(sound);\n if (metadata) {\n // Handle single sound\n if (metadata.id && metadata.sound) {\n engine.addSound({\n id: metadata.id,\n src: metadata.sound,\n loop: metadata.loop,\n volume: metadata.volume,\n });\n }\n // Handle multiple sounds\n if (metadata.sounds) {\n Object.entries(metadata.sounds).forEach(([soundId, soundSrc]) => {\n engine.addSound({\n id: soundId,\n src: soundSrc,\n loop: metadata.loop,\n volume: metadata.volume,\n });\n });\n }\n } else {\n // Not a decorated class, treat as regular sound object\n engine.addSound(sound);\n }\n } else {\n // Regular sound object\n engine.addSound(sound);\n }\n });\n },\n };\n }\n if (module.soundResolver) {\n const resolver = module.soundResolver;\n module.soundResolver = {\n load: (engine: RpgClientEngine) => {\n engine.setSoundResolver(resolver);\n },\n };\n }\n if (module.gui) {\n const gui = [...module.gui];\n module.gui = {\n load: (engine: RpgClientEngine) => {\n const guiService = inject(engine.context, RpgGui) as RpgGui;\n gui.forEach((gui) => {\n guiService.add(gui);\n });\n },\n };\n }\n if (module.componentAnimations) {\n const componentAnimations = [...module.componentAnimations];\n module.componentAnimations = {\n load: (engine: RpgClientEngine) => {\n componentAnimations.forEach((componentAnimation) => {\n engine.addComponentAnimation(componentAnimation);\n });\n },\n };\n }\n if (module.clientVisuals) {\n const clientVisuals = { ...module.clientVisuals };\n module.clientVisuals = {\n load: (engine: RpgClientEngine) => {\n engine.registerClientVisuals(clientVisuals);\n },\n };\n }\n if (module.projectiles) {\n const projectiles = { ...module.projectiles };\n module.projectiles = {\n ...projectiles,\n load: (engine: RpgClientEngine) => {\n if (projectiles.components) {\n Object.entries(projectiles.components).forEach(([type, component]) => {\n engine.registerProjectileComponent(type, component);\n });\n }\n },\n };\n }\n if (module.interactions) {\n const interactions = module.interactions;\n module.interactions = {\n ...interactions,\n load: (engine: RpgClientEngine) => {\n if (typeof interactions === \"function\") {\n interactions(engine);\n return;\n }\n interactions.load?.(engine);\n interactions.setup?.(engine);\n if (Array.isArray(interactions.use)) {\n interactions.use.forEach(([matcher, behavior]) => {\n engine.interactions.use(matcher, behavior);\n });\n }\n },\n };\n }\n if (module.transitions) {\n const transitions = [...module.transitions];\n module.transitions = {\n load: (engine: RpgClientEngine) => {\n const guiService = inject(engine.context, RpgGui) as RpgGui;\n transitions.forEach((transition) => {\n guiService.add({\n name: transition.id,\n component: transition.component,\n data: transition.props || {}\n });\n });\n },\n };\n }\n if (module.particles) {\n const particles = [...module.particles];\n module.particles = {\n load: (engine: RpgClientEngine) => {\n particles.forEach((particle) => {\n engine.addParticle(particle);\n });\n },\n };\n }\n if (module.sprite) {\n const sprite = {...module.sprite};\n module.sprite = {\n ...sprite,\n load: (engine: RpgClientEngine) => {\n if (sprite.componentsBehind) {\n sprite.componentsBehind.forEach((component) => {\n engine.addSpriteComponentBehind(component);\n });\n }\n if (sprite.componentsInFront) {\n sprite.componentsInFront.forEach((component) => {\n engine.addSpriteComponentInFront(component);\n });\n }\n if (sprite.components) {\n Object.entries(sprite.components).forEach(([id, component]) => {\n engine.registerSpriteComponent(id, component);\n });\n }\n if (sprite.eventComponent) {\n engine.addEventComponentResolver(sprite.eventComponent);\n }\n },\n };\n }\n return module;\n });\n return modules\n });\n}\n\nexport const GlobalConfigToken = \"GlobalConfigToken\";\n\nexport function provideGlobalConfig(config: any) {\n return {\n provide: GlobalConfigToken,\n useValue: config ?? {},\n };\n}\n\nexport function provideClientGlobalConfig(config: any = {}) {\n config.keyboardControls = {\n up: 'up',\n down: 'down',\n left: 'left',\n right: 'right',\n action: 'space',\n dash: 'shift',\n escape: 'escape',\n ...(config.keyboardControls ?? {}),\n }\n return provideGlobalConfig(config)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"module.js","names":[],"sources":["../src/module.ts"],"sourcesContent":["import { findModules, provideModules, registerI18nMessages } from \"@rpgjs/common\";\nimport type { RpgFactoryProvider } from \"@rpgjs/common\";\nimport { RpgClientEngine } from \"./RpgClientEngine\";\nimport { RpgClient } from \"./RpgClient\";\nimport { inject } from \"@signe/di\";\nimport { RpgGui } from \"./Gui/Gui\";\nimport { getSoundMetadata } from \"./Sound\";\n\n/**\n * Type for client modules that can be either:\n * - An object implementing RpgClient interface\n * - A class decorated with @RpgModule decorator\n */\nexport type RpgClientModule = RpgClient | (new () => any);\n\n/**\n * Provides client modules configuration to Dependency Injection\n * \n * This function accepts an array of client modules that can be either:\n * - Objects implementing the RpgClient interface\n * - Classes decorated with the @RpgModule decorator (which will be instantiated)\n * \n * @param modules - Array of client modules (objects or classes)\n * @returns FactoryProvider configuration for DI\n * @example\n * ```ts\n * // Using an object\n * provideClientModules([\n * {\n * engine: {\n * onConnected(engine) {\n * console.log('Client connected')\n * }\n * }\n * }\n * ])\n * \n * // Using a decorated class\n * @RpgModule<RpgClient>({\n * engine: {\n * onStart(engine) {\n * console.log('Client started')\n * }\n * }\n * })\n * class MyClientModule {}\n * \n * provideClientModules([MyClientModule])\n * ```\n */\nexport function provideClientModules(modules: RpgClientModule[]): RpgFactoryProvider {\n return provideModules(modules, \"client\", (modules, context) => {\n const mainModuleClient = findModules(context, 'Client')\n modules = [...mainModuleClient, ...modules]\n modules = modules.map((module) => {\n // If module is a class (constructor function), instantiate it\n // The RpgModule decorator adds properties to the prototype, which will be accessible via the instance\n if (typeof module === 'function') {\n const instance = new module() as any;\n // Copy all enumerable properties (including from prototype) to a plain object\n const moduleObj: any = {};\n for (const key in instance) {\n moduleObj[key] = instance[key];\n }\n module = moduleObj;\n }\n if ('client' in module) {\n module = module.client as any;\n }\n if (module.i18n) {\n registerI18nMessages(context, module.i18n, \"client-module\", 10);\n }\n if (module.spritesheets) {\n const spritesheets = [...module.spritesheets];\n module.spritesheets = {\n load: (engine: RpgClientEngine) => {\n spritesheets.forEach((spritesheet) => {\n engine.addSpriteSheet(spritesheet);\n });\n },\n };\n }\n if (module.spritesheetResolver) {\n const resolver = module.spritesheetResolver;\n module.spritesheetResolver = {\n load: (engine: RpgClientEngine) => {\n engine.setSpritesheetResolver(resolver);\n },\n };\n }\n if (module.sounds) {\n const sounds = [...module.sounds];\n module.sounds = {\n load: (engine: RpgClientEngine) => {\n sounds.forEach((sound) => {\n // Check if it's a class decorated with @Sound\n if (typeof sound === 'function' || (sound && sound.constructor && sound.constructor !== Object)) {\n const metadata = getSoundMetadata(sound);\n if (metadata) {\n // Handle single sound\n if (metadata.id && metadata.sound) {\n engine.addSound({\n id: metadata.id,\n src: metadata.sound,\n loop: metadata.loop,\n volume: metadata.volume,\n });\n }\n // Handle multiple sounds\n if (metadata.sounds) {\n Object.entries(metadata.sounds).forEach(([soundId, soundSrc]) => {\n engine.addSound({\n id: soundId,\n src: soundSrc,\n loop: metadata.loop,\n volume: metadata.volume,\n });\n });\n }\n } else {\n // Not a decorated class, treat as regular sound object\n engine.addSound(sound);\n }\n } else {\n // Regular sound object\n engine.addSound(sound);\n }\n });\n },\n };\n }\n if (module.soundResolver) {\n const resolver = module.soundResolver;\n module.soundResolver = {\n load: (engine: RpgClientEngine) => {\n engine.setSoundResolver(resolver);\n },\n };\n }\n if (module.gui) {\n const gui = [...module.gui];\n module.gui = {\n load: (engine: RpgClientEngine) => {\n const guiService = inject(engine.context, RpgGui) as RpgGui;\n gui.forEach((gui) => {\n guiService.add(gui);\n });\n },\n };\n }\n if (module.componentAnimations) {\n const componentAnimations = [...module.componentAnimations];\n module.componentAnimations = {\n load: (engine: RpgClientEngine) => {\n componentAnimations.forEach((componentAnimation) => {\n engine.addComponentAnimation(componentAnimation);\n });\n },\n };\n }\n if (module.clientVisuals) {\n const clientVisuals = { ...module.clientVisuals };\n module.clientVisuals = {\n load: (engine: RpgClientEngine) => {\n engine.registerClientVisuals(clientVisuals);\n },\n };\n }\n if (module.projectiles) {\n const projectiles = { ...module.projectiles };\n module.projectiles = {\n ...projectiles,\n load: (engine: RpgClientEngine) => {\n if (projectiles.components) {\n Object.entries(projectiles.components).forEach(([type, component]) => {\n engine.registerProjectileComponent(type, component);\n });\n }\n },\n };\n }\n if (module.interactions) {\n const interactions = module.interactions;\n module.interactions = {\n ...interactions,\n load: (engine: RpgClientEngine) => {\n if (typeof interactions === \"function\") {\n interactions(engine);\n return;\n }\n interactions.load?.(engine);\n interactions.setup?.(engine);\n if (Array.isArray(interactions.use)) {\n interactions.use.forEach(([matcher, behavior]) => {\n engine.interactions.use(matcher, behavior);\n });\n }\n },\n };\n }\n if (module.transitions) {\n const transitions = [...module.transitions];\n module.transitions = {\n load: (engine: RpgClientEngine) => {\n const guiService = inject(engine.context, RpgGui) as RpgGui;\n transitions.forEach((transition) => {\n guiService.add({\n name: transition.id,\n component: transition.component,\n data: transition.props || {}\n });\n });\n },\n };\n }\n if (module.particles) {\n const particles = [...module.particles];\n module.particles = {\n load: (engine: RpgClientEngine) => {\n particles.forEach((particle) => {\n engine.addParticle(particle);\n });\n },\n };\n }\n if (module.sprite) {\n const sprite = {...module.sprite};\n module.sprite = {\n ...sprite,\n load: (engine: RpgClientEngine) => {\n if (sprite.componentsBehind) {\n sprite.componentsBehind.forEach((component) => {\n engine.addSpriteComponentBehind(component);\n });\n }\n if (sprite.componentsInFront) {\n sprite.componentsInFront.forEach((component) => {\n engine.addSpriteComponentInFront(component);\n });\n }\n if (sprite.components) {\n Object.entries(sprite.components).forEach(([id, component]) => {\n engine.registerSpriteComponent(id, component);\n });\n }\n if (sprite.eventComponent) {\n engine.addEventComponentResolver(sprite.eventComponent);\n }\n },\n };\n }\n return module;\n });\n return modules\n });\n}\n\nexport const GlobalConfigToken = \"GlobalConfigToken\";\n\nexport function provideGlobalConfig(config: any) {\n return {\n provide: GlobalConfigToken,\n useValue: config ?? {},\n };\n}\n\nexport function provideClientGlobalConfig(config: any = {}) {\n config.keyboardControls = {\n up: 'up',\n down: 'down',\n left: 'left',\n right: 'right',\n action: 'space',\n dash: 'shift',\n escape: 'escape',\n ...(config.keyboardControls ?? {}),\n }\n return provideGlobalConfig(config)\n}\n"],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -1,4 +1,4 @@
1
- import { Context } from '@signe/di';
1
+ import { RpgContext } from '@rpgjs/common';
2
2
  export declare const WebSocketToken = "websocket";
3
3
  export type SocketQueryValue = string | null | undefined;
4
4
  export type SocketQuery = Record<string, SocketQueryValue>;
@@ -9,9 +9,9 @@ export type SocketUpdateProperties = {
9
9
  };
10
10
  export type WebSocketMode = "standalone" | "mmorpg";
11
11
  export declare abstract class AbstractWebsocket {
12
- protected context: Context;
12
+ protected context: RpgContext;
13
13
  readonly mode?: WebSocketMode;
14
- constructor(context: Context);
14
+ constructor(context: RpgContext);
15
15
  abstract connection(listeners?: (data: any) => void): Promise<void>;
16
16
  abstract emit(event: string, data: any): void;
17
17
  abstract on(event: string, callback: (data: any) => void): void;
@@ -1 +1 @@
1
- {"version":3,"file":"AbstractSocket.js","names":[],"sources":["../../src/services/AbstractSocket.ts"],"sourcesContent":["import { Context } from \"@signe/di\";\n\nexport const WebSocketToken = \"websocket\";\n\nexport type SocketQueryValue = string | null | undefined;\nexport type SocketQuery = Record<string, SocketQueryValue>;\nexport type SocketUpdateProperties = {\n room: string;\n host?: string;\n query?: SocketQuery;\n};\nexport type WebSocketMode = \"standalone\" | \"mmorpg\";\n\nexport abstract class AbstractWebsocket {\n readonly mode?: WebSocketMode;\n\n constructor(protected context: Context) {}\n\n abstract connection(listeners?: (data: any) => void): Promise<void>;\n abstract emit(event: string, data: any): void;\n abstract on(event: string, callback: (data: any) => void): void;\n abstract off(event: string, callback: (data: any) => void): void;\n abstract updateProperties(params: SocketUpdateProperties): void;\n abstract reconnect(listeners?: (data: any) => void): Promise<void>;\n}\n"],"mappings":";AAEA,IAAa,iBAAiB;AAW9B,IAAsB,oBAAtB,MAAwC;CAGtC,YAAY,SAA4B;EAAlB,KAAA,UAAA;CAAmB;AAQ3C"}
1
+ {"version":3,"file":"AbstractSocket.js","names":[],"sources":["../../src/services/AbstractSocket.ts"],"sourcesContent":["import type { RpgContext } from \"@rpgjs/common\";\n\nexport const WebSocketToken = \"websocket\";\n\nexport type SocketQueryValue = string | null | undefined;\nexport type SocketQuery = Record<string, SocketQueryValue>;\nexport type SocketUpdateProperties = {\n room: string;\n host?: string;\n query?: SocketQuery;\n};\nexport type WebSocketMode = \"standalone\" | \"mmorpg\";\n\nexport abstract class AbstractWebsocket {\n readonly mode?: WebSocketMode;\n\n constructor(protected context: RpgContext) {}\n\n abstract connection(listeners?: (data: any) => void): Promise<void>;\n abstract emit(event: string, data: any): void;\n abstract on(event: string, callback: (data: any) => void): void;\n abstract off(event: string, callback: (data: any) => void): void;\n abstract updateProperties(params: SocketUpdateProperties): void;\n abstract reconnect(listeners?: (data: any) => void): Promise<void>;\n}\n"],"mappings":";AAEA,IAAa,iBAAiB;AAW9B,IAAsB,oBAAtB,MAAwC;CAGtC,YAAY,SAA+B;EAArB,KAAA,UAAA;CAAsB;AAQ9C"}
@@ -1,5 +1,5 @@
1
- import { Context } from '@signe/di';
2
- import { LightingState } from '@rpgjs/common';
1
+ import { LightingState, RpgContext, RpgProvider } from '@rpgjs/common';
2
+ import { RpgClientMap } from '../Game/Map';
3
3
  export declare const LoadMapToken = "LoadMapToken";
4
4
  /**
5
5
  * Represents the structure of map data that should be returned by the load map callback.
@@ -7,7 +7,7 @@ export declare const LoadMapToken = "LoadMapToken";
7
7
  *
8
8
  * @interface MapData
9
9
  */
10
- type MapData = {
10
+ export type MapData = {
11
11
  /** Raw map data that will be passed to the map component */
12
12
  data: any;
13
13
  /** CanvasEngine component that will render the map */
@@ -28,6 +28,13 @@ type MapData = {
28
28
  id?: string;
29
29
  /** Optional initial lighting state for the loaded map */
30
30
  lighting?: LightingState | null;
31
+ /** Optional render parameters passed to the map component. */
32
+ params?: Record<string, unknown>;
33
+ /** Internal controller used by a progressive map provider. */
34
+ streamController?: {
35
+ attach(map: RpgClientMap): void;
36
+ detach(): void;
37
+ };
31
38
  };
32
39
  /**
33
40
  * Callback function type for loading map data.
@@ -43,7 +50,8 @@ export declare class LoadMapService {
43
50
  private context;
44
51
  private options;
45
52
  private updateMapService;
46
- constructor(context: Context, options: LoadMapOptions);
53
+ constructor(context: RpgContext, options: LoadMapOptions);
54
+ initialize(): void;
47
55
  load(mapId: string): Promise<MapData>;
48
56
  }
49
57
  /**
@@ -137,8 +145,4 @@ export declare class LoadMapService {
137
145
  * @see {@link LoadMapOptions} for callback function signature
138
146
  * @see {@link MapData} for return data structure
139
147
  */
140
- export declare function provideLoadMap(options: LoadMapOptions): {
141
- provide: string;
142
- useFactory: (context: Context) => void;
143
- }[];
144
- export {};
148
+ export declare function provideLoadMap(options: LoadMapOptions): RpgProvider[];
@@ -6,10 +6,11 @@ var LoadMapService = class {
6
6
  constructor(context, options) {
7
7
  this.context = context;
8
8
  this.options = options;
9
- if (context["side"] === "server") return;
10
- this.updateMapService = inject(context, UpdateMapToken);
9
+ if (context.side === "server") return;
11
10
  }
11
+ initialize() {}
12
12
  async load(mapId) {
13
+ this.updateMapService ??= inject(this.context, UpdateMapToken);
13
14
  const map = await this.options(mapId.replace("map-", ""));
14
15
  await this.updateMapService.update(map);
15
16
  return map;
@@ -110,7 +111,7 @@ function provideLoadMap(options) {
110
111
  return [{
111
112
  provide: UpdateMapToken,
112
113
  useFactory: (context) => {
113
- if (context["side"] === "client") console.warn("UpdateMapToken is not overridden");
114
+ if (context.side === "client") console.warn("UpdateMapToken is not overridden");
114
115
  }
115
116
  }, {
116
117
  provide: LoadMapToken,
@@ -1 +1 @@
1
- {"version":3,"file":"loadMap.js","names":[],"sources":["../../src/services/loadMap.ts"],"sourcesContent":["import { Context, inject } from \"@signe/di\";\nimport { UpdateMapToken, UpdateMapService, type LightingState } from \"@rpgjs/common\";\n\nexport const LoadMapToken = 'LoadMapToken'\n\n/**\n * Represents the structure of map data that should be returned by the load map callback.\n * This interface defines all the properties that can be provided when loading a custom map.\n * \n * @interface MapData\n */\ntype MapData = {\n /** Raw map data that will be passed to the map component */\n data: any;\n /** CanvasEngine component that will render the map */\n component: any;\n /** Optional map width in pixels, used for viewport calculations */\n width?: number;\n /** Optional map height in pixels, used for viewport calculations */\n height?: number;\n /** Optional map events data (NPCs, interactive objects, etc.) */\n events?: any;\n /** Optional named positions, for example Tiled point objects used by changeMap(\"map\", \"name\") */\n positions?: Record<string, { x: number; y: number; z?: number }>;\n /** Optional map identifier, defaults to the mapId parameter if not provided */\n id?: string;\n /** Optional initial lighting state for the loaded map */\n lighting?: LightingState | null;\n}\n\n/**\n * Callback function type for loading map data.\n * This function receives a map ID and should return either a MapData object directly\n * or a Promise that resolves to a MapData object.\n * \n * @callback LoadMapOptions\n * @param {string} mapId - The identifier of the map to load\n * @returns {Promise<MapData> | MapData} The map data object or a promise resolving to it\n */\nexport type LoadMapOptions = (mapId: string) => Promise<MapData> | MapData \n\nexport class LoadMapService {\n private updateMapService: UpdateMapService;\n\n constructor(private context: Context, private options: LoadMapOptions) {\n if (context['side'] === 'server') {\n return\n }\n this.updateMapService = inject(context, UpdateMapToken);\n }\n\n async load(mapId: string) {\n const map = await this.options(mapId.replace('map-', ''))\n await this.updateMapService.update(map);\n return map;\n }\n}\n\n/**\n * Creates a dependency injection configuration for custom map loading on the client side.\n * \n * This function allows you to customize how maps are loaded and displayed by providing\n * a callback that defines custom map data and rendering components. It's designed to work\n * with the RPG-JS dependency injection system and enables integration of custom map formats\n * like Tiled TMX files or any other map data structure.\n * \n * The function sets up the necessary service providers for map loading, including:\n * - UpdateMapToken: Handles map updates in the client context\n * - LoadMapToken: Provides the LoadMapService with your custom loading logic\n * \n * **Design Concept:**\n * The function follows the provider pattern, creating a modular way to inject custom\n * map loading behavior into the RPG-JS client engine. It separates the concern of\n * map data fetching from map rendering, allowing developers to focus on their specific\n * map format while leveraging the engine's rendering capabilities.\n * \n * @param {LoadMapOptions} options - Callback function that handles map loading logic\n * @returns {Array<Object>} Array of dependency injection provider configurations\n * \n * @example\n * ```typescript\n * import { provideLoadMap } from '@rpgjs/client'\n * import { createModule } from '@rpgjs/common'\n * import MyTiledMapComponent from './MyTiledMapComponent.ce'\n * \n * // Basic usage with JSON map data\n * export function provideCustomMap() {\n * return createModule(\"CustomMap\", [\n * provideLoadMap(async (mapId) => {\n * const response = await fetch(`/maps/${mapId}.json`)\n * const mapData = await response.json()\n * \n * return {\n * data: mapData,\n * component: MyTiledMapComponent,\n * width: mapData.width,\n * height: mapData.height,\n * events: mapData.events\n * }\n * })\n * ])\n * }\n * \n * // Advanced usage with Tiled TMX files\n * export function provideTiledMap() {\n * return createModule(\"TiledMap\", [\n * provideLoadMap(async (mapId) => {\n * // Load TMX file\n * const tmxResponse = await fetch(`/tiled/${mapId}.tmx`)\n * const tmxData = await tmxResponse.text()\n * \n * // Parse TMX data (using a TMX parser)\n * const parsedMap = parseTMX(tmxData)\n * \n * return {\n * data: parsedMap,\n * component: TiledMapRenderer,\n * width: parsedMap.width * parsedMap.tilewidth,\n * height: parsedMap.height * parsedMap.tileheight,\n * events: parsedMap.objectGroups?.find(g => g.name === 'events')?.objects\n * }\n * })\n * ])\n * }\n * \n * // Synchronous usage for static maps\n * export function provideStaticMap() {\n * return createModule(\"StaticMap\", [\n * provideLoadMap((mapId) => {\n * const staticMaps = {\n * 'town': { tiles: [...], npcs: [...] },\n * 'dungeon': { tiles: [...], monsters: [...] }\n * }\n * \n * return {\n * data: staticMaps[mapId],\n * component: StaticMapComponent,\n * width: 800,\n * height: 600\n * }\n * })\n * ])\n * }\n * ```\n * \n * @since 4.0.0\n * @see {@link LoadMapOptions} for callback function signature\n * @see {@link MapData} for return data structure\n */\nexport function provideLoadMap(options: LoadMapOptions) {\n return [\n {\n provide: UpdateMapToken,\n useFactory: (context: Context) => {\n if (context['side'] === 'client') {\n console.warn('UpdateMapToken is not overridden')\n }\n return\n },\n },\n {\n provide: LoadMapToken,\n useFactory: (context: Context) => new LoadMapService(context, options),\n },\n ];\n}\n"],"mappings":";;;AAGA,IAAa,eAAe;AAsC5B,IAAa,iBAAb,MAA4B;CAG1B,YAAY,SAA0B,SAAiC;EAAnD,KAAA,UAAA;EAA0B,KAAA,UAAA;EAC5C,IAAI,QAAQ,YAAY,UACtB;EAEF,KAAK,mBAAmB,OAAO,SAAS,cAAc;CACxD;CAEA,MAAM,KAAK,OAAe;EACxB,MAAM,MAAM,MAAM,KAAK,QAAQ,MAAM,QAAQ,QAAQ,EAAE,CAAC;EACxD,MAAM,KAAK,iBAAiB,OAAO,GAAG;EACtC,OAAO;CACT;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6FA,SAAgB,eAAe,SAAyB;CACtD,OAAO,CACL;EACE,SAAS;EACT,aAAa,YAAqB;GAChC,IAAI,QAAQ,YAAY,UACtB,QAAQ,KAAK,kCAAkC;EAGnD;CACF,GACA;EACE,SAAS;EACT,aAAa,YAAqB,IAAI,eAAe,SAAS,OAAO;CACvE,CACF;AACF"}
1
+ {"version":3,"file":"loadMap.js","names":[],"sources":["../../src/services/loadMap.ts"],"sourcesContent":["import { Context, inject } from \"@signe/di\";\nimport { UpdateMapToken, UpdateMapService, type LightingState, type RpgContext, type RpgProvider } from \"@rpgjs/common\";\nimport type { RpgClientMap } from \"../Game/Map\";\n\nexport const LoadMapToken = 'LoadMapToken'\n\n/**\n * Represents the structure of map data that should be returned by the load map callback.\n * This interface defines all the properties that can be provided when loading a custom map.\n * \n * @interface MapData\n */\nexport type MapData = {\n /** Raw map data that will be passed to the map component */\n data: any;\n /** CanvasEngine component that will render the map */\n component: any;\n /** Optional map width in pixels, used for viewport calculations */\n width?: number;\n /** Optional map height in pixels, used for viewport calculations */\n height?: number;\n /** Optional map events data (NPCs, interactive objects, etc.) */\n events?: any;\n /** Optional named positions, for example Tiled point objects used by changeMap(\"map\", \"name\") */\n positions?: Record<string, { x: number; y: number; z?: number }>;\n /** Optional map identifier, defaults to the mapId parameter if not provided */\n id?: string;\n /** Optional initial lighting state for the loaded map */\n lighting?: LightingState | null;\n /** Optional render parameters passed to the map component. */\n params?: Record<string, unknown>;\n /** Internal controller used by a progressive map provider. */\n streamController?: { attach(map: RpgClientMap): void; detach(): void };\n}\n\n/**\n * Callback function type for loading map data.\n * This function receives a map ID and should return either a MapData object directly\n * or a Promise that resolves to a MapData object.\n * \n * @callback LoadMapOptions\n * @param {string} mapId - The identifier of the map to load\n * @returns {Promise<MapData> | MapData} The map data object or a promise resolving to it\n */\nexport type LoadMapOptions = (mapId: string) => Promise<MapData> | MapData \n\nexport class LoadMapService {\n private updateMapService: UpdateMapService;\n\n constructor(private context: RpgContext, private options: LoadMapOptions) {\n if (context.side === 'server') {\n return\n }\n }\n\n initialize(): void {}\n\n async load(mapId: string) {\n this.updateMapService ??= inject(this.context as Context, UpdateMapToken);\n const map = await this.options(mapId.replace('map-', ''))\n await this.updateMapService.update(map);\n return map;\n }\n}\n\n/**\n * Creates a dependency injection configuration for custom map loading on the client side.\n * \n * This function allows you to customize how maps are loaded and displayed by providing\n * a callback that defines custom map data and rendering components. It's designed to work\n * with the RPG-JS dependency injection system and enables integration of custom map formats\n * like Tiled TMX files or any other map data structure.\n * \n * The function sets up the necessary service providers for map loading, including:\n * - UpdateMapToken: Handles map updates in the client context\n * - LoadMapToken: Provides the LoadMapService with your custom loading logic\n * \n * **Design Concept:**\n * The function follows the provider pattern, creating a modular way to inject custom\n * map loading behavior into the RPG-JS client engine. It separates the concern of\n * map data fetching from map rendering, allowing developers to focus on their specific\n * map format while leveraging the engine's rendering capabilities.\n * \n * @param {LoadMapOptions} options - Callback function that handles map loading logic\n * @returns {Array<Object>} Array of dependency injection provider configurations\n * \n * @example\n * ```typescript\n * import { provideLoadMap } from '@rpgjs/client'\n * import { createModule } from '@rpgjs/common'\n * import MyTiledMapComponent from './MyTiledMapComponent.ce'\n * \n * // Basic usage with JSON map data\n * export function provideCustomMap() {\n * return createModule(\"CustomMap\", [\n * provideLoadMap(async (mapId) => {\n * const response = await fetch(`/maps/${mapId}.json`)\n * const mapData = await response.json()\n * \n * return {\n * data: mapData,\n * component: MyTiledMapComponent,\n * width: mapData.width,\n * height: mapData.height,\n * events: mapData.events\n * }\n * })\n * ])\n * }\n * \n * // Advanced usage with Tiled TMX files\n * export function provideTiledMap() {\n * return createModule(\"TiledMap\", [\n * provideLoadMap(async (mapId) => {\n * // Load TMX file\n * const tmxResponse = await fetch(`/tiled/${mapId}.tmx`)\n * const tmxData = await tmxResponse.text()\n * \n * // Parse TMX data (using a TMX parser)\n * const parsedMap = parseTMX(tmxData)\n * \n * return {\n * data: parsedMap,\n * component: TiledMapRenderer,\n * width: parsedMap.width * parsedMap.tilewidth,\n * height: parsedMap.height * parsedMap.tileheight,\n * events: parsedMap.objectGroups?.find(g => g.name === 'events')?.objects\n * }\n * })\n * ])\n * }\n * \n * // Synchronous usage for static maps\n * export function provideStaticMap() {\n * return createModule(\"StaticMap\", [\n * provideLoadMap((mapId) => {\n * const staticMaps = {\n * 'town': { tiles: [...], npcs: [...] },\n * 'dungeon': { tiles: [...], monsters: [...] }\n * }\n * \n * return {\n * data: staticMaps[mapId],\n * component: StaticMapComponent,\n * width: 800,\n * height: 600\n * }\n * })\n * ])\n * }\n * ```\n * \n * @since 4.0.0\n * @see {@link LoadMapOptions} for callback function signature\n * @see {@link MapData} for return data structure\n */\nexport function provideLoadMap(options: LoadMapOptions): RpgProvider[] {\n return [\n {\n provide: UpdateMapToken,\n useFactory: (context: RpgContext) => {\n if (context.side === 'client') {\n console.warn('UpdateMapToken is not overridden')\n }\n return\n },\n },\n {\n provide: LoadMapToken,\n useFactory: (context: RpgContext) => new LoadMapService(context, options),\n },\n ];\n}\n"],"mappings":";;;AAIA,IAAa,eAAe;AA0C5B,IAAa,iBAAb,MAA4B;CAG1B,YAAY,SAA6B,SAAiC;EAAtD,KAAA,UAAA;EAA6B,KAAA,UAAA;EAC/C,IAAI,QAAQ,SAAS,UACnB;CAEJ;CAEA,aAAmB,CAAC;CAEpB,MAAM,KAAK,OAAe;EACxB,KAAK,qBAAqB,OAAO,KAAK,SAAoB,cAAc;EACxE,MAAM,MAAM,MAAM,KAAK,QAAQ,MAAM,QAAQ,QAAQ,EAAE,CAAC;EACxD,MAAM,KAAK,iBAAiB,OAAO,GAAG;EACtC,OAAO;CACT;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6FA,SAAgB,eAAe,SAAwC;CACrE,OAAO,CACL;EACE,SAAS;EACT,aAAa,YAAwB;GACnC,IAAI,QAAQ,SAAS,UACnB,QAAQ,KAAK,kCAAkC;EAGnD;CACF,GACA;EACE,SAAS;EACT,aAAa,YAAwB,IAAI,eAAe,SAAS,OAAO;CAC1E,CACF;AACF"}
@@ -0,0 +1,71 @@
1
+ import { MapChunkHitbox, MapStreamChunk, MapStreamManifest, MapStreamPacket, RpgProvider } from '@rpgjs/common';
2
+ import { MapData, LoadMapOptions } from './loadMap';
3
+ export interface ClientMapStreamingAdapter<TManifestData = unknown, TChunkData = unknown, TState = unknown> {
4
+ /** CanvasEngine component that renders the provider-specific state. */
5
+ component: unknown;
6
+ /** Create empty client render state from public manifest data. */
7
+ createState(manifest: MapStreamManifest<TManifestData>): TState;
8
+ /** Apply one disclosed render chunk to the state. */
9
+ applyChunk(state: TState, chunk: MapStreamChunk<TChunkData>): void;
10
+ /** Remove one chunk that left the retention window. */
11
+ removeChunk(state: TState, key: string): void;
12
+ /** Return the serializable/component-ready map value exposed to CanvasEngine. */
13
+ getData(state: TState): unknown;
14
+ /** Optionally derive component parameters from the public manifest. */
15
+ getParams?(manifest: MapStreamManifest<TManifestData>): Record<string, unknown>;
16
+ }
17
+ export interface ClientMapStreamingOptions<TManifestData = unknown, TChunkData = unknown, TState = unknown> {
18
+ /** Format adapter paired with the server compiler. */
19
+ adapter: ClientMapStreamingAdapter<TManifestData, TChunkData, TState>;
20
+ /** Optional direct loader used only by standalone RPG mode. */
21
+ directLoad?: LoadMapOptions;
22
+ /** Maximum wait for the initial authoritative manifest. Defaults to 10 seconds. */
23
+ timeoutMs?: number;
24
+ }
25
+ type StreamingPhysicsMap = {
26
+ data: {
27
+ (): MapData | null;
28
+ set(value: MapData): void;
29
+ };
30
+ replaceStreamedStaticHitboxes(namespace: string, hitboxes: MapChunkHitbox[]): void;
31
+ clearStreamedStaticHitboxes(namespace: string): void;
32
+ };
33
+ export declare class MapStreamClientController<TManifestData, TChunkData, TState> {
34
+ private readonly adapter;
35
+ private state;
36
+ private manifest;
37
+ private readonly chunks;
38
+ private attachedMap?;
39
+ constructor(adapter: ClientMapStreamingAdapter<TManifestData, TChunkData, TState>, manifest: MapStreamManifest<TManifestData>);
40
+ get revision(): string;
41
+ reset(manifest: MapStreamManifest<TManifestData>): void;
42
+ receive(packet: MapStreamPacket<TManifestData, TChunkData>): void;
43
+ toMapData(): MapData;
44
+ attach(map: StreamingPhysicsMap): void;
45
+ detach(): void;
46
+ private publish;
47
+ private updatePredictionBoundary;
48
+ }
49
+ /**
50
+ * Provide a transport-neutral client map loader backed by authoritative chunks.
51
+ * Standalone mode may keep a direct loader while MMORPG mode never fetches the
52
+ * private source map.
53
+ *
54
+ * @param options - Client adapter, optional standalone loader, and timeout.
55
+ * @returns Dependency-injection providers for the RPGJS map loader.
56
+ *
57
+ * @example
58
+ * ```ts
59
+ * provideClientMapStreaming({
60
+ * adapter: {
61
+ * component: MyMap,
62
+ * createState: (manifest) => ({ manifest, chunks: new Map() }),
63
+ * applyChunk: (state, chunk) => state.chunks.set(chunk.key, chunk.renderData),
64
+ * removeChunk: (state, key) => state.chunks.delete(key),
65
+ * getData: (state) => state,
66
+ * },
67
+ * })
68
+ * ```
69
+ */
70
+ export declare function provideClientMapStreaming<TManifestData, TChunkData, TState>(options: ClientMapStreamingOptions<TManifestData, TChunkData, TState>): RpgProvider[];
71
+ export {};