@rpgjs/client 5.0.0-beta.26 → 5.0.0-beta.28

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 (135) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/dist/Game/AnimationManager.js.map +1 -1
  3. package/dist/Game/ClientVisuals.js.map +1 -1
  4. package/dist/Game/Map.js +2 -2
  5. package/dist/Game/Map.js.map +1 -1
  6. package/dist/Game/Object.js +1 -1
  7. package/dist/Game/Object.js.map +1 -1
  8. package/dist/Game/ProjectileManager.js.map +1 -1
  9. package/dist/Gui/Gui.d.ts +36 -17
  10. package/dist/Gui/Gui.js +40 -22
  11. package/dist/Gui/Gui.js.map +1 -1
  12. package/dist/Gui/NotificationManager.js.map +1 -1
  13. package/dist/RpgClient.d.ts +3 -64
  14. package/dist/RpgClientEngine.js +1 -1
  15. package/dist/RpgClientEngine.js.map +1 -1
  16. package/dist/_virtual/{_@oxc-project_runtime@0.133.0 → _@oxc-project_runtime@0.139.0}/helpers/esm/decorate.js +2 -2
  17. package/dist/_virtual/{_@oxc-project_runtime@0.133.0 → _@oxc-project_runtime@0.139.0}/helpers/esm/decorateMetadata.js +2 -2
  18. package/dist/components/animations/animation.ce.js.map +1 -1
  19. package/dist/components/animations/fx.ce.js.map +1 -1
  20. package/dist/components/animations/hit.ce.js.map +1 -1
  21. package/dist/components/character-hitbox.js +1 -1
  22. package/dist/components/character.ce.js +3 -1
  23. package/dist/components/character.ce.js.map +1 -1
  24. package/dist/components/dynamics/bar.ce.js.map +1 -1
  25. package/dist/components/dynamics/image.ce.js.map +1 -1
  26. package/dist/components/dynamics/parse-value.js +1 -1
  27. package/dist/components/dynamics/parse-value.js.map +1 -1
  28. package/dist/components/dynamics/shape-utils.js +1 -1
  29. package/dist/components/dynamics/shape.ce.js.map +1 -1
  30. package/dist/components/dynamics/text.ce.js.map +1 -1
  31. package/dist/components/gui/box.ce.js.map +1 -1
  32. package/dist/components/gui/dialogbox/index.ce.js.map +1 -1
  33. package/dist/components/gui/gameover.ce.js.map +1 -1
  34. package/dist/components/gui/hud/hud.ce.js.map +1 -1
  35. package/dist/components/gui/index.js +17 -16
  36. package/dist/components/gui/input-field.ce.js.map +1 -1
  37. package/dist/components/gui/input.ce.js.map +1 -1
  38. package/dist/components/gui/menu/equip-menu.ce.js +1 -1
  39. package/dist/components/gui/menu/equip-menu.ce.js.map +1 -1
  40. package/dist/components/gui/menu/exit-menu.ce.js.map +1 -1
  41. package/dist/components/gui/menu/items-menu.ce.js.map +1 -1
  42. package/dist/components/gui/menu/main-menu.ce.js.map +1 -1
  43. package/dist/components/gui/menu/options-menu.ce.js.map +1 -1
  44. package/dist/components/gui/menu/skills-menu.ce.js.map +1 -1
  45. package/dist/components/gui/mobile/mobile.ce.js.map +1 -1
  46. package/dist/components/gui/notification/notification.ce.js.map +1 -1
  47. package/dist/components/gui/save-load.ce.js.map +1 -1
  48. package/dist/components/gui/shop/shop.ce.js +4 -2
  49. package/dist/components/gui/shop/shop.ce.js.map +1 -1
  50. package/dist/components/gui/title-screen.ce.js.map +1 -1
  51. package/dist/components/index.js +4 -3
  52. package/dist/components/interaction-components.ce.js.map +1 -1
  53. package/dist/components/player-components-utils.js +1 -1
  54. package/dist/components/player-components-utils.js.map +1 -1
  55. package/dist/components/player-components.ce.js.map +1 -1
  56. package/dist/components/prebuilt/hp-bar.ce.js.map +1 -1
  57. package/dist/components/prebuilt/index.js +3 -2
  58. package/dist/components/prebuilt/light-halo.ce.js.map +1 -1
  59. package/dist/components/scenes/canvas.ce.js.map +1 -1
  60. package/dist/components/scenes/draw-map.ce.js.map +1 -1
  61. package/dist/components/scenes/event-layer.ce.js.map +1 -1
  62. package/dist/components/scenes/weather-tick-lifecycle.js.map +1 -1
  63. package/dist/core/inject.d.ts +4 -4
  64. package/dist/core/inject.js.map +1 -1
  65. package/dist/core/setup.d.ts +3 -3
  66. package/dist/core/setup.js +1 -1
  67. package/dist/core/setup.js.map +1 -1
  68. package/dist/core/setup.spec.d.ts +1 -0
  69. package/dist/i18n.d.ts +1 -1
  70. package/dist/index.d.ts +1 -1
  71. package/dist/index.js +1 -2
  72. package/dist/module.d.ts +2 -2
  73. package/dist/module.js.map +1 -1
  74. package/dist/node_modules/.pnpm/@signe_di@3.1.0/node_modules/@signe/di/dist/index.js +1 -1
  75. package/dist/node_modules/.pnpm/@signe_reactive@3.1.0/node_modules/@signe/reactive/dist/index.js +1 -1
  76. package/dist/node_modules/.pnpm/@signe_reactive@3.1.0/node_modules/@signe/reactive/dist/index.js.map +1 -1
  77. package/dist/node_modules/.pnpm/@signe_room@3.1.0_react@19.2.7/node_modules/@signe/room/dist/index.js +2 -2
  78. package/dist/node_modules/.pnpm/@signe_room@3.1.0_react@19.2.7/node_modules/@signe/room/dist/index.js.map +1 -1
  79. package/dist/node_modules/.pnpm/@signe_sync@3.1.0_react@19.2.7/node_modules/@signe/sync/dist/client/index.js +1 -1
  80. package/dist/node_modules/.pnpm/@signe_sync@3.1.0_react@19.2.7/node_modules/@signe/sync/dist/index.js +1 -1
  81. package/dist/node_modules/.pnpm/@signe_sync@3.1.0_react@19.2.7/node_modules/@signe/sync/dist/index.js.map +1 -1
  82. package/dist/node_modules/.pnpm/partysocket@1.2.0_react@19.2.7/node_modules/partysocket/dist/index.js +2 -2
  83. package/dist/node_modules/.pnpm/partysocket@1.2.0_react@19.2.7/node_modules/partysocket/dist/index.js.map +1 -1
  84. package/dist/node_modules/.pnpm/partysocket@1.2.0_react@19.2.7/node_modules/partysocket/dist/ws.js +1 -1
  85. package/dist/node_modules/.pnpm/partysocket@1.2.0_react@19.2.7/node_modules/partysocket/dist/ws.js.map +1 -1
  86. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/index.js +7 -7
  87. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/ZodError.js.map +1 -1
  88. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/errors.js +1 -1
  89. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js +2 -2
  90. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/errorUtil.js.map +1 -1
  91. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js.map +1 -1
  92. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/util.js.map +1 -1
  93. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js +1 -1
  94. package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js.map +1 -1
  95. package/dist/presets/faceset.js.map +1 -1
  96. package/dist/presets/rmspritesheet.js.map +1 -1
  97. package/dist/public-api-types.spec.d.ts +1 -0
  98. package/dist/services/AbstractSocket.d.ts +3 -3
  99. package/dist/services/AbstractSocket.js.map +1 -1
  100. package/dist/services/cameraFollow.js +1 -1
  101. package/dist/services/cameraFollow.js.map +1 -1
  102. package/dist/services/interactions.js.map +1 -1
  103. package/dist/services/loadMap.d.ts +3 -7
  104. package/dist/services/loadMap.js +2 -2
  105. package/dist/services/loadMap.js.map +1 -1
  106. package/dist/services/mapStreaming.d.ts +2 -16
  107. package/dist/services/mapStreaming.js.map +1 -1
  108. package/dist/services/mmorpg-connection.js +1 -1
  109. package/dist/services/mmorpg-connection.js.map +1 -1
  110. package/dist/services/mmorpg.d.ts +4 -26
  111. package/dist/services/mmorpg.js.map +1 -1
  112. package/dist/services/pointerContext.js +2 -1
  113. package/dist/services/pointerContext.js.map +1 -1
  114. package/dist/services/save.js.map +1 -1
  115. package/dist/services/standalone.d.ts +2 -98
  116. package/dist/services/standalone.js.map +1 -1
  117. package/dist/utils/getEntityProp.js.map +1 -1
  118. package/dist/utils/syncHitbox.js.map +1 -1
  119. package/package.json +10 -10
  120. package/src/Game/Object.spec.ts +1 -1
  121. package/src/Game/Object.ts +4 -4
  122. package/src/Gui/Gui.spec.ts +26 -1
  123. package/src/Gui/Gui.ts +104 -40
  124. package/src/RpgClient.ts +2 -63
  125. package/src/core/inject.ts +6 -5
  126. package/src/core/setup.spec.ts +35 -0
  127. package/src/core/setup.ts +8 -6
  128. package/src/index.ts +12 -1
  129. package/src/module.ts +2 -2
  130. package/src/public-api-types.spec.ts +37 -0
  131. package/src/services/AbstractSocket.ts +2 -2
  132. package/src/services/loadMap.ts +8 -8
  133. package/src/services/mapStreaming.ts +7 -5
  134. package/src/services/mmorpg.ts +6 -7
  135. package/src/services/standalone.ts +4 -5
package/src/Gui/Gui.ts CHANGED
@@ -1,16 +1,28 @@
1
1
  import { Context, inject } from "@signe/di";
2
- import { signal, Signal, WritableSignal } from "canvasengine";
2
+ import { signal, Signal, WritableSignal, type ComponentFunction } from "canvasengine";
3
3
  import { AbstractWebsocket, WebSocketToken } from "../services/AbstractSocket";
4
4
  import { DialogboxComponent, ShopComponent, SaveLoadComponent, MainMenuComponent, NotificationComponent, TitleScreenComponent, GameoverComponent, InputComponent } from "../components/gui";
5
5
  import { combineLatest, Subscription } from "rxjs";
6
- import { PrebuiltGui } from "@rpgjs/common";
6
+ import { PrebuiltGui, type RpgContext } from "@rpgjs/common";
7
7
 
8
- interface GuiOptions {
8
+ export type GuiRenderer = "canvas" | "vue";
9
+
10
+ export type GuiComponent = ComponentFunction | object;
11
+
12
+ export interface GuiRegistration<
13
+ TData = unknown,
14
+ TComponent extends GuiComponent = GuiComponent,
15
+ > {
9
16
  name?: string;
10
17
  id?: string;
11
- component?: any;
18
+ component: TComponent;
19
+ /**
20
+ * Renderer responsible for this component. CanvasEngine is the v5 default.
21
+ * `@rpgjs/vue` registers the official DOM overlay renderer.
22
+ */
23
+ renderer?: GuiRenderer;
12
24
  display?: boolean;
13
- data?: any;
25
+ data?: TData;
14
26
  /**
15
27
  * Auto display the GUI when added to the system
16
28
  * @default false
@@ -21,7 +33,7 @@ interface GuiOptions {
21
33
  * The GUI will only display when all dependencies are resolved (!= undefined)
22
34
  * @returns Array of Signal dependencies
23
35
  */
24
- dependencies?: () => Signal[];
36
+ dependencies?: () => Signal<unknown>[];
25
37
  /**
26
38
  * Attach the GUI to sprites instead of displaying globally
27
39
  * When true, the GUI will be rendered in character.ce for each sprite
@@ -34,30 +46,42 @@ interface GuiOptions {
34
46
  rpgAttachToSprite?: boolean;
35
47
  }
36
48
 
37
- export interface GuiInstance {
49
+ export type GuiEntry<TData = unknown> =
50
+ | GuiRegistration<TData>
51
+ | GuiComponent;
52
+
53
+ export interface GuiInstance<
54
+ TData = unknown,
55
+ TComponent extends GuiComponent = GuiComponent,
56
+ > {
38
57
  name: string;
39
- component: any;
58
+ component: TComponent;
59
+ renderer: GuiRenderer;
40
60
  display: WritableSignal<boolean>;
41
- data: WritableSignal<any>;
61
+ data: WritableSignal<TData>;
42
62
  openId?: string;
43
63
  autoDisplay: boolean;
44
- dependencies?: Signal[];
64
+ dependencies?: Signal<unknown>[];
45
65
  subscription?: Subscription;
46
66
  attachToSprite?: boolean;
47
67
  }
48
68
 
49
- type GuiState = {
69
+ export type GuiRenderState<
70
+ TData = unknown,
71
+ TComponent extends GuiComponent = GuiComponent,
72
+ > = {
50
73
  name: string;
51
- component: any;
74
+ component: TComponent;
75
+ renderer: GuiRenderer;
52
76
  display: boolean;
53
- data: any;
77
+ data: TData;
54
78
  openId?: string;
55
79
  attachToSprite: boolean;
56
80
  };
57
81
 
58
82
  type VueGuiBridge = {
59
- updateGuiState?: (state: GuiState) => void;
60
- initializeGuiStates?: (states: GuiState[]) => void;
83
+ updateGuiState?: (state: GuiRenderState) => void;
84
+ initializeGuiStates?: (states: GuiRenderState[]) => void;
61
85
  };
62
86
 
63
87
  interface GuiAction {
@@ -145,40 +169,48 @@ export class RpgGui {
145
169
  */
146
170
  attachedGuiDisplayState = signal<Record<string, boolean>>({});
147
171
 
148
- constructor(private context: Context) {
149
- this.webSocket = inject(context, WebSocketToken);
172
+ constructor(private context: RpgContext) {
173
+ this.webSocket = inject(context as Context, WebSocketToken);
150
174
  this.add({
151
175
  name: "rpg-dialog",
152
176
  component: DialogboxComponent,
177
+ renderer: "canvas",
153
178
  });
154
179
  this.add({
155
180
  name: PrebuiltGui.MainMenu,
156
181
  component: MainMenuComponent,
182
+ renderer: "canvas",
157
183
  });
158
184
  this.add({
159
185
  name: PrebuiltGui.Shop,
160
186
  component: ShopComponent,
187
+ renderer: "canvas",
161
188
  });
162
189
  this.add({
163
190
  name: PrebuiltGui.Notification,
164
191
  component: NotificationComponent,
192
+ renderer: "canvas",
165
193
  autoDisplay: true,
166
194
  });
167
195
  this.add({
168
196
  name: PrebuiltGui.Save,
169
197
  component: SaveLoadComponent,
198
+ renderer: "canvas",
170
199
  });
171
200
  this.add({
172
201
  name: PrebuiltGui.TitleScreen,
173
202
  component: TitleScreenComponent,
203
+ renderer: "canvas",
174
204
  });
175
205
  this.add({
176
206
  name: PrebuiltGui.Gameover,
177
207
  component: GameoverComponent,
208
+ renderer: "canvas",
178
209
  });
179
210
  this.add({
180
211
  name: PrebuiltGui.Input,
181
212
  component: InputComponent,
213
+ renderer: "canvas",
182
214
  });
183
215
 
184
216
  this.registerOptimisticReducer(PrebuiltGui.MainMenu, mainMenuOptimisticReducer);
@@ -311,21 +343,24 @@ export class RpgGui {
311
343
  * });
312
344
  * ```
313
345
  */
314
- add(gui: GuiOptions | any) {
315
- const component = this.resolveComponent(gui);
316
- const guiId = this.resolveGuiId(gui, component);
346
+ add(gui: GuiEntry) {
347
+ const registration = this.resolveRegistration(gui);
348
+ const component = registration.component;
349
+ const guiId = registration.name || registration.id || this.resolveComponentName(component);
317
350
  if (!guiId) {
318
351
  throw new Error("GUI must have a name or id");
319
352
  }
320
- const attachToSprite = this.resolveAttachToSprite(gui, component);
353
+ const attachToSprite = this.resolveAttachToSprite(registration, component);
354
+ const renderer = registration.renderer ?? this.resolveLegacyRenderer(component);
321
355
  const guiInstance: GuiInstance = {
322
356
  name: guiId,
323
357
  component,
324
- display: signal<boolean>(gui.display || false),
325
- data: signal<any>(gui.data || {}),
358
+ renderer,
359
+ display: signal<boolean>(registration.display || false),
360
+ data: signal(registration.data ?? {}),
326
361
  openId: undefined,
327
- autoDisplay: gui.autoDisplay || false,
328
- dependencies: gui.dependencies ? gui.dependencies() : [],
362
+ autoDisplay: registration.autoDisplay || false,
363
+ dependencies: registration.dependencies ? registration.dependencies() : [],
329
364
  attachToSprite,
330
365
  };
331
366
 
@@ -342,7 +377,7 @@ export class RpgGui {
342
377
  this._initializeVueComponents();
343
378
 
344
379
  if (guiInstance.autoDisplay) {
345
- this.display(guiId, gui.data);
380
+ this.display(guiId, registration.data);
346
381
  } else {
347
382
  this._notifyVueGui(guiId, guiInstance.display(), guiInstance.data());
348
383
  }
@@ -354,8 +389,8 @@ export class RpgGui {
354
389
  this._initializeVueComponents();
355
390
 
356
391
  // Auto display if enabled and it's a CanvasEngine component
357
- if (guiInstance.autoDisplay && typeof gui.component === 'function') {
358
- this.display(guiId, gui.data);
392
+ if (guiInstance.autoDisplay && typeof component === 'function') {
393
+ this.display(guiId, registration.data);
359
394
  }
360
395
  }
361
396
 
@@ -400,15 +435,15 @@ export class RpgGui {
400
435
  return this.attachedGuiDisplayState()[playerId] === true;
401
436
  }
402
437
 
403
- get(id: string): GuiInstance | undefined {
438
+ get<TData = unknown>(id: string): GuiInstance<TData> | undefined {
404
439
  // Check CanvasEngine GUIs first
405
440
  const canvasGui = this.gui()[id];
406
441
  if (canvasGui) {
407
- return canvasGui;
442
+ return canvasGui as GuiInstance<TData>;
408
443
  }
409
444
 
410
445
  // Check Vue GUIs in extraGuis
411
- return this.extraGuis.find(gui => gui.name === id);
446
+ return this.extraGuis.find(gui => gui.name === id) as GuiInstance<TData> | undefined;
412
447
  }
413
448
 
414
449
  exists(id: string): boolean {
@@ -447,7 +482,12 @@ export class RpgGui {
447
482
  * gui.display('shop', { shopId: 1 }, [playerSignal, shopSignal]);
448
483
  * ```
449
484
  */
450
- display(id: string, data = {}, dependencies: Signal[] = [], openId?: string) {
485
+ display(
486
+ id: string,
487
+ data: unknown = {},
488
+ dependencies: Signal<unknown>[] = [],
489
+ openId?: string,
490
+ ) {
451
491
  if (!this.exists(id)) {
452
492
  throw throwError(id);
453
493
  }
@@ -595,7 +635,7 @@ export class RpgGui {
595
635
  }
596
636
 
597
637
  private isVueComponentInstance(gui: GuiInstance) {
598
- return typeof gui.component !== "function";
638
+ return gui.renderer === "vue";
599
639
  }
600
640
 
601
641
  private removeCanvasGui(guiId: string) {
@@ -614,22 +654,46 @@ export class RpgGui {
614
654
  }
615
655
  }
616
656
 
617
- private resolveComponent(gui: GuiOptions | any) {
618
- return gui?.component ?? gui;
657
+ private resolveRegistration(gui: GuiEntry): GuiRegistration {
658
+ if (
659
+ typeof gui === "object"
660
+ && gui !== null
661
+ && "component" in gui
662
+ ) {
663
+ return gui as GuiRegistration;
664
+ }
665
+ return { component: gui };
666
+ }
667
+
668
+ private resolveComponentName(component: GuiComponent): string | undefined {
669
+ const named = component as { name?: unknown; __name?: unknown };
670
+ if (typeof named.name === "string" && named.name.length > 0) return named.name;
671
+ if (typeof named.__name === "string" && named.__name.length > 0) return named.__name;
672
+ return undefined;
619
673
  }
620
674
 
621
- private resolveGuiId(gui: GuiOptions | any, component: any) {
622
- return gui?.name || gui?.id || component?.name || component?.__name;
675
+ private resolveLegacyRenderer(component: GuiComponent): GuiRenderer {
676
+ return typeof component === "function" ? "canvas" : "vue";
623
677
  }
624
678
 
625
- private resolveAttachToSprite(gui: GuiOptions | any, component: any) {
626
- return !!(gui?.attachToSprite || gui?.rpgAttachToSprite || component?.attachToSprite || component?.rpgAttachToSprite);
679
+ private resolveAttachToSprite(gui: GuiRegistration, component: GuiComponent) {
680
+ const componentOptions = component as {
681
+ attachToSprite?: unknown;
682
+ rpgAttachToSprite?: unknown;
683
+ };
684
+ return !!(
685
+ gui.attachToSprite
686
+ || gui.rpgAttachToSprite
687
+ || componentOptions.attachToSprite
688
+ || componentOptions.rpgAttachToSprite
689
+ );
627
690
  }
628
691
 
629
- private toGuiState(gui: GuiInstance, display = gui.display(), data = gui.data()): GuiState {
692
+ private toGuiState(gui: GuiInstance, display = gui.display(), data = gui.data()): GuiRenderState {
630
693
  return {
631
694
  name: gui.name,
632
695
  component: gui.component,
696
+ renderer: gui.renderer,
633
697
  display,
634
698
  data,
635
699
  openId: gui.openId,
package/src/RpgClient.ts CHANGED
@@ -13,6 +13,7 @@ import type {
13
13
  RpgInteractionBehavior,
14
14
  RpgInteractionMatcher,
15
15
  } from './services/interactions'
16
+ import type { GuiEntry } from './Gui/Gui'
16
17
 
17
18
  type RpgClass<T = any> = new (...args: any[]) => T
18
19
  type RpgComponent = RpgClientObject
@@ -570,69 +571,7 @@ export interface RpgClient {
570
571
  * @prop {Array<GuiOptions>} [gui]
571
572
  * @memberof RpgClient
572
573
  * */
573
- gui?: ({
574
- id: string,
575
- component: ComponentFunction,
576
- /**
577
- * Auto display the GUI when added to the system
578
- * @default false
579
- */
580
- autoDisplay?: boolean,
581
- /**
582
- * Function that returns an array of Signal dependencies
583
- * The GUI will only display when all dependencies are resolved (!= undefined)
584
- */
585
- dependencies?: () => Signal[]
586
- /**
587
- * Attach the GUI to sprites instead of displaying globally
588
- *
589
- * When set to `true`, the GUI component will be rendered directly on each sprite
590
- * in the game world, rather than being displayed as a fixed overlay on the screen.
591
- * This is useful for tooltips, health bars, name tags, or any UI element that
592
- * should follow a specific sprite.
593
- *
594
- * The GUI will be rendered in the `character.ce` component for each sprite.
595
- * You can control the display state of attached GUIs from the server side using
596
- * `player.showAttachedGui()` and `player.hideAttachedGui()` methods.
597
- *
598
- * @default false
599
- * @example
600
- * ```ts
601
- * import { defineModule, RpgClient } from '@rpgjs/client'
602
- * import TooltipComponent from './tooltip.ce'
603
- *
604
- * defineModule<RpgClient>({
605
- * gui: [
606
- * {
607
- * id: "my-tooltip",
608
- * component: TooltipComponent,
609
- * attachToSprite: true
610
- * }
611
- * ]
612
- * })
613
- * ```
614
- *
615
- * On the server side, control the display:
616
- * ```ts
617
- * // Show the tooltip for this player
618
- * player.showAttachedGui()
619
- *
620
- * // Hide the tooltip for this player
621
- * player.hideAttachedGui()
622
- *
623
- * // Show tooltip for other players
624
- * player.showAttachedGui([otherPlayer1, otherPlayer2])
625
- * ```
626
- */
627
- attachToSprite?: boolean
628
- /**
629
- * Vue v4 compatibility alias for `attachToSprite`.
630
- *
631
- * Prefer `attachToSprite` in v5 projects. This is read by `@rpgjs/vue`
632
- * for Vue GUI components migrated from the v4 GUI API.
633
- */
634
- rpgAttachToSprite?: boolean
635
- } | any)[],
574
+ gui?: GuiEntry[],
636
575
 
637
576
  /**
638
577
  * Array containing the list of sounds
@@ -1,17 +1,18 @@
1
1
  import { Context, inject as injector } from "@signe/di";
2
+ import type { RpgContext } from "@rpgjs/common";
2
3
 
3
- export let context: Context | null = null
4
+ export let context: RpgContext | null = null
4
5
 
5
- export function inject<T>(service: (new (...args: any[]) => T) | string, _context?: Context): T {
6
+ export function inject<T>(service: (new (...args: any[]) => T) | string, _context?: RpgContext): T {
6
7
  const c = _context ?? context
7
8
  if (!c) throw new Error("Context is not set. use setInject() to set the context");
8
- return injector(c, service);
9
+ return injector(c as Context, service);
9
10
  }
10
11
 
11
- export function setInject(_context: Context) {
12
+ export function setInject(_context: RpgContext) {
12
13
  context = _context;
13
14
  }
14
15
 
15
16
  export function clearInject() {
16
17
  context = null
17
- }
18
+ }
@@ -0,0 +1,35 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { RpgClientEngine } from "../RpgClientEngine";
3
+ import { inject } from "./inject";
4
+ import { startGame } from "./setup";
5
+
6
+ describe("startGame", () => {
7
+ test("initializes recursively nested RPGJS providers", async () => {
8
+ let engineStarted = false;
9
+
10
+ const context = await startGame({
11
+ providers: [
12
+ [
13
+ [
14
+ {
15
+ provide: "deep-provider",
16
+ useValue: 42,
17
+ },
18
+ ],
19
+ ],
20
+ {
21
+ provide: RpgClientEngine,
22
+ useValue: {
23
+ async start() {
24
+ engineStarted = true;
25
+ },
26
+ },
27
+ },
28
+ ],
29
+ });
30
+
31
+ expect(context.side).toBe("client");
32
+ expect(inject("deep-provider")).toBe(42);
33
+ expect(engineStarted).toBe(true);
34
+ });
35
+ });
package/src/core/setup.ts CHANGED
@@ -1,20 +1,22 @@
1
- import { Context, FactoryProvider, findProvider, findProviders, inject, injector, Providers } from "@signe/di";
1
+ import { Context, inject, injector, type Providers } from "@signe/di";
2
+ import type { RpgContext, RpgProviders } from "@rpgjs/common";
2
3
  import { RpgClientEngine } from "../RpgClientEngine";
3
4
  import { setInject } from "./inject";
4
5
 
5
6
  interface SetupOptions {
6
- providers: Providers;
7
+ providers: RpgProviders;
7
8
  }
8
9
 
9
10
 
10
- export async function startGame(options: SetupOptions) {
11
+ export async function startGame(options: SetupOptions): Promise<RpgContext> {
11
12
  const context = new Context();
12
13
  context['side'] = 'client'
13
- setInject(context);
14
+ setInject(context as unknown as RpgContext);
14
15
 
15
- await injector(context, options.providers);
16
+ const providers = (options.providers as unknown[]).flat(Infinity) as Providers;
17
+ await injector(context, providers);
16
18
 
17
19
  const engine = inject<RpgClientEngine>(context, RpgClientEngine);
18
20
  await engine.start();
19
- return context;
21
+ return context as unknown as RpgContext;
20
22
  }
package/src/index.ts CHANGED
@@ -22,10 +22,21 @@ export * from "./Sound";
22
22
  export * from "./Resource";
23
23
  export * from "./decorators/spritesheet";
24
24
  export * from "./utils/getEntityProp";
25
- export { Context } from "@signe/di";
26
25
  export { KeyboardControls, Input } from "canvasengine";
27
26
  export { Control } from "./services/keyboardControls";
28
27
  export { defineModule } from "@rpgjs/common";
28
+ export type {
29
+ RpgClassProvider,
30
+ RpgContext,
31
+ RpgExistingProvider,
32
+ RpgFactoryProvider,
33
+ RpgProvider,
34
+ RpgProviders,
35
+ RpgProviderToken,
36
+ RpgReadableSignal,
37
+ RpgValueProvider,
38
+ RpgWritableSignal,
39
+ } from "@rpgjs/common";
29
40
  export { RpgClientObject } from "./Game/Object";
30
41
  export { RpgClientPlayer } from "./Game/Player";
31
42
  export { RpgClientEvent } from "./Game/Event";
package/src/module.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { findModules, provideModules, registerI18nMessages } from "@rpgjs/common";
2
- import { FactoryProvider } from "@signe/di";
2
+ import type { RpgFactoryProvider } from "@rpgjs/common";
3
3
  import { RpgClientEngine } from "./RpgClientEngine";
4
4
  import { RpgClient } from "./RpgClient";
5
5
  import { inject } from "@signe/di";
@@ -48,7 +48,7 @@ export type RpgClientModule = RpgClient | (new () => any);
48
48
  * provideClientModules([MyClientModule])
49
49
  * ```
50
50
  */
51
- export function provideClientModules(modules: RpgClientModule[]): FactoryProvider {
51
+ export function provideClientModules(modules: RpgClientModule[]): RpgFactoryProvider {
52
52
  return provideModules(modules, "client", (modules, context) => {
53
53
  const mainModuleClient = findModules(context, 'Client')
54
54
  modules = [...mainModuleClient, ...modules]
@@ -0,0 +1,37 @@
1
+ import { describe, expectTypeOf, test } from "vitest";
2
+ import type { RpgContext, RpgProvider } from "@rpgjs/common";
3
+ import {
4
+ provideMmorpg,
5
+ startGame,
6
+ type GuiRegistration,
7
+ type GuiRenderer,
8
+ } from "./index";
9
+
10
+ describe("client public API types", () => {
11
+ test("bootstrap and providers expose RPGJS-owned contracts", () => {
12
+ expectTypeOf(provideMmorpg({})).toEqualTypeOf<RpgProvider[]>();
13
+ const assertions = (start: typeof startGame) => {
14
+ expectTypeOf(start({ providers: [] })).toEqualTypeOf<Promise<RpgContext>>();
15
+ };
16
+ expectTypeOf(assertions).toBeFunction();
17
+ });
18
+
19
+ test("the Signe Context class is not re-exported", () => {
20
+ // @ts-expect-error Context is available only from direct advanced Signe usage
21
+ type LegacyContext = typeof import("./index")["Context"];
22
+ expectTypeOf<LegacyContext>();
23
+ });
24
+
25
+ test("GUI registrations expose renderer-neutral typed data", () => {
26
+ type DialogData = { message: string };
27
+ const registration: GuiRegistration<DialogData> = {
28
+ id: "dialog",
29
+ component: () => null,
30
+ renderer: "canvas",
31
+ data: { message: "Hello" },
32
+ };
33
+
34
+ expectTypeOf(registration.renderer).toEqualTypeOf<GuiRenderer | undefined>();
35
+ expectTypeOf(registration.data).toEqualTypeOf<DialogData | undefined>();
36
+ });
37
+ });
@@ -1,4 +1,4 @@
1
- import { Context } from "@signe/di";
1
+ import type { RpgContext } from "@rpgjs/common";
2
2
 
3
3
  export const WebSocketToken = "websocket";
4
4
 
@@ -14,7 +14,7 @@ export type WebSocketMode = "standalone" | "mmorpg";
14
14
  export abstract class AbstractWebsocket {
15
15
  readonly mode?: WebSocketMode;
16
16
 
17
- constructor(protected context: Context) {}
17
+ constructor(protected context: RpgContext) {}
18
18
 
19
19
  abstract connection(listeners?: (data: any) => void): Promise<void>;
20
20
  abstract emit(event: string, data: any): void;
@@ -1,5 +1,5 @@
1
1
  import { Context, inject } from "@signe/di";
2
- import { UpdateMapToken, UpdateMapService, type LightingState } from "@rpgjs/common";
2
+ import { UpdateMapToken, UpdateMapService, type LightingState, type RpgContext, type RpgProvider } from "@rpgjs/common";
3
3
  import type { RpgClientMap } from "../Game/Map";
4
4
 
5
5
  export const LoadMapToken = 'LoadMapToken'
@@ -47,8 +47,8 @@ export type LoadMapOptions = (mapId: string) => Promise<MapData> | MapData
47
47
  export class LoadMapService {
48
48
  private updateMapService: UpdateMapService;
49
49
 
50
- constructor(private context: Context, private options: LoadMapOptions) {
51
- if (context['side'] === 'server') {
50
+ constructor(private context: RpgContext, private options: LoadMapOptions) {
51
+ if (context.side === 'server') {
52
52
  return
53
53
  }
54
54
  }
@@ -56,7 +56,7 @@ export class LoadMapService {
56
56
  initialize(): void {}
57
57
 
58
58
  async load(mapId: string) {
59
- this.updateMapService ??= inject(this.context, UpdateMapToken);
59
+ this.updateMapService ??= inject(this.context as Context, UpdateMapToken);
60
60
  const map = await this.options(mapId.replace('map-', ''))
61
61
  await this.updateMapService.update(map);
62
62
  return map;
@@ -154,12 +154,12 @@ export class LoadMapService {
154
154
  * @see {@link LoadMapOptions} for callback function signature
155
155
  * @see {@link MapData} for return data structure
156
156
  */
157
- export function provideLoadMap(options: LoadMapOptions) {
157
+ export function provideLoadMap(options: LoadMapOptions): RpgProvider[] {
158
158
  return [
159
159
  {
160
160
  provide: UpdateMapToken,
161
- useFactory: (context: Context) => {
162
- if (context['side'] === 'client') {
161
+ useFactory: (context: RpgContext) => {
162
+ if (context.side === 'client') {
163
163
  console.warn('UpdateMapToken is not overridden')
164
164
  }
165
165
  return
@@ -167,7 +167,7 @@ export function provideLoadMap(options: LoadMapOptions) {
167
167
  },
168
168
  {
169
169
  provide: LoadMapToken,
170
- useFactory: (context: Context) => new LoadMapService(context, options),
170
+ useFactory: (context: RpgContext) => new LoadMapService(context, options),
171
171
  },
172
172
  ];
173
173
  }
@@ -6,6 +6,8 @@ import {
6
6
  type MapStreamChunk,
7
7
  type MapStreamManifest,
8
8
  type MapStreamPacket,
9
+ type RpgContext,
10
+ type RpgProvider,
9
11
  } from "@rpgjs/common";
10
12
  import { AbstractWebsocket, WebSocketToken } from "./AbstractSocket";
11
13
  import { LoadMapToken, type MapData, type LoadMapOptions } from "./loadMap";
@@ -221,13 +223,13 @@ class MapStreamingLoadMapService<TManifestData, TChunkData, TState> {
221
223
  private updateMap?: UpdateMapService;
222
224
 
223
225
  constructor(
224
- private readonly context: Context,
226
+ private readonly context: RpgContext,
225
227
  private readonly options: ClientMapStreamingOptions<TManifestData, TChunkData, TState>,
226
228
  ) {}
227
229
 
228
230
  initialize(): void {
229
231
  if (this.stream) return;
230
- this.socket = inject<AbstractWebsocket>(this.context, WebSocketToken);
232
+ this.socket = inject<AbstractWebsocket>(this.context as Context, WebSocketToken);
231
233
  this.stream = new MapStreamingClientService(this.socket, this.options);
232
234
  }
233
235
 
@@ -236,7 +238,7 @@ class MapStreamingLoadMapService<TManifestData, TChunkData, TState> {
236
238
  const map = this.socket?.mode === "standalone" && this.options.directLoad
237
239
  ? await this.options.directLoad(mapId.replace(/^map-/, ""))
238
240
  : await this.stream!.load(mapId);
239
- this.updateMap ??= inject<UpdateMapService>(this.context, UpdateMapToken);
241
+ this.updateMap ??= inject<UpdateMapService>(this.context as Context, UpdateMapToken);
240
242
  await this.updateMap.update(map);
241
243
  return map;
242
244
  }
@@ -265,11 +267,11 @@ class MapStreamingLoadMapService<TManifestData, TChunkData, TState> {
265
267
  */
266
268
  export function provideClientMapStreaming<TManifestData, TChunkData, TState>(
267
269
  options: ClientMapStreamingOptions<TManifestData, TChunkData, TState>,
268
- ) {
270
+ ): RpgProvider[] {
269
271
  return [
270
272
  {
271
273
  provide: LoadMapToken,
272
- useFactory: (context: Context) => new MapStreamingLoadMapService(context, options),
274
+ useFactory: (context: RpgContext) => new MapStreamingLoadMapService(context, options),
273
275
  },
274
276
  ];
275
277
  }