gdcore-tools 2.0.0-gd-v5.5.224-autobuild → 2.0.0-gd-v5.5.226-autobuild

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 (202) hide show
  1. package/dist/Runtime/CustomRuntimeObject.js +1 -1
  2. package/dist/Runtime/CustomRuntimeObject.js.map +2 -2
  3. package/dist/Runtime/CustomRuntimeObject2D.js.map +2 -2
  4. package/dist/Runtime/Extensions/3D/A_RuntimeObject3D.js.map +2 -2
  5. package/dist/Runtime/Extensions/3D/Base3DBehavior.js.map +2 -2
  6. package/dist/Runtime/Extensions/3D/Cube3DRuntimeObject.js +1 -1
  7. package/dist/Runtime/Extensions/3D/Cube3DRuntimeObject.js.map +2 -2
  8. package/dist/Runtime/Extensions/3D/Cube3DRuntimeObjectPixiRenderer.js +1 -1
  9. package/dist/Runtime/Extensions/3D/Cube3DRuntimeObjectPixiRenderer.js.map +2 -2
  10. package/dist/Runtime/Extensions/3D/CustomRuntimeObject3D.js.map +2 -2
  11. package/dist/Runtime/Extensions/3D/CustomRuntimeObject3DRenderer.js +1 -1
  12. package/dist/Runtime/Extensions/3D/CustomRuntimeObject3DRenderer.js.map +2 -2
  13. package/dist/Runtime/Extensions/3D/JsExtension.js +219 -108
  14. package/dist/Runtime/Extensions/3D/Model3DRuntimeObject.js +1 -1
  15. package/dist/Runtime/Extensions/3D/Model3DRuntimeObject.js.map +2 -2
  16. package/dist/Runtime/Extensions/3D/Model3DRuntimeObject3DRenderer.js +1 -1
  17. package/dist/Runtime/Extensions/3D/Model3DRuntimeObject3DRenderer.js.map +2 -2
  18. package/dist/Runtime/Extensions/AdMob/JsExtension.js +63 -1
  19. package/dist/Runtime/Extensions/AdMob/admobtools.js +1 -1
  20. package/dist/Runtime/Extensions/AdMob/admobtools.js.map +2 -2
  21. package/dist/Runtime/Extensions/AdvancedWindow/electron-advancedwindowtools.js.map +2 -2
  22. package/dist/Runtime/Extensions/AnchorBehavior/anchorruntimebehavior.js +1 -1
  23. package/dist/Runtime/Extensions/AnchorBehavior/anchorruntimebehavior.js.map +2 -2
  24. package/dist/Runtime/Extensions/BBText/JsExtension.js +10 -9
  25. package/dist/Runtime/Extensions/BBText/bbtextruntimeobject.js.map +2 -2
  26. package/dist/Runtime/Extensions/BitmapText/JsExtension.js +4 -6
  27. package/dist/Runtime/Extensions/BitmapText/bitmaptextruntimeobject-pixi-renderer.js.map +2 -2
  28. package/dist/Runtime/Extensions/BitmapText/bitmaptextruntimeobject.js.map +2 -2
  29. package/dist/Runtime/Extensions/DialogueTree/dialoguetools.js.map +2 -2
  30. package/dist/Runtime/Extensions/DraggableBehavior/draggableruntimebehavior.js.map +2 -2
  31. package/dist/Runtime/Extensions/Effects/adjustment-pixi-filter.js.map +2 -2
  32. package/dist/Runtime/Extensions/Effects/advanced-bloom-pixi-filter.js.map +2 -2
  33. package/dist/Runtime/Extensions/Effects/ascii-pixi-filter.js.map +2 -2
  34. package/dist/Runtime/Extensions/Effects/bevel-pixi-filter.js.map +2 -2
  35. package/dist/Runtime/Extensions/Effects/black-and-white-pixi-filter.js.map +2 -2
  36. package/dist/Runtime/Extensions/Effects/blending-mode-pixi-filter.js.map +2 -2
  37. package/dist/Runtime/Extensions/Effects/brightness-pixi-filter.js.map +2 -2
  38. package/dist/Runtime/Extensions/Effects/bulge-pinch-pixi-filter.js.map +2 -2
  39. package/dist/Runtime/Extensions/Effects/color-map-pixi-filter.js.map +2 -2
  40. package/dist/Runtime/Extensions/Effects/color-replace-pixi-filter.js.map +2 -2
  41. package/dist/Runtime/Extensions/Effects/crt-pixi-filter.js.map +2 -2
  42. package/dist/Runtime/Extensions/Effects/displacement-pixi-filter.js.map +2 -2
  43. package/dist/Runtime/Extensions/Effects/dot-pixi-filter.js.map +2 -2
  44. package/dist/Runtime/Extensions/Effects/drop-shadow-pixi-filter.js.map +2 -2
  45. package/dist/Runtime/Extensions/Effects/glitch-pixi-filter.js.map +2 -2
  46. package/dist/Runtime/Extensions/Effects/glow-pixi-filter.js.map +2 -2
  47. package/dist/Runtime/Extensions/Effects/godray-pixi-filter.js.map +2 -2
  48. package/dist/Runtime/Extensions/Effects/kawase-blur-pixi-filter.js.map +2 -2
  49. package/dist/Runtime/Extensions/Effects/noise-pixi-filter.js.map +2 -2
  50. package/dist/Runtime/Extensions/Effects/old-film-pixi-filter.js.map +2 -2
  51. package/dist/Runtime/Extensions/Effects/outline-pixi-filter.js.map +2 -2
  52. package/dist/Runtime/Extensions/Effects/pixelate-pixi-filter.js.map +2 -2
  53. package/dist/Runtime/Extensions/Effects/pixi-filters/types/drop-shadow/types.d.ts +10 -4
  54. package/dist/Runtime/Extensions/Effects/radial-blur-pixi-filter.js.map +2 -2
  55. package/dist/Runtime/Extensions/Effects/reflection-pixi-filter.js.map +2 -2
  56. package/dist/Runtime/Extensions/Effects/rgb-split-pixi-filter.js.map +2 -2
  57. package/dist/Runtime/Extensions/Effects/sepia-pixi-filter.js.map +2 -2
  58. package/dist/Runtime/Extensions/Effects/shockwave-pixi-filter.js.map +2 -2
  59. package/dist/Runtime/Extensions/Effects/tilt-shift-pixi-filter.js.map +2 -2
  60. package/dist/Runtime/Extensions/Effects/twist-pixi-filter.js.map +2 -2
  61. package/dist/Runtime/Extensions/Effects/zoom-blur-pixi-filter.js.map +2 -2
  62. package/dist/Runtime/Extensions/ExampleJsExtension/dummyeffect.js.map +2 -2
  63. package/dist/Runtime/Extensions/FacebookInstantGames/facebookinstantgamestools.js.map +2 -2
  64. package/dist/Runtime/Extensions/Firebase/A_firebasejs/firebase.d.ts +5 -4
  65. package/dist/Runtime/Extensions/Firebase/B_firebasetools/D_cloudfirestoretools.js.map +2 -2
  66. package/dist/Runtime/Extensions/Firebase/B_firebasetools/D_remoteconfigtools.js.map +2 -2
  67. package/dist/Runtime/Extensions/Firebase/JsExtension.js +21 -21
  68. package/dist/Runtime/Extensions/JsExtensionTypes.d.ts +1 -0
  69. package/dist/Runtime/Extensions/Leaderboards/leaderboardstools.js.map +2 -2
  70. package/dist/Runtime/Extensions/Lighting/JsExtension.js +2 -2
  71. package/dist/Runtime/Extensions/Lighting/lightobstacleruntimebehavior.js.map +2 -2
  72. package/dist/Runtime/Extensions/Lighting/lightruntimeobject-pixi-renderer.js.map +2 -2
  73. package/dist/Runtime/Extensions/Lighting/lightruntimeobject.js.map +2 -2
  74. package/dist/Runtime/Extensions/LinkedObjects/linkedobjects.js.map +2 -2
  75. package/dist/Runtime/Extensions/Multiplayer/JsExtension.js +122 -0
  76. package/dist/Runtime/Extensions/Multiplayer/messageManager.js.map +2 -2
  77. package/dist/Runtime/Extensions/Multiplayer/multiplayerVariablesManager.js.map +2 -2
  78. package/dist/Runtime/Extensions/Multiplayer/multiplayercomponents.js +1 -1
  79. package/dist/Runtime/Extensions/Multiplayer/multiplayercomponents.js.map +2 -2
  80. package/dist/Runtime/Extensions/Multiplayer/multiplayerobjectruntimebehavior.js.map +2 -2
  81. package/dist/Runtime/Extensions/Multiplayer/multiplayertools.js +1 -1
  82. package/dist/Runtime/Extensions/Multiplayer/multiplayertools.js.map +2 -2
  83. package/dist/Runtime/Extensions/Multiplayer/peerJsHelper.js +1 -1
  84. package/dist/Runtime/Extensions/Multiplayer/peerJsHelper.js.map +2 -2
  85. package/dist/Runtime/Extensions/Multiplayer/peerjs.d.ts +8 -10
  86. package/dist/Runtime/Extensions/P2P/peerjs.d.ts +8 -10
  87. package/dist/Runtime/Extensions/PanelSpriteObject/panelspriteruntimeobject-pixi-renderer.js.map +2 -2
  88. package/dist/Runtime/Extensions/PanelSpriteObject/panelspriteruntimeobject.js.map +2 -2
  89. package/dist/Runtime/Extensions/ParticleSystem/particleemitterobject-pixi-renderer.js.map +2 -2
  90. package/dist/Runtime/Extensions/ParticleSystem/particleemitterobject.js.map +2 -2
  91. package/dist/Runtime/Extensions/ParticleSystem/pixi-particles-pixi-renderer.d.ts +2 -1
  92. package/dist/Runtime/Extensions/PathfindingBehavior/pathfindingobstacleruntimebehavior.js.map +2 -2
  93. package/dist/Runtime/Extensions/PathfindingBehavior/pathfindingruntimebehavior.js.map +2 -2
  94. package/dist/Runtime/Extensions/Physics2Behavior/JsExtension.js +106 -106
  95. package/dist/Runtime/Extensions/Physics2Behavior/box2d.d.ts +13 -7
  96. package/dist/Runtime/Extensions/Physics2Behavior/physics2runtimebehavior.js.map +2 -2
  97. package/dist/Runtime/Extensions/Physics3DBehavior/Physics3DRuntimeBehavior.js.map +2 -2
  98. package/dist/Runtime/Extensions/Physics3DBehavior/PhysicsCharacter3DRuntimeBehavior.js +1 -1
  99. package/dist/Runtime/Extensions/Physics3DBehavior/PhysicsCharacter3DRuntimeBehavior.js.map +2 -2
  100. package/dist/Runtime/Extensions/PhysicsBehavior/physicsruntimebehavior.js.map +2 -2
  101. package/dist/Runtime/Extensions/PlatformBehavior/platformerobjectruntimebehavior.js.map +2 -2
  102. package/dist/Runtime/Extensions/PlatformBehavior/platformruntimebehavior.js.map +2 -2
  103. package/dist/Runtime/Extensions/PlayerAuthentication/playerauthenticationcomponents.js.map +1 -1
  104. package/dist/Runtime/Extensions/PlayerAuthentication/playerauthenticationtools.js +1 -1
  105. package/dist/Runtime/Extensions/PlayerAuthentication/playerauthenticationtools.js.map +2 -2
  106. package/dist/Runtime/Extensions/PrimitiveDrawing/shapepainterruntimeobject-pixi-renderer.js.map +2 -2
  107. package/dist/Runtime/Extensions/PrimitiveDrawing/shapepainterruntimeobject.js.map +2 -2
  108. package/dist/Runtime/Extensions/SpatialSound/spatialsoundtools.js +1 -1
  109. package/dist/Runtime/Extensions/SpatialSound/spatialsoundtools.js.map +2 -2
  110. package/dist/Runtime/Extensions/Spine/JsExtension.js +5 -4
  111. package/dist/Runtime/Extensions/Spine/managers/pixi-spine-atlas-manager.js +1 -1
  112. package/dist/Runtime/Extensions/Spine/managers/pixi-spine-atlas-manager.js.map +2 -2
  113. package/dist/Runtime/Extensions/Spine/managers/pixi-spine-manager.js +1 -1
  114. package/dist/Runtime/Extensions/Spine/managers/pixi-spine-manager.js.map +2 -2
  115. package/dist/Runtime/Extensions/Spine/spineruntimeobject-pixi-renderer.js +1 -1
  116. package/dist/Runtime/Extensions/Spine/spineruntimeobject-pixi-renderer.js.map +2 -2
  117. package/dist/Runtime/Extensions/Spine/spineruntimeobject.js.map +2 -2
  118. package/dist/Runtime/Extensions/Steamworks/JsExtension.js +12 -12
  119. package/dist/Runtime/Extensions/Steamworks/Z_steamworksinputtools.js.map +2 -2
  120. package/dist/Runtime/Extensions/Steamworks/steamworkstools.js.map +2 -2
  121. package/dist/Runtime/Extensions/TextEntryObject/textentryruntimeobject-pixi-renderer.js.map +2 -2
  122. package/dist/Runtime/Extensions/TextInput/textinputruntimeobject-pixi-renderer.js +1 -1
  123. package/dist/Runtime/Extensions/TextInput/textinputruntimeobject-pixi-renderer.js.map +2 -2
  124. package/dist/Runtime/Extensions/TextInput/textinputruntimeobject.js.map +2 -2
  125. package/dist/Runtime/Extensions/TextObject/textruntimeobject-pixi-renderer.js.map +2 -2
  126. package/dist/Runtime/Extensions/TextObject/textruntimeobject.js.map +2 -2
  127. package/dist/Runtime/Extensions/TileMap/JsExtension.js +20 -18
  128. package/dist/Runtime/Extensions/TileMap/TileMapRuntimeManager.js.map +2 -2
  129. package/dist/Runtime/Extensions/TileMap/collision/TransformedTileMap.js.map +2 -2
  130. package/dist/Runtime/Extensions/TileMap/helper/dts/model/TileMapModel.d.ts +1 -3
  131. package/dist/Runtime/Extensions/TileMap/simpletilemapruntimeobject.js.map +2 -2
  132. package/dist/Runtime/Extensions/TileMap/tilemapcollisionmaskruntimeobject.js.map +2 -2
  133. package/dist/Runtime/Extensions/TileMap/tilemapruntimeobject-pixi-renderer.js.map +1 -1
  134. package/dist/Runtime/Extensions/TileMap/tilemapruntimeobject.js.map +2 -2
  135. package/dist/Runtime/Extensions/TiledSpriteObject/tiledspriteruntimeobject-pixi-renderer.js.map +2 -2
  136. package/dist/Runtime/Extensions/TiledSpriteObject/tiledspriteruntimeobject.js.map +2 -2
  137. package/dist/Runtime/Extensions/TopDownMovementBehavior/topdownmovementruntimebehavior.js.map +2 -2
  138. package/dist/Runtime/Extensions/TweenBehavior/tweenruntimebehavior.js.map +1 -1
  139. package/dist/Runtime/Extensions/Video/JsExtension.js +2 -1
  140. package/dist/Runtime/Extensions/Video/videoruntimeobject-pixi-renderer.js.map +2 -2
  141. package/dist/Runtime/Extensions/Video/videoruntimeobject.js.map +2 -2
  142. package/dist/Runtime/InAppTutorialMessage.js +6 -0
  143. package/dist/Runtime/InAppTutorialMessage.js.map +7 -0
  144. package/dist/Runtime/Model3DManager.js.map +2 -2
  145. package/dist/Runtime/ResourceLoader.js.map +2 -2
  146. package/dist/Runtime/RuntimeCustomObjectLayer.js +1 -1
  147. package/dist/Runtime/RuntimeCustomObjectLayer.js.map +2 -2
  148. package/dist/Runtime/RuntimeInstanceContainer.js.map +1 -1
  149. package/dist/Runtime/RuntimeLayer.js.map +2 -2
  150. package/dist/Runtime/SpriteAnimator.js.map +2 -2
  151. package/dist/Runtime/affinetransformation.js.map +1 -1
  152. package/dist/Runtime/debugger-client/abstract-debugger-client.js.map +2 -2
  153. package/dist/Runtime/debugger-client/hot-reloader.js +2 -2
  154. package/dist/Runtime/debugger-client/hot-reloader.js.map +2 -2
  155. package/dist/Runtime/debugger-client/websocket-debugger-client.js +1 -1
  156. package/dist/Runtime/debugger-client/websocket-debugger-client.js.map +2 -2
  157. package/dist/Runtime/events-tools/networktools.js +1 -1
  158. package/dist/Runtime/events-tools/networktools.js.map +2 -2
  159. package/dist/Runtime/fontfaceobserver-font-manager/fontfaceobserver-font-manager.js.map +2 -2
  160. package/dist/Runtime/gd.js.map +2 -2
  161. package/dist/Runtime/howler-sound-manager/howler-sound-manager.js +1 -1
  162. package/dist/Runtime/howler-sound-manager/howler-sound-manager.js.map +2 -2
  163. package/dist/Runtime/inputmanager.js.map +2 -2
  164. package/dist/Runtime/jsonmanager.js.map +2 -2
  165. package/dist/Runtime/layer.js.map +2 -2
  166. package/dist/Runtime/libs/nanomarkdown.js +5 -0
  167. package/dist/Runtime/libs/nanomarkdown.js.map +7 -0
  168. package/dist/Runtime/object-capabilities/AnimatableBehavior.js.map +2 -2
  169. package/dist/Runtime/object-capabilities/EffectBehavior.js.map +2 -2
  170. package/dist/Runtime/object-capabilities/FlippableBehavior.js.map +2 -2
  171. package/dist/Runtime/object-capabilities/OpacityBehavior.js.map +2 -2
  172. package/dist/Runtime/object-capabilities/ResizableBehavior.js.map +2 -2
  173. package/dist/Runtime/object-capabilities/ScalableBehavior.js.map +2 -2
  174. package/dist/Runtime/object-capabilities/TextContainerBehavior.js.map +2 -2
  175. package/dist/Runtime/pixi-renderers/CustomRuntimeObject2DPixiRenderer.js.map +2 -2
  176. package/dist/Runtime/pixi-renderers/DebuggerPixiRenderer.js.map +2 -2
  177. package/dist/Runtime/pixi-renderers/layer-pixi-renderer.js.map +2 -2
  178. package/dist/Runtime/pixi-renderers/loadingscreen-pixi-renderer.js.map +2 -2
  179. package/dist/Runtime/pixi-renderers/pixi-bitmapfont-manager.js.map +2 -2
  180. package/dist/Runtime/pixi-renderers/pixi-filters-tools.js.map +2 -2
  181. package/dist/Runtime/pixi-renderers/pixi-image-manager.js +1 -1
  182. package/dist/Runtime/pixi-renderers/pixi-image-manager.js.map +2 -2
  183. package/dist/Runtime/pixi-renderers/pixi.js +123 -177
  184. package/dist/Runtime/pixi-renderers/runtimegame-pixi-renderer.js +1 -1
  185. package/dist/Runtime/pixi-renderers/runtimegame-pixi-renderer.js.map +2 -2
  186. package/dist/Runtime/pixi-renderers/runtimescene-pixi-renderer.js.map +2 -2
  187. package/dist/Runtime/pixi-renderers/spriteruntimeobject-pixi-renderer.js.map +2 -2
  188. package/dist/Runtime/profiler.js.map +2 -2
  189. package/dist/Runtime/runtimegame.js +1 -1
  190. package/dist/Runtime/runtimegame.js.map +2 -2
  191. package/dist/Runtime/runtimeobject.js +1 -1
  192. package/dist/Runtime/runtimeobject.js.map +2 -2
  193. package/dist/Runtime/runtimescene.js.map +2 -2
  194. package/dist/Runtime/runtimewatermark.js.map +2 -2
  195. package/dist/Runtime/scenestack.js.map +2 -2
  196. package/dist/Runtime/spriteruntimeobject.js.map +2 -2
  197. package/dist/Runtime/variable.js.map +2 -2
  198. package/dist/Runtime/variablescontainer.js.map +2 -2
  199. package/dist/lib/libGD.cjs +1 -1
  200. package/dist/lib/libGD.wasm +0 -0
  201. package/gd.d.ts +5 -2
  202. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../GDevelop/Extensions/PlatformBehavior/platformruntimebehavior.ts"],
4
- "sourcesContent": ["/*\nGDevelop - Platform Behavior Extension\nCopyright (c) 2013-2016 Florian Rival (Florian.Rival@gmail.com)\n */\nnamespace gdjs {\n declare var rbush: any;\n type SearchArea = { minX: float; minY: float; maxX: float; maxY: float };\n\n /**\n * Manages the common objects shared by objects having a\n * platform behavior: in particular, the platforms behaviors are required to\n * declare themselves (see PlatformObjectsManager.addPlatform) to the manager\n * of their associated container (see PlatformRuntimeBehavior.getManager).\n */\n export class PlatformObjectsManager {\n private _platformRBush: any;\n\n constructor(instanceContainer: gdjs.RuntimeInstanceContainer) {\n this._platformRBush = new rbush();\n }\n\n /**\n * Get the platforms manager of an instance container.\n */\n static getManager(instanceContainer: gdjs.RuntimeInstanceContainer) {\n // @ts-ignore\n if (!instanceContainer.platformsObjectsManager) {\n //Create the shared manager if necessary.\n // @ts-ignore\n instanceContainer.platformsObjectsManager = new gdjs.PlatformObjectsManager(\n instanceContainer\n );\n }\n // @ts-ignore\n return instanceContainer.platformsObjectsManager;\n }\n\n /**\n * Add a platform to the list of existing platforms.\n */\n addPlatform(platformBehavior: gdjs.PlatformRuntimeBehavior) {\n if (platformBehavior.currentRBushAABB)\n platformBehavior.currentRBushAABB.updateAABBFromOwner();\n else\n platformBehavior.currentRBushAABB = new gdjs.BehaviorRBushAABB(\n platformBehavior\n );\n this._platformRBush.insert(platformBehavior.currentRBushAABB);\n }\n\n /**\n * Remove a platform from the list of existing platforms. Be sure that the platform was\n * added before.\n */\n removePlatform(platformBehavior: gdjs.PlatformRuntimeBehavior) {\n this._platformRBush.remove(platformBehavior.currentRBushAABB);\n }\n\n /**\n * Returns all the platforms around the specified object.\n * @param maxMovementLength The maximum distance, in pixels, the object is going to do.\n * @return An array with all platforms near the object.\n */\n getAllPlatformsAround(\n object: gdjs.RuntimeObject,\n maxMovementLength: number,\n result: PlatformRuntimeBehavior[]\n ): any {\n // TODO: This would better be done using the object AABB (getAABB), as (`getCenterX`;`getCenterY`) point\n // is not necessarily in the middle of the object (for sprites for example).\n const ow = object.getWidth();\n const oh = object.getHeight();\n const x = object.getDrawableX() + object.getCenterX();\n const y = object.getDrawableY() + object.getCenterY();\n const searchArea: SearchArea = gdjs.staticObject(\n PlatformObjectsManager.prototype.getAllPlatformsAround\n ) as SearchArea;\n searchArea.minX = x - ow / 2 - maxMovementLength;\n searchArea.minY = y - oh / 2 - maxMovementLength;\n searchArea.maxX = x + ow / 2 + maxMovementLength;\n searchArea.maxY = y + oh / 2 + maxMovementLength;\n const nearbyPlatforms: gdjs.BehaviorRBushAABB<\n PlatformRuntimeBehavior\n >[] = this._platformRBush.search(searchArea);\n\n result.length = 0;\n\n // Extra check on the platform owner AABB\n // TODO: PR https://github.com/4ian/GDevelop/pull/2602 should remove the need\n // for this extra check once merged.\n for (let i = 0; i < nearbyPlatforms.length; i++) {\n const platform = nearbyPlatforms[i].behavior;\n const platformAABB = platform.owner.getAABB();\n const platformIsStillAround =\n platformAABB.min[0] <= searchArea.maxX &&\n platformAABB.min[1] <= searchArea.maxY &&\n platformAABB.max[0] >= searchArea.minX &&\n platformAABB.max[1] >= searchArea.minY;\n // Filter platforms that are not in the searched area anymore.\n // This can happen because platforms are not updated in the RBush before that\n // characters movement are being processed.\n if (platformIsStillAround) {\n result.push(platform);\n }\n }\n }\n }\n\n /**\n * PlatformRuntimeBehavior represents a behavior allowing objects to be\n * considered as a platform by objects having PlatformerObject Behavior.\n */\n export class PlatformRuntimeBehavior extends gdjs.RuntimeBehavior {\n //Load the platform type\n _platformType: integer;\n _canBeGrabbed: boolean;\n _yGrabOffset: float;\n\n //Note that we can't use getX(), getWidth()... of owner here: The owner is not fully constructed.\n _oldX: float = 0;\n _oldY: float = 0;\n _oldWidth: float = 0;\n _oldHeight: float = 0;\n _oldAngle: float = 0;\n currentRBushAABB: gdjs.BehaviorRBushAABB<\n PlatformRuntimeBehavior\n > | null = null;\n _manager: gdjs.PlatformObjectsManager;\n _registeredInManager: boolean = false;\n\n constructor(\n instanceContainer: gdjs.RuntimeInstanceContainer,\n behaviorData,\n owner: gdjs.RuntimeObject\n ) {\n super(instanceContainer, behaviorData, owner);\n this._platformType = behaviorData.platformType;\n if (behaviorData.platformType === 'Ladder') {\n this._platformType = PlatformRuntimeBehavior.LADDER;\n } else if (behaviorData.platformType === 'Jumpthru') {\n this._platformType = PlatformRuntimeBehavior.JUMPTHRU;\n } else {\n this._platformType = PlatformRuntimeBehavior.NORMALPLATFORM;\n }\n this._canBeGrabbed = behaviorData.canBeGrabbed || false;\n this._yGrabOffset = behaviorData.yGrabOffset || 0;\n this._manager = PlatformObjectsManager.getManager(instanceContainer);\n }\n\n updateFromBehaviorData(oldBehaviorData, newBehaviorData): boolean {\n if (oldBehaviorData.platformType !== newBehaviorData.platformType) {\n this.changePlatformType(newBehaviorData.platformType);\n }\n if (oldBehaviorData.canBeGrabbed !== newBehaviorData.canBeGrabbed) {\n this._canBeGrabbed = newBehaviorData.canBeGrabbed;\n }\n if (oldBehaviorData.yGrabOffset !== newBehaviorData.yGrabOffset) {\n this._yGrabOffset = newBehaviorData.yGrabOffset;\n }\n return true;\n }\n\n onDestroy() {\n if (this._manager && this._registeredInManager) {\n this._manager.removePlatform(this);\n }\n }\n\n doStepPreEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {\n //Scene change is not supported\n /*if ( parentScene != &scene ) //Parent scene has changed\n {\n if ( sceneManager ) //Remove the object from any old scene manager.\n sceneManager->RemovePlatform(this);\n parentScene = &scene;\n sceneManager = parentScene ? &ScenePlatformObjectsManager::managers[&scene] : NULL;\n registeredInManager = false;\n }*/\n\n //Make sure the platform is or is not in the platforms manager.\n if (!this.activated() && this._registeredInManager) {\n this._manager.removePlatform(this);\n this._registeredInManager = false;\n } else {\n if (this.activated() && !this._registeredInManager) {\n this._manager.addPlatform(this);\n this._registeredInManager = true;\n }\n }\n\n //Track changes in size or position\n if (\n this._oldX !== this.owner.getX() ||\n this._oldY !== this.owner.getY() ||\n this._oldWidth !== this.owner.getWidth() ||\n this._oldHeight !== this.owner.getHeight() ||\n this._oldAngle !== this.owner.getAngle()\n ) {\n if (this._registeredInManager) {\n this._manager.removePlatform(this);\n this._manager.addPlatform(this);\n }\n this._oldX = this.owner.getX();\n this._oldY = this.owner.getY();\n this._oldWidth = this.owner.getWidth();\n this._oldHeight = this.owner.getHeight();\n this._oldAngle = this.owner.getAngle();\n }\n }\n\n doStepPostEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {}\n\n onActivate() {\n if (this._registeredInManager) {\n return;\n }\n this._manager.addPlatform(this);\n this._registeredInManager = true;\n }\n\n onDeActivate() {\n if (!this._registeredInManager) {\n return;\n }\n this._manager.removePlatform(this);\n this._registeredInManager = false;\n }\n\n changePlatformType(platformType: string) {\n if (platformType === 'Ladder') {\n this._platformType = PlatformRuntimeBehavior.LADDER;\n } else if (platformType === 'Jumpthru') {\n this._platformType = PlatformRuntimeBehavior.JUMPTHRU;\n } else {\n this._platformType = PlatformRuntimeBehavior.NORMALPLATFORM;\n }\n }\n\n getPlatformType() {\n return this._platformType;\n }\n\n canBeGrabbed() {\n return this._canBeGrabbed;\n }\n\n getYGrabOffset() {\n return this._yGrabOffset;\n }\n\n static NORMALPLATFORM = 0;\n /** @deprecated Use NORMALPLATFORM instead. */\n static NORMALPLAFTORM = PlatformRuntimeBehavior.NORMALPLATFORM;\n static JUMPTHRU = 1;\n static LADDER = 2;\n\n static isOnPlatformTest(\n object1: gdjs.RuntimeObject,\n object2: gdjs.RuntimeObject,\n behaviorName: string\n ): boolean {\n const behavior1 = object1.getBehavior(\n behaviorName\n ) as PlatformerObjectRuntimeBehavior;\n if (!behavior1) {\n return false;\n }\n return behavior1.isOnFloorObject(object2);\n }\n }\n gdjs.registerBehavior(\n 'PlatformBehavior::PlatformBehavior',\n gdjs.PlatformRuntimeBehavior\n );\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/*\nGDevelop - Platform Behavior Extension\nCopyright (c) 2013-2016 Florian Rival (Florian.Rival@gmail.com)\n */\nnamespace gdjs {\n declare var rbush: any;\n type SearchArea = { minX: float; minY: float; maxX: float; maxY: float };\n\n /**\n * Manages the common objects shared by objects having a\n * platform behavior: in particular, the platforms behaviors are required to\n * declare themselves (see PlatformObjectsManager.addPlatform) to the manager\n * of their associated container (see PlatformRuntimeBehavior.getManager).\n */\n export class PlatformObjectsManager {\n private _platformRBush: any;\n\n constructor(instanceContainer: gdjs.RuntimeInstanceContainer) {\n this._platformRBush = new rbush();\n }\n\n /**\n * Get the platforms manager of an instance container.\n */\n static getManager(instanceContainer: gdjs.RuntimeInstanceContainer) {\n // @ts-ignore\n if (!instanceContainer.platformsObjectsManager) {\n //Create the shared manager if necessary.\n // @ts-ignore\n instanceContainer.platformsObjectsManager =\n new gdjs.PlatformObjectsManager(instanceContainer);\n }\n // @ts-ignore\n return instanceContainer.platformsObjectsManager;\n }\n\n /**\n * Add a platform to the list of existing platforms.\n */\n addPlatform(platformBehavior: gdjs.PlatformRuntimeBehavior) {\n if (platformBehavior.currentRBushAABB)\n platformBehavior.currentRBushAABB.updateAABBFromOwner();\n else\n platformBehavior.currentRBushAABB = new gdjs.BehaviorRBushAABB(\n platformBehavior\n );\n this._platformRBush.insert(platformBehavior.currentRBushAABB);\n }\n\n /**\n * Remove a platform from the list of existing platforms. Be sure that the platform was\n * added before.\n */\n removePlatform(platformBehavior: gdjs.PlatformRuntimeBehavior) {\n this._platformRBush.remove(platformBehavior.currentRBushAABB);\n }\n\n /**\n * Returns all the platforms around the specified object.\n * @param maxMovementLength The maximum distance, in pixels, the object is going to do.\n * @return An array with all platforms near the object.\n */\n getAllPlatformsAround(\n object: gdjs.RuntimeObject,\n maxMovementLength: number,\n result: PlatformRuntimeBehavior[]\n ): any {\n // TODO: This would better be done using the object AABB (getAABB), as (`getCenterX`;`getCenterY`) point\n // is not necessarily in the middle of the object (for sprites for example).\n const ow = object.getWidth();\n const oh = object.getHeight();\n const x = object.getDrawableX() + object.getCenterX();\n const y = object.getDrawableY() + object.getCenterY();\n const searchArea: SearchArea = gdjs.staticObject(\n PlatformObjectsManager.prototype.getAllPlatformsAround\n ) as SearchArea;\n searchArea.minX = x - ow / 2 - maxMovementLength;\n searchArea.minY = y - oh / 2 - maxMovementLength;\n searchArea.maxX = x + ow / 2 + maxMovementLength;\n searchArea.maxY = y + oh / 2 + maxMovementLength;\n const nearbyPlatforms: gdjs.BehaviorRBushAABB<PlatformRuntimeBehavior>[] =\n this._platformRBush.search(searchArea);\n\n result.length = 0;\n\n // Extra check on the platform owner AABB\n // TODO: PR https://github.com/4ian/GDevelop/pull/2602 should remove the need\n // for this extra check once merged.\n for (let i = 0; i < nearbyPlatforms.length; i++) {\n const platform = nearbyPlatforms[i].behavior;\n const platformAABB = platform.owner.getAABB();\n const platformIsStillAround =\n platformAABB.min[0] <= searchArea.maxX &&\n platformAABB.min[1] <= searchArea.maxY &&\n platformAABB.max[0] >= searchArea.minX &&\n platformAABB.max[1] >= searchArea.minY;\n // Filter platforms that are not in the searched area anymore.\n // This can happen because platforms are not updated in the RBush before that\n // characters movement are being processed.\n if (platformIsStillAround) {\n result.push(platform);\n }\n }\n }\n }\n\n /**\n * PlatformRuntimeBehavior represents a behavior allowing objects to be\n * considered as a platform by objects having PlatformerObject Behavior.\n */\n export class PlatformRuntimeBehavior extends gdjs.RuntimeBehavior {\n //Load the platform type\n _platformType: integer;\n _canBeGrabbed: boolean;\n _yGrabOffset: float;\n\n //Note that we can't use getX(), getWidth()... of owner here: The owner is not fully constructed.\n _oldX: float = 0;\n _oldY: float = 0;\n _oldWidth: float = 0;\n _oldHeight: float = 0;\n _oldAngle: float = 0;\n currentRBushAABB: gdjs.BehaviorRBushAABB<PlatformRuntimeBehavior> | null =\n null;\n _manager: gdjs.PlatformObjectsManager;\n _registeredInManager: boolean = false;\n\n constructor(\n instanceContainer: gdjs.RuntimeInstanceContainer,\n behaviorData,\n owner: gdjs.RuntimeObject\n ) {\n super(instanceContainer, behaviorData, owner);\n this._platformType = behaviorData.platformType;\n if (behaviorData.platformType === 'Ladder') {\n this._platformType = PlatformRuntimeBehavior.LADDER;\n } else if (behaviorData.platformType === 'Jumpthru') {\n this._platformType = PlatformRuntimeBehavior.JUMPTHRU;\n } else {\n this._platformType = PlatformRuntimeBehavior.NORMALPLATFORM;\n }\n this._canBeGrabbed = behaviorData.canBeGrabbed || false;\n this._yGrabOffset = behaviorData.yGrabOffset || 0;\n this._manager = PlatformObjectsManager.getManager(instanceContainer);\n }\n\n updateFromBehaviorData(oldBehaviorData, newBehaviorData): boolean {\n if (oldBehaviorData.platformType !== newBehaviorData.platformType) {\n this.changePlatformType(newBehaviorData.platformType);\n }\n if (oldBehaviorData.canBeGrabbed !== newBehaviorData.canBeGrabbed) {\n this._canBeGrabbed = newBehaviorData.canBeGrabbed;\n }\n if (oldBehaviorData.yGrabOffset !== newBehaviorData.yGrabOffset) {\n this._yGrabOffset = newBehaviorData.yGrabOffset;\n }\n return true;\n }\n\n onDestroy() {\n if (this._manager && this._registeredInManager) {\n this._manager.removePlatform(this);\n }\n }\n\n doStepPreEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {\n //Scene change is not supported\n /*if ( parentScene != &scene ) //Parent scene has changed\n {\n if ( sceneManager ) //Remove the object from any old scene manager.\n sceneManager->RemovePlatform(this);\n parentScene = &scene;\n sceneManager = parentScene ? &ScenePlatformObjectsManager::managers[&scene] : NULL;\n registeredInManager = false;\n }*/\n\n //Make sure the platform is or is not in the platforms manager.\n if (!this.activated() && this._registeredInManager) {\n this._manager.removePlatform(this);\n this._registeredInManager = false;\n } else {\n if (this.activated() && !this._registeredInManager) {\n this._manager.addPlatform(this);\n this._registeredInManager = true;\n }\n }\n\n //Track changes in size or position\n if (\n this._oldX !== this.owner.getX() ||\n this._oldY !== this.owner.getY() ||\n this._oldWidth !== this.owner.getWidth() ||\n this._oldHeight !== this.owner.getHeight() ||\n this._oldAngle !== this.owner.getAngle()\n ) {\n if (this._registeredInManager) {\n this._manager.removePlatform(this);\n this._manager.addPlatform(this);\n }\n this._oldX = this.owner.getX();\n this._oldY = this.owner.getY();\n this._oldWidth = this.owner.getWidth();\n this._oldHeight = this.owner.getHeight();\n this._oldAngle = this.owner.getAngle();\n }\n }\n\n doStepPostEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {}\n\n onActivate() {\n if (this._registeredInManager) {\n return;\n }\n this._manager.addPlatform(this);\n this._registeredInManager = true;\n }\n\n onDeActivate() {\n if (!this._registeredInManager) {\n return;\n }\n this._manager.removePlatform(this);\n this._registeredInManager = false;\n }\n\n changePlatformType(platformType: string) {\n if (platformType === 'Ladder') {\n this._platformType = PlatformRuntimeBehavior.LADDER;\n } else if (platformType === 'Jumpthru') {\n this._platformType = PlatformRuntimeBehavior.JUMPTHRU;\n } else {\n this._platformType = PlatformRuntimeBehavior.NORMALPLATFORM;\n }\n }\n\n getPlatformType() {\n return this._platformType;\n }\n\n canBeGrabbed() {\n return this._canBeGrabbed;\n }\n\n getYGrabOffset() {\n return this._yGrabOffset;\n }\n\n static NORMALPLATFORM = 0;\n /** @deprecated Use NORMALPLATFORM instead. */\n static NORMALPLAFTORM = PlatformRuntimeBehavior.NORMALPLATFORM;\n static JUMPTHRU = 1;\n static LADDER = 2;\n\n static isOnPlatformTest(\n object1: gdjs.RuntimeObject,\n object2: gdjs.RuntimeObject,\n behaviorName: string\n ): boolean {\n const behavior1 = object1.getBehavior(\n behaviorName\n ) as PlatformerObjectRuntimeBehavior;\n if (!behavior1) {\n return false;\n }\n return behavior1.isOnFloorObject(object2);\n }\n }\n gdjs.registerBehavior(\n 'PlatformBehavior::PlatformBehavior',\n gdjs.PlatformRuntimeBehavior\n );\n}\n"],
5
+ "mappings": "AAIA,GAAU,MAAV,UAAU,EAAV,CAUS,OAA6B,CAGlC,YAAY,EAAkD,CAC5D,KAAK,eAAiB,GAAI,aAMrB,YAAW,EAAkD,CAElE,MAAK,GAAkB,yBAGrB,GAAkB,wBAChB,GAAI,GAAK,uBAAuB,IAG7B,EAAkB,wBAM3B,YAAY,EAAgD,CAC1D,AAAI,EAAiB,iBACnB,EAAiB,iBAAiB,sBAElC,EAAiB,iBAAmB,GAAI,GAAK,kBAC3C,GAEJ,KAAK,eAAe,OAAO,EAAiB,kBAO9C,eAAe,EAAgD,CAC7D,KAAK,eAAe,OAAO,EAAiB,kBAQ9C,sBACE,EACA,EACA,EACK,CAGL,KAAM,GAAK,EAAO,WACZ,EAAK,EAAO,YACZ,EAAI,EAAO,eAAiB,EAAO,aACnC,EAAI,EAAO,eAAiB,EAAO,aACnC,EAAyB,EAAK,aAClC,EAAuB,UAAU,uBAEnC,EAAW,KAAO,EAAI,EAAK,EAAI,EAC/B,EAAW,KAAO,EAAI,EAAK,EAAI,EAC/B,EAAW,KAAO,EAAI,EAAK,EAAI,EAC/B,EAAW,KAAO,EAAI,EAAK,EAAI,EAC/B,KAAM,GACJ,KAAK,eAAe,OAAO,GAE7B,EAAO,OAAS,EAKhB,OAAS,GAAI,EAAG,EAAI,EAAgB,OAAQ,IAAK,CAC/C,KAAM,GAAW,EAAgB,GAAG,SAC9B,EAAe,EAAS,MAAM,UASpC,AAAI,AAPF,EAAa,IAAI,IAAM,EAAW,MAClC,EAAa,IAAI,IAAM,EAAW,MAClC,EAAa,IAAI,IAAM,EAAW,MAClC,EAAa,IAAI,IAAM,EAAW,MAKlC,EAAO,KAAK,KAtFb,EAAM,yBAgGN,qBAAsC,GAAK,eAAgB,CAiBhE,YACE,EACA,EACA,EACA,CACA,MAAM,EAAmB,EAAc,GAfzC,WAAe,EACf,WAAe,EACf,eAAmB,EACnB,gBAAoB,EACpB,eAAmB,EACnB,sBACE,KAEF,0BAAgC,GAQ9B,KAAK,cAAgB,EAAa,aAClC,AAAI,EAAa,eAAiB,SAChC,KAAK,cAAgB,EAAwB,OACxC,AAAI,EAAa,eAAiB,WACvC,KAAK,cAAgB,EAAwB,SAE7C,KAAK,cAAgB,EAAwB,eAE/C,KAAK,cAAgB,EAAa,cAAgB,GAClD,KAAK,aAAe,EAAa,aAAe,EAChD,KAAK,SAAW,EAAuB,WAAW,GAGpD,uBAAuB,EAAiB,EAA0B,CAChE,MAAI,GAAgB,eAAiB,EAAgB,cACnD,KAAK,mBAAmB,EAAgB,cAEtC,EAAgB,eAAiB,EAAgB,cACnD,MAAK,cAAgB,EAAgB,cAEnC,EAAgB,cAAgB,EAAgB,aAClD,MAAK,aAAe,EAAgB,aAE/B,GAGT,WAAY,CACV,AAAI,KAAK,UAAY,KAAK,sBACxB,KAAK,SAAS,eAAe,MAIjC,gBAAgB,EAAkD,CAYhE,AAAI,CAAC,KAAK,aAAe,KAAK,qBAC5B,MAAK,SAAS,eAAe,MAC7B,KAAK,qBAAuB,IAExB,KAAK,aAAe,CAAC,KAAK,sBAC5B,MAAK,SAAS,YAAY,MAC1B,KAAK,qBAAuB,IAM9B,MAAK,QAAU,KAAK,MAAM,QAC1B,KAAK,QAAU,KAAK,MAAM,QAC1B,KAAK,YAAc,KAAK,MAAM,YAC9B,KAAK,aAAe,KAAK,MAAM,aAC/B,KAAK,YAAc,KAAK,MAAM,aAE1B,MAAK,sBACP,MAAK,SAAS,eAAe,MAC7B,KAAK,SAAS,YAAY,OAE5B,KAAK,MAAQ,KAAK,MAAM,OACxB,KAAK,MAAQ,KAAK,MAAM,OACxB,KAAK,UAAY,KAAK,MAAM,WAC5B,KAAK,WAAa,KAAK,MAAM,YAC7B,KAAK,UAAY,KAAK,MAAM,YAIhC,iBAAiB,EAAkD,EAEnE,YAAa,CACX,AAAI,KAAK,sBAGT,MAAK,SAAS,YAAY,MAC1B,KAAK,qBAAuB,IAG9B,cAAe,CACb,AAAI,CAAC,KAAK,sBAGV,MAAK,SAAS,eAAe,MAC7B,KAAK,qBAAuB,IAG9B,mBAAmB,EAAsB,CACvC,AAAI,IAAiB,SACnB,KAAK,cAAgB,EAAwB,OACxC,AAAI,IAAiB,WAC1B,KAAK,cAAgB,EAAwB,SAE7C,KAAK,cAAgB,EAAwB,eAIjD,iBAAkB,CAChB,MAAO,MAAK,cAGd,cAAe,CACb,MAAO,MAAK,cAGd,gBAAiB,CACf,MAAO,MAAK,mBASP,kBACL,EACA,EACA,EACS,CACT,KAAM,GAAY,EAAQ,YACxB,GAEF,MAAK,GAGE,EAAU,gBAAgB,GAFxB,KAxJN,QAyIE,AAzIF,EAyIE,eAAiB,EAEjB,AA3IF,EA2IE,eAAiB,EAAwB,eACzC,AA5IF,EA4IE,SAAW,EACX,AA7IF,EA6IE,OAAS,EA7IX,EAAM,0BA6Jb,EAAK,iBACH,qCACA,EAAK,2BAzQC",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../GDevelop/Extensions/PlayerAuthentication/playerauthenticationcomponents.ts"],
4
- "sourcesContent": ["namespace gdjs {\n const logger = new gdjs.Logger('Player Authentication');\n export namespace playerAuthenticationComponents {\n const getPlayerLoginMessages = ({\n platform,\n isGameRegistered,\n }: {\n platform: 'cordova-websocket' | 'electron' | 'web-iframe' | 'web';\n isGameRegistered: boolean;\n }) =>\n isGameRegistered\n ? {\n title: 'Logging in...',\n text1:\n platform === 'cordova-websocket'\n ? \"One moment, we're opening a window for you to log in.\"\n : \"One moment, we're opening a new page with your web browser for you to log in.\",\n text2:\n platform === 'cordova-websocket'\n ? ''\n : 'If the window did not open, please check your pop-up blocker and click the button below to try again.',\n }\n : {\n title: 'Publish your game!',\n text1:\n \"GDevelop's player accounts are only available for published games.\",\n text2:\n 'Click the button below to learn how to publish your game then try again.',\n };\n\n /**\n * Creates a DOM element that will contain the loader or a message if the game is not registered.\n */\n export const computeAuthenticationContainer = function (\n onCloseAuthenticationContainer: () => void\n ): {\n rootContainer: HTMLDivElement;\n loaderContainer: HTMLDivElement;\n iframeContainer: HTMLDivElement;\n } {\n const rootContainer = document.createElement('div');\n rootContainer.id = 'authentication-root-container';\n rootContainer.style.position = 'relative';\n rootContainer.style.backgroundColor = 'rgba(14, 6, 45, 0.5)';\n rootContainer.style.opacity = '1';\n rootContainer.style.width = '100%';\n rootContainer.style.height = '100%';\n rootContainer.style.zIndex = '2';\n rootContainer.style.pointerEvents = 'all';\n\n const frameContainer = document.createElement('div');\n frameContainer.id = 'authentication-frame-container';\n frameContainer.style.backgroundColor = '#FFFFFF';\n frameContainer.style.position = 'absolute';\n frameContainer.style.top = '16px';\n frameContainer.style.bottom = '16px';\n frameContainer.style.left = '16px';\n frameContainer.style.right = '16px';\n frameContainer.style.borderRadius = '8px';\n frameContainer.style.boxShadow = '0px 4px 4px rgba(0, 0, 0, 0.25)';\n frameContainer.style.padding = '16px';\n\n const _closeContainer: HTMLDivElement = document.createElement('div');\n _closeContainer.style.cursor = 'pointer';\n _closeContainer.style.display = 'flex';\n _closeContainer.style.justifyContent = 'right';\n _closeContainer.style.alignItems = 'center';\n _closeContainer.style.zIndex = '3';\n addTouchAndClickEventListeners(\n _closeContainer,\n onCloseAuthenticationContainer\n );\n\n const _close = document.createElement('img');\n _close.setAttribute('width', '15px');\n _close.setAttribute(\n 'src',\n 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOCIgaGVpZ2h0PSI4IiB2aWV3Qm94PSIwIDAgOCA4IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTcuODUzNTUgMC4xNDY0NDdDOC4wNDg4MiAwLjM0MTcwOSA4LjA0ODgyIDAuNjU4MjkxIDcuODUzNTUgMC44NTM1NTNMMC44NTM1NTMgNy44NTM1NUMwLjY1ODI5MSA4LjA0ODgyIDAuMzQxNzA5IDguMDQ4ODIgMC4xNDY0NDcgNy44NTM1NUMtMC4wNDg4MTU1IDcuNjU4MjkgLTAuMDQ4ODE1NSA3LjM0MTcxIDAuMTQ2NDQ3IDcuMTQ2NDVMNy4xNDY0NSAwLjE0NjQ0N0M3LjM0MTcxIC0wLjA0ODgxNTUgNy42NTgyOSAtMC4wNDg4MTU1IDcuODUzNTUgMC4xNDY0NDdaIiBmaWxsPSIjMUQxRDI2Ii8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMC4xNDY0NDcgMC4xNDY0NDdDMC4zNDE3MDkgLTAuMDQ4ODE1NSAwLjY1ODI5MSAtMC4wNDg4MTU1IDAuODUzNTUzIDAuMTQ2NDQ3TDcuODUzNTUgNy4xNDY0NUM4LjA0ODgyIDcuMzQxNzEgOC4wNDg4MiA3LjY1ODI5IDcuODUzNTUgNy44NTM1NUM3LjY1ODI5IDguMDQ4ODIgNy4zNDE3MSA4LjA0ODgyIDcuMTQ2NDUgNy44NTM1NUwwLjE0NjQ0NyAwLjg1MzU1M0MtMC4wNDg4MTU1IDAuNjU4MjkxIC0wLjA0ODgxNTUgMC4zNDE3MDkgMC4xNDY0NDcgMC4xNDY0NDdaIiBmaWxsPSIjMUQxRDI2Ii8+Cjwvc3ZnPgo='\n );\n _closeContainer.appendChild(_close);\n\n const loaderContainer: HTMLDivElement = document.createElement('div');\n loaderContainer.id = 'authentication-container-loader';\n loaderContainer.style.display = 'flex';\n loaderContainer.style.flexDirection = 'column';\n loaderContainer.style.height = '100%';\n loaderContainer.style.width = '100%';\n loaderContainer.style.justifyContent = 'center';\n loaderContainer.style.alignItems = 'center';\n\n const _loader = document.createElement('img');\n _loader.setAttribute('width', '28px');\n _loader.setAttribute(\n 'src',\n 'data:image/svg+xml;base64,PHN2ZyBjbGFzcz0iYW5pbWF0ZS1zcGluIC1tbC0xIG1yLTMgaC01IHctNSB0ZXh0LXdoaXRlIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCAyNCAyNCI+CjxjaXJjbGUgb3BhY2l0eT0nMC4yNScgY3g9IjEyIiBjeT0iMTIiIHI9IjEwIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSI0Ij48L2NpcmNsZT4KPHBhdGggb3BhY2l0eT0nMC43NScgZmlsbD0iY3VycmVudENvbG9yIiBkPSJNNCAxMmE4IDggMCAwMTgtOFYwQzUuMzczIDAgMCA1LjM3MyAwIDEyaDR6bTIgNS4yOTFBNy45NjIgNy45NjIgMCAwMTQgMTJIMGMwIDMuMDQyIDEuMTM1IDUuODI0IDMgNy45MzhsMy0yLjY0N3oiPjwvcGF0aD4KPC9zdmc+'\n );\n _loader.style.marginTop = '50px';\n try {\n _loader.animate(\n [{ transform: 'rotate(0deg)' }, { transform: 'rotate(359deg)' }],\n {\n duration: 3000,\n iterations: Infinity,\n }\n );\n } catch {\n logger.warn('Animation not supported, loader will be fixed.');\n }\n\n loaderContainer.appendChild(_loader);\n\n const iframeContainer: HTMLDivElement = document.createElement('div');\n iframeContainer.style.display = 'flex';\n iframeContainer.style.flexDirection = 'column';\n iframeContainer.style.height = '100%';\n iframeContainer.style.width = '100%';\n iframeContainer.style.justifyContent = 'stretch';\n iframeContainer.style.alignItems = 'stretch';\n iframeContainer.style.display = 'none';\n\n frameContainer.appendChild(_closeContainer);\n frameContainer.appendChild(loaderContainer);\n frameContainer.appendChild(iframeContainer);\n rootContainer.appendChild(frameContainer);\n\n return { rootContainer, loaderContainer, iframeContainer };\n };\n\n export const displayIframeInsideAuthenticationContainer = (\n iframeContainer: HTMLDivElement,\n loaderContainer: HTMLDivElement,\n textContainer: HTMLDivElement,\n url: string\n ) => {\n const iframe = document.createElement('iframe');\n iframe.setAttribute(\n 'sandbox',\n 'allow-forms allow-modals allow-orientation-lock allow-popups allow-same-origin allow-scripts'\n );\n iframe.addEventListener('load', () => {\n iframeContainer.style.display = 'flex';\n loaderContainer.style.display = 'none';\n });\n iframe.addEventListener('loaderror', () => {\n addAuthenticationUrlToTextsContainer(() => {\n // Try again.\n iframeContainer.removeChild(iframe);\n iframeContainer.style.display = 'none';\n loaderContainer.style.display = 'flex';\n displayIframeInsideAuthenticationContainer(\n iframeContainer,\n loaderContainer,\n textContainer,\n url\n );\n }, textContainer);\n });\n iframe.src = url;\n iframe.style.flex = '1';\n iframe.style.border = '0';\n\n iframeContainer.appendChild(iframe);\n };\n\n /**\n * Helper to add the texts to the authentication container\n * based on the platform or if the game is registered.\n */\n export const addAuthenticationTextsToLoadingContainer = (\n loaderContainer: HTMLDivElement,\n platform: 'cordova-websocket' | 'electron' | 'web-iframe' | 'web',\n isGameRegistered: boolean,\n wikiOpenAction: (() => void) | null\n ) => {\n const textContainer: HTMLDivElement = document.createElement('div');\n textContainer.id = 'authentication-container-texts';\n textContainer.style.display = 'flex';\n textContainer.style.flexDirection = 'column';\n textContainer.style.width = '100%';\n textContainer.style.justifyContent = 'center';\n textContainer.style.alignItems = 'center';\n textContainer.style.position = 'relative';\n textContainer.style.zIndex = '3';\n textContainer.style.fontSize = '11pt';\n textContainer.style.fontFamily =\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\"';\n\n const messages = getPlayerLoginMessages({\n platform,\n isGameRegistered,\n });\n const title = document.createElement('h1');\n title.innerText = messages.title;\n title.style.fontSize = '20pt';\n title.style.fontWeight = 'bold';\n const text1 = document.createElement('p');\n text1.innerText = messages.text1;\n const text2 = document.createElement('p');\n text2.innerText = messages.text2;\n textContainer.appendChild(title);\n textContainer.appendChild(text1);\n textContainer.appendChild(text2);\n\n if (!isGameRegistered) {\n // Remove the loader and add the wiki link.\n loaderContainer.innerHTML = '';\n\n if (wikiOpenAction) {\n const link = document.createElement('a');\n addTouchAndClickEventListeners(link, wikiOpenAction);\n link.innerText = 'How to publish my game';\n link.style.color = '#0078d4';\n link.style.textDecoration = 'none';\n link.style.textDecoration = 'underline';\n link.style.cursor = 'pointer';\n\n textContainer.appendChild(link);\n }\n }\n\n loaderContainer.prepend(textContainer);\n\n return textContainer;\n };\n\n /**\n * Helper to add the authentication link in case the window hasn't opened properly.\n * Useful for Electron & Web platforms.\n */\n export const addAuthenticationUrlToTextsContainer = (\n onClick: () => void,\n textContainer: HTMLDivElement\n ) => {\n const link = document.createElement('a');\n addTouchAndClickEventListeners(link, onClick);\n link.innerText = 'Try again';\n link.style.color = '#0078d4';\n link.style.textDecoration = 'none';\n link.style.textDecoration = 'underline';\n link.style.cursor = 'pointer';\n\n textContainer.appendChild(link);\n };\n\n /**\n * Creates a DOM element to display a dismissable banner.\n */\n export const computeDismissableBanner = function (\n onDismissBanner: () => void\n ): HTMLDivElement {\n const divContainer = document.createElement('div');\n\n divContainer.id = 'authenticated-banner';\n divContainer.style.position = 'absolute';\n divContainer.style.pointerEvents = 'all';\n divContainer.style.backgroundColor = '#0E062D';\n divContainer.style.top = '0px';\n divContainer.style.height = '48px';\n divContainer.style.left = '0px';\n divContainer.style.width = '100%';\n divContainer.style.padding = '6px 16px';\n // Use zIndex 1 to make sure it is below the authentication iframe or webview.\n divContainer.style.zIndex = '1';\n divContainer.style.display = 'flex';\n divContainer.style.flexDirection = 'row-reverse';\n divContainer.style.justifyContent = 'space-between';\n divContainer.style.alignItems = 'center';\n divContainer.style.boxShadow = '0px 4px 4px rgba(0, 0, 0, 0.25)';\n divContainer.style.fontSize = '11pt';\n divContainer.style.color = '#FFFFFF';\n divContainer.style.fontFamily =\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\"';\n\n const _closeContainer: HTMLDivElement = document.createElement('div');\n _closeContainer.style.cursor = 'pointer';\n _closeContainer.style.display = 'flex';\n _closeContainer.style.justifyContent = 'center';\n _closeContainer.style.alignItems = 'center';\n _closeContainer.style.zIndex = '3';\n _closeContainer.style.marginRight = '32px';\n _closeContainer.style.height = '100%';\n addTouchAndClickEventListeners(_closeContainer, onDismissBanner);\n\n const _close = document.createElement('img');\n _close.setAttribute('width', '30px');\n _close.setAttribute(\n 'src',\n 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAiIGhlaWdodD0iMzAiIHZpZXdCb3g9IjAgMCAzMCAzMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTIzLjc1IDguMDEyNUwyMS45ODc1IDYuMjVMMTUgMTMuMjM3NUw4LjAxMjUgNi4yNUw2LjI1IDguMDEyNUwxMy4yMzc1IDE1TDYuMjUgMjEuOTg3NUw4LjAxMjUgMjMuNzVMMTUgMTYuNzYyNUwyMS45ODc1IDIzLjc1TDIzLjc1IDIxLjk4NzVMMTYuNzYyNSAxNUwyMy43NSA4LjAxMjVaIiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4K'\n );\n\n _closeContainer.appendChild(_close);\n divContainer.appendChild(_closeContainer);\n\n return divContainer;\n };\n\n /**\n * Creates a DOM element representing a banner for the user to know which account\n * they're using and also to allow switching to another account.\n */\n export const computeAuthenticatedBanner = function (\n onOpenAuthenticationWindow: () => void,\n onDismissBanner: () => void,\n username: string | null\n ): HTMLDivElement {\n const divContainer = computeDismissableBanner(onDismissBanner);\n\n const playerUsername = username || 'Anonymous';\n\n const _textContainer: HTMLDivElement = document.createElement('div');\n const loggedText = document.createElement('p');\n loggedText.id = 'loggedText';\n loggedText.innerHTML = `<img style=\"margin-right:4px\" src=\"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOSIgaGVpZ2h0PSI5IiB2aWV3Qm94PSIwIDAgOSA5IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBkPSJNNC4xNjY2NyAwQzEuODY2NjcgMCAwIDEuODY2NjcgMCA0LjE2NjY3QzAgNi40NjY2NyAxLjg2NjY3IDguMzMzMzMgNC4xNjY2NyA4LjMzMzMzQzYuNDY2NjcgOC4zMzMzMyA4LjMzMzMzIDYuNDY2NjcgOC4zMzMzMyA0LjE2NjY3QzguMzMzMzMgMS44NjY2NyA2LjQ2NjY3IDAgNC4xNjY2NyAwWk0zLjMzMzMzIDYuMjVMMS4yNSA0LjE2NjY3TDEuODM3NSAzLjU3OTE3TDMuMzMzMzMgNS4wNzA4M0w2LjQ5NTgzIDEuOTA4MzNMNy4wODMzMyAyLjVMMy4zMzMzMyA2LjI1WiIgZmlsbD0iIzAwQ0M4MyIvPgo8L3N2Zz4K\" />\n Logged as ${playerUsername}`;\n loggedText.style.margin = '0px';\n\n const changeAccountText = document.createElement('p');\n changeAccountText.id = 'changeAccountText';\n changeAccountText.innerText = `Click here to switch to another account.`;\n changeAccountText.style.margin = '0px';\n changeAccountText.style.marginTop = '4px';\n changeAccountText.style.textDecoration = 'underline';\n changeAccountText.style.cursor = 'pointer';\n addTouchAndClickEventListeners(\n changeAccountText,\n onOpenAuthenticationWindow\n );\n\n _textContainer.appendChild(loggedText);\n _textContainer.appendChild(changeAccountText);\n divContainer.appendChild(_textContainer);\n\n return divContainer;\n };\n\n /**\n * Creates a DOM element representing a banner for the user to know\n * they are not connected and to allow logging in.\n */\n export const computeNotAuthenticatedBanner = function (\n onOpenAuthenticationWindow: () => void,\n onDismissBanner: () => void\n ): HTMLDivElement {\n const divContainer = computeDismissableBanner(onDismissBanner);\n\n const _textContainer: HTMLDivElement = document.createElement('div');\n const loggedText = document.createElement('p');\n loggedText.id = 'loggedText';\n loggedText.innerHTML = `You are not authenticated.`;\n loggedText.style.margin = '0px';\n\n const changeAccountText = document.createElement('p');\n changeAccountText.id = 'changeAccountText';\n changeAccountText.innerText = `Click here to log in.`;\n changeAccountText.style.margin = '0px';\n changeAccountText.style.marginTop = '4px';\n changeAccountText.style.textDecoration = 'underline';\n changeAccountText.style.cursor = 'pointer';\n addTouchAndClickEventListeners(\n changeAccountText,\n onOpenAuthenticationWindow\n );\n\n _textContainer.appendChild(loggedText);\n _textContainer.appendChild(changeAccountText);\n divContainer.appendChild(_textContainer);\n\n return divContainer;\n };\n\n /**\n * Create, display, and hide the logged in confirmation.\n */\n export const displayLoggedInNotification = function (\n domContainer: HTMLDivElement,\n username: string\n ) {\n showNotification(\n domContainer,\n 'authenticated-notification',\n `<img style=\"margin-right:4px\" src=\"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOSIgaGVpZ2h0PSI5IiB2aWV3Qm94PSIwIDAgOSA5IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBkPSJNNC4xNjY2NyAwQzEuODY2NjcgMCAwIDEuODY2NjcgMCA0LjE2NjY3QzAgNi40NjY2NyAxLjg2NjY3IDguMzMzMzMgNC4xNjY2NyA4LjMzMzMzQzYuNDY2NjcgOC4zMzMzMyA4LjMzMzMzIDYuNDY2NjcgOC4zMzMzMyA0LjE2NjY3QzguMzMzMzMgMS44NjY2NyA2LjQ2NjY3IDAgNC4xNjY2NyAwWk0zLjMzMzMzIDYuMjVMMS4yNSA0LjE2NjY3TDEuODM3NSAzLjU3OTE3TDMuMzMzMzMgNS4wNzA4M0w2LjQ5NTgzIDEuOTA4MzNMNy4wODMzMyAyLjVMMy4zMzMzMyA2LjI1WiIgZmlsbD0iIzAwQ0M4MyIvPgo8L3N2Zz4K\" />\n Logged as ${username}`,\n 'success'\n );\n };\n\n /**\n * Create, display, and hide the logged in confirmation.\n */\n export const displayLoggedOutNotification = function (\n domContainer: HTMLDivElement\n ) {\n showNotification(\n domContainer,\n 'authenticated-notification',\n `<img style=\"margin-right:4px\" src=\"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOSIgaGVpZ2h0PSI5IiB2aWV3Qm94PSIwIDAgOSA5IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBkPSJNNC4xNjY2NyAwQzEuODY2NjcgMCAwIDEuODY2NjcgMCA0LjE2NjY3QzAgNi40NjY2NyAxLjg2NjY3IDguMzMzMzMgNC4xNjY2NyA4LjMzMzMzQzYuNDY2NjcgOC4zMzMzMyA4LjMzMzMzIDYuNDY2NjcgOC4zMzMzMyA0LjE2NjY3QzguMzMzMzMgMS44NjY2NyA2LjQ2NjY3IDAgNC4xNjY2NyAwWk0zLjMzMzMzIDYuMjVMMS4yNSA0LjE2NjY3TDEuODM3NSAzLjU3OTE3TDMuMzMzMzMgNS4wNzA4M0w2LjQ5NTgzIDEuOTA4MzNMNy4wODMzMyAyLjVMMy4zMzMzMyA2LjI1WiIgZmlsbD0iIzAwQ0M4MyIvPgo8L3N2Zz4K\" />\n Logged out`,\n 'success'\n );\n };\n\n /**\n * Create, display, and hide an error notification.\n */\n export const displayErrorNotification = function (\n domContainer: HTMLDivElement\n ) {\n showNotification(\n domContainer,\n 'error-notification',\n 'An error occurred while authenticating, please try again.',\n 'error'\n );\n };\n\n /**\n * Helper to show a notification to the user, that disappears automatically.\n */\n export const showNotification = function (\n domContainer: HTMLDivElement,\n id: string,\n content: string,\n type: 'success' | 'error'\n ) {\n const divContainer = document.createElement('div');\n\n divContainer.id = id;\n divContainer.style.position = 'absolute';\n divContainer.style.pointerEvents = 'all';\n divContainer.style.backgroundColor =\n type === 'success' ? '#0E062D' : 'red';\n divContainer.style.top = '12px';\n divContainer.style.right = '16px';\n divContainer.style.padding = '6px 32px 6px 6px';\n // Use zIndex 1 to make sure it is below the authentication iframe or webview.\n divContainer.style.zIndex = '1';\n divContainer.style.display = 'flex';\n divContainer.style.flexDirection = 'row-reverse';\n divContainer.style.justifyContent = 'space-between';\n divContainer.style.alignItems = 'center';\n divContainer.style.boxShadow = '0px 4px 4px rgba(0, 0, 0, 0.25)';\n divContainer.style.borderRadius = '4px';\n divContainer.style.fontSize = '11pt';\n divContainer.style.color = '#FFFFFF';\n divContainer.style.fontFamily =\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\"';\n try {\n divContainer.animate(\n [\n { transform: 'translateY(-30px)', opacity: 0 },\n { transform: 'translateY(0px)', opacity: 1 },\n ],\n {\n duration: 700,\n easing: 'ease-out',\n }\n );\n } catch {\n logger.warn('Animation not supported, div will be fixed.');\n }\n const loggedText = document.createElement('p');\n loggedText.id = 'loggedText';\n loggedText.innerHTML = content;\n loggedText.style.margin = '0px';\n\n divContainer.appendChild(loggedText);\n\n domContainer.appendChild(divContainer);\n const animationTime = 700;\n const notificationTime = 5000;\n setTimeout(() => {\n try {\n divContainer.animate(\n [\n { transform: 'translateY(0px)', opacity: 1 },\n { transform: 'translateY(-30px)', opacity: 0 },\n ],\n {\n duration: animationTime,\n easing: 'ease-in',\n }\n );\n } catch {\n logger.warn('Animation not supported, div will be fixed.');\n }\n }, notificationTime);\n // Use timeout because onanimationend listener does not work.\n setTimeout(() => {\n divContainer.remove();\n }, notificationTime + animationTime);\n };\n\n /**\n * Helper to add event listeners on a pressable/clickable element\n * to work on both desktop and mobile.\n */\n const addTouchAndClickEventListeners = function (\n element: HTMLElement,\n action: () => void\n ) {\n // Touch start event listener for mobile.\n element.addEventListener('touchstart', (event) => {\n action();\n });\n // Click event listener for desktop.\n element.addEventListener('click', (event) => {\n action();\n });\n };\n }\n}\n"],
4
+ "sourcesContent": ["namespace gdjs {\n const logger = new gdjs.Logger('Player Authentication');\n export namespace playerAuthenticationComponents {\n const getPlayerLoginMessages = ({\n platform,\n isGameRegistered,\n }: {\n platform: PlayerAuthenticationPlatform;\n isGameRegistered: boolean;\n }) =>\n isGameRegistered\n ? {\n title: 'Logging in...',\n text1:\n platform === 'cordova-websocket'\n ? \"One moment, we're opening a window for you to log in.\"\n : \"One moment, we're opening a new page with your web browser for you to log in.\",\n text2:\n platform === 'cordova-websocket'\n ? ''\n : 'If the window did not open, please check your pop-up blocker and click the button below to try again.',\n }\n : {\n title: 'Publish your game!',\n text1:\n \"GDevelop's player accounts are only available for published games.\",\n text2:\n 'Click the button below to learn how to publish your game then try again.',\n };\n\n /**\n * Creates a DOM element that will contain the loader or a message if the game is not registered.\n */\n export const computeAuthenticationContainer = function (\n onCloseAuthenticationContainer: () => void\n ): {\n rootContainer: HTMLDivElement;\n loaderContainer: HTMLDivElement;\n iframeContainer: HTMLDivElement;\n } {\n const rootContainer = document.createElement('div');\n rootContainer.id = 'authentication-root-container';\n rootContainer.style.position = 'relative';\n rootContainer.style.backgroundColor = 'rgba(14, 6, 45, 0.5)';\n rootContainer.style.opacity = '1';\n rootContainer.style.width = '100%';\n rootContainer.style.height = '100%';\n rootContainer.style.zIndex = '2';\n rootContainer.style.pointerEvents = 'all';\n\n const frameContainer = document.createElement('div');\n frameContainer.id = 'authentication-frame-container';\n frameContainer.style.backgroundColor = '#FFFFFF';\n frameContainer.style.position = 'absolute';\n frameContainer.style.top = '16px';\n frameContainer.style.bottom = '16px';\n frameContainer.style.left = '16px';\n frameContainer.style.right = '16px';\n frameContainer.style.borderRadius = '8px';\n frameContainer.style.boxShadow = '0px 4px 4px rgba(0, 0, 0, 0.25)';\n frameContainer.style.padding = '16px';\n\n const _closeContainer: HTMLDivElement = document.createElement('div');\n _closeContainer.style.cursor = 'pointer';\n _closeContainer.style.display = 'flex';\n _closeContainer.style.justifyContent = 'right';\n _closeContainer.style.alignItems = 'center';\n _closeContainer.style.zIndex = '3';\n addTouchAndClickEventListeners(\n _closeContainer,\n onCloseAuthenticationContainer\n );\n\n const _close = document.createElement('img');\n _close.setAttribute('width', '15px');\n _close.setAttribute(\n 'src',\n 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOCIgaGVpZ2h0PSI4IiB2aWV3Qm94PSIwIDAgOCA4IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTcuODUzNTUgMC4xNDY0NDdDOC4wNDg4MiAwLjM0MTcwOSA4LjA0ODgyIDAuNjU4MjkxIDcuODUzNTUgMC44NTM1NTNMMC44NTM1NTMgNy44NTM1NUMwLjY1ODI5MSA4LjA0ODgyIDAuMzQxNzA5IDguMDQ4ODIgMC4xNDY0NDcgNy44NTM1NUMtMC4wNDg4MTU1IDcuNjU4MjkgLTAuMDQ4ODE1NSA3LjM0MTcxIDAuMTQ2NDQ3IDcuMTQ2NDVMNy4xNDY0NSAwLjE0NjQ0N0M3LjM0MTcxIC0wLjA0ODgxNTUgNy42NTgyOSAtMC4wNDg4MTU1IDcuODUzNTUgMC4xNDY0NDdaIiBmaWxsPSIjMUQxRDI2Ii8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMC4xNDY0NDcgMC4xNDY0NDdDMC4zNDE3MDkgLTAuMDQ4ODE1NSAwLjY1ODI5MSAtMC4wNDg4MTU1IDAuODUzNTUzIDAuMTQ2NDQ3TDcuODUzNTUgNy4xNDY0NUM4LjA0ODgyIDcuMzQxNzEgOC4wNDg4MiA3LjY1ODI5IDcuODUzNTUgNy44NTM1NUM3LjY1ODI5IDguMDQ4ODIgNy4zNDE3MSA4LjA0ODgyIDcuMTQ2NDUgNy44NTM1NUwwLjE0NjQ0NyAwLjg1MzU1M0MtMC4wNDg4MTU1IDAuNjU4MjkxIC0wLjA0ODgxNTUgMC4zNDE3MDkgMC4xNDY0NDcgMC4xNDY0NDdaIiBmaWxsPSIjMUQxRDI2Ii8+Cjwvc3ZnPgo='\n );\n _closeContainer.appendChild(_close);\n\n const loaderContainer: HTMLDivElement = document.createElement('div');\n loaderContainer.id = 'authentication-container-loader';\n loaderContainer.style.display = 'flex';\n loaderContainer.style.flexDirection = 'column';\n loaderContainer.style.height = '100%';\n loaderContainer.style.width = '100%';\n loaderContainer.style.justifyContent = 'center';\n loaderContainer.style.alignItems = 'center';\n\n const _loader = document.createElement('img');\n _loader.setAttribute('width', '28px');\n _loader.setAttribute(\n 'src',\n 'data:image/svg+xml;base64,PHN2ZyBjbGFzcz0iYW5pbWF0ZS1zcGluIC1tbC0xIG1yLTMgaC01IHctNSB0ZXh0LXdoaXRlIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCAyNCAyNCI+CjxjaXJjbGUgb3BhY2l0eT0nMC4yNScgY3g9IjEyIiBjeT0iMTIiIHI9IjEwIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSI0Ij48L2NpcmNsZT4KPHBhdGggb3BhY2l0eT0nMC43NScgZmlsbD0iY3VycmVudENvbG9yIiBkPSJNNCAxMmE4IDggMCAwMTgtOFYwQzUuMzczIDAgMCA1LjM3MyAwIDEyaDR6bTIgNS4yOTFBNy45NjIgNy45NjIgMCAwMTQgMTJIMGMwIDMuMDQyIDEuMTM1IDUuODI0IDMgNy45MzhsMy0yLjY0N3oiPjwvcGF0aD4KPC9zdmc+'\n );\n _loader.style.marginTop = '50px';\n try {\n _loader.animate(\n [{ transform: 'rotate(0deg)' }, { transform: 'rotate(359deg)' }],\n {\n duration: 3000,\n iterations: Infinity,\n }\n );\n } catch {\n logger.warn('Animation not supported, loader will be fixed.');\n }\n\n loaderContainer.appendChild(_loader);\n\n const iframeContainer: HTMLDivElement = document.createElement('div');\n iframeContainer.style.display = 'flex';\n iframeContainer.style.flexDirection = 'column';\n iframeContainer.style.height = '100%';\n iframeContainer.style.width = '100%';\n iframeContainer.style.justifyContent = 'stretch';\n iframeContainer.style.alignItems = 'stretch';\n iframeContainer.style.display = 'none';\n\n frameContainer.appendChild(_closeContainer);\n frameContainer.appendChild(loaderContainer);\n frameContainer.appendChild(iframeContainer);\n rootContainer.appendChild(frameContainer);\n\n return { rootContainer, loaderContainer, iframeContainer };\n };\n\n export const displayIframeInsideAuthenticationContainer = (\n iframeContainer: HTMLDivElement,\n loaderContainer: HTMLDivElement,\n textContainer: HTMLDivElement,\n url: string\n ) => {\n const iframe = document.createElement('iframe');\n iframe.setAttribute(\n 'sandbox',\n 'allow-forms allow-modals allow-orientation-lock allow-popups allow-same-origin allow-scripts'\n );\n iframe.addEventListener('load', () => {\n iframeContainer.style.display = 'flex';\n loaderContainer.style.display = 'none';\n });\n iframe.addEventListener('loaderror', () => {\n addAuthenticationUrlToTextsContainer(() => {\n // Try again.\n iframeContainer.removeChild(iframe);\n iframeContainer.style.display = 'none';\n loaderContainer.style.display = 'flex';\n displayIframeInsideAuthenticationContainer(\n iframeContainer,\n loaderContainer,\n textContainer,\n url\n );\n }, textContainer);\n });\n iframe.src = url;\n iframe.style.flex = '1';\n iframe.style.border = '0';\n\n iframeContainer.appendChild(iframe);\n };\n\n /**\n * Helper to add the texts to the authentication container\n * based on the platform or if the game is registered.\n */\n export const addAuthenticationTextsToLoadingContainer = (\n loaderContainer: HTMLDivElement,\n platform: PlayerAuthenticationPlatform,\n isGameRegistered: boolean,\n wikiOpenAction: (() => void) | null\n ) => {\n const textContainer: HTMLDivElement = document.createElement('div');\n textContainer.id = 'authentication-container-texts';\n textContainer.style.display = 'flex';\n textContainer.style.flexDirection = 'column';\n textContainer.style.width = '100%';\n textContainer.style.justifyContent = 'center';\n textContainer.style.alignItems = 'center';\n textContainer.style.position = 'relative';\n textContainer.style.zIndex = '3';\n textContainer.style.fontSize = '11pt';\n textContainer.style.fontFamily =\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\"';\n\n const messages = getPlayerLoginMessages({\n platform,\n isGameRegistered,\n });\n const title = document.createElement('h1');\n title.innerText = messages.title;\n title.style.fontSize = '20pt';\n title.style.fontWeight = 'bold';\n const text1 = document.createElement('p');\n text1.innerText = messages.text1;\n const text2 = document.createElement('p');\n text2.innerText = messages.text2;\n textContainer.appendChild(title);\n textContainer.appendChild(text1);\n textContainer.appendChild(text2);\n\n if (!isGameRegistered) {\n // Remove the loader and add the wiki link.\n loaderContainer.innerHTML = '';\n\n if (wikiOpenAction) {\n const link = document.createElement('a');\n addTouchAndClickEventListeners(link, wikiOpenAction);\n link.innerText = 'How to publish my game';\n link.style.color = '#0078d4';\n link.style.textDecoration = 'none';\n link.style.textDecoration = 'underline';\n link.style.cursor = 'pointer';\n\n textContainer.appendChild(link);\n }\n }\n\n loaderContainer.prepend(textContainer);\n\n return textContainer;\n };\n\n /**\n * Helper to add the authentication link in case the window hasn't opened properly.\n * Useful for Electron & Web platforms.\n */\n export const addAuthenticationUrlToTextsContainer = (\n onClick: () => void,\n textContainer: HTMLDivElement\n ) => {\n const link = document.createElement('a');\n addTouchAndClickEventListeners(link, onClick);\n link.innerText = 'Try again';\n link.style.color = '#0078d4';\n link.style.textDecoration = 'none';\n link.style.textDecoration = 'underline';\n link.style.cursor = 'pointer';\n\n textContainer.appendChild(link);\n };\n\n /**\n * Creates a DOM element to display a dismissable banner.\n */\n export const computeDismissableBanner = function (\n onDismissBanner: () => void\n ): HTMLDivElement {\n const divContainer = document.createElement('div');\n\n divContainer.id = 'authenticated-banner';\n divContainer.style.position = 'absolute';\n divContainer.style.pointerEvents = 'all';\n divContainer.style.backgroundColor = '#0E062D';\n divContainer.style.top = '0px';\n divContainer.style.height = '48px';\n divContainer.style.left = '0px';\n divContainer.style.width = '100%';\n divContainer.style.padding = '6px 16px';\n // Use zIndex 1 to make sure it is below the authentication iframe or webview.\n divContainer.style.zIndex = '1';\n divContainer.style.display = 'flex';\n divContainer.style.flexDirection = 'row-reverse';\n divContainer.style.justifyContent = 'space-between';\n divContainer.style.alignItems = 'center';\n divContainer.style.boxShadow = '0px 4px 4px rgba(0, 0, 0, 0.25)';\n divContainer.style.fontSize = '11pt';\n divContainer.style.color = '#FFFFFF';\n divContainer.style.fontFamily =\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\"';\n\n const _closeContainer: HTMLDivElement = document.createElement('div');\n _closeContainer.style.cursor = 'pointer';\n _closeContainer.style.display = 'flex';\n _closeContainer.style.justifyContent = 'center';\n _closeContainer.style.alignItems = 'center';\n _closeContainer.style.zIndex = '3';\n _closeContainer.style.marginRight = '32px';\n _closeContainer.style.height = '100%';\n addTouchAndClickEventListeners(_closeContainer, onDismissBanner);\n\n const _close = document.createElement('img');\n _close.setAttribute('width', '30px');\n _close.setAttribute(\n 'src',\n 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAiIGhlaWdodD0iMzAiIHZpZXdCb3g9IjAgMCAzMCAzMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTIzLjc1IDguMDEyNUwyMS45ODc1IDYuMjVMMTUgMTMuMjM3NUw4LjAxMjUgNi4yNUw2LjI1IDguMDEyNUwxMy4yMzc1IDE1TDYuMjUgMjEuOTg3NUw4LjAxMjUgMjMuNzVMMTUgMTYuNzYyNUwyMS45ODc1IDIzLjc1TDIzLjc1IDIxLjk4NzVMMTYuNzYyNSAxNUwyMy43NSA4LjAxMjVaIiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4K'\n );\n\n _closeContainer.appendChild(_close);\n divContainer.appendChild(_closeContainer);\n\n return divContainer;\n };\n\n /**\n * Creates a DOM element representing a banner for the user to know which account\n * they're using and also to allow switching to another account.\n */\n export const computeAuthenticatedBanner = function (\n onOpenAuthenticationWindow: () => void,\n onDismissBanner: () => void,\n username: string | null\n ): HTMLDivElement {\n const divContainer = computeDismissableBanner(onDismissBanner);\n\n const playerUsername = username || 'Anonymous';\n\n const _textContainer: HTMLDivElement = document.createElement('div');\n const loggedText = document.createElement('p');\n loggedText.id = 'loggedText';\n loggedText.innerHTML = `<img style=\"margin-right:4px\" src=\"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOSIgaGVpZ2h0PSI5IiB2aWV3Qm94PSIwIDAgOSA5IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBkPSJNNC4xNjY2NyAwQzEuODY2NjcgMCAwIDEuODY2NjcgMCA0LjE2NjY3QzAgNi40NjY2NyAxLjg2NjY3IDguMzMzMzMgNC4xNjY2NyA4LjMzMzMzQzYuNDY2NjcgOC4zMzMzMyA4LjMzMzMzIDYuNDY2NjcgOC4zMzMzMyA0LjE2NjY3QzguMzMzMzMgMS44NjY2NyA2LjQ2NjY3IDAgNC4xNjY2NyAwWk0zLjMzMzMzIDYuMjVMMS4yNSA0LjE2NjY3TDEuODM3NSAzLjU3OTE3TDMuMzMzMzMgNS4wNzA4M0w2LjQ5NTgzIDEuOTA4MzNMNy4wODMzMyAyLjVMMy4zMzMzMyA2LjI1WiIgZmlsbD0iIzAwQ0M4MyIvPgo8L3N2Zz4K\" />\n Logged as ${playerUsername}`;\n loggedText.style.margin = '0px';\n\n const changeAccountText = document.createElement('p');\n changeAccountText.id = 'changeAccountText';\n changeAccountText.innerText = `Click here to switch to another account.`;\n changeAccountText.style.margin = '0px';\n changeAccountText.style.marginTop = '4px';\n changeAccountText.style.textDecoration = 'underline';\n changeAccountText.style.cursor = 'pointer';\n addTouchAndClickEventListeners(\n changeAccountText,\n onOpenAuthenticationWindow\n );\n\n _textContainer.appendChild(loggedText);\n _textContainer.appendChild(changeAccountText);\n divContainer.appendChild(_textContainer);\n\n return divContainer;\n };\n\n /**\n * Creates a DOM element representing a banner for the user to know\n * they are not connected and to allow logging in.\n */\n export const computeNotAuthenticatedBanner = function (\n onOpenAuthenticationWindow: () => void,\n onDismissBanner: () => void\n ): HTMLDivElement {\n const divContainer = computeDismissableBanner(onDismissBanner);\n\n const _textContainer: HTMLDivElement = document.createElement('div');\n const loggedText = document.createElement('p');\n loggedText.id = 'loggedText';\n loggedText.innerHTML = `You are not authenticated.`;\n loggedText.style.margin = '0px';\n\n const changeAccountText = document.createElement('p');\n changeAccountText.id = 'changeAccountText';\n changeAccountText.innerText = `Click here to log in.`;\n changeAccountText.style.margin = '0px';\n changeAccountText.style.marginTop = '4px';\n changeAccountText.style.textDecoration = 'underline';\n changeAccountText.style.cursor = 'pointer';\n addTouchAndClickEventListeners(\n changeAccountText,\n onOpenAuthenticationWindow\n );\n\n _textContainer.appendChild(loggedText);\n _textContainer.appendChild(changeAccountText);\n divContainer.appendChild(_textContainer);\n\n return divContainer;\n };\n\n /**\n * Create, display, and hide the logged in confirmation.\n */\n export const displayLoggedInNotification = function (\n domContainer: HTMLDivElement,\n username: string\n ) {\n showNotification(\n domContainer,\n 'authenticated-notification',\n `<img style=\"margin-right:4px\" src=\"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOSIgaGVpZ2h0PSI5IiB2aWV3Qm94PSIwIDAgOSA5IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBkPSJNNC4xNjY2NyAwQzEuODY2NjcgMCAwIDEuODY2NjcgMCA0LjE2NjY3QzAgNi40NjY2NyAxLjg2NjY3IDguMzMzMzMgNC4xNjY2NyA4LjMzMzMzQzYuNDY2NjcgOC4zMzMzMyA4LjMzMzMzIDYuNDY2NjcgOC4zMzMzMyA0LjE2NjY3QzguMzMzMzMgMS44NjY2NyA2LjQ2NjY3IDAgNC4xNjY2NyAwWk0zLjMzMzMzIDYuMjVMMS4yNSA0LjE2NjY3TDEuODM3NSAzLjU3OTE3TDMuMzMzMzMgNS4wNzA4M0w2LjQ5NTgzIDEuOTA4MzNMNy4wODMzMyAyLjVMMy4zMzMzMyA2LjI1WiIgZmlsbD0iIzAwQ0M4MyIvPgo8L3N2Zz4K\" />\n Logged as ${username}`,\n 'success'\n );\n };\n\n /**\n * Create, display, and hide the logged in confirmation.\n */\n export const displayLoggedOutNotification = function (\n domContainer: HTMLDivElement\n ) {\n showNotification(\n domContainer,\n 'authenticated-notification',\n `<img style=\"margin-right:4px\" src=\"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOSIgaGVpZ2h0PSI5IiB2aWV3Qm94PSIwIDAgOSA5IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBkPSJNNC4xNjY2NyAwQzEuODY2NjcgMCAwIDEuODY2NjcgMCA0LjE2NjY3QzAgNi40NjY2NyAxLjg2NjY3IDguMzMzMzMgNC4xNjY2NyA4LjMzMzMzQzYuNDY2NjcgOC4zMzMzMyA4LjMzMzMzIDYuNDY2NjcgOC4zMzMzMyA0LjE2NjY3QzguMzMzMzMgMS44NjY2NyA2LjQ2NjY3IDAgNC4xNjY2NyAwWk0zLjMzMzMzIDYuMjVMMS4yNSA0LjE2NjY3TDEuODM3NSAzLjU3OTE3TDMuMzMzMzMgNS4wNzA4M0w2LjQ5NTgzIDEuOTA4MzNMNy4wODMzMyAyLjVMMy4zMzMzMyA2LjI1WiIgZmlsbD0iIzAwQ0M4MyIvPgo8L3N2Zz4K\" />\n Logged out`,\n 'success'\n );\n };\n\n /**\n * Create, display, and hide an error notification.\n */\n export const displayErrorNotification = function (\n domContainer: HTMLDivElement\n ) {\n showNotification(\n domContainer,\n 'error-notification',\n 'An error occurred while authenticating, please try again.',\n 'error'\n );\n };\n\n /**\n * Helper to show a notification to the user, that disappears automatically.\n */\n export const showNotification = function (\n domContainer: HTMLDivElement,\n id: string,\n content: string,\n type: 'success' | 'error'\n ) {\n const divContainer = document.createElement('div');\n\n divContainer.id = id;\n divContainer.style.position = 'absolute';\n divContainer.style.pointerEvents = 'all';\n divContainer.style.backgroundColor =\n type === 'success' ? '#0E062D' : 'red';\n divContainer.style.top = '12px';\n divContainer.style.right = '16px';\n divContainer.style.padding = '6px 32px 6px 6px';\n // Use zIndex 1 to make sure it is below the authentication iframe or webview.\n divContainer.style.zIndex = '1';\n divContainer.style.display = 'flex';\n divContainer.style.flexDirection = 'row-reverse';\n divContainer.style.justifyContent = 'space-between';\n divContainer.style.alignItems = 'center';\n divContainer.style.boxShadow = '0px 4px 4px rgba(0, 0, 0, 0.25)';\n divContainer.style.borderRadius = '4px';\n divContainer.style.fontSize = '11pt';\n divContainer.style.color = '#FFFFFF';\n divContainer.style.fontFamily =\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\"';\n try {\n divContainer.animate(\n [\n { transform: 'translateY(-30px)', opacity: 0 },\n { transform: 'translateY(0px)', opacity: 1 },\n ],\n {\n duration: 700,\n easing: 'ease-out',\n }\n );\n } catch {\n logger.warn('Animation not supported, div will be fixed.');\n }\n const loggedText = document.createElement('p');\n loggedText.id = 'loggedText';\n loggedText.innerHTML = content;\n loggedText.style.margin = '0px';\n\n divContainer.appendChild(loggedText);\n\n domContainer.appendChild(divContainer);\n const animationTime = 700;\n const notificationTime = 5000;\n setTimeout(() => {\n try {\n divContainer.animate(\n [\n { transform: 'translateY(0px)', opacity: 1 },\n { transform: 'translateY(-30px)', opacity: 0 },\n ],\n {\n duration: animationTime,\n easing: 'ease-in',\n }\n );\n } catch {\n logger.warn('Animation not supported, div will be fixed.');\n }\n }, notificationTime);\n // Use timeout because onanimationend listener does not work.\n setTimeout(() => {\n divContainer.remove();\n }, notificationTime + animationTime);\n };\n\n /**\n * Helper to add event listeners on a pressable/clickable element\n * to work on both desktop and mobile.\n */\n const addTouchAndClickEventListeners = function (\n element: HTMLElement,\n action: () => void\n ) {\n // Touch start event listener for mobile.\n element.addEventListener('touchstart', (event) => {\n action();\n });\n // Click event listener for desktop.\n element.addEventListener('click', (event) => {\n action();\n });\n };\n }\n}\n"],
5
5
  "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,2 +1,2 @@
1
- var gdjs;(function(u){const r=new u.Logger("Player Authentication"),g=u.playerAuthenticationComponents;let X;(function(s){let p=null,T=null,b=null,O=!1,v=!1,D=null,A=null,k=null,W=null,f=null,d=null,j=null,S=null,I=null,C=null,m=null;const Y=e=>{B(e)==="web"&&(N(),I=t=>{_({runtimeScene:e,event:t,checkOrigin:!0})},window.addEventListener("message",I,!0),r.info("Notifying parent window that player authentication is ready."),window.parent.postMessage({id:"playerAuthReady"},"*"),j=setTimeout(()=>{r.info("Removing automatic games platform authentication listener."),N()},3e3))},Z=e=>{const t=e.getGame().getAdditionalOptions();if(t&&t.isPreview){const n=t.playerId,i=t.playerToken,o=t.playerUsername;n&&i&&(r.info(`Automatically logging in the player with ID ${n} as it's a preview.`),x({userId:n,username:o||null,userToken:i}),q(e))}};u.registerRuntimeScenePostEventsCallback(()=>{O=!1}),u.registerFirstRuntimeSceneLoadedCallback(e=>{Z(e),Y(e)});const M=e=>`${e}_authenticatedUser`,U=({runtimeGame:e,gameId:t,connectionId:n,authWindowOptions:i})=>{const o="https://gd.games",a=new URLSearchParams;if(a.set("gameId",t),n&&a.set("connectionId",n),e.isUsingGDevelopDevelopmentEnvironment()&&a.set("dev","true"),a.set("allowLoginProviders","true"),i)for(const[l,c]of Object.entries(i))a.set(l,c.toString());return`${o}/auth?${a.toString()}`},B=e=>e.getGame().getRenderer().getElectron()?"electron":ee(e)?"web-iframe":typeof cordova!="undefined"?"cordova-websocket":"web",ee=e=>{const t=e.getGame().getAdditionalOptions();return t&&t.isPreview&&t.allowAuthenticationUsingIframeForPreview};s.isAuthenticated=()=>(v||R(),b!==null),s.hasLoggedIn=()=>O,s.getUsername=()=>(v||R(),p||""),s.getUserToken=()=>(v||R(),b||null),s.getUserId=()=>(v||R(),T||"");const H=(e,t,n=0)=>{const o=`${e.isUsingGDevelopDevelopmentEnvironment()?"https://api-dev.gdevelop.io":"https://api.gdevelop.io"}/game/public-game/${t}`;return fetch(o,{method:"HEAD"}).then(a=>a.status!==200?(r.warn(`Error while fetching the game: ${a.status} ${a.statusText}`),a.status===404||n>2?!1:H(e,t,n+1)):!0,a=>(r.error("Error while fetching game:",a),!1))};s.logout=e=>{p=null,b=null,T=null;const t=u.projectData.properties.projectUuid;if(!t){r.error("Missing game id in project properties.");return}window.localStorage.removeItem(M(t)),G(e),s.removeAuthenticationBanner(e);const n=e.getGame().getRenderer().getDomElementContainer();if(!n){h(e,"The div element covering the game couldn't be found, the authentication banner cannot be displayed.");return}g.displayLoggedOutNotification(n)};const R=()=>{const e=u.projectData.properties.projectUuid;if(!e){r.error("Missing game id in project properties.");return}try{const t=window.localStorage.getItem(M(e));if(!t){v=!0;return}const n=JSON.parse(t);p=n.username,T=n.userId,b=n.userToken,v=!0}catch(t){r.warn("Unable to read authentication details from localStorage. Player authentication will not be available.",t)}},G=e=>{if(s.removeAuthenticationContainer(e),V(),m&&(r.info("Closing authentication websocket connection."),m.close(),m=null),D&&(D.close(),D=null),typeof SafariViewController!="undefined")try{SafariViewController.hide()}catch{r.info("Could not hide login window. Waiting for user to do it.")}},x=({username:e,userId:t,userToken:n})=>{e||r.warn("The authenticated player does not have a username"),p=e,T=t,b=n,O=!0;const i=u.projectData.properties.projectUuid;if(!i){r.error("Missing game id in project properties.");return}try{window.localStorage.setItem(M(i),JSON.stringify({username:p,userId:T,userToken:b}))}catch(o){r.warn("Unable to save the authentication details to localStorage. Player authentication will not be available.",o)}};s.login=({runtimeScene:e,userId:t,username:n,userToken:i})=>{x({userId:t,username:n,userToken:i}),G(e),s.removeAuthenticationBanner(e);const o=e.getGame().getRenderer().getDomElementContainer();if(!o){h(e,"The div element covering the game couldn't be found, the authentication banner cannot be displayed.");return}g.displayLoggedInNotification(o,p||"Anonymous")};const _=function({runtimeScene:e,event:t,checkOrigin:n,onDone:i}){if(!(n&&!["https://liluo.io","https://gd.games"].includes(t.origin))){if(!t.data.id)throw new Error("Malformed message");switch(t.data.id){case"authenticationResult":{if(!(t.data.body&&t.data.body.token))throw new Error("Malformed message.");s.login({runtimeScene:e,userId:t.data.body.userId,username:t.data.body.username,userToken:t.data.body.token}),P(e),i&&i("logged");break}case"alreadyAuthenticated":{if(!(t.data.body&&t.data.body.token))throw new Error("Malformed message.");x({userId:t.data.body.userId,username:t.data.body.username,userToken:t.data.body.token}),N(),q(e);break}}}},h=function(e,t){r.error(t),G(e);const n=e.getGame().getRenderer().getDomElementContainer();if(!n){h(e,"The div element covering the game couldn't be found, the authentication banner cannot be displayed.");return}g.displayErrorNotification(n),P(e)},te=e=>{V();const t=15*60*1e3;S=setTimeout(()=>{r.info("Authentication window did not send message in time. Closing it."),G(e),P(e)},t)},V=()=>{S&&clearTimeout(S)},K=function(e){const t=()=>{s.removeAuthenticationBanner(e)},n=()=>{s.openAuthenticationWindow(e)};return b?g.computeAuthenticatedBanner(n,t,p):g.computeNotAuthenticatedBanner(n,t)};s.displayAuthenticationBanner=function(e){if(d){d.style.opacity="1";return}v||R();const t=e.getGame().getRenderer().getDomElementContainer();if(!t){h(e,"The div element covering the game couldn't be found, the authentication banner cannot be displayed.");return}d=K(e),t.appendChild(d)};const q=function(e){if(!d)return;const t=e.getGame().getRenderer().getDomElementContainer();if(!t){h(e,"The div element covering the game couldn't be found, the authentication banner cannot be displayed.");return}const n=d;d=K(e),t.replaceChild(d,n)},z=(e,t,n)=>new Promise(i=>{let o=!1;const a=e.getGame().isUsingGDevelopDevelopmentEnvironment()?`wss://api-ws-dev.gdevelop.io/play?gameId=${t}&connectionType=login`:`wss://api-ws.gdevelop.io/play?gameId=${t}&connectionType=login`;m=new WebSocket(a),m.onopen=()=>{r.info("Opened authentication websocket connection."),m&&m.send(JSON.stringify({action:"getConnectionId"}))},m.onerror=()=>{r.info("Error in authentication websocket connection."),o||(o=!0,i("errored")),h(e,"Error while connecting to the authentication server.")},m.onclose=()=>{r.info("Closing authentication websocket connection."),o||(o=!0,i("dismissed"))},m.onmessage=l=>{if(l.data){const c=JSON.parse(l.data);switch(c.type){case"authenticationResult":{const w=c.data;s.login({runtimeScene:e,userId:w.userId,username:w.username,userToken:w.token}),P(e),o=!0,i("logged");break}case"connectionId":{const y=c.data.connectionId;if(!y){r.error("No WebSocket connectionId received"),o=!0,i("errored");return}r.info("WebSocket connectionId received."),n({connectionId:y,resolve:i});break}}}}}),oe=(e,t,n)=>z(e,t,({connectionId:i})=>{const o=U({runtimeGame:e.getGame(),gameId:t,connectionId:i,authWindowOptions:n}),a=e.getGame().getRenderer().getElectron(),l=()=>a.shell.openExternal(o);l(),f&&g.addAuthenticationUrlToTextsContainer(l,f)}),ie=(e,t,n)=>z(e,t,({connectionId:i,resolve:o})=>{const a=U({runtimeGame:e.getGame(),gameId:t,connectionId:i,authWindowOptions:n});SafariViewController.isAvailable(function(l){l?SafariViewController.show({url:a,hidden:!1,animated:!0,transition:"slide",enterReaderModeIfAvailable:!1,barColor:"#000000",tintColor:"#ffffff",controlTintColor:"#ffffff"},function(c){c.event==="closed"&&o("dismissed")},function(c){r.log("Error opening webview: "+JSON.stringify(c)),o("errored")}):(r.error("Plugin SafariViewController is not available"),o("errored"))})}),ae=(e,t,n)=>new Promise(i=>{const o=U({runtimeGame:e.getGame(),gameId:t,authWindowOptions:n});let a=!1;C=F=>{_({runtimeScene:e,event:F,checkOrigin:!0,onDone:L=>{a||(a=!0,i(L))}})},window.addEventListener("message",C,!0);const l=screen.width/2-500/2,c=screen.height/2-600/2,w=`left=${l},top=${c},width=500,height=600`,y=()=>{D=window.open(o,"authentication",w)};y(),f&&g.addAuthenticationUrlToTextsContainer(y,f)}),re=(e,t,n)=>new Promise(i=>{if(!W||!k||!f){r.error("Can't open an authentication iframe - no iframe container, loader container or text container was opened for it.");return}const o=U({runtimeGame:e.getGame(),gameId:t,authWindowOptions:n});C=a=>{_({runtimeScene:e,event:a,checkOrigin:!0,onDone:i})},window.addEventListener("message",C,!0),g.displayIframeInsideAuthenticationContainer(W,k,f,o)});s.openAuthenticationWindow=(e,t={disableGuestLogin:!1})=>new u.PromiseTask(new Promise(n=>{const i=e.getGame().getRenderer().getDomElementContainer();if(!i){h(e,"The div element covering the game couldn't be found, the authentication window cannot be displayed."),n({status:"errored"});return}const o=u.projectData.properties.projectUuid;if(!o){h(e,"The game ID is missing, the authentication window cannot be opened."),n({status:"errored"});return}let a=!1;const l=()=>{G(e),s.displayAuthenticationBanner(e),a=!0,n({status:"dismissed"})};d&&(d.style.opacity="0");const c=B(e),{rootContainer:w,loaderContainer:y,iframeContainer:F}=g.computeAuthenticationContainer(l);A=w,k=y,W=F,i.appendChild(A),(async()=>{const L=await H(e.getGame(),o);if(k){const Q=e.getGame().getRenderer().getElectron(),de=Q?()=>Q.shell.openExternal("https://wiki.gdevelop.io/gdevelop5/publishing/web"):null;f=g.addAuthenticationTextsToLoadingContainer(k,c,L,de)}if(!L)return;te(e);let E;switch(c){case"electron":E=await oe(e,o,t);break;case"cordova-websocket":E=await ie(e,o,t);break;case"web-iframe":E=await re(e,o,t);break;case"web":default:E=await ae(e,o,t);break}a||(E==="dismissed"&&l(),n({status:E}))})()})),s.isAuthenticationWindowOpen=function(){return!!A},s.removeAuthenticationContainer=function(e){ce();const t=e.getGame().getRenderer().getDomElementContainer();if(!t){r.info("The div element covering the game couldn't be found, the authentication must be already closed.");return}A&&t.removeChild(A),A=null,k=null,W=null,f=null};const ce=function(){C&&(window.removeEventListener("message",C,!0),C=null)},N=function(){I&&(window.removeEventListener("message",I,!0),I=null),j&&(clearTimeout(j),j=null)};s.removeAuthenticationBanner=function(e){if(!d){r.info("The authentication banner couldn't be found, the authentication banner must be already closed.");return}const t=e.getGame().getRenderer().getDomElementContainer();if(!t){r.info("The div element covering the game couldn't be found, the authentication must be already closed.");return}t.removeChild(d),d=null};const P=function(e){const t=e.getGame().getRenderer().getCanvas();t&&t.focus()}})(X=u.playerAuthentication||(u.playerAuthentication={}))})(gdjs||(gdjs={}));
1
+ var gdjs;(function(u){const r=new u.Logger("Player Authentication"),g=u.playerAuthenticationComponents;let X;(function(s){let b=null,T=null,v=null,S=!1,y=!1,W=null,A=null,E=null,D=null,h=null,d=null,P=null,x=null,R=null,f=null,m=null;const Y=e=>{N(e)==="games-platform"&&(r.info("Notifying parent window that player authentication is ready."),window.parent.postMessage({id:"playerAuthReady"},"*"),P=setTimeout(()=>{r.info("Removing automatic games platform authentication listener."),U()},3e3))},Z=e=>{N(e)==="games-platform"&&(U(),R=t=>{L({runtimeScene:e,event:t,checkOrigin:!0})},window.addEventListener("message",R,!0))},ee=e=>{const t=e.getGame().getAdditionalOptions();if(t&&t.isPreview){const n=t.playerId,i=t.playerToken,o=t.playerUsername;n&&i&&(r.info(`Automatically logging in the player with ID ${n} as it's a preview.`),F({userId:n,username:o||null,userToken:i}),q(e))}};u.registerRuntimeScenePostEventsCallback(()=>{S=!1}),u.registerFirstRuntimeSceneLoadedCallback(e=>{ee(e),Z(e),Y(e)});const _=e=>`${e}_authenticatedUser`,j=({runtimeGame:e,gameId:t,connectionId:n,authWindowOptions:i})=>{const o="https://gd.games",a=new URLSearchParams;if(a.set("gameId",t),n&&a.set("connectionId",n),e.isUsingGDevelopDevelopmentEnvironment()&&a.set("dev","true"),a.set("allowLoginProviders","true"),i)for(const[c,l]of Object.entries(i))a.set(c,l.toString());return`${o}/auth?${a.toString()}`},N=e=>e.getGame().getRenderer().getElectron()?"electron":te(e)?"web-iframe":typeof cordova!="undefined"?"cordova-websocket":window.parent!==window&&(document.referrer.indexOf("https://gd.games")===0||document.referrer.indexOf("http://localhost:4000")===0)?"games-platform":"web",te=e=>{const t=e.getGame().getAdditionalOptions();return t&&t.isPreview&&t.allowAuthenticationUsingIframeForPreview};s.isAuthenticated=()=>(y||I(),v!==null),s.hasLoggedIn=()=>S,s.getUsername=()=>(y||I(),b||""),s.getUserToken=()=>(y||I(),v||null),s.getUserId=()=>(y||I(),T||"");const H=(e,t,n=0)=>{const o=`${e.isUsingGDevelopDevelopmentEnvironment()?"https://api-dev.gdevelop.io":"https://api.gdevelop.io"}/game/public-game/${t}`;return fetch(o,{method:"HEAD"}).then(a=>a.status!==200?(r.warn(`Error while fetching the game: ${a.status} ${a.statusText}`),a.status===404||n>2?!1:H(e,t,n+1)):!0,a=>(r.error("Error while fetching game:",a),!1))};s.logout=e=>{b=null,v=null,T=null;const t=u.projectData.properties.projectUuid;if(!t){r.error("Missing game id in project properties.");return}window.localStorage.removeItem(_(t)),G(e),s.removeAuthenticationBanner(e);const n=e.getGame().getRenderer().getDomElementContainer();if(!n){w(e,"The div element covering the game couldn't be found, the authentication banner cannot be displayed.");return}g.displayLoggedOutNotification(n)};const I=()=>{const e=u.projectData.properties.projectUuid;if(!e){r.error("Missing game id in project properties.");return}try{const t=window.localStorage.getItem(_(e));if(!t){y=!0;return}const n=JSON.parse(t);b=n.username,T=n.userId,v=n.userToken,y=!0}catch(t){r.warn("Unable to read authentication details from localStorage. Player authentication will not be available.",t)}},G=e=>{if(s.removeAuthenticationContainer(e),V(),m&&(r.info("Closing authentication websocket connection."),m.close(),m=null),W&&(W.close(),W=null),typeof SafariViewController!="undefined")try{SafariViewController.hide()}catch{r.info("Could not hide login window. Waiting for user to do it.")}},F=({username:e,userId:t,userToken:n})=>{e||r.warn("The authenticated player does not have a username"),b=e,T=t,v=n,S=!0;const i=u.projectData.properties.projectUuid;if(!i){r.error("Missing game id in project properties.");return}try{window.localStorage.setItem(_(i),JSON.stringify({username:b,userId:T,userToken:v}))}catch(o){r.warn("Unable to save the authentication details to localStorage. Player authentication will not be available.",o)}};s.login=({runtimeScene:e,userId:t,username:n,userToken:i})=>{F({userId:t,username:n,userToken:i}),G(e),s.removeAuthenticationBanner(e);const o=e.getGame().getRenderer().getDomElementContainer();if(!o){w(e,"The div element covering the game couldn't be found, the authentication banner cannot be displayed.");return}g.displayLoggedInNotification(o,b||"Anonymous")};const L=function({runtimeScene:e,event:t,checkOrigin:n,onDone:i}){const o=l=>{s.login({runtimeScene:e,userId:l.data.body.userId,username:l.data.body.username,userToken:l.data.body.token}),O(e),i&&i("logged")},a=l=>{F({userId:l.data.body.userId,username:l.data.body.username,userToken:l.data.body.token}),U(),q(e),i&&i("logged")};if(!(n&&!["https://gd.games","http://localhost:4000"].includes(t.origin))){if(!t.data.id)throw new Error("Malformed message");switch(t.data.id){case"authenticationResult":{if(!(t.data.body&&t.data.body.token))throw new Error("Malformed message.");r.info("Received authentication result, logging in player."),o(t);break}case"alreadyAuthenticated":{if(!(t.data.body&&t.data.body.token))throw new Error("Malformed message.");if(r.info("Player is already authenticated, logging in player."),A){o(t);break}a(t);break}}}},w=function(e,t){r.error(t),G(e);const n=e.getGame().getRenderer().getDomElementContainer();if(!n){w(e,"The div element covering the game couldn't be found, the authentication banner cannot be displayed.");return}g.displayErrorNotification(n),O(e)},ne=e=>{V();const t=15*60*1e3;x=setTimeout(()=>{r.info("Authentication window did not send message in time. Closing it."),G(e),O(e)},t)},V=()=>{x&&clearTimeout(x)},K=function(e){const t=()=>{s.removeAuthenticationBanner(e)},n=()=>{s.openAuthenticationWindow(e)};return v?g.computeAuthenticatedBanner(n,t,b):g.computeNotAuthenticatedBanner(n,t)};s.displayAuthenticationBanner=function(e){if(d){d.style.opacity="1";return}y||I();const t=e.getGame().getRenderer().getDomElementContainer();if(!t){w(e,"The div element covering the game couldn't be found, the authentication banner cannot be displayed.");return}d=K(e),t.appendChild(d)};const q=function(e){if(!d)return;const t=e.getGame().getRenderer().getDomElementContainer();if(!t){w(e,"The div element covering the game couldn't be found, the authentication banner cannot be displayed.");return}const n=d;d=K(e),t.replaceChild(d,n)},z=(e,t,n)=>new Promise(i=>{let o=!1;const a=e.getGame().isUsingGDevelopDevelopmentEnvironment()?`wss://api-ws-dev.gdevelop.io/play?gameId=${t}&connectionType=login`:`wss://api-ws.gdevelop.io/play?gameId=${t}&connectionType=login`;m=new WebSocket(a),m.onopen=()=>{r.info("Opened authentication websocket connection."),m&&m.send(JSON.stringify({action:"getConnectionId"}))},m.onerror=()=>{r.info("Error in authentication websocket connection."),o||(o=!0,i("errored")),w(e,"Error while connecting to the authentication server.")},m.onclose=()=>{r.info("Closing authentication websocket connection."),o||(o=!0,i("dismissed"))},m.onmessage=c=>{if(c.data){const l=JSON.parse(c.data);switch(l.type){case"authenticationResult":{const p=l.data;s.login({runtimeScene:e,userId:p.userId,username:p.username,userToken:p.token}),O(e),o=!0,i("logged");break}case"connectionId":{const C=l.data.connectionId;if(!C){r.error("No WebSocket connectionId received"),o=!0,i("errored");return}r.info("WebSocket connectionId received."),n({connectionId:C,resolve:i});break}}}}}),ie=(e,t,n)=>z(e,t,({connectionId:i})=>{const o=j({runtimeGame:e.getGame(),gameId:t,connectionId:i,authWindowOptions:n}),a=e.getGame().getRenderer().getElectron(),c=()=>a.shell.openExternal(o);c(),h&&g.addAuthenticationUrlToTextsContainer(c,h)}),ae=(e,t,n)=>z(e,t,({connectionId:i,resolve:o})=>{const a=j({runtimeGame:e.getGame(),gameId:t,connectionId:i,authWindowOptions:n});SafariViewController.isAvailable(function(c){c?SafariViewController.show({url:a,hidden:!1,animated:!0,transition:"slide",enterReaderModeIfAvailable:!1,barColor:"#000000",tintColor:"#ffffff",controlTintColor:"#ffffff"},function(l){l.event==="closed"&&o("dismissed")},function(l){r.log("Error opening webview: "+JSON.stringify(l)),o("errored")}):(r.error("Plugin SafariViewController is not available"),o("errored"))})}),re=(e,t,n)=>new Promise(i=>{const o=j({runtimeGame:e.getGame(),gameId:t,authWindowOptions:n});let a=!1;f=B=>{L({runtimeScene:e,event:B,checkOrigin:!0,onDone:M=>{a||(a=!0,i(M))}})},window.addEventListener("message",f,!0);const c=screen.width/2-500/2,l=screen.height/2-600/2,p=`left=${c},top=${l},width=500,height=600`,C=()=>{W=window.open(o,"authentication",p)};C(),h&&g.addAuthenticationUrlToTextsContainer(C,h)}),se=(e,t,n)=>new Promise(i=>{if(!D||!E||!h){r.error("Can't open an authentication iframe - no iframe container, loader container or text container was opened for it.");return}const o=j({runtimeGame:e.getGame(),gameId:t,authWindowOptions:n});f=a=>{L({runtimeScene:e,event:a,checkOrigin:!0,onDone:i})},window.addEventListener("message",f,!0),g.displayIframeInsideAuthenticationContainer(D,E,h,o)});s.openAuthenticationWindowForGamesPlatform=(e,t,n)=>new Promise(i=>{U(),f=o=>{L({runtimeScene:e,event:o,checkOrigin:!0,onDone:i})},window.addEventListener("message",f,!0),window.parent.postMessage({id:"openGameAuthenticationDialog",gameId:t,disableGuestLogin:n.disableGuestLogin},"*")}),s.openAuthenticationWindow=(e,t={disableGuestLogin:!1})=>new u.PromiseTask(new Promise(n=>{const i=e.getGame().getRenderer().getDomElementContainer();if(!i){w(e,"The div element covering the game couldn't be found, the authentication window cannot be displayed."),n({status:"errored"});return}const o=u.projectData.properties.projectUuid;if(!o){w(e,"The game ID is missing, the authentication window cannot be opened."),n({status:"errored"});return}let a=!1;const c=()=>{G(e),s.displayAuthenticationBanner(e),a=!0,n({status:"dismissed"})};d&&(d.style.opacity="0");const l=N(e),{rootContainer:p,loaderContainer:C,iframeContainer:B}=g.computeAuthenticationContainer(c);A=p,E=C,D=B,i.appendChild(A),(async()=>{const M=await H(e.getGame(),o);if(E){const Q=e.getGame().getRenderer().getElectron(),ge=Q?()=>Q.shell.openExternal("https://wiki.gdevelop.io/gdevelop5/publishing/web"):null;h=g.addAuthenticationTextsToLoadingContainer(E,l,M,ge)}if(!M)return;ne(e);let k;switch(l){case"electron":k=await ie(e,o,t);break;case"cordova-websocket":k=await ae(e,o,t);break;case"web-iframe":k=await se(e,o,t);break;case"games-platform":k=await s.openAuthenticationWindowForGamesPlatform(e,o,t);break;case"web":default:k=await re(e,o,t);break}a||(k==="dismissed"&&c(),n({status:k}))})()})),s.isAuthenticationWindowOpen=function(){return!!A},s.removeAuthenticationContainer=function(e){ue();const t=e.getGame().getRenderer().getDomElementContainer();if(!t){r.info("The div element covering the game couldn't be found, the authentication must be already closed.");return}A&&t.removeChild(A),A=null,E=null,D=null,h=null};const ue=function(){f&&(window.removeEventListener("message",f,!0),f=null)},U=function(){R&&(window.removeEventListener("message",R,!0),R=null),P&&(clearTimeout(P),P=null)};s.removeAuthenticationBanner=function(e){if(!d){r.info("The authentication banner couldn't be found, the authentication banner must be already closed.");return}const t=e.getGame().getRenderer().getDomElementContainer();if(!t){r.info("The div element covering the game couldn't be found, the authentication must be already closed.");return}t.removeChild(d),d=null};const O=function(e){const t=e.getGame().getRenderer().getCanvas();t&&t.focus()}})(X=u.playerAuthentication||(u.playerAuthentication={}))})(gdjs||(gdjs={}));
2
2
  //# sourceMappingURL=playerauthenticationtools.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../GDevelop/Extensions/PlayerAuthentication/playerauthenticationtools.ts"],
4
- "sourcesContent": ["namespace gdjs {\n declare var cordova: any;\n declare var SafariViewController: any;\n\n const logger = new gdjs.Logger('Player Authentication');\n const authComponents = gdjs.playerAuthenticationComponents;\n // TODO EBO Replace runtimeScene to instanceContainer.\n export namespace playerAuthentication {\n // Authentication information.\n let _username: string | null = null;\n let _userId: string | null = null;\n let _userToken: string | null = null;\n let _justLoggedIn = false;\n\n let _checkedLocalStorage: boolean = false;\n\n // Authentication display\n let _authenticationWindow: Window | null = null; // For Web.\n let _authenticationRootContainer: HTMLDivElement | null = null;\n let _authenticationLoaderContainer: HTMLDivElement | null = null;\n let _authenticationIframeContainer: HTMLDivElement | null = null;\n let _authenticationTextContainer: HTMLDivElement | null = null;\n let _authenticationBanner: HTMLDivElement | null = null;\n let _automaticGamesPlatformAuthenticationTimeoutId: NodeJS.Timeout | null = null;\n let _authenticationTimeoutId: NodeJS.Timeout | null = null;\n\n // Communication methods.\n let _automaticGamesPlatformAuthenticationCallback:\n | ((event: MessageEvent) => void)\n | null = null;\n let _authenticationMessageCallback:\n | ((event: MessageEvent) => void)\n | null = null;\n let _websocket: WebSocket | null = null;\n\n type AuthenticationWindowStatus = 'logged' | 'errored' | 'dismissed';\n type AuthenticationWindowOptions = { disableGuestLogin: boolean };\n\n const handleAutomaticGamesPlatformAuthentication = (\n runtimeScene: gdjs.RuntimeScene\n ) => {\n if (getPlayerAuthPlatform(runtimeScene) !== 'web') {\n // Automatic authentication is only valid when the game is hosted on GDevelop games platform.\n return;\n }\n\n removeAutomaticGamesPlatformAuthenticationCallback(); // Remove any callback that could have been registered before.\n _automaticGamesPlatformAuthenticationCallback = (event: MessageEvent) => {\n receiveAuthenticationMessage({\n runtimeScene,\n event,\n checkOrigin: true,\n });\n };\n window.addEventListener(\n 'message',\n _automaticGamesPlatformAuthenticationCallback,\n true\n );\n logger.info(\n 'Notifying parent window that player authentication is ready.'\n );\n window.parent.postMessage(\n {\n id: 'playerAuthReady',\n },\n '*' // We could restrict to GDevelop games platform but it's not necessary as the message is not sensitive, and it allows easy debugging.\n );\n // If no answer after 3 seconds, assume that the game is not embedded in GDevelop games platform, and remove the listener.\n _automaticGamesPlatformAuthenticationTimeoutId = setTimeout(() => {\n logger.info(\n 'Removing automatic games platform authentication listener.'\n );\n removeAutomaticGamesPlatformAuthenticationCallback();\n }, 3000);\n };\n\n const handleAutomaticPreviewAuthentication = (\n runtimeScene: gdjs.RuntimeScene\n ) => {\n const runtimeGameOptions = runtimeScene.getGame().getAdditionalOptions();\n if (runtimeGameOptions && runtimeGameOptions.isPreview) {\n // If the game is a preview, and the user is already authenticated, we can log them in automatically.\n const playerId = runtimeGameOptions.playerId;\n const playerToken = runtimeGameOptions.playerToken;\n const playerUsername = runtimeGameOptions.playerUsername;\n if (playerId && playerToken) {\n logger.info(\n `Automatically logging in the player with ID ${playerId} as it's a preview.`\n );\n saveAuthKeyToStorage({\n userId: playerId,\n username: playerUsername || null,\n userToken: playerToken,\n });\n refreshAuthenticationBannerIfAny(runtimeScene);\n }\n }\n };\n\n // Ensure that the condition \"just logged in\" is valid only for one frame.\n gdjs.registerRuntimeScenePostEventsCallback(() => {\n _justLoggedIn = false;\n });\n\n // If the extension is used, register an eventlistener to know if the user is\n // logged in while playing the game on GDevelop games platform.\n // Then send a message to the parent iframe to say that the player auth is ready.\n gdjs.registerFirstRuntimeSceneLoadedCallback(\n (runtimeScene: RuntimeScene) => {\n handleAutomaticPreviewAuthentication(runtimeScene);\n handleAutomaticGamesPlatformAuthentication(runtimeScene);\n }\n );\n\n const getLocalStorageKey = (gameId: string) =>\n `${gameId}_authenticatedUser`;\n\n const getAuthWindowUrl = ({\n runtimeGame,\n gameId,\n connectionId,\n authWindowOptions,\n }: {\n runtimeGame: gdjs.RuntimeGame;\n gameId: string;\n connectionId?: string;\n authWindowOptions?: AuthenticationWindowOptions;\n }) => {\n // Uncomment to test the case of a failing loading:\n // return 'https://gd.games.wronglink';\n\n const baseUrl = 'https://gd.games';\n // const baseUrl = 'http://localhost:4000';\n\n const searchParams = new URLSearchParams();\n searchParams.set('gameId', gameId);\n if (connectionId) searchParams.set('connectionId', connectionId);\n if (runtimeGame.isUsingGDevelopDevelopmentEnvironment()) {\n searchParams.set('dev', 'true');\n }\n searchParams.set('allowLoginProviders', 'true');\n if (authWindowOptions) {\n for (const [key, value] of Object.entries(authWindowOptions)) {\n searchParams.set(key, value.toString());\n }\n }\n\n return `${baseUrl}/auth?${searchParams.toString()}`;\n };\n\n /**\n * Get the platform running the game, which changes how the authentication\n * window is opened.\n */\n const getPlayerAuthPlatform = (\n runtimeScene: RuntimeScene\n ): 'electron' | 'cordova-websocket' | 'web-iframe' | 'web' => {\n const runtimeGame = runtimeScene.getGame();\n const electron = runtimeGame.getRenderer().getElectron();\n if (electron) {\n // This can be a:\n // - Preview in GDevelop desktop app.\n // - Desktop game running on Electron.\n return 'electron';\n }\n\n // This can be a:\n // - Preview in GDevelop mobile app (iOS only)\n if (shouldAuthenticationUseIframe(runtimeScene)) return 'web-iframe';\n\n if (typeof cordova !== 'undefined') {\n // The game is an Android or an iOS app.\n return 'cordova-websocket';\n }\n\n // This can be a:\n // - Preview in GDevelop web-app\n // - Preview in Gdevelop mobile app (Android only)\n // - Web game (gd.games or any website/server) accessed via a desktop browser...\n // - Or a web game accessed via a mobile browser (Android/iOS).\n return 'web';\n };\n\n /**\n * Check if, in some exceptional cases, we allow authentication\n * to be done through a iframe.\n * This is usually discouraged as the user can't verify that the authentication\n * window is a genuine one. It's only to be used in trusted contexts (e.g:\n * preview in the GDevelop mobile app).\n */\n const shouldAuthenticationUseIframe = (runtimeScene: RuntimeScene) => {\n const runtimeGameOptions = runtimeScene.getGame().getAdditionalOptions();\n return (\n runtimeGameOptions &&\n runtimeGameOptions.isPreview &&\n runtimeGameOptions.allowAuthenticationUsingIframeForPreview\n );\n };\n\n /**\n * Returns true if a user token is present in the local storage.\n */\n export const isAuthenticated = () => {\n if (!_checkedLocalStorage) {\n readAuthenticatedUserFromLocalStorage();\n }\n return _userToken !== null;\n };\n\n /**\n * Returns true if the user just logged in.\n * Useful to update username or trigger messages in the game.\n */\n export const hasLoggedIn = () => _justLoggedIn;\n\n /**\n * Returns the username from the local storage.\n */\n export const getUsername = () => {\n if (!_checkedLocalStorage) {\n readAuthenticatedUserFromLocalStorage();\n }\n return _username || '';\n };\n\n /**\n * Returns the user token from the local storage.\n */\n export const getUserToken = () => {\n if (!_checkedLocalStorage) {\n readAuthenticatedUserFromLocalStorage();\n }\n return _userToken || null;\n };\n\n /**\n * Returns the username from the local storage.\n */\n export const getUserId = () => {\n if (!_checkedLocalStorage) {\n readAuthenticatedUserFromLocalStorage();\n }\n return _userId || '';\n };\n\n /**\n * Returns true if the game is registered, false otherwise.\n * Useful to display a message to the user to register the game before logging in.\n */\n const checkIfGameIsRegistered = (\n runtimeGame: gdjs.RuntimeGame,\n gameId: string,\n tries: number = 0\n ): Promise<boolean> => {\n const rootApi = runtimeGame.isUsingGDevelopDevelopmentEnvironment()\n ? 'https://api-dev.gdevelop.io'\n : 'https://api.gdevelop.io';\n const url = `${rootApi}/game/public-game/${gameId}`;\n return fetch(url, { method: 'HEAD' }).then(\n (response) => {\n if (response.status !== 200) {\n logger.warn(\n `Error while fetching the game: ${response.status} ${response.statusText}`\n );\n\n // If the response is not 404, it may be a timeout, so retry a few times.\n if (response.status === 404 || tries > 2) {\n return false;\n }\n\n return checkIfGameIsRegistered(runtimeGame, gameId, tries + 1);\n }\n return true;\n },\n (err) => {\n logger.error('Error while fetching game:', err);\n return false;\n }\n );\n };\n\n /**\n * Remove the user information from the local storage.\n */\n export const logout = (runtimeScene: RuntimeScene) => {\n _username = null;\n _userToken = null;\n _userId = null;\n\n const gameId = gdjs.projectData.properties.projectUuid;\n if (!gameId) {\n logger.error('Missing game id in project properties.');\n return;\n }\n window.localStorage.removeItem(getLocalStorageKey(gameId));\n cleanUpAuthWindowAndTimeouts(runtimeScene);\n removeAuthenticationBanner(runtimeScene);\n const domElementContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domElementContainer) {\n handleAuthenticationError(\n runtimeScene,\n \"The div element covering the game couldn't be found, the authentication banner cannot be displayed.\"\n );\n return;\n }\n authComponents.displayLoggedOutNotification(domElementContainer);\n };\n\n /**\n * Retrieves the user information from the local storage, and store\n * them in the extension variables.\n */\n const readAuthenticatedUserFromLocalStorage = () => {\n const gameId = gdjs.projectData.properties.projectUuid;\n if (!gameId) {\n logger.error('Missing game id in project properties.');\n return;\n }\n try {\n const authenticatedUserStorageItem = window.localStorage.getItem(\n getLocalStorageKey(gameId)\n );\n if (!authenticatedUserStorageItem) {\n _checkedLocalStorage = true;\n return;\n }\n const authenticatedUser = JSON.parse(authenticatedUserStorageItem);\n\n _username = authenticatedUser.username;\n _userId = authenticatedUser.userId;\n _userToken = authenticatedUser.userToken;\n _checkedLocalStorage = true;\n } catch (err) {\n logger.warn(\n 'Unable to read authentication details from localStorage. Player authentication will not be available.',\n err\n );\n }\n };\n\n /**\n * Helper to be called on login or error.\n * Removes all the UI and timeouts.\n */\n const cleanUpAuthWindowAndTimeouts = (runtimeScene: RuntimeScene) => {\n removeAuthenticationContainer(runtimeScene);\n clearAuthenticationWindowTimeout();\n\n // If there is a websocket communication (electron, cordova), close it.\n if (_websocket) {\n logger.info('Closing authentication websocket connection.');\n _websocket.close();\n _websocket = null;\n }\n // If a new window was opened (web), close it.\n if (_authenticationWindow) {\n _authenticationWindow.close();\n _authenticationWindow = null;\n }\n\n // If cordova (native mobile app), hide external window.\n // TODO: calling hide does nothing on Android, the plugin should be updated to handle the action `hide`.\n if (typeof SafariViewController !== 'undefined') {\n try {\n SafariViewController.hide();\n } catch (error) {\n logger.info(\n 'Could not hide login window. Waiting for user to do it.'\n );\n }\n }\n };\n\n const saveAuthKeyToStorage = ({\n username,\n userId,\n userToken,\n }: {\n username: string | null;\n userId: string;\n userToken: string;\n }) => {\n if (!username) {\n logger.warn('The authenticated player does not have a username');\n }\n _username = username;\n _userId = userId;\n _userToken = userToken;\n _justLoggedIn = true;\n\n const gameId = gdjs.projectData.properties.projectUuid;\n if (!gameId) {\n logger.error('Missing game id in project properties.');\n return;\n }\n try {\n window.localStorage.setItem(\n getLocalStorageKey(gameId),\n JSON.stringify({\n username: _username,\n userId: _userId,\n userToken: _userToken,\n })\n );\n } catch (err) {\n logger.warn(\n 'Unable to save the authentication details to localStorage. Player authentication will not be available.',\n err\n );\n }\n };\n\n export const login = ({\n runtimeScene,\n userId,\n username,\n userToken,\n }: {\n runtimeScene: gdjs.RuntimeScene;\n userId: string;\n username: string | null;\n userToken: string;\n }) => {\n saveAuthKeyToStorage({ userId, username, userToken });\n cleanUpAuthWindowAndTimeouts(runtimeScene);\n removeAuthenticationBanner(runtimeScene);\n\n const domElementContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domElementContainer) {\n handleAuthenticationError(\n runtimeScene,\n \"The div element covering the game couldn't be found, the authentication banner cannot be displayed.\"\n );\n return;\n }\n authComponents.displayLoggedInNotification(\n domElementContainer,\n _username || 'Anonymous'\n );\n };\n\n /**\n * Reads the event sent by the authentication window and\n * display the appropriate banner.\n */\n const receiveAuthenticationMessage = function ({\n runtimeScene,\n event,\n checkOrigin,\n onDone,\n }: {\n runtimeScene: gdjs.RuntimeScene;\n event: MessageEvent;\n checkOrigin: boolean;\n onDone?: (status: 'logged' | 'errored' | 'dismissed') => void;\n }) {\n const allowedOrigins = ['https://liluo.io', 'https://gd.games'];\n // const allowedOrigins = ['localhost:4000'];\n\n // Check origin of message.\n if (checkOrigin && !allowedOrigins.includes(event.origin)) {\n // Automatic authentication message ignored: wrong origin. Return silently.\n return;\n }\n // Check that message is not malformed.\n if (!event.data.id) {\n throw new Error('Malformed message');\n }\n\n // Handle message.\n switch (event.data.id) {\n case 'authenticationResult': {\n if (!(event.data.body && event.data.body.token)) {\n throw new Error('Malformed message.');\n }\n\n login({\n runtimeScene,\n userId: event.data.body.userId,\n username: event.data.body.username,\n userToken: event.data.body.token,\n });\n focusOnGame(runtimeScene);\n if (onDone) onDone('logged');\n break;\n }\n case 'alreadyAuthenticated': {\n if (!(event.data.body && event.data.body.token)) {\n throw new Error('Malformed message.');\n }\n\n saveAuthKeyToStorage({\n userId: event.data.body.userId,\n username: event.data.body.username,\n userToken: event.data.body.token,\n });\n removeAutomaticGamesPlatformAuthenticationCallback();\n refreshAuthenticationBannerIfAny(runtimeScene);\n break;\n }\n }\n };\n\n /**\n * Handle any error that can occur as part of the authentication process.\n */\n const handleAuthenticationError = function (\n runtimeScene: gdjs.RuntimeScene,\n message: string\n ) {\n logger.error(message);\n cleanUpAuthWindowAndTimeouts(runtimeScene);\n\n const domElementContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domElementContainer) {\n handleAuthenticationError(\n runtimeScene,\n \"The div element covering the game couldn't be found, the authentication banner cannot be displayed.\"\n );\n return;\n }\n authComponents.displayErrorNotification(domElementContainer);\n focusOnGame(runtimeScene);\n };\n\n /**\n * If after 5min, no message has been received from the authentication window,\n * show a notification and remove the authentication container.\n */\n const startAuthenticationWindowTimeout = (\n runtimeScene: gdjs.RuntimeScene\n ) => {\n clearAuthenticationWindowTimeout();\n const time = 15 * 60 * 1000; // 15 minutes, in case the user needs time to authenticate.\n _authenticationTimeoutId = setTimeout(() => {\n logger.info(\n 'Authentication window did not send message in time. Closing it.'\n );\n cleanUpAuthWindowAndTimeouts(runtimeScene);\n focusOnGame(runtimeScene);\n }, time);\n };\n\n /**\n * Clear all existing authentication timeouts.\n * Useful when:\n * - a new authentication starts\n * - the authentication succeeded\n * - the authentication window is closed\n */\n const clearAuthenticationWindowTimeout = () => {\n if (_authenticationTimeoutId) clearTimeout(_authenticationTimeoutId);\n };\n\n /**\n * Helper to create the authentication banner based on the authentication status.\n */\n const createAuthenticationBanner = function (\n runtimeScene: gdjs.RuntimeScene\n ): HTMLDivElement {\n const onDismissBanner = () => {\n removeAuthenticationBanner(runtimeScene);\n };\n const onOpenAuthenticationWindow = () => {\n openAuthenticationWindow(runtimeScene);\n };\n\n return _userToken\n ? authComponents.computeAuthenticatedBanner(\n onOpenAuthenticationWindow,\n onDismissBanner,\n _username\n )\n : authComponents.computeNotAuthenticatedBanner(\n onOpenAuthenticationWindow,\n onDismissBanner\n );\n };\n\n /**\n * Action to display the banner to the user, depending on their authentication status.\n */\n export const displayAuthenticationBanner = function (\n runtimeScene: gdjs.RuntimeScene\n ) {\n if (_authenticationBanner) {\n // Banner already displayed, ensure it's visible.\n _authenticationBanner.style.opacity = '1';\n return;\n }\n if (!_checkedLocalStorage) {\n readAuthenticatedUserFromLocalStorage();\n }\n\n const domElementContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domElementContainer) {\n handleAuthenticationError(\n runtimeScene,\n \"The div element covering the game couldn't be found, the authentication banner cannot be displayed.\"\n );\n return;\n }\n\n _authenticationBanner = createAuthenticationBanner(runtimeScene);\n domElementContainer.appendChild(_authenticationBanner);\n };\n\n /**\n * Helper to recompute the authentication banner.\n * This is useful if the user is already logged on GDevelop games platform\n * and we want to display the banner with the username.\n */\n const refreshAuthenticationBannerIfAny = function (\n runtimeScene: gdjs.RuntimeScene\n ) {\n if (!_authenticationBanner) return;\n const domElementContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domElementContainer) {\n handleAuthenticationError(\n runtimeScene,\n \"The div element covering the game couldn't be found, the authentication banner cannot be displayed.\"\n );\n return;\n }\n const oldAuthenticationBanner = _authenticationBanner;\n _authenticationBanner = createAuthenticationBanner(runtimeScene);\n domElementContainer.replaceChild(\n _authenticationBanner,\n oldAuthenticationBanner\n );\n };\n\n const setupWebsocketForAuthenticationWindow = (\n runtimeScene: gdjs.RuntimeScene,\n gameId: string,\n onOpenAuthenticationWindow: (options: {\n connectionId: string;\n resolve: (AuthenticationWindowStatus) => void;\n }) => void\n ) =>\n new Promise<AuthenticationWindowStatus>((resolve) => {\n let hasFinishedAlready = false;\n const wsPlayApi = runtimeScene\n .getGame()\n .isUsingGDevelopDevelopmentEnvironment()\n ? `wss://api-ws-dev.gdevelop.io/play?gameId=${gameId}&connectionType=login`\n : `wss://api-ws.gdevelop.io/play?gameId=${gameId}&connectionType=login`;\n _websocket = new WebSocket(wsPlayApi);\n _websocket.onopen = () => {\n logger.info('Opened authentication websocket connection.');\n // When socket is open, ask for the connectionId, so that we can open the authentication window.\n if (_websocket) {\n _websocket.send(JSON.stringify({ action: 'getConnectionId' }));\n }\n };\n _websocket.onerror = () => {\n logger.info('Error in authentication websocket connection.');\n if (!hasFinishedAlready) {\n hasFinishedAlready = true;\n resolve('errored');\n }\n handleAuthenticationError(\n runtimeScene,\n 'Error while connecting to the authentication server.'\n );\n };\n _websocket.onclose = () => {\n logger.info('Closing authentication websocket connection.');\n if (!hasFinishedAlready) {\n hasFinishedAlready = true;\n resolve('dismissed');\n }\n };\n _websocket.onmessage = (event) => {\n if (event.data) {\n const messageContent = JSON.parse(event.data);\n switch (messageContent.type) {\n case 'authenticationResult': {\n const messageData = messageContent.data;\n\n login({\n runtimeScene,\n userId: messageData.userId,\n username: messageData.username,\n userToken: messageData.token,\n });\n focusOnGame(runtimeScene);\n\n hasFinishedAlready = true;\n resolve('logged');\n break;\n }\n case 'connectionId': {\n const messageData = messageContent.data;\n const connectionId = messageData.connectionId;\n if (!connectionId) {\n logger.error('No WebSocket connectionId received');\n hasFinishedAlready = true;\n resolve('errored');\n return;\n }\n\n logger.info('WebSocket connectionId received.');\n onOpenAuthenticationWindow({ connectionId, resolve });\n break;\n }\n }\n }\n };\n });\n\n /**\n * Helper to handle authentication window on Electron.\n * We open a new window, and create a websocket to know when the user is logged in.\n */\n const openAuthenticationWindowForElectron = (\n runtimeScene: gdjs.RuntimeScene,\n gameId: string,\n authWindowOptions: AuthenticationWindowOptions\n ) =>\n setupWebsocketForAuthenticationWindow(\n runtimeScene,\n gameId,\n ({ connectionId }) => {\n const targetUrl = getAuthWindowUrl({\n runtimeGame: runtimeScene.getGame(),\n gameId,\n connectionId,\n authWindowOptions,\n });\n\n const electron = runtimeScene.getGame().getRenderer().getElectron();\n const openWindow = () => electron.shell.openExternal(targetUrl);\n\n openWindow();\n\n // Add the link to the window in case a popup blocker is preventing the window from opening.\n if (_authenticationTextContainer) {\n authComponents.addAuthenticationUrlToTextsContainer(\n openWindow,\n _authenticationTextContainer\n );\n }\n }\n );\n\n /**\n * Helper to handle authentication window on Cordova on iOS and Android.\n * We open an external window, and listen to the websocket to know when the user is logged in.\n */\n const openAuthenticationWindowForCordovaWithWebSocket = (\n runtimeScene: gdjs.RuntimeScene,\n gameId: string,\n authWindowOptions: AuthenticationWindowOptions\n ) =>\n setupWebsocketForAuthenticationWindow(\n runtimeScene,\n gameId,\n ({ connectionId, resolve }) => {\n const targetUrl = getAuthWindowUrl({\n runtimeGame: runtimeScene.getGame(),\n gameId,\n connectionId,\n authWindowOptions,\n });\n\n SafariViewController.isAvailable(function (available: boolean) {\n if (available) {\n SafariViewController.show(\n {\n url: targetUrl,\n hidden: false,\n animated: true,\n transition: 'slide',\n enterReaderModeIfAvailable: false,\n barColor: '#000000',\n tintColor: '#ffffff',\n controlTintColor: '#ffffff',\n },\n function (result: any) {\n // Other events are `opened` and `loaded`.\n if (result.event === 'closed') {\n resolve('dismissed');\n }\n },\n function (error: any) {\n logger.log('Error opening webview: ' + JSON.stringify(error));\n resolve('errored');\n }\n );\n } else {\n logger.error('Plugin SafariViewController is not available');\n resolve('errored');\n }\n });\n }\n );\n\n /**\n * Helper to handle authentication window on web.\n * We open a new window, and listen to messages posted back to the game window.\n */\n const openAuthenticationWindowForWeb = (\n runtimeScene: gdjs.RuntimeScene,\n gameId: string,\n authWindowOptions: AuthenticationWindowOptions\n ) =>\n new Promise<AuthenticationWindowStatus>((resolve) => {\n // If we're on a browser, open a new window.\n const targetUrl = getAuthWindowUrl({\n runtimeGame: runtimeScene.getGame(),\n gameId,\n authWindowOptions,\n });\n\n // Listen to messages posted by the authentication window, so that we can\n // know when the user is authenticated.\n let isDoneAlready = false;\n _authenticationMessageCallback = (event: MessageEvent) => {\n receiveAuthenticationMessage({\n runtimeScene,\n event,\n checkOrigin: true,\n onDone: (status) => {\n if (isDoneAlready) return;\n isDoneAlready = true;\n resolve(status);\n },\n });\n };\n window.addEventListener(\n 'message',\n _authenticationMessageCallback,\n true\n );\n\n const left = screen.width / 2 - 500 / 2;\n const top = screen.height / 2 - 600 / 2;\n const windowFeatures = `left=${left},top=${top},width=500,height=600`;\n const openWindow = () => {\n _authenticationWindow = window.open(\n targetUrl,\n 'authentication',\n windowFeatures\n );\n };\n\n openWindow();\n\n // Add the link to the window in case a popup blocker is preventing the window from opening.\n if (_authenticationTextContainer) {\n authComponents.addAuthenticationUrlToTextsContainer(\n openWindow,\n _authenticationTextContainer\n );\n }\n });\n\n /**\n * Helper to handle authentication iframe on web.\n * We open an iframe, and listen to messages posted back to the game window.\n */\n const openAuthenticationIframeForWeb = (\n runtimeScene: gdjs.RuntimeScene,\n gameId: string,\n authWindowOptions: AuthenticationWindowOptions\n ) =>\n new Promise<AuthenticationWindowStatus>((resolve) => {\n if (\n !_authenticationIframeContainer ||\n !_authenticationLoaderContainer ||\n !_authenticationTextContainer\n ) {\n logger.error(\n \"Can't open an authentication iframe - no iframe container, loader container or text container was opened for it.\"\n );\n return;\n }\n\n const targetUrl = getAuthWindowUrl({\n runtimeGame: runtimeScene.getGame(),\n gameId,\n authWindowOptions,\n });\n\n // Listen to messages posted by the authentication window, so that we can\n // know when the user is authenticated.\n _authenticationMessageCallback = (event: MessageEvent) => {\n receiveAuthenticationMessage({\n runtimeScene,\n event,\n checkOrigin: true,\n onDone: resolve,\n });\n };\n window.addEventListener(\n 'message',\n _authenticationMessageCallback,\n true\n );\n\n authComponents.displayIframeInsideAuthenticationContainer(\n _authenticationIframeContainer,\n _authenticationLoaderContainer,\n _authenticationTextContainer,\n targetUrl\n );\n });\n\n /**\n * Action to display the authentication window to the user.\n */\n export const openAuthenticationWindow = (\n runtimeScene: gdjs.RuntimeScene,\n authWindowOptions: AuthenticationWindowOptions = {\n disableGuestLogin: false,\n }\n ): gdjs.PromiseTask<{ status: 'logged' | 'errored' | 'dismissed' }> =>\n new gdjs.PromiseTask(\n new Promise((resolve) => {\n // Create the authentication container for the player to wait.\n const domElementContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domElementContainer) {\n handleAuthenticationError(\n runtimeScene,\n \"The div element covering the game couldn't be found, the authentication window cannot be displayed.\"\n );\n resolve({ status: 'errored' });\n return;\n }\n\n const _gameId = gdjs.projectData.properties.projectUuid;\n if (!_gameId) {\n handleAuthenticationError(\n runtimeScene,\n 'The game ID is missing, the authentication window cannot be opened.'\n );\n resolve({ status: 'errored' });\n return;\n }\n\n let isDismissedAlready = false;\n const onAuthenticationContainerDismissed = () => {\n cleanUpAuthWindowAndTimeouts(runtimeScene);\n displayAuthenticationBanner(runtimeScene);\n\n isDismissedAlready = true;\n resolve({ status: 'dismissed' });\n };\n\n // If the banner is displayed, hide it, so that it can be shown again if the user closes the window.\n if (_authenticationBanner) _authenticationBanner.style.opacity = '0';\n\n const playerAuthPlatform = getPlayerAuthPlatform(runtimeScene);\n const {\n rootContainer,\n loaderContainer,\n iframeContainer,\n } = authComponents.computeAuthenticationContainer(\n onAuthenticationContainerDismissed\n );\n _authenticationRootContainer = rootContainer;\n _authenticationLoaderContainer = loaderContainer;\n _authenticationIframeContainer = iframeContainer;\n\n // Display the authentication window right away, to show a loader\n // while the call for game registration is happening.\n domElementContainer.appendChild(_authenticationRootContainer);\n\n // If the game is registered, open the authentication window.\n // Otherwise, open the window indicating that the game is not registered.\n (async () => {\n const isGameRegistered = await checkIfGameIsRegistered(\n runtimeScene.getGame(),\n _gameId\n );\n\n if (_authenticationLoaderContainer) {\n const electron = runtimeScene\n .getGame()\n .getRenderer()\n .getElectron();\n const wikiOpenAction = electron\n ? () =>\n electron.shell.openExternal(\n 'https://wiki.gdevelop.io/gdevelop5/publishing/web'\n )\n : null; // Only show a link if we're on electron.\n\n _authenticationTextContainer = authComponents.addAuthenticationTextsToLoadingContainer(\n _authenticationLoaderContainer,\n playerAuthPlatform,\n isGameRegistered,\n wikiOpenAction\n );\n }\n if (!isGameRegistered) return;\n\n startAuthenticationWindowTimeout(runtimeScene);\n\n // Based on which platform the game is running, we open the authentication window\n // with a different window, with or without a websocket.\n let status: AuthenticationWindowStatus;\n switch (playerAuthPlatform) {\n case 'electron':\n // This can be a:\n // - Preview in GDevelop desktop app.\n // - Desktop game running on Electron.\n status = await openAuthenticationWindowForElectron(\n runtimeScene,\n _gameId,\n authWindowOptions\n );\n break;\n case 'cordova-websocket':\n // The game is an iOS app.\n status = await openAuthenticationWindowForCordovaWithWebSocket(\n runtimeScene,\n _gameId,\n authWindowOptions\n );\n break;\n case 'web-iframe':\n // This can be a:\n // - Preview in GDevelop mobile app (iOS only)\n status = await openAuthenticationIframeForWeb(\n runtimeScene,\n _gameId,\n authWindowOptions\n );\n break;\n case 'web':\n default:\n // This can be a:\n // - Preview in GDevelop web-app\n // - Preview in Gdevelop mobile app (Android only)\n // - Web game (gd.games or any website/server) accessed via a desktop browser...\n // - Or a web game accessed via a mobile browser (Android/iOS).\n status = await openAuthenticationWindowForWeb(\n runtimeScene,\n _gameId,\n authWindowOptions\n );\n break;\n }\n\n if (isDismissedAlready) return;\n if (status === 'dismissed') {\n onAuthenticationContainerDismissed();\n }\n\n resolve({ status });\n })();\n })\n );\n\n /**\n * Condition to check if the window is open, so that the game can be paused in the background.\n */\n export const isAuthenticationWindowOpen = function (): boolean {\n return !!_authenticationRootContainer;\n };\n\n /**\n * Remove the container displaying the authentication window and the callback.\n */\n export const removeAuthenticationContainer = function (\n runtimeScene: gdjs.RuntimeScene\n ) {\n removeAuthenticationCallbacks();\n const domElementContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domElementContainer) {\n logger.info(\n \"The div element covering the game couldn't be found, the authentication must be already closed.\"\n );\n return;\n }\n\n // Remove the authentication root container.\n if (_authenticationRootContainer) {\n domElementContainer.removeChild(_authenticationRootContainer);\n }\n\n _authenticationRootContainer = null;\n _authenticationLoaderContainer = null;\n _authenticationIframeContainer = null;\n _authenticationTextContainer = null;\n };\n\n /*\n * Remove the authentication callbacks from web or cordova.\n */\n const removeAuthenticationCallbacks = function () {\n // Remove the authentication callbacks.\n if (_authenticationMessageCallback) {\n window.removeEventListener(\n 'message',\n _authenticationMessageCallback,\n true\n );\n _authenticationMessageCallback = null;\n }\n };\n\n /*\n * Remove the automatic authentication callback when running on web.\n */\n const removeAutomaticGamesPlatformAuthenticationCallback = function () {\n if (_automaticGamesPlatformAuthenticationCallback) {\n window.removeEventListener(\n 'message',\n _automaticGamesPlatformAuthenticationCallback,\n true\n );\n _automaticGamesPlatformAuthenticationCallback = null;\n }\n if (_automaticGamesPlatformAuthenticationTimeoutId) {\n clearTimeout(_automaticGamesPlatformAuthenticationTimeoutId);\n _automaticGamesPlatformAuthenticationTimeoutId = null;\n }\n };\n\n /**\n * Remove the banner displaying the authentication status.\n */\n export const removeAuthenticationBanner = function (\n runtimeScene: gdjs.RuntimeScene\n ) {\n if (!_authenticationBanner) {\n logger.info(\n \"The authentication banner couldn't be found, the authentication banner must be already closed.\"\n );\n return;\n }\n const domElementContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domElementContainer) {\n logger.info(\n \"The div element covering the game couldn't be found, the authentication must be already closed.\"\n );\n return;\n }\n\n domElementContainer.removeChild(_authenticationBanner);\n _authenticationBanner = null;\n };\n\n /**\n * Focus on game canvas to allow user to interact with it.\n */\n const focusOnGame = function (runtimeScene: gdjs.RuntimeScene) {\n const gameCanvas = runtimeScene.getGame().getRenderer().getCanvas();\n if (gameCanvas) gameCanvas.focus();\n };\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["namespace gdjs {\n declare var cordova: any;\n declare var SafariViewController: any;\n\n const logger = new gdjs.Logger('Player Authentication');\n const authComponents = gdjs.playerAuthenticationComponents;\n\n export type PlayerAuthenticationPlatform =\n | 'electron'\n | 'cordova-websocket'\n | 'web-iframe'\n | 'web'\n | 'games-platform';\n\n // TODO EBO Replace runtimeScene to instanceContainer.\n export namespace playerAuthentication {\n // Authentication information.\n let _username: string | null = null;\n let _userId: string | null = null;\n let _userToken: string | null = null;\n let _justLoggedIn = false;\n\n let _checkedLocalStorage: boolean = false;\n\n // Authentication display\n let _authenticationWindow: Window | null = null; // For Web.\n let _authenticationRootContainer: HTMLDivElement | null = null;\n let _authenticationLoaderContainer: HTMLDivElement | null = null;\n let _authenticationIframeContainer: HTMLDivElement | null = null;\n let _authenticationTextContainer: HTMLDivElement | null = null;\n let _authenticationBanner: HTMLDivElement | null = null;\n let _automaticGamesPlatformAuthenticationTimeoutId: NodeJS.Timeout | null =\n null;\n let _authenticationTimeoutId: NodeJS.Timeout | null = null;\n\n // Communication methods.\n let _automaticGamesPlatformAuthenticationCallback:\n | ((event: MessageEvent) => void)\n | null = null;\n let _authenticationMessageCallback: ((event: MessageEvent) => void) | null =\n null;\n let _websocket: WebSocket | null = null;\n\n type AuthenticationWindowStatus = 'logged' | 'errored' | 'dismissed';\n type AuthenticationWindowOptions = { disableGuestLogin: boolean };\n\n const notifyParentWindowThatPlayerAuthIsReady = (\n runtimeScene: gdjs.RuntimeScene\n ) => {\n if (getPlayerAuthPlatform(runtimeScene) !== 'games-platform') {\n // Automatic authentication is only valid when the game is hosted on GDevelop games platform.\n return;\n }\n\n logger.info(\n 'Notifying parent window that player authentication is ready.'\n );\n window.parent.postMessage(\n {\n id: 'playerAuthReady',\n },\n '*' // We could restrict to GDevelop games platform but it's not necessary as the message is not sensitive, and it allows easy debugging.\n );\n\n // If no answer after 3 seconds, assume that the game is not embedded in GDevelop games platform, and remove the listener.\n _automaticGamesPlatformAuthenticationTimeoutId = setTimeout(() => {\n logger.info(\n 'Removing automatic games platform authentication listener.'\n );\n removeAutomaticGamesPlatformAuthenticationCallback();\n }, 3000);\n };\n\n const handleAutomaticGamesPlatformAuthentication = (\n runtimeScene: gdjs.RuntimeScene\n ) => {\n if (getPlayerAuthPlatform(runtimeScene) !== 'games-platform') {\n // Automatic authentication is only valid when the game is hosted on GDevelop games platform.\n return;\n }\n\n removeAutomaticGamesPlatformAuthenticationCallback(); // Remove any callback that could have been registered before.\n _automaticGamesPlatformAuthenticationCallback = (event: MessageEvent) => {\n receiveAuthenticationMessage({\n runtimeScene,\n event,\n checkOrigin: true,\n });\n };\n window.addEventListener(\n 'message',\n _automaticGamesPlatformAuthenticationCallback,\n true\n );\n };\n\n const handleAutomaticPreviewAuthentication = (\n runtimeScene: gdjs.RuntimeScene\n ) => {\n const runtimeGameOptions = runtimeScene.getGame().getAdditionalOptions();\n if (runtimeGameOptions && runtimeGameOptions.isPreview) {\n // If the game is a preview, and the user is already authenticated, we can log them in automatically.\n const playerId = runtimeGameOptions.playerId;\n const playerToken = runtimeGameOptions.playerToken;\n const playerUsername = runtimeGameOptions.playerUsername;\n if (playerId && playerToken) {\n logger.info(\n `Automatically logging in the player with ID ${playerId} as it's a preview.`\n );\n saveAuthKeyToStorage({\n userId: playerId,\n username: playerUsername || null,\n userToken: playerToken,\n });\n refreshAuthenticationBannerIfAny(runtimeScene);\n }\n }\n };\n\n // Ensure that the condition \"just logged in\" is valid only for one frame.\n gdjs.registerRuntimeScenePostEventsCallback(() => {\n _justLoggedIn = false;\n });\n\n // If the extension is used, register an eventlistener to know if the user is\n // logged in while playing the game on GDevelop games platform.\n // Then send a message to the parent iframe to say that the player auth is ready.\n gdjs.registerFirstRuntimeSceneLoadedCallback(\n (runtimeScene: RuntimeScene) => {\n handleAutomaticPreviewAuthentication(runtimeScene);\n handleAutomaticGamesPlatformAuthentication(runtimeScene);\n notifyParentWindowThatPlayerAuthIsReady(runtimeScene);\n }\n );\n\n const getLocalStorageKey = (gameId: string) =>\n `${gameId}_authenticatedUser`;\n\n const getAuthWindowUrl = ({\n runtimeGame,\n gameId,\n connectionId,\n authWindowOptions,\n }: {\n runtimeGame: gdjs.RuntimeGame;\n gameId: string;\n connectionId?: string;\n authWindowOptions?: AuthenticationWindowOptions;\n }) => {\n // Uncomment to test the case of a failing loading:\n // return 'https://gd.games.wronglink';\n\n const baseUrl = 'https://gd.games';\n // const baseUrl = 'http://localhost:4000';\n\n const searchParams = new URLSearchParams();\n searchParams.set('gameId', gameId);\n if (connectionId) searchParams.set('connectionId', connectionId);\n if (runtimeGame.isUsingGDevelopDevelopmentEnvironment()) {\n searchParams.set('dev', 'true');\n }\n searchParams.set('allowLoginProviders', 'true');\n if (authWindowOptions) {\n for (const [key, value] of Object.entries(authWindowOptions)) {\n searchParams.set(key, value.toString());\n }\n }\n\n return `${baseUrl}/auth?${searchParams.toString()}`;\n };\n\n /**\n * Get the platform running the game, which changes how the authentication\n * window is opened.\n */\n const getPlayerAuthPlatform = (\n runtimeScene: RuntimeScene\n ): PlayerAuthenticationPlatform => {\n const runtimeGame = runtimeScene.getGame();\n const electron = runtimeGame.getRenderer().getElectron();\n if (electron) {\n // This can be a:\n // - Preview in GDevelop desktop app.\n // - Desktop game running on Electron.\n return 'electron';\n }\n\n // This can be a:\n // - Preview in GDevelop mobile app (iOS only)\n if (shouldAuthenticationUseIframe(runtimeScene)) return 'web-iframe';\n\n if (typeof cordova !== 'undefined') {\n // The game is an Android or an iOS app.\n return 'cordova-websocket';\n }\n\n // This can be a:\n // - Game hosted on GDevelop games platform (gd.games)\n if (\n window.parent !== window &&\n (document.referrer.indexOf('https://gd.games') === 0 ||\n document.referrer.indexOf('http://localhost:4000') === 0)\n ) {\n return 'games-platform';\n }\n\n // This can be a:\n // - Preview in GDevelop web-app\n // - Preview in Gdevelop mobile app (Android only)\n // - Web game (any website/server other than GDevelop's game-platform) accessed via a desktop browser...\n // - Or a web game accessed via a mobile browser (Android/iOS).\n return 'web';\n };\n\n /**\n * Check if, in some exceptional cases, we allow authentication\n * to be done through a iframe.\n * This is usually discouraged as the user can't verify that the authentication\n * window is a genuine one. It's only to be used in trusted contexts (e.g:\n * preview in the GDevelop mobile app).\n */\n const shouldAuthenticationUseIframe = (runtimeScene: RuntimeScene) => {\n const runtimeGameOptions = runtimeScene.getGame().getAdditionalOptions();\n return (\n runtimeGameOptions &&\n runtimeGameOptions.isPreview &&\n runtimeGameOptions.allowAuthenticationUsingIframeForPreview\n );\n };\n\n /**\n * Returns true if a user token is present in the local storage.\n */\n export const isAuthenticated = () => {\n if (!_checkedLocalStorage) {\n readAuthenticatedUserFromLocalStorage();\n }\n return _userToken !== null;\n };\n\n /**\n * Returns true if the user just logged in.\n * Useful to update username or trigger messages in the game.\n */\n export const hasLoggedIn = () => _justLoggedIn;\n\n /**\n * Returns the username from the local storage.\n */\n export const getUsername = () => {\n if (!_checkedLocalStorage) {\n readAuthenticatedUserFromLocalStorage();\n }\n return _username || '';\n };\n\n /**\n * Returns the user token from the local storage.\n */\n export const getUserToken = () => {\n if (!_checkedLocalStorage) {\n readAuthenticatedUserFromLocalStorage();\n }\n return _userToken || null;\n };\n\n /**\n * Returns the username from the local storage.\n */\n export const getUserId = () => {\n if (!_checkedLocalStorage) {\n readAuthenticatedUserFromLocalStorage();\n }\n return _userId || '';\n };\n\n /**\n * Returns true if the game is registered, false otherwise.\n * Useful to display a message to the user to register the game before logging in.\n */\n const checkIfGameIsRegistered = (\n runtimeGame: gdjs.RuntimeGame,\n gameId: string,\n tries: number = 0\n ): Promise<boolean> => {\n const rootApi = runtimeGame.isUsingGDevelopDevelopmentEnvironment()\n ? 'https://api-dev.gdevelop.io'\n : 'https://api.gdevelop.io';\n const url = `${rootApi}/game/public-game/${gameId}`;\n return fetch(url, { method: 'HEAD' }).then(\n (response) => {\n if (response.status !== 200) {\n logger.warn(\n `Error while fetching the game: ${response.status} ${response.statusText}`\n );\n\n // If the response is not 404, it may be a timeout, so retry a few times.\n if (response.status === 404 || tries > 2) {\n return false;\n }\n\n return checkIfGameIsRegistered(runtimeGame, gameId, tries + 1);\n }\n return true;\n },\n (err) => {\n logger.error('Error while fetching game:', err);\n return false;\n }\n );\n };\n\n /**\n * Remove the user information from the local storage.\n */\n export const logout = (runtimeScene: RuntimeScene) => {\n _username = null;\n _userToken = null;\n _userId = null;\n\n const gameId = gdjs.projectData.properties.projectUuid;\n if (!gameId) {\n logger.error('Missing game id in project properties.');\n return;\n }\n window.localStorage.removeItem(getLocalStorageKey(gameId));\n cleanUpAuthWindowAndTimeouts(runtimeScene);\n removeAuthenticationBanner(runtimeScene);\n const domElementContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domElementContainer) {\n handleAuthenticationError(\n runtimeScene,\n \"The div element covering the game couldn't be found, the authentication banner cannot be displayed.\"\n );\n return;\n }\n authComponents.displayLoggedOutNotification(domElementContainer);\n };\n\n /**\n * Retrieves the user information from the local storage, and store\n * them in the extension variables.\n */\n const readAuthenticatedUserFromLocalStorage = () => {\n const gameId = gdjs.projectData.properties.projectUuid;\n if (!gameId) {\n logger.error('Missing game id in project properties.');\n return;\n }\n try {\n const authenticatedUserStorageItem = window.localStorage.getItem(\n getLocalStorageKey(gameId)\n );\n if (!authenticatedUserStorageItem) {\n _checkedLocalStorage = true;\n return;\n }\n const authenticatedUser = JSON.parse(authenticatedUserStorageItem);\n\n _username = authenticatedUser.username;\n _userId = authenticatedUser.userId;\n _userToken = authenticatedUser.userToken;\n _checkedLocalStorage = true;\n } catch (err) {\n logger.warn(\n 'Unable to read authentication details from localStorage. Player authentication will not be available.',\n err\n );\n }\n };\n\n /**\n * Helper to be called on login or error.\n * Removes all the UI and timeouts.\n */\n const cleanUpAuthWindowAndTimeouts = (runtimeScene: RuntimeScene) => {\n removeAuthenticationContainer(runtimeScene);\n clearAuthenticationWindowTimeout();\n\n // If there is a websocket communication (electron, cordova), close it.\n if (_websocket) {\n logger.info('Closing authentication websocket connection.');\n _websocket.close();\n _websocket = null;\n }\n // If a new window was opened (web), close it.\n if (_authenticationWindow) {\n _authenticationWindow.close();\n _authenticationWindow = null;\n }\n\n // If cordova (native mobile app), hide external window.\n // TODO: calling hide does nothing on Android, the plugin should be updated to handle the action `hide`.\n if (typeof SafariViewController !== 'undefined') {\n try {\n SafariViewController.hide();\n } catch (error) {\n logger.info(\n 'Could not hide login window. Waiting for user to do it.'\n );\n }\n }\n };\n\n const saveAuthKeyToStorage = ({\n username,\n userId,\n userToken,\n }: {\n username: string | null;\n userId: string;\n userToken: string;\n }) => {\n if (!username) {\n logger.warn('The authenticated player does not have a username');\n }\n _username = username;\n _userId = userId;\n _userToken = userToken;\n _justLoggedIn = true;\n\n const gameId = gdjs.projectData.properties.projectUuid;\n if (!gameId) {\n logger.error('Missing game id in project properties.');\n return;\n }\n try {\n window.localStorage.setItem(\n getLocalStorageKey(gameId),\n JSON.stringify({\n username: _username,\n userId: _userId,\n userToken: _userToken,\n })\n );\n } catch (err) {\n logger.warn(\n 'Unable to save the authentication details to localStorage. Player authentication will not be available.',\n err\n );\n }\n };\n\n export const login = ({\n runtimeScene,\n userId,\n username,\n userToken,\n }: {\n runtimeScene: gdjs.RuntimeScene;\n userId: string;\n username: string | null;\n userToken: string;\n }) => {\n saveAuthKeyToStorage({ userId, username, userToken });\n cleanUpAuthWindowAndTimeouts(runtimeScene);\n removeAuthenticationBanner(runtimeScene);\n\n const domElementContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domElementContainer) {\n handleAuthenticationError(\n runtimeScene,\n \"The div element covering the game couldn't be found, the authentication banner cannot be displayed.\"\n );\n return;\n }\n authComponents.displayLoggedInNotification(\n domElementContainer,\n _username || 'Anonymous'\n );\n };\n\n /**\n * Reads the event sent by the authentication window and\n * display the appropriate banner.\n */\n const receiveAuthenticationMessage = function ({\n runtimeScene,\n event,\n checkOrigin,\n onDone,\n }: {\n runtimeScene: gdjs.RuntimeScene;\n event: MessageEvent;\n checkOrigin: boolean;\n onDone?: (status: 'logged' | 'errored' | 'dismissed') => void;\n }) {\n // This happens when the user triggers the login from the game.\n // -> This will close the authentication window and log the user in.\n const onManualLoginSuccessFul = (event: MessageEvent) => {\n login({\n runtimeScene,\n userId: event.data.body.userId,\n username: event.data.body.username,\n userToken: event.data.body.token,\n });\n focusOnGame(runtimeScene);\n if (onDone) onDone('logged');\n };\n\n // This happens when the login happens automatically, because the parent window\n // (e.g: GDevelop games platform) is already authenticated.\n // -> This logs the user in, without closing the banner or showing the login notification.\n const onAutomaticLoginSuccessful = (event: MessageEvent) => {\n saveAuthKeyToStorage({\n userId: event.data.body.userId,\n username: event.data.body.username,\n userToken: event.data.body.token,\n });\n removeAutomaticGamesPlatformAuthenticationCallback();\n refreshAuthenticationBannerIfAny(runtimeScene);\n if (onDone) onDone('logged');\n };\n\n const allowedOrigins = ['https://gd.games', 'http://localhost:4000'];\n // Check origin of message.\n if (checkOrigin && !allowedOrigins.includes(event.origin)) {\n // Automatic authentication message ignored: wrong origin. Return silently.\n return;\n }\n // Check that message is not malformed.\n if (!event.data.id) {\n throw new Error('Malformed message');\n }\n\n // Handle message.\n switch (event.data.id) {\n case 'authenticationResult': {\n if (!(event.data.body && event.data.body.token)) {\n throw new Error('Malformed message.');\n }\n\n logger.info('Received authentication result, logging in player.');\n onManualLoginSuccessFul(event);\n break;\n }\n case 'alreadyAuthenticated': {\n if (!(event.data.body && event.data.body.token)) {\n throw new Error('Malformed message.');\n }\n\n logger.info('Player is already authenticated, logging in player.');\n // If we receive this message while the authentication dialog is open,\n // it can come from the parent window (e.g: GDevelop games platform) which is handling the authentication for the game.\n // In this case, we assume the log-in was successful and initiated by the player.\n if (_authenticationRootContainer) {\n onManualLoginSuccessFul(event);\n break;\n }\n\n // If the authentication dialog is not open, it means that the parent window (e.g: GDevelop games platform) has informed the game\n // that the player is already authenticated. We can log the player in automatically.\n onAutomaticLoginSuccessful(event);\n break;\n }\n }\n };\n\n /**\n * Handle any error that can occur as part of the authentication process.\n */\n const handleAuthenticationError = function (\n runtimeScene: gdjs.RuntimeScene,\n message: string\n ) {\n logger.error(message);\n cleanUpAuthWindowAndTimeouts(runtimeScene);\n\n const domElementContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domElementContainer) {\n handleAuthenticationError(\n runtimeScene,\n \"The div element covering the game couldn't be found, the authentication banner cannot be displayed.\"\n );\n return;\n }\n authComponents.displayErrorNotification(domElementContainer);\n focusOnGame(runtimeScene);\n };\n\n /**\n * If after 5min, no message has been received from the authentication window,\n * show a notification and remove the authentication container.\n */\n const startAuthenticationWindowTimeout = (\n runtimeScene: gdjs.RuntimeScene\n ) => {\n clearAuthenticationWindowTimeout();\n const time = 15 * 60 * 1000; // 15 minutes, in case the user needs time to authenticate.\n _authenticationTimeoutId = setTimeout(() => {\n logger.info(\n 'Authentication window did not send message in time. Closing it.'\n );\n cleanUpAuthWindowAndTimeouts(runtimeScene);\n focusOnGame(runtimeScene);\n }, time);\n };\n\n /**\n * Clear all existing authentication timeouts.\n * Useful when:\n * - a new authentication starts\n * - the authentication succeeded\n * - the authentication window is closed\n */\n const clearAuthenticationWindowTimeout = () => {\n if (_authenticationTimeoutId) clearTimeout(_authenticationTimeoutId);\n };\n\n /**\n * Helper to create the authentication banner based on the authentication status.\n */\n const createAuthenticationBanner = function (\n runtimeScene: gdjs.RuntimeScene\n ): HTMLDivElement {\n const onDismissBanner = () => {\n removeAuthenticationBanner(runtimeScene);\n };\n const onOpenAuthenticationWindow = () => {\n openAuthenticationWindow(runtimeScene);\n };\n\n return _userToken\n ? authComponents.computeAuthenticatedBanner(\n onOpenAuthenticationWindow,\n onDismissBanner,\n _username\n )\n : authComponents.computeNotAuthenticatedBanner(\n onOpenAuthenticationWindow,\n onDismissBanner\n );\n };\n\n /**\n * Action to display the banner to the user, depending on their authentication status.\n */\n export const displayAuthenticationBanner = function (\n runtimeScene: gdjs.RuntimeScene\n ) {\n if (_authenticationBanner) {\n // Banner already displayed, ensure it's visible.\n _authenticationBanner.style.opacity = '1';\n return;\n }\n if (!_checkedLocalStorage) {\n readAuthenticatedUserFromLocalStorage();\n }\n\n const domElementContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domElementContainer) {\n handleAuthenticationError(\n runtimeScene,\n \"The div element covering the game couldn't be found, the authentication banner cannot be displayed.\"\n );\n return;\n }\n\n _authenticationBanner = createAuthenticationBanner(runtimeScene);\n domElementContainer.appendChild(_authenticationBanner);\n };\n\n /**\n * Helper to recompute the authentication banner.\n * This is useful if the user is already logged on GDevelop games platform\n * and we want to display the banner with the username.\n */\n const refreshAuthenticationBannerIfAny = function (\n runtimeScene: gdjs.RuntimeScene\n ) {\n if (!_authenticationBanner) return;\n const domElementContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domElementContainer) {\n handleAuthenticationError(\n runtimeScene,\n \"The div element covering the game couldn't be found, the authentication banner cannot be displayed.\"\n );\n return;\n }\n const oldAuthenticationBanner = _authenticationBanner;\n _authenticationBanner = createAuthenticationBanner(runtimeScene);\n domElementContainer.replaceChild(\n _authenticationBanner,\n oldAuthenticationBanner\n );\n };\n\n const setupWebsocketForAuthenticationWindow = (\n runtimeScene: gdjs.RuntimeScene,\n gameId: string,\n onOpenAuthenticationWindow: (options: {\n connectionId: string;\n resolve: (AuthenticationWindowStatus) => void;\n }) => void\n ) =>\n new Promise<AuthenticationWindowStatus>((resolve) => {\n let hasFinishedAlready = false;\n const wsPlayApi = runtimeScene\n .getGame()\n .isUsingGDevelopDevelopmentEnvironment()\n ? `wss://api-ws-dev.gdevelop.io/play?gameId=${gameId}&connectionType=login`\n : `wss://api-ws.gdevelop.io/play?gameId=${gameId}&connectionType=login`;\n _websocket = new WebSocket(wsPlayApi);\n _websocket.onopen = () => {\n logger.info('Opened authentication websocket connection.');\n // When socket is open, ask for the connectionId, so that we can open the authentication window.\n if (_websocket) {\n _websocket.send(JSON.stringify({ action: 'getConnectionId' }));\n }\n };\n _websocket.onerror = () => {\n logger.info('Error in authentication websocket connection.');\n if (!hasFinishedAlready) {\n hasFinishedAlready = true;\n resolve('errored');\n }\n handleAuthenticationError(\n runtimeScene,\n 'Error while connecting to the authentication server.'\n );\n };\n _websocket.onclose = () => {\n logger.info('Closing authentication websocket connection.');\n if (!hasFinishedAlready) {\n hasFinishedAlready = true;\n resolve('dismissed');\n }\n };\n _websocket.onmessage = (event) => {\n if (event.data) {\n const messageContent = JSON.parse(event.data);\n switch (messageContent.type) {\n case 'authenticationResult': {\n const messageData = messageContent.data;\n\n login({\n runtimeScene,\n userId: messageData.userId,\n username: messageData.username,\n userToken: messageData.token,\n });\n focusOnGame(runtimeScene);\n\n hasFinishedAlready = true;\n resolve('logged');\n break;\n }\n case 'connectionId': {\n const messageData = messageContent.data;\n const connectionId = messageData.connectionId;\n if (!connectionId) {\n logger.error('No WebSocket connectionId received');\n hasFinishedAlready = true;\n resolve('errored');\n return;\n }\n\n logger.info('WebSocket connectionId received.');\n onOpenAuthenticationWindow({ connectionId, resolve });\n break;\n }\n }\n }\n };\n });\n\n /**\n * Helper to handle authentication window on Electron.\n * We open a new window, and create a websocket to know when the user is logged in.\n */\n const openAuthenticationWindowForElectron = (\n runtimeScene: gdjs.RuntimeScene,\n gameId: string,\n authWindowOptions: AuthenticationWindowOptions\n ) =>\n setupWebsocketForAuthenticationWindow(\n runtimeScene,\n gameId,\n ({ connectionId }) => {\n const targetUrl = getAuthWindowUrl({\n runtimeGame: runtimeScene.getGame(),\n gameId,\n connectionId,\n authWindowOptions,\n });\n\n const electron = runtimeScene.getGame().getRenderer().getElectron();\n const openWindow = () => electron.shell.openExternal(targetUrl);\n\n openWindow();\n\n // Add the link to the window in case a popup blocker is preventing the window from opening.\n if (_authenticationTextContainer) {\n authComponents.addAuthenticationUrlToTextsContainer(\n openWindow,\n _authenticationTextContainer\n );\n }\n }\n );\n\n /**\n * Helper to handle authentication window on Cordova on iOS and Android.\n * We open an external window, and listen to the websocket to know when the user is logged in.\n */\n const openAuthenticationWindowForCordovaWithWebSocket = (\n runtimeScene: gdjs.RuntimeScene,\n gameId: string,\n authWindowOptions: AuthenticationWindowOptions\n ) =>\n setupWebsocketForAuthenticationWindow(\n runtimeScene,\n gameId,\n ({ connectionId, resolve }) => {\n const targetUrl = getAuthWindowUrl({\n runtimeGame: runtimeScene.getGame(),\n gameId,\n connectionId,\n authWindowOptions,\n });\n\n SafariViewController.isAvailable(function (available: boolean) {\n if (available) {\n SafariViewController.show(\n {\n url: targetUrl,\n hidden: false,\n animated: true,\n transition: 'slide',\n enterReaderModeIfAvailable: false,\n barColor: '#000000',\n tintColor: '#ffffff',\n controlTintColor: '#ffffff',\n },\n function (result: any) {\n // Other events are `opened` and `loaded`.\n if (result.event === 'closed') {\n resolve('dismissed');\n }\n },\n function (error: any) {\n logger.log('Error opening webview: ' + JSON.stringify(error));\n resolve('errored');\n }\n );\n } else {\n logger.error('Plugin SafariViewController is not available');\n resolve('errored');\n }\n });\n }\n );\n\n /**\n * Helper to handle authentication window on web.\n * We open a new window, and listen to messages posted back to the game window.\n */\n const openAuthenticationWindowForWeb = (\n runtimeScene: gdjs.RuntimeScene,\n gameId: string,\n authWindowOptions: AuthenticationWindowOptions\n ) =>\n new Promise<AuthenticationWindowStatus>((resolve) => {\n // If we're on a browser, open a new window.\n const targetUrl = getAuthWindowUrl({\n runtimeGame: runtimeScene.getGame(),\n gameId,\n authWindowOptions,\n });\n\n // Listen to messages posted by the authentication window, so that we can\n // know when the user is authenticated.\n let isDoneAlready = false;\n _authenticationMessageCallback = (event: MessageEvent) => {\n receiveAuthenticationMessage({\n runtimeScene,\n event,\n checkOrigin: true,\n onDone: (status) => {\n if (isDoneAlready) return;\n isDoneAlready = true;\n resolve(status);\n },\n });\n };\n window.addEventListener(\n 'message',\n _authenticationMessageCallback,\n true\n );\n\n const left = screen.width / 2 - 500 / 2;\n const top = screen.height / 2 - 600 / 2;\n const windowFeatures = `left=${left},top=${top},width=500,height=600`;\n const openWindow = () => {\n _authenticationWindow = window.open(\n targetUrl,\n 'authentication',\n windowFeatures\n );\n };\n\n openWindow();\n\n // Add the link to the window in case a popup blocker is preventing the window from opening.\n if (_authenticationTextContainer) {\n authComponents.addAuthenticationUrlToTextsContainer(\n openWindow,\n _authenticationTextContainer\n );\n }\n });\n\n /**\n * Helper to handle authentication iframe on web.\n * We open an iframe, and listen to messages posted back to the game window.\n */\n const openAuthenticationIframeForWeb = (\n runtimeScene: gdjs.RuntimeScene,\n gameId: string,\n authWindowOptions: AuthenticationWindowOptions\n ) =>\n new Promise<AuthenticationWindowStatus>((resolve) => {\n if (\n !_authenticationIframeContainer ||\n !_authenticationLoaderContainer ||\n !_authenticationTextContainer\n ) {\n logger.error(\n \"Can't open an authentication iframe - no iframe container, loader container or text container was opened for it.\"\n );\n return;\n }\n\n const targetUrl = getAuthWindowUrl({\n runtimeGame: runtimeScene.getGame(),\n gameId,\n authWindowOptions,\n });\n\n // Listen to messages posted by the authentication window, so that we can\n // know when the user is authenticated.\n _authenticationMessageCallback = (event: MessageEvent) => {\n receiveAuthenticationMessage({\n runtimeScene,\n event,\n checkOrigin: true,\n onDone: resolve,\n });\n };\n window.addEventListener(\n 'message',\n _authenticationMessageCallback,\n true\n );\n\n authComponents.displayIframeInsideAuthenticationContainer(\n _authenticationIframeContainer,\n _authenticationLoaderContainer,\n _authenticationTextContainer,\n targetUrl\n );\n });\n\n export const openAuthenticationWindowForGamesPlatform = (\n runtimeScene: gdjs.RuntimeScene,\n gameId: string,\n authWindowOptions: AuthenticationWindowOptions\n ) =>\n new Promise<AuthenticationWindowStatus>((resolve) => {\n // First, clear the automatic authentication timeout.\n // It can still exist if the user triggers a log-in manually, while the automatic authentication is still waiting.\n removeAutomaticGamesPlatformAuthenticationCallback();\n\n // Listen to messages posted by the authentication window, so that we can\n // know when the user is authenticated.\n _authenticationMessageCallback = (event: MessageEvent) => {\n receiveAuthenticationMessage({\n runtimeScene,\n event,\n checkOrigin: true,\n onDone: resolve,\n });\n };\n window.addEventListener(\n 'message',\n _authenticationMessageCallback,\n true\n );\n\n // Login dialog will be handled by the platform.\n window.parent.postMessage(\n {\n id: 'openGameAuthenticationDialog',\n gameId,\n disableGuestLogin: authWindowOptions.disableGuestLogin,\n },\n '*' // We could restrict to GDevelop games platform but it's not necessary as the message is not sensitive, and it allows easy debugging.\n );\n });\n\n /**\n * Action to display the authentication window to the user.\n */\n export const openAuthenticationWindow = (\n runtimeScene: gdjs.RuntimeScene,\n authWindowOptions: AuthenticationWindowOptions = {\n disableGuestLogin: false,\n }\n ): gdjs.PromiseTask<{ status: 'logged' | 'errored' | 'dismissed' }> =>\n new gdjs.PromiseTask(\n new Promise((resolve) => {\n // Create the authentication container for the player to wait.\n const domElementContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domElementContainer) {\n handleAuthenticationError(\n runtimeScene,\n \"The div element covering the game couldn't be found, the authentication window cannot be displayed.\"\n );\n resolve({ status: 'errored' });\n return;\n }\n\n const _gameId = gdjs.projectData.properties.projectUuid;\n if (!_gameId) {\n handleAuthenticationError(\n runtimeScene,\n 'The game ID is missing, the authentication window cannot be opened.'\n );\n resolve({ status: 'errored' });\n return;\n }\n\n let isDismissedAlready = false;\n const onAuthenticationContainerDismissed = () => {\n cleanUpAuthWindowAndTimeouts(runtimeScene);\n displayAuthenticationBanner(runtimeScene);\n\n isDismissedAlready = true;\n resolve({ status: 'dismissed' });\n };\n\n // If the banner is displayed, hide it, so that it can be shown again if the user closes the window.\n if (_authenticationBanner) _authenticationBanner.style.opacity = '0';\n\n const playerAuthPlatform = getPlayerAuthPlatform(runtimeScene);\n const { rootContainer, loaderContainer, iframeContainer } =\n authComponents.computeAuthenticationContainer(\n onAuthenticationContainerDismissed\n );\n _authenticationRootContainer = rootContainer;\n _authenticationLoaderContainer = loaderContainer;\n _authenticationIframeContainer = iframeContainer;\n\n // Display the authentication window right away, to show a loader\n // while the call for game registration is happening.\n domElementContainer.appendChild(_authenticationRootContainer);\n\n // If the game is registered, open the authentication window.\n // Otherwise, open the window indicating that the game is not registered.\n (async () => {\n const isGameRegistered = await checkIfGameIsRegistered(\n runtimeScene.getGame(),\n _gameId\n );\n\n if (_authenticationLoaderContainer) {\n const electron = runtimeScene\n .getGame()\n .getRenderer()\n .getElectron();\n const wikiOpenAction = electron\n ? () =>\n electron.shell.openExternal(\n 'https://wiki.gdevelop.io/gdevelop5/publishing/web'\n )\n : null; // Only show a link if we're on electron.\n\n _authenticationTextContainer =\n authComponents.addAuthenticationTextsToLoadingContainer(\n _authenticationLoaderContainer,\n playerAuthPlatform,\n isGameRegistered,\n wikiOpenAction\n );\n }\n if (!isGameRegistered) return;\n\n startAuthenticationWindowTimeout(runtimeScene);\n\n // Based on which platform the game is running, we open the authentication window\n // with a different window, with or without a websocket.\n let status: AuthenticationWindowStatus;\n switch (playerAuthPlatform) {\n case 'electron':\n // This can be a:\n // - Preview in GDevelop desktop app.\n // - Desktop game running on Electron.\n status = await openAuthenticationWindowForElectron(\n runtimeScene,\n _gameId,\n authWindowOptions\n );\n break;\n case 'cordova-websocket':\n // The game is an iOS app.\n status = await openAuthenticationWindowForCordovaWithWebSocket(\n runtimeScene,\n _gameId,\n authWindowOptions\n );\n break;\n case 'web-iframe':\n // This can be a:\n // - Preview in GDevelop mobile app (iOS only)\n status = await openAuthenticationIframeForWeb(\n runtimeScene,\n _gameId,\n authWindowOptions\n );\n break;\n case 'games-platform':\n // This game is running on gd.games.\n // The authentication is handled by the platform.\n status = await openAuthenticationWindowForGamesPlatform(\n runtimeScene,\n _gameId,\n authWindowOptions\n );\n break;\n case 'web':\n default:\n // This can be a:\n // - Preview in GDevelop web-app\n // - Preview in Gdevelop mobile app (Android only)\n // - Web game (any website/server except gd.games) accessed via a desktop browser...\n // - Or a web game accessed via a mobile browser (Android/iOS).\n status = await openAuthenticationWindowForWeb(\n runtimeScene,\n _gameId,\n authWindowOptions\n );\n break;\n }\n\n if (isDismissedAlready) return;\n if (status === 'dismissed') {\n onAuthenticationContainerDismissed();\n }\n\n resolve({ status });\n })();\n })\n );\n\n /**\n * Condition to check if the window is open, so that the game can be paused in the background.\n */\n export const isAuthenticationWindowOpen = function (): boolean {\n return !!_authenticationRootContainer;\n };\n\n /**\n * Remove the container displaying the authentication window and the callback.\n */\n export const removeAuthenticationContainer = function (\n runtimeScene: gdjs.RuntimeScene\n ) {\n removeAuthenticationCallbacks();\n const domElementContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domElementContainer) {\n logger.info(\n \"The div element covering the game couldn't be found, the authentication must be already closed.\"\n );\n return;\n }\n\n // Remove the authentication root container.\n if (_authenticationRootContainer) {\n domElementContainer.removeChild(_authenticationRootContainer);\n }\n\n _authenticationRootContainer = null;\n _authenticationLoaderContainer = null;\n _authenticationIframeContainer = null;\n _authenticationTextContainer = null;\n };\n\n /*\n * Remove the authentication callbacks from web or cordova.\n */\n const removeAuthenticationCallbacks = function () {\n // Remove the authentication callbacks.\n if (_authenticationMessageCallback) {\n window.removeEventListener(\n 'message',\n _authenticationMessageCallback,\n true\n );\n _authenticationMessageCallback = null;\n }\n };\n\n /*\n * Remove the automatic authentication callback when running on web.\n */\n const removeAutomaticGamesPlatformAuthenticationCallback = function () {\n if (_automaticGamesPlatformAuthenticationCallback) {\n window.removeEventListener(\n 'message',\n _automaticGamesPlatformAuthenticationCallback,\n true\n );\n _automaticGamesPlatformAuthenticationCallback = null;\n }\n if (_automaticGamesPlatformAuthenticationTimeoutId) {\n clearTimeout(_automaticGamesPlatformAuthenticationTimeoutId);\n _automaticGamesPlatformAuthenticationTimeoutId = null;\n }\n };\n\n /**\n * Remove the banner displaying the authentication status.\n */\n export const removeAuthenticationBanner = function (\n runtimeScene: gdjs.RuntimeScene\n ) {\n if (!_authenticationBanner) {\n logger.info(\n \"The authentication banner couldn't be found, the authentication banner must be already closed.\"\n );\n return;\n }\n const domElementContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domElementContainer) {\n logger.info(\n \"The div element covering the game couldn't be found, the authentication must be already closed.\"\n );\n return;\n }\n\n domElementContainer.removeChild(_authenticationBanner);\n _authenticationBanner = null;\n };\n\n /**\n * Focus on game canvas to allow user to interact with it.\n */\n const focusOnGame = function (runtimeScene: gdjs.RuntimeScene) {\n const gameCanvas = runtimeScene.getGame().getRenderer().getCanvas();\n if (gameCanvas) gameCanvas.focus();\n };\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }