@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.
Files changed (164) 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 +2 -1
  61. package/lib/RpgClientEngine.js +192 -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.js +26 -28
  111. package/lib/clientEntryPoint.js.map +1 -1
  112. package/lib/index.js +21 -75
  113. package/lib/index.js.map +1 -1
  114. package/package.json +19 -19
  115. package/src/Components/AbstractComponent.ts +120 -0
  116. package/src/Components/BarComponent.ts +179 -0
  117. package/src/Components/Component.ts +506 -0
  118. package/src/Components/DebugComponent.ts +36 -0
  119. package/src/Components/ImageComponent.ts +30 -0
  120. package/src/Components/ShapeComponent.ts +64 -0
  121. package/src/Components/TextComponent.ts +33 -0
  122. package/src/Components/TileComponent.ts +43 -0
  123. package/src/Effects/Animation.ts +297 -0
  124. package/src/Effects/AnimationCharacter.ts +7 -0
  125. package/src/Effects/Spinner.ts +19 -0
  126. package/src/Effects/Timeline.ts +294 -0
  127. package/src/Effects/TransitionScene.ts +57 -0
  128. package/src/GameEngine.ts +284 -0
  129. package/src/Interfaces/Character.ts +7 -0
  130. package/src/Interfaces/Scene.ts +9 -0
  131. package/src/KeyboardControls.ts +552 -0
  132. package/src/Logger.ts +3 -0
  133. package/src/Presets/AnimationSpritesheet.ts +36 -0
  134. package/src/Presets/Scene.ts +3 -0
  135. package/src/Renderer.ts +263 -0
  136. package/src/Resources.ts +40 -0
  137. package/src/RpgClient.ts +333 -0
  138. package/src/RpgClientEngine.ts +709 -0
  139. package/src/RpgGui.ts +553 -0
  140. package/src/RpgGuiCompiled.ts +43 -0
  141. package/src/Scene/EventLayer.ts +9 -0
  142. package/src/Scene/Map.ts +393 -0
  143. package/src/Scene/Scene.ts +270 -0
  144. package/src/Scene/SceneData.ts +13 -0
  145. package/src/Sound/RpgSound.ts +50 -0
  146. package/src/Sound/Sound.ts +91 -0
  147. package/src/Sound/Sounds.ts +7 -0
  148. package/src/Sprite/Character.ts +149 -0
  149. package/src/Sprite/Player.ts +3 -0
  150. package/src/Sprite/Spritesheet.ts +392 -0
  151. package/src/Sprite/Spritesheets.ts +8 -0
  152. package/src/Tilemap/CommonLayer.ts +20 -0
  153. package/src/Tilemap/ImageLayer.ts +20 -0
  154. package/src/Tilemap/Tile.ts +80 -0
  155. package/src/Tilemap/TileLayer.ts +142 -0
  156. package/src/Tilemap/TileSet.ts +40 -0
  157. package/src/Tilemap/index.ts +173 -0
  158. package/src/clientEntryPoint.ts +141 -0
  159. package/src/index.ts +25 -0
  160. package/src/types/howler.d.ts +73 -0
  161. package/tsconfig.json +30 -0
  162. package/lib/Components/ColorComponent.d.ts +0 -9
  163. package/lib/Components/ColorComponent.js +0 -18
  164. 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
+ }