@rpgjs/server 5.0.0-alpha.30 → 5.0.0-alpha.32

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rpgjs/server",
3
- "version": "5.0.0-alpha.30",
3
+ "version": "5.0.0-alpha.32",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "publishConfig": {
@@ -11,9 +11,9 @@
11
11
  "license": "MIT",
12
12
  "description": "",
13
13
  "dependencies": {
14
- "@rpgjs/common": "5.0.0-alpha.30",
15
- "@rpgjs/physic": "5.0.0-alpha.30",
16
- "@rpgjs/testing": "5.0.0-alpha.30",
14
+ "@rpgjs/common": "5.0.0-alpha.32",
15
+ "@rpgjs/physic": "5.0.0-alpha.32",
16
+ "@rpgjs/testing": "5.0.0-alpha.32",
17
17
  "@rpgjs/database": "^4.3.0",
18
18
  "@signe/di": "^2.8.2",
19
19
  "@signe/reactive": "^2.8.2",
@@ -119,7 +119,7 @@ export class MenuGui extends Gui {
119
119
  }))
120
120
  const saveLoad = this.buildSaveLoad(options)
121
121
 
122
- return { menus, items, equips: menuEquips, skills, saveLoad, playerStats: buildStats() }
122
+ return { menus, items, equips: menuEquips, skills, saveLoad, playerStats: buildStats(), expForNextlevel: player.expForNextlevel }
123
123
  }
124
124
 
125
125
  private refreshMenu(clientActionId?: string) {
@@ -744,7 +744,8 @@ export function WithMoveManager<TBase extends PlayerCtor>(Base: TBase) {
744
744
  * Move toward a target player or position using AI pathfinding
745
745
  *
746
746
  * Uses the `SeekAvoid` strategy to navigate toward the target while avoiding obstacles.
747
- * The movement speed is based on the player's current `speed` property, scaled appropriately.
747
+ * The movement speed is based on the player's current `speed` and `frequency` settings,
748
+ * scaled appropriately.
748
749
  *
749
750
  * @param target - Target player or position `{ x, y }` to move toward
750
751
  *
@@ -764,10 +765,14 @@ export function WithMoveManager<TBase extends PlayerCtor>(Base: TBase) {
764
765
  const playerId = (this as unknown as PlayerWithMixins).id;
765
766
  const engine = map.physic;
766
767
 
767
- // Calculate maxSpeed based on player's speed
768
+ // Calculate maxSpeed based on player's speed and frequency
768
769
  // Original values: 180 for player target, 80 for position target (with default speed=4)
769
770
  // Factor: 45 for player (180/4), 20 for position (80/4)
770
771
  const playerSpeed = (this as any).speed();
772
+ const rawFrequency = (this as any).frequency;
773
+ const playerFrequency = typeof rawFrequency === 'function' ? rawFrequency() : rawFrequency;
774
+ const frequencyScale = playerFrequency > 0 ? Frequency.High / playerFrequency : 1;
775
+ const normalizedFrequencyScale = Number.isFinite(frequencyScale) && frequencyScale > 0 ? frequencyScale : 1;
771
776
 
772
777
  // Remove ALL movement strategies that could interfere with SeekAvoid
773
778
  // This includes SeekAvoid, Dash, Knockback, and LinearRepulsion
@@ -789,7 +794,7 @@ export function WithMoveManager<TBase extends PlayerCtor>(Base: TBase) {
789
794
  return body;
790
795
  };
791
796
  // Factor 45: with speed=4 gives 180 (original value)
792
- const maxSpeed = playerSpeed * 45;
797
+ const maxSpeed = playerSpeed * 45 * normalizedFrequencyScale;
793
798
  map.moveManager.add(
794
799
  playerId,
795
800
  new SeekAvoid(engine, targetProvider, maxSpeed, 140, 80, 48)
@@ -804,7 +809,7 @@ export function WithMoveManager<TBase extends PlayerCtor>(Base: TBase) {
804
809
  staticTarget.freeze();
805
810
 
806
811
  // Factor 20: with speed=4 gives 80 (original value)
807
- const maxSpeed = playerSpeed * 20;
812
+ const maxSpeed = playerSpeed * 20 * normalizedFrequencyScale;
808
813
  map.moveManager.add(
809
814
  playerId,
810
815
  new SeekAvoid(engine, () => staticTarget, maxSpeed, 140, 80, 48)
@@ -920,8 +925,8 @@ export function WithMoveManager<TBase extends PlayerCtor>(Base: TBase) {
920
925
  this.getActiveMovements().some(s => s instanceof Knockback || s instanceof AdditiveKnockback);
921
926
 
922
927
  const setAnimationName = (name: string): void => {
923
- if (typeof selfAny.setAnimation === 'function') {
924
- selfAny.setAnimation(name);
928
+ if (typeof selfAny.setGraphicAnimation === 'function') {
929
+ selfAny.setGraphicAnimation(name);
925
930
  return;
926
931
  }
927
932
  const animSignal = selfAny.animationName;
@@ -1178,6 +1183,9 @@ export function WithMoveManager<TBase extends PlayerCtor>(Base: TBase) {
1178
1183
  private readonly onStuck?: MoveRoutesOptions['onStuck'];
1179
1184
  private readonly stuckTimeout: number;
1180
1185
  private readonly stuckThreshold: number;
1186
+ private remainingDistance = 0;
1187
+ private segmentDirection: Direction | null = null;
1188
+ private segmentStep = 0;
1181
1189
 
1182
1190
  // Frequency wait state
1183
1191
  private waitingForFrequency = false;
@@ -1219,6 +1227,9 @@ export function WithMoveManager<TBase extends PlayerCtor>(Base: TBase) {
1219
1227
  // Reset frequency wait state when processing a new route
1220
1228
  this.waitingForFrequency = false;
1221
1229
  this.frequencyWaitStartTime = 0;
1230
+ this.remainingDistance = 0;
1231
+ this.segmentDirection = null;
1232
+ this.segmentStep = 0;
1222
1233
 
1223
1234
  // Check if we've completed all routes
1224
1235
  if (this.routeIndex >= this.routes.length) {
@@ -1323,52 +1334,15 @@ export function WithMoveManager<TBase extends PlayerCtor>(Base: TBase) {
1323
1334
  }
1324
1335
  }
1325
1336
 
1326
- // Calculate target top-left position
1327
- let targetTopLeftX = currentTopLeftX;
1328
- let targetTopLeftY = currentTopLeftY;
1329
-
1330
- switch (moveDirection) {
1331
- case Direction.Right:
1332
- case 'right' as any:
1333
- targetTopLeftX = currentTopLeftX + distance;
1334
- break;
1335
- case Direction.Left:
1336
- case 'left' as any:
1337
- targetTopLeftX = currentTopLeftX - distance;
1338
- break;
1339
- case Direction.Down:
1340
- case 'down' as any:
1341
- targetTopLeftY = currentTopLeftY + distance;
1342
- break;
1343
- case Direction.Up:
1344
- case 'up' as any:
1345
- targetTopLeftY = currentTopLeftY - distance;
1346
- break;
1347
- }
1348
-
1349
- // Convert target top-left to center position for physics engine
1350
- // Get entity to access hitbox dimensions
1351
- const entity = map.physic.getEntityByUUID(this.player.id);
1352
- if (!entity) {
1353
- this.finished = true;
1354
- this.onComplete(false);
1355
- return;
1356
- }
1357
-
1358
- // Get hitbox dimensions for conversion
1359
- const hitbox = this.player.hitbox();
1360
- const hitboxWidth = hitbox?.w ?? 32;
1361
- const hitboxHeight = hitbox?.h ?? 32;
1362
-
1363
- // Convert top-left to center: center = topLeft + (size / 2)
1364
- const targetX = targetTopLeftX + hitboxWidth / 2;
1365
- const targetY = targetTopLeftY + hitboxHeight / 2;
1366
-
1367
- this.currentTarget = { x: targetX, y: targetY }; // Center position for physics engine
1368
- this.currentTargetTopLeft = { x: targetTopLeftX, y: targetTopLeftY }; // Top-left position for player.x() comparison
1369
- this.currentDirection = { x: 0, y: 0 };
1337
+ // Prepare segmented movement (per tile)
1338
+ this.remainingDistance = distance;
1339
+ this.segmentDirection = moveDirection;
1340
+ this.segmentStep = this.getTileStepDistance(playerSpeed);
1341
+ this.setNextSegmentTarget(currentTopLeftX, currentTopLeftY);
1370
1342
 
1371
- this.debugLog(`MOVE direction=${moveDirection} from=(${currentTopLeftX.toFixed(1)}, ${currentTopLeftY.toFixed(1)}) to=(${targetTopLeftX.toFixed(1)}, ${targetTopLeftY.toFixed(1)}) dist=${distance.toFixed(1)}`);
1343
+ if (this.currentTargetTopLeft) {
1344
+ this.debugLog(`MOVE direction=${moveDirection} from=(${currentTopLeftX.toFixed(1)}, ${currentTopLeftY.toFixed(1)}) to=(${this.currentTargetTopLeft.x.toFixed(1)}, ${this.currentTargetTopLeft.y.toFixed(1)}) dist=${distance.toFixed(1)}`);
1345
+ }
1372
1346
 
1373
1347
  // Reset stuck detection when starting a new movement
1374
1348
  this.lastPosition = null;
@@ -1415,7 +1389,13 @@ export function WithMoveManager<TBase extends PlayerCtor>(Base: TBase) {
1415
1389
 
1416
1390
  if (frequencyMs > 0 && Date.now() - this.frequencyWaitStartTime >= frequencyMs * this.ratioFrequency) {
1417
1391
  this.waitingForFrequency = false;
1418
- this.processNextRoute();
1392
+ if (this.remainingDistance > 0) {
1393
+ const currentTopLeftX = this.player.x();
1394
+ const currentTopLeftY = this.player.y();
1395
+ this.setNextSegmentTarget(currentTopLeftX, currentTopLeftY);
1396
+ } else {
1397
+ this.processNextRoute();
1398
+ }
1419
1399
  }
1420
1400
  return;
1421
1401
  }
@@ -1473,12 +1453,16 @@ export function WithMoveManager<TBase extends PlayerCtor>(Base: TBase) {
1473
1453
  this.lastDistanceToTarget = null;
1474
1454
  this.stuckCheckInitialized = false;
1475
1455
 
1476
- // Wait for frequency before processing next route
1456
+ // Wait for frequency before processing next route or segment
1477
1457
  if (!this.finished) {
1478
1458
  const playerFrequency = this.player.frequency;
1479
1459
  if (playerFrequency && playerFrequency > 0) {
1480
1460
  this.waitingForFrequency = true;
1481
1461
  this.frequencyWaitStartTime = Date.now();
1462
+ } else if (this.remainingDistance > 0) {
1463
+ const nextTopLeftX = this.player.x();
1464
+ const nextTopLeftY = this.player.y();
1465
+ this.setNextSegmentTarget(nextTopLeftX, nextTopLeftY);
1482
1466
  } else {
1483
1467
  // No frequency delay, process immediately
1484
1468
  this.processNextRoute();
@@ -1505,12 +1489,16 @@ export function WithMoveManager<TBase extends PlayerCtor>(Base: TBase) {
1505
1489
  this.lastDistanceToTarget = null;
1506
1490
  this.stuckCheckInitialized = false;
1507
1491
 
1508
- // Wait for frequency before processing next route
1492
+ // Wait for frequency before processing next route or segment
1509
1493
  if (!this.finished) {
1510
1494
  const playerFrequency = player.frequency;
1511
1495
  if (playerFrequency && playerFrequency > 0) {
1512
1496
  this.waitingForFrequency = true;
1513
1497
  this.frequencyWaitStartTime = Date.now();
1498
+ } else if (this.remainingDistance > 0) {
1499
+ const nextTopLeftX = this.player.x();
1500
+ const nextTopLeftY = this.player.y();
1501
+ this.setNextSegmentTarget(nextTopLeftX, nextTopLeftY);
1514
1502
  } else {
1515
1503
  // No frequency delay, process immediately
1516
1504
  this.processNextRoute();
@@ -1623,6 +1611,10 @@ export function WithMoveManager<TBase extends PlayerCtor>(Base: TBase) {
1623
1611
  if (playerFrequency && playerFrequency > 0) {
1624
1612
  this.waitingForFrequency = true;
1625
1613
  this.frequencyWaitStartTime = Date.now();
1614
+ } else if (this.remainingDistance > 0) {
1615
+ const nextTopLeftX = this.player.x();
1616
+ const nextTopLeftY = this.player.y();
1617
+ this.setNextSegmentTarget(nextTopLeftX, nextTopLeftY);
1626
1618
  } else {
1627
1619
  // No frequency delay, process immediately
1628
1620
  this.processNextRoute();
@@ -1652,6 +1644,69 @@ export function WithMoveManager<TBase extends PlayerCtor>(Base: TBase) {
1652
1644
  onFinished(): void {
1653
1645
  this.onComplete(true);
1654
1646
  }
1647
+
1648
+ private getTileStepDistance(playerSpeed: number): number {
1649
+ if (!Number.isFinite(playerSpeed) || playerSpeed <= 0) {
1650
+ return this.tileSize;
1651
+ }
1652
+ const stepsPerTile = Math.max(1, Math.floor(this.tileSize / playerSpeed));
1653
+ return stepsPerTile * playerSpeed;
1654
+ }
1655
+
1656
+ private setNextSegmentTarget(currentTopLeftX: number, currentTopLeftY: number): void {
1657
+ if (!this.segmentDirection || this.remainingDistance <= 0) {
1658
+ return;
1659
+ }
1660
+
1661
+ const map = this.player.getCurrentMap() as any;
1662
+ if (!map) {
1663
+ this.finished = true;
1664
+ this.onComplete(false);
1665
+ return;
1666
+ }
1667
+
1668
+ const entity = map.physic.getEntityByUUID(this.player.id);
1669
+ if (!entity) {
1670
+ this.finished = true;
1671
+ this.onComplete(false);
1672
+ return;
1673
+ }
1674
+
1675
+ const segmentDistance = Math.min(this.segmentStep || this.remainingDistance, this.remainingDistance);
1676
+ let targetTopLeftX = currentTopLeftX;
1677
+ let targetTopLeftY = currentTopLeftY;
1678
+
1679
+ switch (this.segmentDirection) {
1680
+ case Direction.Right:
1681
+ case 'right' as any:
1682
+ targetTopLeftX = currentTopLeftX + segmentDistance;
1683
+ break;
1684
+ case Direction.Left:
1685
+ case 'left' as any:
1686
+ targetTopLeftX = currentTopLeftX - segmentDistance;
1687
+ break;
1688
+ case Direction.Down:
1689
+ case 'down' as any:
1690
+ targetTopLeftY = currentTopLeftY + segmentDistance;
1691
+ break;
1692
+ case Direction.Up:
1693
+ case 'up' as any:
1694
+ targetTopLeftY = currentTopLeftY - segmentDistance;
1695
+ break;
1696
+ }
1697
+
1698
+ const hitbox = this.player.hitbox();
1699
+ const hitboxWidth = hitbox?.w ?? 32;
1700
+ const hitboxHeight = hitbox?.h ?? 32;
1701
+
1702
+ const targetX = targetTopLeftX + hitboxWidth / 2;
1703
+ const targetY = targetTopLeftY + hitboxHeight / 2;
1704
+
1705
+ this.currentTarget = { x: targetX, y: targetY };
1706
+ this.currentTargetTopLeft = { x: targetTopLeftX, y: targetTopLeftY };
1707
+ this.currentDirection = { x: 0, y: 0 };
1708
+ this.remainingDistance -= segmentDistance;
1709
+ }
1655
1710
  }
1656
1711
 
1657
1712
  // Create and add the route movement strategy
@@ -1948,4 +2003,3 @@ export interface IMoveManager {
1948
2003
  */
1949
2004
  replayRoutes(): void;
1950
2005
  }
1951
-
@@ -1,7 +1,14 @@
1
1
  import { isString, PlayerCtor } from "@rpgjs/common";
2
2
  import { signal, computed, WritableSignal, ComputedSignal } from "@signe/reactive";
3
3
  import { MAXHP, MAXSP } from "@rpgjs/common";
4
- import { sync, type } from "@signe/sync";
4
+ import { type } from "@signe/sync";
5
+
6
+ export type ExpCurve = {
7
+ basis: number;
8
+ extra: number;
9
+ accelerationA: number;
10
+ accelerationB: number;
11
+ };
5
12
 
6
13
  /**
7
14
  * Interface for Parameter Manager functionality
@@ -53,12 +60,7 @@ export interface IParameterManager {
53
60
  * ```
54
61
  * @memberof ParameterManager
55
62
  * */
56
- expCurve: {
57
- basis: number,
58
- extra: number,
59
- accelerationA: number
60
- accelerationB: number
61
- };
63
+ expCurve: ExpCurve;
62
64
 
63
65
  /**
64
66
  * Changes the health points
@@ -393,12 +395,12 @@ export function WithParameterManager<TBase extends PlayerCtor>(Base: TBase) {
393
395
  * console.log(player.param[MAXHP]); // Updated value
394
396
  * ```
395
397
  */
396
- private _paramsModifierSignal = signal<{
398
+ private _paramsModifierSignal = type(signal<{
397
399
  [key: string]: {
398
400
  value?: number,
399
401
  rate?: number
400
402
  }
401
- }>({})
403
+ }>({}) as any, '_paramsModifierSignal', { persist: true }, this as any)
402
404
 
403
405
  /**
404
406
  * Signal for base parameters configuration
@@ -406,12 +408,12 @@ export function WithParameterManager<TBase extends PlayerCtor>(Base: TBase) {
406
408
  * Stores the start and end values for each parameter's level curve.
407
409
  * Changes to this signal trigger recalculation of all parameter values.
408
410
  */
409
- private _parametersSignal = signal<{
411
+ private _parametersSignal = type(signal<{
410
412
  [key: string]: {
411
413
  start: number,
412
414
  end: number
413
415
  }
414
- }>({})
416
+ }>({}) as any, '_parametersSignal', { persist: true }, this as any)
415
417
 
416
418
  /**
417
419
  * Computed signal for all parameter values
@@ -432,13 +434,12 @@ export function WithParameterManager<TBase extends PlayerCtor>(Base: TBase) {
432
434
  _param = type(computed(() => {
433
435
  const obj = {}
434
436
  const parameters = this._parametersSignal()
437
+ const allModifiers = this._getAggregatedModifiers()
435
438
  const level = this._level()
436
439
 
437
440
  for (const [name, paramConfig] of Object.entries(parameters)) {
438
441
  let curveVal = Math.floor((paramConfig.end - paramConfig.start) * ((level - 1) / (this.finalLevel - this.initialLevel))) + paramConfig.start
439
442
 
440
- // Apply modifiers from equipment, states, etc.
441
- const allModifiers = this._getAggregatedModifiers()
442
443
  const modifier = allModifiers[name]
443
444
  if (modifier) {
444
445
  if (modifier.rate) curveVal *= modifier.rate
@@ -548,11 +549,14 @@ export function WithParameterManager<TBase extends PlayerCtor>(Base: TBase) {
548
549
  * ```
549
550
  * @memberof ParameterManager
550
551
  * */
551
- public expCurve: {
552
- basis: number,
553
- extra: number,
554
- accelerationA: number
555
- accelerationB: number
552
+ public _expCurveSignal = type(signal<string>('') as any, '_expCurveSignal', { persist: true }, this as any)
553
+
554
+ get expCurve(): ExpCurve {
555
+ return JSON.parse(this._expCurveSignal())
556
+ }
557
+
558
+ set expCurve(val: ExpCurve) {
559
+ this._expCurveSignal.set(JSON.stringify(val))
556
560
  }
557
561
 
558
562
  /**
@@ -686,7 +690,6 @@ export function WithParameterManager<TBase extends PlayerCtor>(Base: TBase) {
686
690
  get level(): number {
687
691
  return this._level()
688
692
  }
689
-
690
693
  /**
691
694
  * ```ts
692
695
  * console.log(player.expForNextlevel) // 150
@@ -903,4 +906,4 @@ export function WithParameterManager<TBase extends PlayerCtor>(Base: TBase) {
903
906
  this.recovery({ hp: 1, sp: 1 })
904
907
  }
905
908
  } as unknown as TBase;
906
- }
909
+ }
@@ -186,21 +186,6 @@ export class RpgPlayer extends BasicPlayerMixins(RpgCommonPlayer) {
186
186
 
187
187
  constructor() {
188
188
  super();
189
- // Use type assertion to access mixin properties
190
- (this as any).expCurve = {
191
- basis: 30,
192
- extra: 20,
193
- accelerationA: 30,
194
- accelerationB: 30
195
- };
196
-
197
- (this as any).addParameter(MAXHP, MAXHP_CURVE);
198
- (this as any).addParameter(MAXSP, MAXSP_CURVE);
199
- (this as any).addParameter(STR, STR_CURVE);
200
- (this as any).addParameter(INT, INT_CURVE);
201
- (this as any).addParameter(DEX, DEX_CURVE);
202
- (this as any).addParameter(AGI, AGI_CURVE);
203
- (this as any).allRecovery();
204
189
 
205
190
  let lastEmitted: { x: number; y: number } | null = null;
206
191
  let pendingUpdate: { x: number; y: number } | null = null;
@@ -238,6 +223,24 @@ export class RpgPlayer extends BasicPlayerMixins(RpgCommonPlayer) {
238
223
  this.hooks.callHooks("server-playerProps-load", this).subscribe();
239
224
  }
240
225
 
226
+ onGameStart() {
227
+ // Use type assertion to access mixin properties
228
+ (this as any).expCurve = {
229
+ basis: 30,
230
+ extra: 20,
231
+ accelerationA: 30,
232
+ accelerationB: 30
233
+ };
234
+
235
+ ;(this as any).addParameter(MAXHP, MAXHP_CURVE);
236
+ (this as any).addParameter(MAXSP, MAXSP_CURVE);
237
+ (this as any).addParameter(STR, STR_CURVE);
238
+ (this as any).addParameter(INT, INT_CURVE);
239
+ (this as any).addParameter(DEX, DEX_CURVE);
240
+ (this as any).addParameter(AGI, AGI_CURVE);
241
+ (this as any).allRecovery();
242
+ }
243
+
241
244
  get hooks() {
242
245
  return inject<Hooks>(this.context as any, ModulesToken);
243
246
  }
@@ -415,7 +418,12 @@ export class RpgPlayer extends BasicPlayerMixins(RpgCommonPlayer) {
415
418
  }
416
419
 
417
420
  snapshot() {
418
- return createStatesSnapshotDeep(this);
421
+ const snapshot = createStatesSnapshotDeep(this);
422
+ const expCurve = (this as any).expCurve;
423
+ if (expCurve) {
424
+ snapshot.expCurve = { ...expCurve };
425
+ }
426
+ return snapshot;
419
427
  }
420
428
 
421
429
  async applySnapshot(snapshot: string | object) {
@@ -426,6 +434,9 @@ export class RpgPlayer extends BasicPlayerMixins(RpgCommonPlayer) {
426
434
  const withClass = (this as any).resolveClassSnapshot?.(withStates) ?? withStates;
427
435
  const resolvedSnapshot = (this as any).resolveEquipmentsSnapshot?.(withClass) ?? withClass;
428
436
  load(this, resolvedSnapshot);
437
+ if (resolvedSnapshot.expCurve) {
438
+ (this as any).expCurve = resolvedSnapshot.expCurve;
439
+ }
429
440
  if (Array.isArray(resolvedSnapshot.items)) {
430
441
  this.items.set(resolvedSnapshot.items);
431
442
  }
@@ -1258,6 +1269,12 @@ export class RpgPlayer extends BasicPlayerMixins(RpgCommonPlayer) {
1258
1269
  }
1259
1270
 
1260
1271
  export class RpgEvent extends RpgPlayer {
1272
+
1273
+ constructor() {
1274
+ super();
1275
+ this.onGameStart()
1276
+ }
1277
+
1261
1278
  override async execMethod(methodName: string, methodData: any[] = [], instance = this) {
1262
1279
  await lastValueFrom(this.hooks
1263
1280
  .callHooks(`server-event-${methodName}`, instance, ...methodData));
package/src/RpgServer.ts CHANGED
@@ -696,10 +696,10 @@ export interface RpgServer {
696
696
  * })
697
697
  * ```
698
698
  *
699
- * @prop { { [dataName]: data } } [database]
699
+ * @prop { { [dataName]: data } | (engine: RpgMap) => { [dataName]: data } | Promise<{ [dataName]: data }> } [database]
700
700
  * @memberof RpgServer
701
701
  * */
702
- database?: object | any[],
702
+ database?: object | any[] | ((engine: RpgMap) => object | any[] | Promise<object | any[]>),
703
703
 
704
704
  /**
705
705
  * Array of all maps. Each element can be either a class (decorated with `@MapData` or not) or a `MapOptions` object
package/src/module.ts CHANGED
@@ -127,14 +127,20 @@ export function provideServerModules(modules: RpgServerModule[]): FactoryProvide
127
127
  }
128
128
  };
129
129
  }
130
- if (module.database && typeof module.database === 'object') {
131
- const database = {...module.database};
130
+ if (module.database) {
131
+ const database = module.database;
132
132
  module = {
133
133
  ...module,
134
134
  databaseHooks: {
135
- load: (engine: RpgMap) => {
136
- for (const key in database) {
137
- engine.addInDatabase(key, database[key]);
135
+ load: async (engine: RpgMap) => {
136
+ const data = typeof database === 'function'
137
+ ? await database(engine)
138
+ : database;
139
+ if (!data || typeof data !== 'object') {
140
+ return;
141
+ }
142
+ for (const key in data) {
143
+ engine.addInDatabase(key, data[key]);
138
144
  }
139
145
  },
140
146
  }
@@ -145,4 +151,4 @@ export function provideServerModules(modules: RpgServerModule[]): FactoryProvide
145
151
  return modules
146
152
  });
147
153
  }
148
-
154
+
@@ -35,6 +35,7 @@ export class LobbyRoom extends BaseRoom {
35
35
  async guiInteraction(player: RpgPlayer, value: { guiId: string, name: string, data: any }) {
36
36
  const id = value.data.id
37
37
  if (id === 'start') {
38
+ player.onGameStart();
38
39
  this.hooks.callHooks("server-player-onStart", player).subscribe();
39
40
  }
40
41
  }