@rpgjs/client 5.0.0-alpha.23 → 5.0.0-alpha.24
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/RpgClientEngine.d.ts +35 -115
- package/dist/components/gui/mobile/index.d.ts +1 -1
- package/dist/index10.js +1 -1
- package/dist/index11.js +2 -2
- package/dist/index12.js +2 -2
- package/dist/index14.js +1 -2
- package/dist/index14.js.map +1 -1
- package/dist/index15.js +4 -4
- package/dist/index16.js.map +1 -1
- package/dist/index17.js.map +1 -1
- package/dist/index2.js +85 -159
- package/dist/index2.js.map +1 -1
- package/dist/index20.js.map +1 -1
- package/dist/index22.js +1 -1
- package/dist/index25.js +2 -2
- package/dist/index25.js.map +1 -1
- package/dist/index26.js +2 -3
- package/dist/index26.js.map +1 -1
- package/dist/index28.js +2 -2
- package/dist/index28.js.map +1 -1
- package/dist/index3.js +1 -1
- package/dist/index31.js +2622 -42
- package/dist/index31.js.map +1 -1
- package/dist/index32.js +88 -2607
- package/dist/index32.js.map +1 -1
- package/dist/index33.js +64 -107
- package/dist/index33.js.map +1 -1
- package/dist/index34.js +12 -62
- package/dist/index34.js.map +1 -1
- package/dist/index35.js +24 -18
- package/dist/index35.js.map +1 -1
- package/dist/index36.js +87 -19
- package/dist/index36.js.map +1 -1
- package/dist/index37.js +20 -74
- package/dist/index37.js.map +1 -1
- package/dist/index38.js +18 -35
- package/dist/index38.js.map +1 -1
- package/dist/index39.js +137 -20
- package/dist/index39.js.map +1 -1
- package/dist/index4.js +1 -1
- package/dist/index40.js +9 -133
- package/dist/index40.js.map +1 -1
- package/dist/index41.js +3 -41
- package/dist/index41.js.map +1 -1
- package/dist/index42.js +536 -1
- package/dist/index42.js.map +1 -1
- package/dist/index43.js +30 -183
- package/dist/index43.js.map +1 -1
- package/dist/index44.js +9 -501
- package/dist/index44.js.map +1 -1
- package/dist/index45.js +6 -334
- package/dist/index45.js.map +1 -1
- package/dist/index46.js +325 -30
- package/dist/index46.js.map +1 -1
- package/dist/index47.js +3680 -67
- package/dist/index47.js.map +1 -1
- package/dist/index48.js +75 -10
- package/dist/index48.js.map +1 -1
- package/dist/index49.js +186 -6
- package/dist/index49.js.map +1 -1
- package/dist/index50.js +499 -3685
- package/dist/index50.js.map +1 -1
- package/dist/index53.js +1 -1
- package/dist/index8.js +6 -1
- package/dist/index8.js.map +1 -1
- package/dist/index9.js +1 -2
- package/dist/index9.js.map +1 -1
- package/package.json +9 -9
- package/src/Gui/Gui.ts +0 -1
- package/src/RpgClientEngine.ts +106 -176
- package/src/components/character.ce +2 -3
- package/src/components/gui/mobile/index.ts +1 -1
- package/src/components/gui/mobile/mobile.ce +73 -88
- package/src/components/prebuilt/light-halo.ce +2 -71
- package/src/components/scenes/canvas.ce +0 -10
- package/src/components/scenes/event-layer.ce +1 -0
- package/src/module.ts +6 -1
- package/dist/Game/TransitionManager.d.ts +0 -56
- package/src/Game/TransitionManager.ts +0 -75
package/dist/index2.js
CHANGED
|
@@ -10,8 +10,7 @@ import { load } from './index28.js';
|
|
|
10
10
|
import { RpgClientMap } from './index29.js';
|
|
11
11
|
import { RpgGui } from './index9.js';
|
|
12
12
|
import { AnimationManager } from './index30.js';
|
|
13
|
-
import {
|
|
14
|
-
import { lastValueFrom } from 'rxjs';
|
|
13
|
+
import { BehaviorSubject, lastValueFrom, combineLatest, filter, take, switchMap } from 'rxjs';
|
|
15
14
|
import { GlobalConfigToken } from './index8.js';
|
|
16
15
|
import * as PIXI from 'pixi.js';
|
|
17
16
|
import { PrebuiltComponentAnimations } from './index12.js';
|
|
@@ -25,7 +24,6 @@ class RpgClientEngine {
|
|
|
25
24
|
this.spritesheets = /* @__PURE__ */ new Map();
|
|
26
25
|
this.sounds = /* @__PURE__ */ new Map();
|
|
27
26
|
this.componentAnimations = [];
|
|
28
|
-
this.transitions = [];
|
|
29
27
|
this.particleSettings = {
|
|
30
28
|
emitters: []
|
|
31
29
|
};
|
|
@@ -36,6 +34,7 @@ class RpgClientEngine {
|
|
|
36
34
|
this.cameraFollowTargetId = signal(null);
|
|
37
35
|
/** Trigger for map shake animation */
|
|
38
36
|
this.mapShakeTrigger = trigger();
|
|
37
|
+
this.controlsReady = signal(void 0);
|
|
39
38
|
this.predictionEnabled = false;
|
|
40
39
|
this.SERVER_CORRECTION_THRESHOLD = 30;
|
|
41
40
|
this.inputFrameCounter = 0;
|
|
@@ -47,6 +46,11 @@ class RpgClientEngine {
|
|
|
47
46
|
this.PING_INTERVAL_MS = 5e3;
|
|
48
47
|
// Send ping every 5 seconds
|
|
49
48
|
this.lastInputTime = 0;
|
|
49
|
+
// Track map loading state for onAfterLoading hook using RxJS
|
|
50
|
+
this.mapLoadCompleted$ = new BehaviorSubject(false);
|
|
51
|
+
this.playerIdReceived$ = new BehaviorSubject(false);
|
|
52
|
+
this.playersReceived$ = new BehaviorSubject(false);
|
|
53
|
+
this.eventsReceived$ = new BehaviorSubject(false);
|
|
50
54
|
this.webSocket = inject(WebSocketToken);
|
|
51
55
|
this.guiService = inject(RpgGui);
|
|
52
56
|
this.loadMapService = inject(LoadMapToken);
|
|
@@ -110,6 +114,7 @@ class RpgClientEngine {
|
|
|
110
114
|
...currentValues,
|
|
111
115
|
values: /* @__PURE__ */ new Map([["__default__", controlInstance]])
|
|
112
116
|
};
|
|
117
|
+
this.controlsReady.set(true);
|
|
113
118
|
}
|
|
114
119
|
async start() {
|
|
115
120
|
this.sceneMap = new RpgClientMap();
|
|
@@ -133,7 +138,6 @@ class RpgClientEngine {
|
|
|
133
138
|
this.hooks.callHooks("client-gui-load", this).subscribe();
|
|
134
139
|
this.hooks.callHooks("client-particles-load", this).subscribe();
|
|
135
140
|
this.hooks.callHooks("client-componentAnimations-load", this).subscribe();
|
|
136
|
-
this.hooks.callHooks("client-transitions-load", this).subscribe();
|
|
137
141
|
this.hooks.callHooks("client-sprite-load", this).subscribe();
|
|
138
142
|
await lastValueFrom(this.hooks.callHooks("client-engine-onStart", this));
|
|
139
143
|
window.addEventListener("resize", () => {
|
|
@@ -154,9 +158,20 @@ class RpgClientEngine {
|
|
|
154
158
|
}
|
|
155
159
|
initListeners() {
|
|
156
160
|
this.webSocket.on("sync", (data) => {
|
|
157
|
-
if (data.pId)
|
|
161
|
+
if (data.pId) {
|
|
162
|
+
this.playerIdSignal.set(data.pId);
|
|
163
|
+
this.playerIdReceived$.next(true);
|
|
164
|
+
}
|
|
158
165
|
this.hooks.callHooks("client-sceneMap-onChanges", this.sceneMap, { partial: data }).subscribe();
|
|
159
166
|
load(this.sceneMap, data, true);
|
|
167
|
+
const players = data.players || this.sceneMap.players();
|
|
168
|
+
if (players && Object.keys(players).length > 0) {
|
|
169
|
+
this.playersReceived$.next(true);
|
|
170
|
+
}
|
|
171
|
+
const events = data.events || this.sceneMap.events();
|
|
172
|
+
if (events !== void 0) {
|
|
173
|
+
this.eventsReceived$.next(true);
|
|
174
|
+
}
|
|
160
175
|
});
|
|
161
176
|
this.webSocket.on("pong", (data) => {
|
|
162
177
|
const now = Date.now();
|
|
@@ -292,8 +307,16 @@ class RpgClientEngine {
|
|
|
292
307
|
});
|
|
293
308
|
}
|
|
294
309
|
async loadScene(mapId) {
|
|
295
|
-
this.hooks.callHooks("client-sceneMap-onBeforeLoading", this.sceneMap)
|
|
310
|
+
await lastValueFrom(this.hooks.callHooks("client-sceneMap-onBeforeLoading", this.sceneMap));
|
|
296
311
|
this.clearClientPredictionStates();
|
|
312
|
+
this.mapLoadCompleted$.next(false);
|
|
313
|
+
this.playerIdReceived$.next(false);
|
|
314
|
+
this.playersReceived$.next(false);
|
|
315
|
+
this.eventsReceived$.next(false);
|
|
316
|
+
if (this.onAfterLoadingSubscription) {
|
|
317
|
+
this.onAfterLoadingSubscription.unsubscribe();
|
|
318
|
+
}
|
|
319
|
+
this.setupOnAfterLoadingObserver();
|
|
297
320
|
this.webSocket.updateProperties({ room: mapId });
|
|
298
321
|
await this.webSocket.reconnect(() => {
|
|
299
322
|
this.initListeners();
|
|
@@ -301,7 +324,18 @@ class RpgClientEngine {
|
|
|
301
324
|
});
|
|
302
325
|
const res = await this.loadMapService.load(mapId);
|
|
303
326
|
this.sceneMap.data.set(res);
|
|
304
|
-
|
|
327
|
+
if (this.playerIdSignal()) {
|
|
328
|
+
this.playerIdReceived$.next(true);
|
|
329
|
+
}
|
|
330
|
+
const players = this.sceneMap.players();
|
|
331
|
+
if (players && Object.keys(players).length > 0) {
|
|
332
|
+
this.playersReceived$.next(true);
|
|
333
|
+
}
|
|
334
|
+
const events = this.sceneMap.events();
|
|
335
|
+
if (events !== void 0) {
|
|
336
|
+
this.eventsReceived$.next(true);
|
|
337
|
+
}
|
|
338
|
+
this.mapLoadCompleted$.next(true);
|
|
305
339
|
this.sceneMap.loadPhysic();
|
|
306
340
|
}
|
|
307
341
|
addSpriteSheet(spritesheetClass, id) {
|
|
@@ -788,147 +822,13 @@ class RpgClientEngine {
|
|
|
788
822
|
}
|
|
789
823
|
return componentAnimation.instance;
|
|
790
824
|
}
|
|
791
|
-
/**
|
|
792
|
-
* Add a transition to the engine
|
|
793
|
-
*
|
|
794
|
-
* Transitions are screen effects that can be displayed during scene changes,
|
|
795
|
-
* map loading, or any other moment where a visual transition is needed.
|
|
796
|
-
* They are displayed on top of the entire canvas and can have custom props
|
|
797
|
-
* that can be functions (similar to ComponentAnimation).
|
|
798
|
-
*
|
|
799
|
-
* @param transition - The transition configuration
|
|
800
|
-
* @param transition.id - Unique identifier for the transition
|
|
801
|
-
* @param transition.component - The component function to render
|
|
802
|
-
* @param transition.props - Optional props to pass to the component (can be a function)
|
|
803
|
-
* @returns The added transition configuration
|
|
804
|
-
*
|
|
805
|
-
* @example
|
|
806
|
-
* ```ts
|
|
807
|
-
* // Add a fade transition
|
|
808
|
-
* engine.addTransition({
|
|
809
|
-
* id: 'fade',
|
|
810
|
-
* component: FadeComponent
|
|
811
|
-
* });
|
|
812
|
-
*
|
|
813
|
-
* // Add a transition with props
|
|
814
|
-
* engine.addTransition({
|
|
815
|
-
* id: 'slide',
|
|
816
|
-
* component: SlideComponent,
|
|
817
|
-
* props: { direction: 'left', duration: 500 }
|
|
818
|
-
* });
|
|
819
|
-
*
|
|
820
|
-
* // Add a transition with function props
|
|
821
|
-
* engine.addTransition({
|
|
822
|
-
* id: 'custom',
|
|
823
|
-
* component: CustomTransition,
|
|
824
|
-
* props: (engine) => ({ width: engine.width(), height: engine.height() })
|
|
825
|
-
* });
|
|
826
|
-
* ```
|
|
827
|
-
*/
|
|
828
|
-
addTransition(transition) {
|
|
829
|
-
const instance = new TransitionManager();
|
|
830
|
-
this.transitions.push({
|
|
831
|
-
id: transition.id,
|
|
832
|
-
component: transition.component,
|
|
833
|
-
props: transition.props,
|
|
834
|
-
instance,
|
|
835
|
-
current: instance.current
|
|
836
|
-
});
|
|
837
|
-
return transition;
|
|
838
|
-
}
|
|
839
|
-
/**
|
|
840
|
-
* Remove a transition from the engine
|
|
841
|
-
*
|
|
842
|
-
* Removes a transition by its ID. This will not affect any currently
|
|
843
|
-
* running transitions, only prevent new ones from being started.
|
|
844
|
-
*
|
|
845
|
-
* @param id - The unique identifier of the transition to remove
|
|
846
|
-
* @returns true if the transition was found and removed, false otherwise
|
|
847
|
-
*
|
|
848
|
-
* @example
|
|
849
|
-
* ```ts
|
|
850
|
-
* // Remove a transition
|
|
851
|
-
* engine.removeTransition('fade');
|
|
852
|
-
* ```
|
|
853
|
-
*/
|
|
854
|
-
removeTransition(id) {
|
|
855
|
-
const index = this.transitions.findIndex((transition) => transition.id === id);
|
|
856
|
-
if (index !== -1) {
|
|
857
|
-
this.transitions.splice(index, 1);
|
|
858
|
-
return true;
|
|
859
|
-
}
|
|
860
|
-
return false;
|
|
861
|
-
}
|
|
862
|
-
/**
|
|
863
|
-
* Modify an existing transition
|
|
864
|
-
*
|
|
865
|
-
* Updates the component or props of an existing transition. This will
|
|
866
|
-
* not affect any currently running transitions, only future ones.
|
|
867
|
-
*
|
|
868
|
-
* @param id - The unique identifier of the transition to modify
|
|
869
|
-
* @param updates - The updates to apply (component and/or props)
|
|
870
|
-
* @returns true if the transition was found and modified, false otherwise
|
|
871
|
-
*
|
|
872
|
-
* @example
|
|
873
|
-
* ```ts
|
|
874
|
-
* // Update transition props
|
|
875
|
-
* engine.modifyTransition('fade', {
|
|
876
|
-
* props: { duration: 2000, color: 'white' }
|
|
877
|
-
* });
|
|
878
|
-
*
|
|
879
|
-
* // Update transition component
|
|
880
|
-
* engine.modifyTransition('fade', {
|
|
881
|
-
* component: NewFadeComponent
|
|
882
|
-
* });
|
|
883
|
-
* ```
|
|
884
|
-
*/
|
|
885
|
-
modifyTransition(id, updates) {
|
|
886
|
-
const transition = this.transitions.find((transition2) => transition2.id === id);
|
|
887
|
-
if (!transition) {
|
|
888
|
-
return false;
|
|
889
|
-
}
|
|
890
|
-
if (updates.component !== void 0) {
|
|
891
|
-
transition.component = updates.component;
|
|
892
|
-
}
|
|
893
|
-
if (updates.props !== void 0) {
|
|
894
|
-
transition.props = updates.props;
|
|
895
|
-
}
|
|
896
|
-
return true;
|
|
897
|
-
}
|
|
898
|
-
/**
|
|
899
|
-
* Get a transition by its ID
|
|
900
|
-
*
|
|
901
|
-
* Retrieves the TransitionManager instance for a specific transition,
|
|
902
|
-
* which can be used to start the transition.
|
|
903
|
-
*
|
|
904
|
-
* @param id - The unique identifier of the transition
|
|
905
|
-
* @returns The TransitionManager instance for the transition
|
|
906
|
-
* @throws Error if the transition is not found
|
|
907
|
-
*
|
|
908
|
-
* @example
|
|
909
|
-
* ```ts
|
|
910
|
-
* // Get a transition and start it
|
|
911
|
-
* const fadeTransition = engine.getTransition('fade');
|
|
912
|
-
* fadeTransition.start({ duration: 1000 });
|
|
913
|
-
* ```
|
|
914
|
-
*/
|
|
915
|
-
getTransition(id) {
|
|
916
|
-
const transition = this.transitions.find((transition2) => transition2.id === id);
|
|
917
|
-
if (!transition) {
|
|
918
|
-
throw new Error(`Transition with id ${id} not found`);
|
|
919
|
-
}
|
|
920
|
-
return transition.instance;
|
|
921
|
-
}
|
|
922
825
|
/**
|
|
923
826
|
* Start a transition
|
|
924
827
|
*
|
|
925
|
-
* Convenience method to
|
|
926
|
-
* getTransition and start into a single call. The transition will
|
|
927
|
-
* automatically receive an onFinish callback to remove itself when done.
|
|
828
|
+
* Convenience method to display a transition by its ID using the GUI system.
|
|
928
829
|
*
|
|
929
830
|
* @param id - The unique identifier of the transition to start
|
|
930
|
-
* @param props -
|
|
931
|
-
* @returns The created transition object
|
|
831
|
+
* @param props - Props to pass to the transition component
|
|
932
832
|
*
|
|
933
833
|
* @example
|
|
934
834
|
* ```ts
|
|
@@ -943,23 +843,10 @@ class RpgClientEngine {
|
|
|
943
843
|
* ```
|
|
944
844
|
*/
|
|
945
845
|
startTransition(id, props = {}) {
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
throw new Error(`Transition with id ${id} not found`);
|
|
949
|
-
}
|
|
950
|
-
let baseProps = {};
|
|
951
|
-
if (transition.props) {
|
|
952
|
-
if (typeof transition.props === "function") {
|
|
953
|
-
baseProps = transition.props(this);
|
|
954
|
-
} else {
|
|
955
|
-
baseProps = transition.props;
|
|
956
|
-
}
|
|
846
|
+
if (!this.guiService.exists(id)) {
|
|
847
|
+
throw new Error(`Transition with id ${id} not found. Make sure to add it using engine.addTransition() or in your module's transitions property.`);
|
|
957
848
|
}
|
|
958
|
-
|
|
959
|
-
...baseProps,
|
|
960
|
-
...props
|
|
961
|
-
};
|
|
962
|
-
return transition.instance.start(finalProps);
|
|
849
|
+
this.guiService.display(id, props);
|
|
963
850
|
}
|
|
964
851
|
async processInput({ input }) {
|
|
965
852
|
const timestamp = Date.now();
|
|
@@ -1050,6 +937,45 @@ class RpgClientEngine {
|
|
|
1050
937
|
getCurrentPlayer() {
|
|
1051
938
|
return this.sceneMap.getCurrentPlayer();
|
|
1052
939
|
}
|
|
940
|
+
/**
|
|
941
|
+
* Setup RxJS observer to wait for all conditions before calling onAfterLoading hook
|
|
942
|
+
*
|
|
943
|
+
* This method uses RxJS `combineLatest` to wait for all conditions to be met,
|
|
944
|
+
* regardless of the order in which they arrive:
|
|
945
|
+
* 1. The map loading is completed (loadMapService.load is finished)
|
|
946
|
+
* 2. We received a player ID (pId)
|
|
947
|
+
* 3. Players array has at least one element
|
|
948
|
+
* 4. Events property is present in the sync data
|
|
949
|
+
*
|
|
950
|
+
* Once all conditions are met, it uses `switchMap` to call the onAfterLoading hook once.
|
|
951
|
+
*
|
|
952
|
+
* ## Design
|
|
953
|
+
*
|
|
954
|
+
* Uses BehaviorSubjects to track each condition state, allowing events to arrive
|
|
955
|
+
* in any order. The `combineLatest` operator waits until all observables emit `true`,
|
|
956
|
+
* then `take(1)` ensures the hook is called only once, and `switchMap` handles
|
|
957
|
+
* the hook execution.
|
|
958
|
+
*
|
|
959
|
+
* @example
|
|
960
|
+
* ```ts
|
|
961
|
+
* // Called automatically in loadScene to setup the observer
|
|
962
|
+
* this.setupOnAfterLoadingObserver();
|
|
963
|
+
* ```
|
|
964
|
+
*/
|
|
965
|
+
setupOnAfterLoadingObserver() {
|
|
966
|
+
this.onAfterLoadingSubscription = combineLatest([
|
|
967
|
+
this.mapLoadCompleted$.pipe(filter((completed) => completed === true)),
|
|
968
|
+
this.playerIdReceived$.pipe(filter((received) => received === true)),
|
|
969
|
+
this.playersReceived$.pipe(filter((received) => received === true)),
|
|
970
|
+
this.eventsReceived$.pipe(filter((received) => received === true))
|
|
971
|
+
]).pipe(
|
|
972
|
+
take(1),
|
|
973
|
+
// Only execute once when all conditions are met
|
|
974
|
+
switchMap(() => {
|
|
975
|
+
return this.hooks.callHooks("client-sceneMap-onAfterLoading", this.sceneMap);
|
|
976
|
+
})
|
|
977
|
+
).subscribe();
|
|
978
|
+
}
|
|
1053
979
|
/**
|
|
1054
980
|
* Clear client prediction states for cleanup
|
|
1055
981
|
*
|