@rpgjs/client 3.3.2 → 4.0.0-beta.10

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