@rpgjs/client 3.3.2 → 4.0.0-beta.3
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/LICENSE +19 -0
- package/lib/Components/AbstractComponent.d.ts +3 -2
- package/lib/Components/AbstractComponent.js +17 -28
- package/lib/Components/AbstractComponent.js.map +1 -1
- package/lib/Components/BarComponent.d.ts +2 -1
- package/lib/Components/BarComponent.js +32 -33
- package/lib/Components/BarComponent.js.map +1 -1
- package/lib/Components/Component.d.ts +3 -3
- package/lib/Components/Component.js +84 -96
- package/lib/Components/Component.js.map +1 -1
- package/lib/Components/DebugComponent.d.ts +2 -1
- package/lib/Components/DebugComponent.js +9 -10
- package/lib/Components/DebugComponent.js.map +1 -1
- package/lib/Components/ImageComponent.d.ts +2 -1
- package/lib/Components/ImageComponent.js +5 -7
- package/lib/Components/ImageComponent.js.map +1 -1
- package/lib/Components/ShapeComponent.d.ts +2 -1
- package/lib/Components/ShapeComponent.js +14 -14
- package/lib/Components/ShapeComponent.js.map +1 -1
- package/lib/Components/TextComponent.d.ts +3 -2
- package/lib/Components/TextComponent.js +10 -9
- package/lib/Components/TextComponent.js.map +1 -1
- package/lib/Components/TileComponent.d.ts +2 -1
- package/lib/Components/TileComponent.js +12 -16
- package/lib/Components/TileComponent.js.map +1 -1
- package/lib/Effects/Animation.d.ts +10 -9
- package/lib/Effects/Animation.js +36 -36
- package/lib/Effects/Animation.js.map +1 -1
- package/lib/Effects/AnimationCharacter.js +2 -5
- package/lib/Effects/AnimationCharacter.js.map +1 -1
- package/lib/Effects/Spinner.d.ts +3 -2
- package/lib/Effects/Spinner.js +2 -5
- package/lib/Effects/Spinner.js.map +1 -1
- package/lib/Effects/Timeline.d.ts +1 -1
- package/lib/Effects/Timeline.js +6 -10
- package/lib/Effects/Timeline.js.map +1 -1
- package/lib/Effects/TransitionScene.d.ts +2 -1
- package/lib/Effects/TransitionScene.js +3 -7
- package/lib/Effects/TransitionScene.js.map +1 -1
- package/lib/GameEngine.js +51 -26
- package/lib/GameEngine.js.map +1 -1
- package/lib/Interfaces/Character.js +1 -2
- package/lib/Interfaces/Scene.js +1 -2
- package/lib/KeyboardControls.d.ts +2 -1
- package/lib/KeyboardControls.js +47 -45
- package/lib/KeyboardControls.js.map +1 -1
- package/lib/Logger.js +1 -5
- package/lib/Logger.js.map +1 -1
- package/lib/Presets/AnimationSpritesheet.js +9 -13
- package/lib/Presets/AnimationSpritesheet.js.map +1 -1
- package/lib/Presets/Scene.js +2 -5
- package/lib/Presets/Scene.js.map +1 -1
- package/lib/Renderer.d.ts +5 -3
- package/lib/Renderer.js +68 -60
- package/lib/Renderer.js.map +1 -1
- package/lib/Resources.js +1 -5
- package/lib/Resources.js.map +1 -1
- package/lib/RpgClient.d.ts +3 -2
- package/lib/RpgClient.js +1 -2
- package/lib/RpgClientEngine.d.ts +2 -1
- package/lib/RpgClientEngine.js +192 -176
- package/lib/RpgClientEngine.js.map +1 -1
- package/lib/RpgGui.js +40 -41
- package/lib/RpgGui.js.map +1 -1
- package/lib/Scene/EventLayer.d.ts +4 -0
- package/lib/Scene/EventLayer.js +8 -0
- package/lib/Scene/EventLayer.js.map +1 -0
- package/lib/Scene/Map.d.ts +17 -3
- package/lib/Scene/Map.js +157 -113
- package/lib/Scene/Map.js.map +1 -1
- package/lib/Scene/Scene.d.ts +3 -3
- package/lib/Scene/Scene.js +24 -20
- package/lib/Scene/Scene.js.map +1 -1
- package/lib/Scene/SceneData.js +1 -5
- package/lib/Scene/SceneData.js.map +1 -1
- package/lib/Sound/RpgSound.js +8 -11
- package/lib/Sound/RpgSound.js.map +1 -1
- package/lib/Sound/Sound.d.ts +2 -2
- package/lib/Sound/Sound.js +1 -5
- package/lib/Sound/Sound.js.map +1 -1
- package/lib/Sound/Sounds.js +4 -8
- package/lib/Sound/Sounds.js.map +1 -1
- package/lib/Sprite/Character.d.ts +3 -2
- package/lib/Sprite/Character.js +15 -16
- package/lib/Sprite/Character.js.map +1 -1
- package/lib/Sprite/Player.js +2 -9
- package/lib/Sprite/Player.js.map +1 -1
- package/lib/Sprite/Spritesheet.d.ts +4 -4
- package/lib/Sprite/Spritesheet.js +1 -5
- package/lib/Sprite/Spritesheet.js.map +1 -1
- package/lib/Sprite/Spritesheets.js +4 -8
- package/lib/Sprite/Spritesheets.js.map +1 -1
- package/lib/Tilemap/CommonLayer.d.ts +2 -1
- package/lib/Tilemap/CommonLayer.js +7 -11
- package/lib/Tilemap/CommonLayer.js.map +1 -1
- package/lib/Tilemap/ImageLayer.js +5 -7
- package/lib/Tilemap/ImageLayer.js.map +1 -1
- package/lib/Tilemap/Tile.d.ts +5 -5
- package/lib/Tilemap/Tile.js +19 -21
- package/lib/Tilemap/Tile.js.map +1 -1
- package/lib/Tilemap/TileLayer.d.ts +0 -7
- package/lib/Tilemap/TileLayer.js +27 -29
- package/lib/Tilemap/TileLayer.js.map +1 -1
- package/lib/Tilemap/TileSet.d.ts +2 -1
- package/lib/Tilemap/TileSet.js +9 -12
- package/lib/Tilemap/TileSet.js.map +1 -1
- package/lib/Tilemap/index.d.ts +3 -11
- package/lib/Tilemap/index.js +22 -61
- package/lib/Tilemap/index.js.map +1 -1
- package/lib/clientEntryPoint.js +26 -28
- package/lib/clientEntryPoint.js.map +1 -1
- package/lib/index.js +21 -75
- package/lib/index.js.map +1 -1
- package/package.json +19 -19
- package/src/Components/AbstractComponent.ts +120 -0
- package/src/Components/BarComponent.ts +179 -0
- package/src/Components/Component.ts +506 -0
- package/src/Components/DebugComponent.ts +36 -0
- package/src/Components/ImageComponent.ts +30 -0
- package/src/Components/ShapeComponent.ts +64 -0
- package/src/Components/TextComponent.ts +33 -0
- package/src/Components/TileComponent.ts +43 -0
- package/src/Effects/Animation.ts +297 -0
- package/src/Effects/AnimationCharacter.ts +7 -0
- package/src/Effects/Spinner.ts +19 -0
- package/src/Effects/Timeline.ts +294 -0
- package/src/Effects/TransitionScene.ts +57 -0
- package/src/GameEngine.ts +284 -0
- package/src/Interfaces/Character.ts +7 -0
- package/src/Interfaces/Scene.ts +9 -0
- package/src/KeyboardControls.ts +552 -0
- package/src/Logger.ts +3 -0
- package/src/Presets/AnimationSpritesheet.ts +36 -0
- package/src/Presets/Scene.ts +3 -0
- package/src/Renderer.ts +263 -0
- package/src/Resources.ts +40 -0
- package/src/RpgClient.ts +333 -0
- package/src/RpgClientEngine.ts +709 -0
- package/src/RpgGui.ts +553 -0
- package/src/RpgGuiCompiled.ts +43 -0
- package/src/Scene/EventLayer.ts +9 -0
- package/src/Scene/Map.ts +393 -0
- package/src/Scene/Scene.ts +270 -0
- package/src/Scene/SceneData.ts +13 -0
- package/src/Sound/RpgSound.ts +50 -0
- package/src/Sound/Sound.ts +91 -0
- package/src/Sound/Sounds.ts +7 -0
- package/src/Sprite/Character.ts +149 -0
- package/src/Sprite/Player.ts +3 -0
- package/src/Sprite/Spritesheet.ts +392 -0
- package/src/Sprite/Spritesheets.ts +8 -0
- package/src/Tilemap/CommonLayer.ts +20 -0
- package/src/Tilemap/ImageLayer.ts +20 -0
- package/src/Tilemap/Tile.ts +80 -0
- package/src/Tilemap/TileLayer.ts +142 -0
- package/src/Tilemap/TileSet.ts +40 -0
- package/src/Tilemap/index.ts +173 -0
- package/src/clientEntryPoint.ts +141 -0
- package/src/index.ts +25 -0
- package/src/types/howler.d.ts +73 -0
- package/tsconfig.json +30 -0
- package/lib/Components/ColorComponent.d.ts +0 -9
- package/lib/Components/ColorComponent.js +0 -18
- package/lib/Components/ColorComponent.js.map +0 -1
|
@@ -0,0 +1,709 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import { KeyboardControls } from './KeyboardControls'
|
|
3
|
+
import { RpgRenderer } from './Renderer'
|
|
4
|
+
import { _initSpritesheet, spritesheets } from './Sprite/Spritesheets'
|
|
5
|
+
import { _initSound, sounds } from './Sound/Sounds'
|
|
6
|
+
import { World } from 'simple-room-client'
|
|
7
|
+
import { BehaviorSubject, Observable, Subject, Subscription, lastValueFrom } from 'rxjs'
|
|
8
|
+
import { ajax } from 'rxjs/ajax'
|
|
9
|
+
import { RpgGui } from './RpgGui'
|
|
10
|
+
import {
|
|
11
|
+
RpgCommonPlayer,
|
|
12
|
+
PrebuiltGui,
|
|
13
|
+
Utils,
|
|
14
|
+
RpgPlugin,
|
|
15
|
+
HookClient,
|
|
16
|
+
RpgCommonMap,
|
|
17
|
+
Scheduler,
|
|
18
|
+
Control,
|
|
19
|
+
} from '@rpgjs/common'
|
|
20
|
+
import { RpgSound } from './Sound/RpgSound'
|
|
21
|
+
import { SceneMap } from './Scene/Map'
|
|
22
|
+
import { GameEngineClient } from './GameEngine'
|
|
23
|
+
import { Scene } from './Scene/Scene'
|
|
24
|
+
import { Spritesheet } from './Sprite/Spritesheet'
|
|
25
|
+
import { log } from './Logger'
|
|
26
|
+
import { Sound } from './Sound/Sound'
|
|
27
|
+
import { constructor, ObjectFixtureList, PlayerType, SocketEvents, SocketMethods, Tick } from '@rpgjs/types'
|
|
28
|
+
import { Assets, utils } from 'pixi.js'
|
|
29
|
+
import * as PIXI from 'pixi.js'
|
|
30
|
+
|
|
31
|
+
declare var __RPGJS_PRODUCTION__: boolean;
|
|
32
|
+
|
|
33
|
+
type FrameData = {
|
|
34
|
+
time: number,
|
|
35
|
+
data: any
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
type MatchMakerResponse = {
|
|
39
|
+
url: string,
|
|
40
|
+
port: string
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export class RpgClientEngine {
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Get the rendering
|
|
47
|
+
*
|
|
48
|
+
* @prop {RpgRenderer} [renderer]
|
|
49
|
+
* @readonly
|
|
50
|
+
* @memberof RpgClientEngine
|
|
51
|
+
* */
|
|
52
|
+
public renderer: RpgRenderer
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Get the socket
|
|
56
|
+
*
|
|
57
|
+
* @prop {Socket} [socket]
|
|
58
|
+
* @readonly
|
|
59
|
+
* @memberof RpgClientEngine
|
|
60
|
+
* */
|
|
61
|
+
public socket: any
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* retrieve the global configurations assigned at the entry point
|
|
65
|
+
*
|
|
66
|
+
* @prop {object} [globalConfig]
|
|
67
|
+
* @readonly
|
|
68
|
+
* @memberof RpgClientEngine
|
|
69
|
+
* */
|
|
70
|
+
public globalConfig: any = {}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Get the class managing the keyboard
|
|
74
|
+
*
|
|
75
|
+
* @prop {KeyboardControls} [controls]
|
|
76
|
+
* @readonly
|
|
77
|
+
* @memberof RpgClientEngine
|
|
78
|
+
* */
|
|
79
|
+
public controls: KeyboardControls
|
|
80
|
+
|
|
81
|
+
public _options: any
|
|
82
|
+
|
|
83
|
+
private _tick: BehaviorSubject<Tick> = new BehaviorSubject({
|
|
84
|
+
timestamp: -1,
|
|
85
|
+
deltaTime: 0,
|
|
86
|
+
frame: 0,
|
|
87
|
+
deltaRatio: 1
|
|
88
|
+
})
|
|
89
|
+
public keyChange: Subject<string> = new Subject()
|
|
90
|
+
public roomJoin: Subject<string> = new Subject()
|
|
91
|
+
private hasBeenDisconnected: boolean = false
|
|
92
|
+
private serverChanging: boolean = false
|
|
93
|
+
private isTeleported: boolean = false
|
|
94
|
+
// TODO, public or private
|
|
95
|
+
io
|
|
96
|
+
private lastTimestamp: number = 0
|
|
97
|
+
private subscriptionWorld: Subscription
|
|
98
|
+
|
|
99
|
+
private clientFrames: Map<number, FrameData> = new Map()
|
|
100
|
+
private serverFrames: Map<number, FrameData> = new Map()
|
|
101
|
+
|
|
102
|
+
private session: string | null = null
|
|
103
|
+
private lastConnection: string = ''
|
|
104
|
+
private lastScene: string = ''
|
|
105
|
+
private matchMakerService: string | (() => MatchMakerResponse) | null = null
|
|
106
|
+
private assetsPath: string = 'assets'
|
|
107
|
+
private serverFps: number = 60
|
|
108
|
+
private scheduler: Scheduler = new Scheduler()
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Read objects synchronized with the server
|
|
112
|
+
*
|
|
113
|
+
* @prop {Observable< {
|
|
114
|
+
[id: string]: {
|
|
115
|
+
object: any,
|
|
116
|
+
paramsChanged: any
|
|
117
|
+
}
|
|
118
|
+
} >} [objects]
|
|
119
|
+
* @readonly
|
|
120
|
+
* @memberof RpgClientEngine
|
|
121
|
+
*/
|
|
122
|
+
objects: Observable<ObjectFixtureList> = this.gameEngine.objects
|
|
123
|
+
|
|
124
|
+
constructor(public gameEngine: GameEngineClient, private options) {
|
|
125
|
+
this.tick.subscribe(({ timestamp, deltaTime }) => {
|
|
126
|
+
if (timestamp != -1) this.step(timestamp, deltaTime)
|
|
127
|
+
})
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private async _init() {
|
|
131
|
+
this.renderer = new RpgRenderer(this)
|
|
132
|
+
|
|
133
|
+
const pluginLoadRessource = async (hookName: string, type: string) => {
|
|
134
|
+
const resource = this.options[type] || []
|
|
135
|
+
this.options[type] = [
|
|
136
|
+
...Utils.arrayFlat(await RpgPlugin.emit(hookName, resource)) || [],
|
|
137
|
+
...resource
|
|
138
|
+
]
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
await pluginLoadRessource(HookClient.AddSpriteSheet, 'spritesheets')
|
|
142
|
+
await pluginLoadRessource(HookClient.AddGui, 'gui')
|
|
143
|
+
await pluginLoadRessource(HookClient.AddSound, 'sounds')
|
|
144
|
+
|
|
145
|
+
this.renderer.options = {
|
|
146
|
+
selector: '#rpg',
|
|
147
|
+
selectorCanvas: '#canvas',
|
|
148
|
+
selectorGui: '#gui',
|
|
149
|
+
canvas: {},
|
|
150
|
+
gui: [],
|
|
151
|
+
spritesheets: [],
|
|
152
|
+
sounds: [],
|
|
153
|
+
...this.options
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
this.io = this.options.io
|
|
157
|
+
if (this.options.serverFps) this.serverFps = this.options.serverFps
|
|
158
|
+
this.globalConfig = this.options.globalConfig
|
|
159
|
+
if (this.globalConfig.assetsPath) this.assetsPath = this.globalConfig.assetsPath
|
|
160
|
+
this.gameEngine.standalone = this.options.standalone
|
|
161
|
+
this.gameEngine.renderer = this.renderer
|
|
162
|
+
this.gameEngine.clientEngine = this
|
|
163
|
+
|
|
164
|
+
this.addSpriteSheet(this.renderer.options.spritesheets)
|
|
165
|
+
this.addSound(this.renderer.options.sounds)
|
|
166
|
+
|
|
167
|
+
if (typeof __RPGJS_PRODUCTION__ != 'undefined' && __RPGJS_PRODUCTION__) {
|
|
168
|
+
if ('serviceWorker' in navigator) {
|
|
169
|
+
window.addEventListener('load', () => {
|
|
170
|
+
navigator.serviceWorker.register('/service-worker.js')
|
|
171
|
+
})
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
this.controls = new KeyboardControls(this)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
private addResource(resourceClass, cb) {
|
|
179
|
+
let array = resourceClass
|
|
180
|
+
if (!Utils.isArray(resourceClass)) {
|
|
181
|
+
array = [resourceClass]
|
|
182
|
+
}
|
|
183
|
+
cb(array, this)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Listen to each frame
|
|
188
|
+
*
|
|
189
|
+
* @prop {Observable<{ timestamp: number, deltaTime: number, frame: number }>} tick
|
|
190
|
+
* @readonly
|
|
191
|
+
* @since 3.0.0-beta.5
|
|
192
|
+
* @memberof RpgClientEngine
|
|
193
|
+
* @example
|
|
194
|
+
*
|
|
195
|
+
* ```ts
|
|
196
|
+
* client.tick.subscribe(({ timestamp, deltaTime, frame }) => {
|
|
197
|
+
*
|
|
198
|
+
* })
|
|
199
|
+
* ```
|
|
200
|
+
* */
|
|
201
|
+
get tick(): Observable<Tick> {
|
|
202
|
+
return this.scheduler.tick as any
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Adds Spritesheet classes
|
|
207
|
+
*
|
|
208
|
+
* @title Add Spritesheet
|
|
209
|
+
* @method addSpriteSheet(spritesheetClass|spritesheetClass[])
|
|
210
|
+
* @param { Class|Class[] } spritesheetClass
|
|
211
|
+
* @method addSpriteSheet(url,id)
|
|
212
|
+
* @param {string} url Define the url of the resource
|
|
213
|
+
* @param {string} id Define a resource identifier
|
|
214
|
+
* @returns {Class}
|
|
215
|
+
* @since 3.0.0-beta.3
|
|
216
|
+
* @memberof RpgClientEngine
|
|
217
|
+
*/
|
|
218
|
+
addSpriteSheet(spritesheetClass: constructor<any>)
|
|
219
|
+
addSpriteSheet(url: string, id: string)
|
|
220
|
+
addSpriteSheet<T = any>(spritesheetClass: constructor<T> | string, id?: string): constructor<T> {
|
|
221
|
+
if (typeof spritesheetClass === 'string') {
|
|
222
|
+
if (!id) {
|
|
223
|
+
throw log('Please, specify the resource ID (second parameter)')
|
|
224
|
+
}
|
|
225
|
+
@Spritesheet({
|
|
226
|
+
id,
|
|
227
|
+
image: this.getResourceUrl(spritesheetClass)
|
|
228
|
+
})
|
|
229
|
+
class AutoSpritesheet { }
|
|
230
|
+
spritesheetClass = AutoSpritesheet as any
|
|
231
|
+
}
|
|
232
|
+
this.addResource(spritesheetClass, _initSpritesheet)
|
|
233
|
+
return spritesheetClass as any
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Adds Sound classes
|
|
238
|
+
*
|
|
239
|
+
* @title Add Sound
|
|
240
|
+
* @method addSound(soundClass|soundClass[])
|
|
241
|
+
* @param { Class|Class[] } soundClass
|
|
242
|
+
* @method addSound(url,id)
|
|
243
|
+
* @param {string} url Define the url of the resource
|
|
244
|
+
* @param {string} id Define a resource identifier
|
|
245
|
+
* @returns {Class}
|
|
246
|
+
* @since 3.0.0-beta.3
|
|
247
|
+
* @memberof RpgClientEngine
|
|
248
|
+
*/
|
|
249
|
+
addSound(soundClass: constructor<any>)
|
|
250
|
+
addSound(url: string, id: string)
|
|
251
|
+
addSound<T = any>(soundClass: constructor<T> | string, id?: string): constructor<T> {
|
|
252
|
+
if (typeof soundClass === 'string') {
|
|
253
|
+
if (!id) {
|
|
254
|
+
throw log('Please, specify the resource ID (second parameter)')
|
|
255
|
+
}
|
|
256
|
+
@Sound({
|
|
257
|
+
id,
|
|
258
|
+
sound: this.getResourceUrl(soundClass)
|
|
259
|
+
})
|
|
260
|
+
class AutoSound { }
|
|
261
|
+
soundClass = AutoSound as any
|
|
262
|
+
}
|
|
263
|
+
this.addResource(soundClass, _initSound)
|
|
264
|
+
return soundClass as any
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
getResourceUrl(source: string): string {
|
|
268
|
+
if (source.startsWith('data:')) {
|
|
269
|
+
return source
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// @ts-ignore
|
|
273
|
+
const staticDir = import.meta.env.VITE_BUILT
|
|
274
|
+
|
|
275
|
+
if (staticDir) {
|
|
276
|
+
return this.assetsPath + '/' + Utils.basename(source)
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return source
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Starts the client side and connects to the server
|
|
284
|
+
*
|
|
285
|
+
* @title Start Client Engine
|
|
286
|
+
* @method start()
|
|
287
|
+
* @returns {Promise< void >}
|
|
288
|
+
* @memberof RpgClientEngine
|
|
289
|
+
*/
|
|
290
|
+
async start(options: { renderLoop: boolean } = {
|
|
291
|
+
renderLoop: true
|
|
292
|
+
}) {
|
|
293
|
+
await this._init()
|
|
294
|
+
await this.renderer.init()
|
|
295
|
+
const { maxFps } = this.options
|
|
296
|
+
|
|
297
|
+
if (options.renderLoop) {
|
|
298
|
+
this.scheduler.start({
|
|
299
|
+
maxFps
|
|
300
|
+
})
|
|
301
|
+
// The processing is outside the rendering loop because if the FPS are lower (or higher) then the sending to the server would be slower or faster. Here it is constant
|
|
302
|
+
setInterval(() => {
|
|
303
|
+
this.processInput()
|
|
304
|
+
}, Utils.fps2ms(this.serverFps))
|
|
305
|
+
}
|
|
306
|
+
const ret: boolean[] = await RpgPlugin.emit(HookClient.Start, this)
|
|
307
|
+
this.matchMakerService = this.options.globalConfig.matchMakerService
|
|
308
|
+
const hasFalseValue = ret.findIndex(el => el === false) != - 1
|
|
309
|
+
if (!hasFalseValue) {
|
|
310
|
+
let serverUri = {} as MatchMakerResponse
|
|
311
|
+
if (this.matchMakerService) {
|
|
312
|
+
if (Utils.isFunction(this.matchMakerService)) {
|
|
313
|
+
serverUri = (this.matchMakerService as Function)()
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
serverUri = await lastValueFrom(ajax.getJSON<MatchMakerResponse>(this.matchMakerService as string))
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
}
|
|
320
|
+
// @ts-ignore
|
|
321
|
+
const envUrl = import.meta.env.VITE_SERVER_URL
|
|
322
|
+
this.connection(
|
|
323
|
+
serverUri.url ? serverUri.url + ':' + serverUri.port :
|
|
324
|
+
envUrl ? envUrl : undefined
|
|
325
|
+
)
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Display the next frame. Useful for unit tests
|
|
331
|
+
*
|
|
332
|
+
* @title Next Frame
|
|
333
|
+
* @since 3.0.0-beta.5
|
|
334
|
+
* @param {number} timestamp Indicate the timestamp of the frame
|
|
335
|
+
* @method nextFrame()
|
|
336
|
+
* @memberof RpgClientEngine
|
|
337
|
+
*/
|
|
338
|
+
nextFrame(timestamp: number): void {
|
|
339
|
+
this.scheduler.nextTick(timestamp)
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
async sendInput(actionName: string | Control) {
|
|
343
|
+
const player = this.player
|
|
344
|
+
if (!player) return
|
|
345
|
+
if (player.canMove) {
|
|
346
|
+
player.pendingMove.push({
|
|
347
|
+
input: actionName,
|
|
348
|
+
frame: this.scheduler.frame
|
|
349
|
+
})
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
get player(): RpgCommonPlayer | null {
|
|
354
|
+
return this.gameEngine.world.getObject(this.gameEngine.playerId)
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
private serverReconciliation(player: RpgCommonPlayer) {
|
|
358
|
+
let garbage: number[] = []
|
|
359
|
+
this.serverFrames.forEach((serverData, frame) => {
|
|
360
|
+
const { data: serverPos, time: serverTime } = serverData
|
|
361
|
+
const client = this.clientFrames.get(frame)
|
|
362
|
+
if (!client || (client && client.data.x != serverPos.x || client.data.y != serverPos.y)) {
|
|
363
|
+
if (serverPos.x) player.position.x = serverPos.x
|
|
364
|
+
if (serverPos.y) player.position.y = serverPos.y
|
|
365
|
+
}
|
|
366
|
+
player.position.z = serverPos.z
|
|
367
|
+
garbage.push(frame)
|
|
368
|
+
})
|
|
369
|
+
garbage.forEach(frame => {
|
|
370
|
+
this.serverFrames.delete(frame)
|
|
371
|
+
this.clientFrames.delete(frame)
|
|
372
|
+
})
|
|
373
|
+
garbage = []
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
private async step(t: number, dt: number) {
|
|
377
|
+
RpgPlugin.emit(HookClient.Step, [this, t, dt], true)
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
async processInput() {
|
|
381
|
+
const player = this.player
|
|
382
|
+
this.controls.preStep()
|
|
383
|
+
if (player) {
|
|
384
|
+
if (player.pendingMove.length > 0) {
|
|
385
|
+
const { inputs: inputEvent } = await this.gameEngine.processInput<RpgCommonPlayer>(this.gameEngine.playerId, this.controls.options)
|
|
386
|
+
if (inputEvent.length == 0) return
|
|
387
|
+
const frame = Date.now()
|
|
388
|
+
this.clientFrames.set(frame, {
|
|
389
|
+
data: player.position.copy(),
|
|
390
|
+
time: frame
|
|
391
|
+
})
|
|
392
|
+
if (this.socket) {
|
|
393
|
+
this.socket.emit('move', { input: inputEvent, frame })
|
|
394
|
+
}
|
|
395
|
+
RpgPlugin.emit(HookClient.SendInput, [this, inputEvent], true)
|
|
396
|
+
}
|
|
397
|
+
if (player.canMove) this.serverReconciliation(player)
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
*Connect to the server
|
|
403
|
+
*
|
|
404
|
+
* @title Connect to server
|
|
405
|
+
* @method connection()
|
|
406
|
+
* @returns {void}
|
|
407
|
+
* @memberof RpgClientEngine
|
|
408
|
+
*/
|
|
409
|
+
connection(uri?: string) {
|
|
410
|
+
const { standalone } = this.gameEngine
|
|
411
|
+
|
|
412
|
+
if (!standalone) {
|
|
413
|
+
this.socket = this.io(uri, {
|
|
414
|
+
auth: {
|
|
415
|
+
token: this.session
|
|
416
|
+
}
|
|
417
|
+
})
|
|
418
|
+
}
|
|
419
|
+
else {
|
|
420
|
+
this.socket = this.io
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
this.socket.on('connect', () => {
|
|
424
|
+
if (RpgGui.exists(PrebuiltGui.Disconnect)) RpgGui.hide(PrebuiltGui.Disconnect)
|
|
425
|
+
RpgPlugin.emit(HookClient.Connected, [this, this.socket], true)
|
|
426
|
+
this.hasBeenDisconnected = false
|
|
427
|
+
})
|
|
428
|
+
|
|
429
|
+
this.socket.on('playerJoined', (playerEvent) => {
|
|
430
|
+
this.gameEngine.playerId = playerEvent.playerId
|
|
431
|
+
this.session = playerEvent.session
|
|
432
|
+
})
|
|
433
|
+
|
|
434
|
+
this.socket.on('connect_error', (err: any) => {
|
|
435
|
+
RpgPlugin.emit(HookClient.ConnectedError, [this, err, this.socket], true)
|
|
436
|
+
})
|
|
437
|
+
|
|
438
|
+
this.socket.on('preLoadScene', (name: string) => {
|
|
439
|
+
if (this.lastScene == name) {
|
|
440
|
+
return
|
|
441
|
+
}
|
|
442
|
+
this.lastScene = name
|
|
443
|
+
this.renderer.transitionScene(name)
|
|
444
|
+
})
|
|
445
|
+
|
|
446
|
+
this.socket.on(SocketEvents.GameReload, () => {
|
|
447
|
+
window.location.reload()
|
|
448
|
+
})
|
|
449
|
+
|
|
450
|
+
this.socket.on(SocketEvents.LoadScene, ({ name, data }) => {
|
|
451
|
+
this.renderer.loadScene(name, data)
|
|
452
|
+
})
|
|
453
|
+
|
|
454
|
+
this.socket.on(SocketEvents.ChangeServer, ({ url, port }) => {
|
|
455
|
+
const connection = url + ':' + port
|
|
456
|
+
if (this.lastConnection == connection) {
|
|
457
|
+
return
|
|
458
|
+
}
|
|
459
|
+
if (this.subscriptionWorld) {
|
|
460
|
+
this.subscriptionWorld.unsubscribe()
|
|
461
|
+
}
|
|
462
|
+
this.lastConnection = connection
|
|
463
|
+
this.serverChanging = true
|
|
464
|
+
this.socket.disconnect()
|
|
465
|
+
this.connection(connection)
|
|
466
|
+
})
|
|
467
|
+
|
|
468
|
+
this.socket.on('changeTile', ({ tiles, x, y }) => {
|
|
469
|
+
const scene = this.renderer.getScene<SceneMap>()
|
|
470
|
+
scene?.changeTile(x, y, tiles)
|
|
471
|
+
})
|
|
472
|
+
|
|
473
|
+
this.socket.on(SocketEvents.CallMethod, ({ objectId, params, name }) => {
|
|
474
|
+
const scene = this.renderer.getScene<SceneMap>()
|
|
475
|
+
const sprite = scene?.getPlayer(objectId)
|
|
476
|
+
if (!sprite) return
|
|
477
|
+
switch (name) {
|
|
478
|
+
case SocketMethods.ShowAnimation:
|
|
479
|
+
scene?.showAnimation({
|
|
480
|
+
attachTo: sprite,
|
|
481
|
+
graphic: params[0],
|
|
482
|
+
animationName: params[1],
|
|
483
|
+
replaceGraphic: params[2]
|
|
484
|
+
})
|
|
485
|
+
break
|
|
486
|
+
case SocketMethods.CameraFollow:
|
|
487
|
+
const [spriteId, options] = params
|
|
488
|
+
scene?.cameraFollowSprite(spriteId, options)
|
|
489
|
+
break
|
|
490
|
+
case SocketMethods.PlaySound:
|
|
491
|
+
RpgSound.play(params[0])
|
|
492
|
+
break
|
|
493
|
+
case SocketMethods.ModeMove:
|
|
494
|
+
const player = this.player
|
|
495
|
+
const { checkCollision } = params[0]
|
|
496
|
+
if (player) {
|
|
497
|
+
player.checkCollision = checkCollision
|
|
498
|
+
}
|
|
499
|
+
break
|
|
500
|
+
}
|
|
501
|
+
})
|
|
502
|
+
|
|
503
|
+
let lastRoomId = ''
|
|
504
|
+
|
|
505
|
+
this.subscriptionWorld = World.listen(this.socket)
|
|
506
|
+
.value
|
|
507
|
+
.subscribe(async (val: { data: any, partial: any, time: number, roomId: string, resetProps: string[] }) => {
|
|
508
|
+
|
|
509
|
+
const scene = this.renderer.getScene<SceneMap>()
|
|
510
|
+
|
|
511
|
+
if (!val.data) {
|
|
512
|
+
return
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
const partialRoom = val.partial
|
|
516
|
+
|
|
517
|
+
if (val.roomId != lastRoomId) {
|
|
518
|
+
this.clientFrames.clear()
|
|
519
|
+
this.serverFrames.clear()
|
|
520
|
+
this.gameEngine.resetObjects()
|
|
521
|
+
lastRoomId = val.roomId
|
|
522
|
+
this.isTeleported = false
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
const objectsChanged = {}
|
|
526
|
+
|
|
527
|
+
const change = (prop, root = val, localEvent = false) => {
|
|
528
|
+
const list = root.data[prop]
|
|
529
|
+
const partial = root.partial[prop]
|
|
530
|
+
const isShape = prop == 'shapes'
|
|
531
|
+
if (!partial) {
|
|
532
|
+
return
|
|
533
|
+
}
|
|
534
|
+
if (val.resetProps.indexOf(prop) != -1) {
|
|
535
|
+
const objects = isShape ? this.gameEngine.getShapes() : this.gameEngine.getObjects()
|
|
536
|
+
for (let key in objects) {
|
|
537
|
+
const obj = objects[key]
|
|
538
|
+
if (obj) {
|
|
539
|
+
this.gameEngine.removeObjectAndShape(key)
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
for (let key in partial) {
|
|
544
|
+
const obj = list[key]
|
|
545
|
+
const paramsChanged = partial ? partial[key] : undefined
|
|
546
|
+
if (obj == null) {
|
|
547
|
+
this.gameEngine.removeObjectAndShape(key)
|
|
548
|
+
}
|
|
549
|
+
if (!obj) continue
|
|
550
|
+
|
|
551
|
+
if (!isShape) {
|
|
552
|
+
obj.type = {
|
|
553
|
+
users: PlayerType.Player,
|
|
554
|
+
events: PlayerType.Event
|
|
555
|
+
}[prop]
|
|
556
|
+
}
|
|
557
|
+
if (prop == 'users' && this.gameEngine.playerId == key) {
|
|
558
|
+
if (obj.events) {
|
|
559
|
+
const nbEvents = Object.values(obj.events)
|
|
560
|
+
if (nbEvents.length == 0) {
|
|
561
|
+
this.gameEngine.events = {}
|
|
562
|
+
}
|
|
563
|
+
else {
|
|
564
|
+
change('events', {
|
|
565
|
+
data: obj,
|
|
566
|
+
partial: paramsChanged,
|
|
567
|
+
time: val.time,
|
|
568
|
+
roomId: val.roomId,
|
|
569
|
+
resetProps: val.resetProps
|
|
570
|
+
}, true)
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
if (partialRoom?.pos && partialRoom?.frame !== undefined) {
|
|
574
|
+
this.serverFrames.set(partialRoom.frame, {
|
|
575
|
+
data: partialRoom.pos,
|
|
576
|
+
time: Date.now()
|
|
577
|
+
})
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
objectsChanged[key] = this.gameEngine.updateObject({
|
|
581
|
+
playerId: key,
|
|
582
|
+
params: obj,
|
|
583
|
+
localEvent,
|
|
584
|
+
paramsChanged,
|
|
585
|
+
isShape
|
|
586
|
+
})
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
if (partialRoom.join) {
|
|
591
|
+
this.roomJoin.next(partialRoom)
|
|
592
|
+
this.roomJoin.complete()
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
change('users')
|
|
596
|
+
change('events')
|
|
597
|
+
change('shapes')
|
|
598
|
+
|
|
599
|
+
this.gameEngine.setObjectsChanged(objectsChanged)
|
|
600
|
+
|
|
601
|
+
if (scene) {
|
|
602
|
+
scene.update(val)
|
|
603
|
+
}
|
|
604
|
+
})
|
|
605
|
+
|
|
606
|
+
this.socket.on('disconnect', (reason: string) => {
|
|
607
|
+
if (this.serverChanging) {
|
|
608
|
+
return
|
|
609
|
+
}
|
|
610
|
+
if (RpgGui.exists(PrebuiltGui.Disconnect)) RpgGui.display(PrebuiltGui.Disconnect)
|
|
611
|
+
RpgPlugin.emit(HookClient.Disconnect, [this, reason, this.socket], true)
|
|
612
|
+
this.hasBeenDisconnected = true
|
|
613
|
+
})
|
|
614
|
+
|
|
615
|
+
RpgGui._setSocket(this.socket)
|
|
616
|
+
|
|
617
|
+
if (standalone) {
|
|
618
|
+
this.socket.connection({
|
|
619
|
+
auth: {
|
|
620
|
+
token: this.session
|
|
621
|
+
}
|
|
622
|
+
})
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
this.serverChanging = false
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
get world(): any {
|
|
629
|
+
return World
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// shortcuts
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* VueJS Application instance
|
|
636
|
+
*
|
|
637
|
+
* [https://v3.vuejs.org/api/application-api.html](https://v3.vuejs.org/api/application-api.html)
|
|
638
|
+
*
|
|
639
|
+
* @prop {Vue} [vueApp]
|
|
640
|
+
* @readonly
|
|
641
|
+
* @memberof RpgClientEngine
|
|
642
|
+
* */
|
|
643
|
+
get vueApp() {
|
|
644
|
+
return this.renderer.app
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/**
|
|
648
|
+
* VueJS Parent component instance
|
|
649
|
+
*
|
|
650
|
+
* [https://v3.vuejs.org/api/instance-properties.html](https://v3.vuejs.org/api/instance-properties.html)
|
|
651
|
+
*
|
|
652
|
+
* @prop {Vue Instance} [vueInstance]
|
|
653
|
+
* @readonly
|
|
654
|
+
* @memberof RpgClientEngine
|
|
655
|
+
* */
|
|
656
|
+
get vueInstance() {
|
|
657
|
+
return this.renderer.vm
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* retrieves the current scene (SceneMap if you are on a map)
|
|
662
|
+
*
|
|
663
|
+
* @prop {RpgScene} [scene]
|
|
664
|
+
* @deprecated
|
|
665
|
+
* @readonly
|
|
666
|
+
* @memberof RpgClientEngine
|
|
667
|
+
* */
|
|
668
|
+
get scene() {
|
|
669
|
+
return this.renderer.getScene()
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* retrieves the current scene (SceneMap if you are on a map)
|
|
674
|
+
*
|
|
675
|
+
* @title Connect to server
|
|
676
|
+
* @method getScene()
|
|
677
|
+
* @returns {RpgScene}
|
|
678
|
+
* @memberof RpgClientEngine
|
|
679
|
+
*/
|
|
680
|
+
getScene<T = Scene>(): T | null {
|
|
681
|
+
return this.renderer.getScene<T>()
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
get PIXI() {
|
|
685
|
+
return PIXI
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
get playerId(): string {
|
|
689
|
+
return this.gameEngine.playerId
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
reset() {
|
|
693
|
+
this.subscriptionWorld.unsubscribe()
|
|
694
|
+
this.world.reset()
|
|
695
|
+
spritesheets.clear()
|
|
696
|
+
sounds.clear()
|
|
697
|
+
Assets.reset()
|
|
698
|
+
utils.clearTextureCache()
|
|
699
|
+
for (let textureUrl in utils.BaseTextureCache) {
|
|
700
|
+
delete utils.BaseTextureCache[textureUrl]
|
|
701
|
+
}
|
|
702
|
+
for (let textureUrl in utils.TextureCache) {
|
|
703
|
+
delete utils.TextureCache[textureUrl]
|
|
704
|
+
}
|
|
705
|
+
RpgGui.clear()
|
|
706
|
+
RpgCommonMap.bufferClient.clear()
|
|
707
|
+
RpgSound.clear()
|
|
708
|
+
}
|
|
709
|
+
}
|