@rpgjs/server 5.0.0-alpha.4 → 5.0.0-alpha.41

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 (101) hide show
  1. package/dist/Gui/DialogGui.d.ts +5 -0
  2. package/dist/Gui/GameoverGui.d.ts +23 -0
  3. package/dist/Gui/Gui.d.ts +6 -0
  4. package/dist/Gui/MenuGui.d.ts +22 -3
  5. package/dist/Gui/NotificationGui.d.ts +1 -2
  6. package/dist/Gui/SaveLoadGui.d.ts +13 -0
  7. package/dist/Gui/ShopGui.d.ts +28 -3
  8. package/dist/Gui/TitleGui.d.ts +23 -0
  9. package/dist/Gui/index.d.ts +10 -1
  10. package/dist/Player/BattleManager.d.ts +34 -12
  11. package/dist/Player/ClassManager.d.ts +46 -13
  12. package/dist/Player/ComponentManager.d.ts +123 -0
  13. package/dist/Player/Components.d.ts +345 -0
  14. package/dist/Player/EffectManager.d.ts +86 -0
  15. package/dist/Player/ElementManager.d.ts +104 -0
  16. package/dist/Player/GoldManager.d.ts +22 -0
  17. package/dist/Player/GuiManager.d.ts +259 -0
  18. package/dist/Player/ItemFixture.d.ts +6 -0
  19. package/dist/Player/ItemManager.d.ts +450 -9
  20. package/dist/Player/MoveManager.d.ts +324 -69
  21. package/dist/Player/ParameterManager.d.ts +344 -14
  22. package/dist/Player/Player.d.ts +460 -8
  23. package/dist/Player/SkillManager.d.ts +197 -15
  24. package/dist/Player/StateManager.d.ts +89 -25
  25. package/dist/Player/VariableManager.d.ts +74 -0
  26. package/dist/RpgServer.d.ts +502 -64
  27. package/dist/RpgServerEngine.d.ts +2 -1
  28. package/dist/decorators/event.d.ts +46 -0
  29. package/dist/decorators/map.d.ts +287 -0
  30. package/dist/index.d.ts +10 -0
  31. package/dist/index.js +21653 -20900
  32. package/dist/index.js.map +1 -1
  33. package/dist/logs/log.d.ts +2 -3
  34. package/dist/module.d.ts +43 -1
  35. package/dist/presets/index.d.ts +0 -9
  36. package/dist/rooms/BaseRoom.d.ts +132 -0
  37. package/dist/rooms/lobby.d.ts +10 -2
  38. package/dist/rooms/map.d.ts +1236 -17
  39. package/dist/services/save.d.ts +43 -0
  40. package/dist/storage/index.d.ts +1 -0
  41. package/dist/storage/localStorage.d.ts +23 -0
  42. package/package.json +14 -10
  43. package/src/Gui/DialogGui.ts +19 -4
  44. package/src/Gui/GameoverGui.ts +39 -0
  45. package/src/Gui/Gui.ts +23 -1
  46. package/src/Gui/MenuGui.ts +155 -6
  47. package/src/Gui/NotificationGui.ts +1 -2
  48. package/src/Gui/SaveLoadGui.ts +60 -0
  49. package/src/Gui/ShopGui.ts +146 -16
  50. package/src/Gui/TitleGui.ts +39 -0
  51. package/src/Gui/index.ts +15 -2
  52. package/src/Player/BattleManager.ts +91 -49
  53. package/src/Player/ClassManager.ts +118 -50
  54. package/src/Player/ComponentManager.ts +425 -19
  55. package/src/Player/Components.ts +380 -0
  56. package/src/Player/EffectManager.ts +81 -44
  57. package/src/Player/ElementManager.ts +109 -86
  58. package/src/Player/GoldManager.ts +32 -35
  59. package/src/Player/GuiManager.ts +308 -150
  60. package/src/Player/ItemFixture.ts +4 -5
  61. package/src/Player/ItemManager.ts +774 -355
  62. package/src/Player/MoveManager.ts +1544 -774
  63. package/src/Player/ParameterManager.ts +546 -104
  64. package/src/Player/Player.ts +1163 -88
  65. package/src/Player/SkillManager.ts +520 -195
  66. package/src/Player/StateManager.ts +170 -182
  67. package/src/Player/VariableManager.ts +101 -63
  68. package/src/RpgServer.ts +525 -63
  69. package/src/core/context.ts +1 -0
  70. package/src/decorators/event.ts +61 -0
  71. package/src/decorators/map.ts +327 -0
  72. package/src/index.ts +11 -1
  73. package/src/logs/log.ts +10 -3
  74. package/src/module.ts +126 -3
  75. package/src/presets/index.ts +1 -10
  76. package/src/rooms/BaseRoom.ts +232 -0
  77. package/src/rooms/lobby.ts +25 -7
  78. package/src/rooms/map.ts +2502 -194
  79. package/src/services/save.ts +147 -0
  80. package/src/storage/index.ts +1 -0
  81. package/src/storage/localStorage.ts +76 -0
  82. package/tests/battle.spec.ts +375 -0
  83. package/tests/change-map.spec.ts +72 -0
  84. package/tests/class.spec.ts +274 -0
  85. package/tests/effect.spec.ts +219 -0
  86. package/tests/element.spec.ts +221 -0
  87. package/tests/event.spec.ts +80 -0
  88. package/tests/gold.spec.ts +99 -0
  89. package/tests/item.spec.ts +609 -0
  90. package/tests/module.spec.ts +38 -0
  91. package/tests/move.spec.ts +601 -0
  92. package/tests/player-param.spec.ts +28 -0
  93. package/tests/prediction-reconciliation.spec.ts +182 -0
  94. package/tests/random-move.spec.ts +65 -0
  95. package/tests/skill.spec.ts +658 -0
  96. package/tests/state.spec.ts +467 -0
  97. package/tests/variable.spec.ts +185 -0
  98. package/tests/world-maps.spec.ts +896 -0
  99. package/vite.config.ts +16 -0
  100. package/dist/Player/Event.d.ts +0 -0
  101. package/src/Player/Event.ts +0 -0
@@ -0,0 +1,232 @@
1
+ import { signal } from "@signe/reactive";
2
+ import { inject } from "../core/inject";
3
+ import { context } from "../core/context";
4
+ import { Hooks, ModulesToken } from "@rpgjs/common";
5
+ import { Action } from "@signe/room";
6
+ import { RpgPlayer } from "../Player/Player";
7
+ import { resolveSaveStorageStrategy } from "../services/save";
8
+ import { lastValueFrom } from "rxjs";
9
+
10
+ /**
11
+ * Base class for rooms that need database functionality
12
+ *
13
+ * This class provides common database management functionality that is shared
14
+ * between RpgMap and LobbyRoom. It includes methods for adding and managing
15
+ * items, classes, and other game data in the room's database.
16
+ *
17
+ * ## Architecture
18
+ *
19
+ * Both RpgMap and LobbyRoom need to store game entities (items, classes, skills, etc.)
20
+ * in a database. This base class provides the common implementation to avoid code duplication.
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * class MyCustomRoom extends BaseRoom {
25
+ * // Your custom room implementation
26
+ * }
27
+ * ```
28
+ */
29
+ export abstract class BaseRoom {
30
+
31
+ /**
32
+ * Signal containing the room's database of items, classes, and other game data
33
+ *
34
+ * This database can be dynamically populated using `addInDatabase()` and
35
+ * `removeInDatabase()` methods. It's used to store game entities like items,
36
+ * classes, skills, etc. that are available in this room.
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * // Add data to database
41
+ * room.addInDatabase('Potion', PotionClass);
42
+ *
43
+ * // Access database
44
+ * const potion = room.database()['Potion'];
45
+ * ```
46
+ */
47
+ database = signal({});
48
+
49
+
50
+ async onStart() {
51
+ await lastValueFrom(this.hooks.callHooks("server-databaseHooks-load", this))
52
+ }
53
+
54
+ /**
55
+ * Add data to the room's database
56
+ *
57
+ * Adds an item, class, or other game entity to the room's database.
58
+ * If the ID already exists and `force` is not enabled, the addition is ignored.
59
+ *
60
+ * ## Architecture
61
+ *
62
+ * This method is used by the item management system to store item definitions
63
+ * in the room's database. When a player adds an item, the system first checks
64
+ * if the item exists in the database, and if not, adds it using this method.
65
+ *
66
+ * @param id - Unique identifier for the data
67
+ * @param data - The data to add (can be a class, object, etc.)
68
+ * @param options - Optional configuration
69
+ * @param options.force - If true, overwrites existing data with the same ID
70
+ * @returns `true` if data was added, `false` if it was ignored (ID already exists)
71
+ *
72
+ * @example
73
+ * ```ts
74
+ * // Add a class to the database
75
+ * room.addInDatabase('Potion', PotionClass);
76
+ *
77
+ * // Add an item object to the database
78
+ * room.addInDatabase('custom-item', {
79
+ * name: 'Custom Item',
80
+ * price: 100
81
+ * });
82
+ *
83
+ * // Force overwrite existing data
84
+ * room.addInDatabase('Potion', UpdatedPotionClass, { force: true });
85
+ * ```
86
+ */
87
+ addInDatabase(id: string, data: any, options?: { force?: boolean }): boolean {
88
+ const database = this.database();
89
+
90
+ // Check if ID already exists
91
+ if (database[id] !== undefined && !options?.force) {
92
+ // Ignore the addition if ID exists and force is not enabled
93
+ return false;
94
+ }
95
+
96
+ // Add or overwrite the data
97
+ database[id] = data;
98
+ this.database.set(database);
99
+ return true;
100
+ }
101
+
102
+ /**
103
+ * Remove data from the room's database
104
+ *
105
+ * This method allows you to remove items or data from the room's database.
106
+ *
107
+ * @param id - Unique identifier of the data to remove
108
+ * @returns `true` if data was removed, `false` if ID didn't exist
109
+ *
110
+ * @example
111
+ * ```ts
112
+ * // Remove an item from the database
113
+ * room.removeInDatabase('Potion');
114
+ *
115
+ * // Check if removal was successful
116
+ * const removed = room.removeInDatabase('custom-item');
117
+ * if (removed) {
118
+ * console.log('Item removed successfully');
119
+ * }
120
+ * ```
121
+ */
122
+ removeInDatabase(id: string): boolean {
123
+ const database = this.database();
124
+
125
+ if (database[id] === undefined) {
126
+ return false;
127
+ }
128
+
129
+ delete database[id];
130
+ this.database.set(database);
131
+ return true;
132
+ }
133
+
134
+ /**
135
+ * Get the hooks system for this map
136
+ *
137
+ * Returns the dependency-injected Hooks instance that allows you to trigger
138
+ * and listen to various game events.
139
+ *
140
+ * @returns The Hooks instance for this map
141
+ *
142
+ * @example
143
+ * ```ts
144
+ * // Trigger a custom hook
145
+ * map.hooks.callHooks('custom-event', data).subscribe();
146
+ * ```
147
+ */
148
+ get hooks() {
149
+ return inject<Hooks>(ModulesToken, context);
150
+ }
151
+
152
+ /**
153
+ * Resolve complex snapshot entries (e.g. inventory items) before load.
154
+ */
155
+ async onSessionRestore({ userSnapshot, user }: { userSnapshot: any; user?: RpgPlayer }) {
156
+ if (!userSnapshot) {
157
+ return userSnapshot;
158
+ }
159
+
160
+ let resolvedSnapshot = userSnapshot;
161
+ if (user && typeof (user as any).resolveItemsSnapshot === 'function') {
162
+ resolvedSnapshot = (user as any).resolveItemsSnapshot(resolvedSnapshot, this);
163
+ }
164
+
165
+ if (user && typeof (user as any).resolveSkillsSnapshot === 'function') {
166
+ resolvedSnapshot = (user as any).resolveSkillsSnapshot(resolvedSnapshot, this);
167
+ }
168
+
169
+ if (user && typeof (user as any).resolveStatesSnapshot === 'function') {
170
+ resolvedSnapshot = (user as any).resolveStatesSnapshot(resolvedSnapshot, this);
171
+ }
172
+
173
+ if (user && typeof (user as any).resolveClassSnapshot === 'function') {
174
+ resolvedSnapshot = (user as any).resolveClassSnapshot(resolvedSnapshot, this);
175
+ }
176
+
177
+ if (user && typeof (user as any).resolveEquipmentsSnapshot === 'function') {
178
+ resolvedSnapshot = (user as any).resolveEquipmentsSnapshot(resolvedSnapshot, this);
179
+ }
180
+
181
+ return resolvedSnapshot;
182
+ }
183
+
184
+ @Action('save.list')
185
+ async listSaveSlots(player: RpgPlayer, value: { requestId: string }) {
186
+ const storage = resolveSaveStorageStrategy();
187
+ try {
188
+ const slots = await storage.list(player);
189
+ player.emit('save.list.result', { requestId: value?.requestId, slots });
190
+ return slots;
191
+ } catch (error: any) {
192
+ player.showNotification(error?.message || 'save.list failed');
193
+ return [];
194
+ }
195
+ }
196
+
197
+ @Action('save.save')
198
+ async saveSlot(player: RpgPlayer, value: { requestId: string; index: number; meta?: any }) {
199
+ const storage = resolveSaveStorageStrategy();
200
+ try {
201
+ if (typeof value?.index !== 'number') {
202
+ throw new Error('save.save requires an index');
203
+ }
204
+ const result = await player.save(value.index, value?.meta, { reason: "manual", source: "gui" });
205
+ if (!result) {
206
+ throw new Error('save.save is not allowed');
207
+ }
208
+ const slots = await storage.list(player);
209
+ player.emit('save.save.result', { requestId: value?.requestId, index: result.index, slots });
210
+ } catch (error: any) {
211
+ player.emit('save.error', { requestId: value?.requestId, message: error?.message || 'save.save failed' });
212
+ }
213
+ }
214
+
215
+ @Action('save.load')
216
+ async loadSlot(player: RpgPlayer, value: { requestId: string; index: number }) {
217
+ try {
218
+ if (typeof value?.index !== 'number') {
219
+ throw new Error('save.load requires an index');
220
+ }
221
+ const result = await player.load(value.index, { reason: "load", source: "gui" }, { changeMap: true });
222
+ player.emit('save.load.result', {
223
+ requestId: value?.requestId,
224
+ index: value.index,
225
+ ok: result.ok,
226
+ slot: result.slot
227
+ });
228
+ } catch (error: any) {
229
+ player.emit('save.error', { requestId: value?.requestId, message: error?.message || 'save.load failed' });
230
+ }
231
+ }
232
+ }
@@ -1,24 +1,42 @@
1
1
  import { inject } from "@signe/di";
2
- import { MockConnection, Room } from "@signe/room";
2
+ import { Action, MockConnection, Room } from "@signe/room";
3
3
  import { Hooks, ModulesToken } from "@rpgjs/common";
4
4
  import { context } from "../core/context";
5
5
  import { users } from "@signe/sync";
6
6
  import { signal } from "@signe/reactive";
7
7
  import { RpgPlayer } from "../Player/Player";
8
+ import { BaseRoom } from "./BaseRoom";
9
+ import { buildSaveSlotMeta, resolveSaveStorageStrategy } from "../services/save";
10
+ import { lastValueFrom } from "rxjs";
8
11
 
9
12
  @Room({
10
13
  path: "lobby-{id}",
11
14
  })
12
- export class LobbyRoom {
15
+ export class LobbyRoom extends BaseRoom {
13
16
  @users(RpgPlayer) players = signal({});
17
+ autoSync: boolean = true;
14
18
 
15
- onJoin(player: RpgPlayer, conn: MockConnection) {
19
+ constructor(room) {
20
+ super();
21
+ const isTest = room.env.TEST === 'true' ? true : false;
22
+ if (isTest) {
23
+ this.autoSync = false;
24
+ }
25
+ }
26
+
27
+ async onJoin(player: RpgPlayer, conn: MockConnection) {
16
28
  player.map = this;
17
29
  player.context = context;
18
30
  player.conn = conn;
19
- const hooks = inject<Hooks>(context, ModulesToken);
20
- hooks
21
- .callHooks("server-player-onConnected", player)
22
- .subscribe();
31
+ this.hooks.callHooks("server-player-onConnected", player).subscribe();
32
+ }
33
+
34
+ @Action('gui.interaction')
35
+ async guiInteraction(player: RpgPlayer, value: { guiId: string, name: string, data: any }) {
36
+ const id = value.data.id
37
+ if (id === 'start') {
38
+ player.onGameStart();
39
+ this.hooks.callHooks("server-player-onStart", player).subscribe();
40
+ }
23
41
  }
24
42
  }