@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.
- package/dist/Gui/DialogGui.d.ts +5 -0
- package/dist/Gui/GameoverGui.d.ts +23 -0
- package/dist/Gui/Gui.d.ts +6 -0
- package/dist/Gui/MenuGui.d.ts +22 -3
- package/dist/Gui/NotificationGui.d.ts +1 -2
- package/dist/Gui/SaveLoadGui.d.ts +13 -0
- package/dist/Gui/ShopGui.d.ts +28 -3
- package/dist/Gui/TitleGui.d.ts +23 -0
- package/dist/Gui/index.d.ts +10 -1
- package/dist/Player/BattleManager.d.ts +34 -12
- package/dist/Player/ClassManager.d.ts +46 -13
- package/dist/Player/ComponentManager.d.ts +123 -0
- package/dist/Player/Components.d.ts +345 -0
- package/dist/Player/EffectManager.d.ts +86 -0
- package/dist/Player/ElementManager.d.ts +104 -0
- package/dist/Player/GoldManager.d.ts +22 -0
- package/dist/Player/GuiManager.d.ts +259 -0
- package/dist/Player/ItemFixture.d.ts +6 -0
- package/dist/Player/ItemManager.d.ts +450 -9
- package/dist/Player/MoveManager.d.ts +324 -69
- package/dist/Player/ParameterManager.d.ts +344 -14
- package/dist/Player/Player.d.ts +460 -8
- package/dist/Player/SkillManager.d.ts +197 -15
- package/dist/Player/StateManager.d.ts +89 -25
- package/dist/Player/VariableManager.d.ts +74 -0
- package/dist/RpgServer.d.ts +502 -64
- package/dist/RpgServerEngine.d.ts +2 -1
- package/dist/decorators/event.d.ts +46 -0
- package/dist/decorators/map.d.ts +287 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +21653 -20900
- package/dist/index.js.map +1 -1
- package/dist/logs/log.d.ts +2 -3
- package/dist/module.d.ts +43 -1
- package/dist/presets/index.d.ts +0 -9
- package/dist/rooms/BaseRoom.d.ts +132 -0
- package/dist/rooms/lobby.d.ts +10 -2
- package/dist/rooms/map.d.ts +1236 -17
- package/dist/services/save.d.ts +43 -0
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/localStorage.d.ts +23 -0
- package/package.json +14 -10
- package/src/Gui/DialogGui.ts +19 -4
- package/src/Gui/GameoverGui.ts +39 -0
- package/src/Gui/Gui.ts +23 -1
- package/src/Gui/MenuGui.ts +155 -6
- package/src/Gui/NotificationGui.ts +1 -2
- package/src/Gui/SaveLoadGui.ts +60 -0
- package/src/Gui/ShopGui.ts +146 -16
- package/src/Gui/TitleGui.ts +39 -0
- package/src/Gui/index.ts +15 -2
- package/src/Player/BattleManager.ts +91 -49
- package/src/Player/ClassManager.ts +118 -50
- package/src/Player/ComponentManager.ts +425 -19
- package/src/Player/Components.ts +380 -0
- package/src/Player/EffectManager.ts +81 -44
- package/src/Player/ElementManager.ts +109 -86
- package/src/Player/GoldManager.ts +32 -35
- package/src/Player/GuiManager.ts +308 -150
- package/src/Player/ItemFixture.ts +4 -5
- package/src/Player/ItemManager.ts +774 -355
- package/src/Player/MoveManager.ts +1544 -774
- package/src/Player/ParameterManager.ts +546 -104
- package/src/Player/Player.ts +1163 -88
- package/src/Player/SkillManager.ts +520 -195
- package/src/Player/StateManager.ts +170 -182
- package/src/Player/VariableManager.ts +101 -63
- package/src/RpgServer.ts +525 -63
- package/src/core/context.ts +1 -0
- package/src/decorators/event.ts +61 -0
- package/src/decorators/map.ts +327 -0
- package/src/index.ts +11 -1
- package/src/logs/log.ts +10 -3
- package/src/module.ts +126 -3
- package/src/presets/index.ts +1 -10
- package/src/rooms/BaseRoom.ts +232 -0
- package/src/rooms/lobby.ts +25 -7
- package/src/rooms/map.ts +2502 -194
- package/src/services/save.ts +147 -0
- package/src/storage/index.ts +1 -0
- package/src/storage/localStorage.ts +76 -0
- package/tests/battle.spec.ts +375 -0
- package/tests/change-map.spec.ts +72 -0
- package/tests/class.spec.ts +274 -0
- package/tests/effect.spec.ts +219 -0
- package/tests/element.spec.ts +221 -0
- package/tests/event.spec.ts +80 -0
- package/tests/gold.spec.ts +99 -0
- package/tests/item.spec.ts +609 -0
- package/tests/module.spec.ts +38 -0
- package/tests/move.spec.ts +601 -0
- package/tests/player-param.spec.ts +28 -0
- package/tests/prediction-reconciliation.spec.ts +182 -0
- package/tests/random-move.spec.ts +65 -0
- package/tests/skill.spec.ts +658 -0
- package/tests/state.spec.ts +467 -0
- package/tests/variable.spec.ts +185 -0
- package/tests/world-maps.spec.ts +896 -0
- package/vite.config.ts +16 -0
- package/dist/Player/Event.d.ts +0 -0
- 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
|
+
}
|
package/src/rooms/lobby.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
}
|