gdcore-tools 2.0.0-beta5 → 2.0.0-beta7

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 (170) hide show
  1. package/dist/Runtime/Cordova/config.xml +4 -0
  2. package/dist/Runtime/Cordova/package.json +12 -20
  3. package/dist/Runtime/CustomRuntimeObject.js +1 -1
  4. package/dist/Runtime/CustomRuntimeObject.js.map +2 -2
  5. package/dist/Runtime/CustomRuntimeObjectInstanceContainer.js +1 -1
  6. package/dist/Runtime/CustomRuntimeObjectInstanceContainer.js.map +2 -2
  7. package/dist/Runtime/Extensions/3D/A_RuntimeObject3D.js +1 -1
  8. package/dist/Runtime/Extensions/3D/A_RuntimeObject3D.js.map +2 -2
  9. package/dist/Runtime/Extensions/3D/AmbientLight.js +1 -1
  10. package/dist/Runtime/Extensions/3D/AmbientLight.js.map +2 -2
  11. package/dist/Runtime/Extensions/3D/CustomRuntimeObject3D.js +1 -1
  12. package/dist/Runtime/Extensions/3D/CustomRuntimeObject3D.js.map +2 -2
  13. package/dist/Runtime/Extensions/3D/DirectionalLight.js +1 -1
  14. package/dist/Runtime/Extensions/3D/DirectionalLight.js.map +2 -2
  15. package/dist/Runtime/Extensions/3D/ExponentialFog.js +1 -1
  16. package/dist/Runtime/Extensions/3D/ExponentialFog.js.map +2 -2
  17. package/dist/Runtime/Extensions/3D/HemisphereLight.js +1 -1
  18. package/dist/Runtime/Extensions/3D/HemisphereLight.js.map +2 -2
  19. package/dist/Runtime/Extensions/3D/JsExtension.js +419 -228
  20. package/dist/Runtime/Extensions/3D/LinearFog.js +1 -1
  21. package/dist/Runtime/Extensions/3D/LinearFog.js.map +2 -2
  22. package/dist/Runtime/Extensions/3D/Model3DRuntimeObject.js +1 -1
  23. package/dist/Runtime/Extensions/3D/Model3DRuntimeObject.js.map +2 -2
  24. package/dist/Runtime/Extensions/AdMob/admobtools.js +1 -1
  25. package/dist/Runtime/Extensions/AdMob/admobtools.js.map +2 -2
  26. package/dist/Runtime/Extensions/AdvancedWindow/electron-advancedwindowtools.js +1 -1
  27. package/dist/Runtime/Extensions/AdvancedWindow/electron-advancedwindowtools.js.map +2 -2
  28. package/dist/Runtime/Extensions/AnchorBehavior/anchorruntimebehavior.js +1 -1
  29. package/dist/Runtime/Extensions/AnchorBehavior/anchorruntimebehavior.js.map +2 -2
  30. package/dist/Runtime/Extensions/BBText/JsExtension.js +45 -42
  31. package/dist/Runtime/Extensions/BBText/bbtextruntimeobject.js +1 -1
  32. package/dist/Runtime/Extensions/BBText/bbtextruntimeobject.js.map +2 -2
  33. package/dist/Runtime/Extensions/BitmapText/JsExtension.js +40 -49
  34. package/dist/Runtime/Extensions/BitmapText/bitmaptextruntimeobject.js +1 -1
  35. package/dist/Runtime/Extensions/BitmapText/bitmaptextruntimeobject.js.map +2 -2
  36. package/dist/Runtime/Extensions/Effects/JsExtension.js +2 -2
  37. package/dist/Runtime/Extensions/Effects/bevel-pixi-filter.js +1 -1
  38. package/dist/Runtime/Extensions/Effects/bevel-pixi-filter.js.map +2 -2
  39. package/dist/Runtime/Extensions/Effects/color-replace-pixi-filter.js +1 -1
  40. package/dist/Runtime/Extensions/Effects/color-replace-pixi-filter.js.map +2 -2
  41. package/dist/Runtime/Extensions/Effects/drop-shadow-pixi-filter.js +1 -1
  42. package/dist/Runtime/Extensions/Effects/drop-shadow-pixi-filter.js.map +2 -2
  43. package/dist/Runtime/Extensions/Effects/glow-pixi-filter.js +1 -1
  44. package/dist/Runtime/Extensions/Effects/glow-pixi-filter.js.map +2 -2
  45. package/dist/Runtime/Extensions/Effects/outline-pixi-filter.js +1 -1
  46. package/dist/Runtime/Extensions/Effects/outline-pixi-filter.js.map +2 -2
  47. package/dist/Runtime/Extensions/ExampleJsExtension/JsExtension.js +18 -21
  48. package/dist/Runtime/Extensions/Firebase/B_firebasetools/C_firebasetools.js +1 -1
  49. package/dist/Runtime/Extensions/Firebase/B_firebasetools/C_firebasetools.js.map +2 -2
  50. package/dist/Runtime/Extensions/JsExtensionTypes.d.ts +8 -2
  51. package/dist/Runtime/Extensions/Leaderboards/leaderboardstools.js +1 -1
  52. package/dist/Runtime/Extensions/Leaderboards/leaderboardstools.js.map +2 -2
  53. package/dist/Runtime/Extensions/Lighting/JsExtension.js +50 -38
  54. package/dist/Runtime/Extensions/Lighting/lightruntimeobject-pixi-renderer.js +1 -1
  55. package/dist/Runtime/Extensions/Lighting/lightruntimeobject-pixi-renderer.js.map +2 -2
  56. package/dist/Runtime/Extensions/Multiplayer/JsExtension.js +112 -7
  57. package/dist/Runtime/Extensions/Multiplayer/messageManager.js +1 -1
  58. package/dist/Runtime/Extensions/Multiplayer/messageManager.js.map +2 -2
  59. package/dist/Runtime/Extensions/Multiplayer/multiplayercomponents.js +1 -1
  60. package/dist/Runtime/Extensions/Multiplayer/multiplayercomponents.js.map +2 -2
  61. package/dist/Runtime/Extensions/Multiplayer/multiplayerobjectruntimebehavior.js +1 -1
  62. package/dist/Runtime/Extensions/Multiplayer/multiplayerobjectruntimebehavior.js.map +2 -2
  63. package/dist/Runtime/Extensions/Multiplayer/multiplayertools.js +1 -1
  64. package/dist/Runtime/Extensions/Multiplayer/multiplayertools.js.map +2 -2
  65. package/dist/Runtime/Extensions/PanelSpriteObject/panelspriteruntimeobject-pixi-renderer.js +1 -1
  66. package/dist/Runtime/Extensions/PanelSpriteObject/panelspriteruntimeobject-pixi-renderer.js.map +2 -2
  67. package/dist/Runtime/Extensions/PanelSpriteObject/panelspriteruntimeobject.js +1 -1
  68. package/dist/Runtime/Extensions/PanelSpriteObject/panelspriteruntimeobject.js.map +2 -2
  69. package/dist/Runtime/Extensions/ParticleSystem/particleemitterobject-pixi-renderer.js +1 -1
  70. package/dist/Runtime/Extensions/ParticleSystem/particleemitterobject-pixi-renderer.js.map +2 -2
  71. package/dist/Runtime/Extensions/ParticleSystem/particleemitterobject.js +1 -1
  72. package/dist/Runtime/Extensions/ParticleSystem/particleemitterobject.js.map +2 -2
  73. package/dist/Runtime/Extensions/Physics2Behavior/JsExtension.js +127 -40
  74. package/dist/Runtime/Extensions/Physics2Behavior/physics2runtimebehavior.js +1 -1
  75. package/dist/Runtime/Extensions/Physics2Behavior/physics2runtimebehavior.js.map +2 -2
  76. package/dist/Runtime/Extensions/PlayerAuthentication/playerauthenticationtools.js +1 -1
  77. package/dist/Runtime/Extensions/PlayerAuthentication/playerauthenticationtools.js.map +2 -2
  78. package/dist/Runtime/Extensions/PrimitiveDrawing/shapepainterruntimeobject.js +1 -1
  79. package/dist/Runtime/Extensions/PrimitiveDrawing/shapepainterruntimeobject.js.map +2 -2
  80. package/dist/Runtime/Extensions/Spine/JsExtension.js +77 -36
  81. package/dist/Runtime/Extensions/Spine/spineruntimeobject-pixi-renderer.js +1 -1
  82. package/dist/Runtime/Extensions/Spine/spineruntimeobject-pixi-renderer.js.map +2 -2
  83. package/dist/Runtime/Extensions/Spine/spineruntimeobject.js +1 -1
  84. package/dist/Runtime/Extensions/Spine/spineruntimeobject.js.map +2 -2
  85. package/dist/Runtime/Extensions/TextInput/JsExtension.js +55 -56
  86. package/dist/Runtime/Extensions/TextInput/textinputruntimeobject-pixi-renderer.js +1 -1
  87. package/dist/Runtime/Extensions/TextInput/textinputruntimeobject-pixi-renderer.js.map +2 -2
  88. package/dist/Runtime/Extensions/TextInput/textinputruntimeobject.js +1 -1
  89. package/dist/Runtime/Extensions/TextInput/textinputruntimeobject.js.map +2 -2
  90. package/dist/Runtime/Extensions/TextObject/textruntimeobject-pixi-renderer.js +1 -1
  91. package/dist/Runtime/Extensions/TextObject/textruntimeobject-pixi-renderer.js.map +2 -2
  92. package/dist/Runtime/Extensions/TextObject/textruntimeobject.js +1 -1
  93. package/dist/Runtime/Extensions/TextObject/textruntimeobject.js.map +2 -2
  94. package/dist/Runtime/Extensions/TileMap/JsExtension.js +511 -287
  95. package/dist/Runtime/Extensions/TileMap/TileMapRuntimeManager.js +1 -1
  96. package/dist/Runtime/Extensions/TileMap/TileMapRuntimeManager.js.map +2 -2
  97. package/dist/Runtime/Extensions/TileMap/collision/TransformedTileMap.js +1 -1
  98. package/dist/Runtime/Extensions/TileMap/collision/TransformedTileMap.js.map +2 -2
  99. package/dist/Runtime/Extensions/TileMap/helper/TileMapHelper.js +1 -1
  100. package/dist/Runtime/Extensions/TileMap/helper/TileMapHelper.js.map +1 -1
  101. package/dist/Runtime/Extensions/TileMap/helper/dts/load/tiled/TiledTileMapLoader.d.ts.map +1 -1
  102. package/dist/Runtime/Extensions/TileMap/helper/dts/model/TileMapModel.d.ts +12 -1
  103. package/dist/Runtime/Extensions/TileMap/helper/dts/model/TileMapModel.d.ts.map +1 -1
  104. package/dist/Runtime/Extensions/TileMap/helper/dts/render/TileMapManager.d.ts.map +1 -1
  105. package/dist/Runtime/Extensions/TileMap/helper/dts/render/TileMapPixiHelper.d.ts +1 -0
  106. package/dist/Runtime/Extensions/TileMap/helper/dts/render/TileMapPixiHelper.d.ts.map +1 -1
  107. package/dist/Runtime/Extensions/TileMap/simpletilemapruntimeobject.js +1 -1
  108. package/dist/Runtime/Extensions/TileMap/simpletilemapruntimeobject.js.map +2 -2
  109. package/dist/Runtime/Extensions/TileMap/tilemapcollisionmaskruntimeobject.js +1 -1
  110. package/dist/Runtime/Extensions/TileMap/tilemapcollisionmaskruntimeobject.js.map +2 -2
  111. package/dist/Runtime/Extensions/TileMap/tilemapruntimeobject-pixi-renderer.js +1 -1
  112. package/dist/Runtime/Extensions/TileMap/tilemapruntimeobject-pixi-renderer.js.map +2 -2
  113. package/dist/Runtime/Extensions/TileMap/tilemapruntimeobject.js +1 -1
  114. package/dist/Runtime/Extensions/TileMap/tilemapruntimeobject.js.map +2 -2
  115. package/dist/Runtime/Extensions/TiledSpriteObject/tiledspriteruntimeobject.js +1 -1
  116. package/dist/Runtime/Extensions/TiledSpriteObject/tiledspriteruntimeobject.js.map +2 -2
  117. package/dist/Runtime/Extensions/TweenBehavior/JsExtension.js +1 -0
  118. package/dist/Runtime/Extensions/Video/JsExtension.js +35 -44
  119. package/dist/Runtime/Extensions/Video/videoruntimeobject.js +1 -1
  120. package/dist/Runtime/Extensions/Video/videoruntimeobject.js.map +2 -2
  121. package/dist/Runtime/ResourceLoader.js +1 -1
  122. package/dist/Runtime/ResourceLoader.js.map +2 -2
  123. package/dist/Runtime/RuntimeInstanceContainer.js.map +2 -2
  124. package/dist/Runtime/SpriteAnimator.js +1 -1
  125. package/dist/Runtime/SpriteAnimator.js.map +2 -2
  126. package/dist/Runtime/debugger-client/InGameDebugger.js +1 -1
  127. package/dist/Runtime/debugger-client/InGameDebugger.js.map +2 -2
  128. package/dist/Runtime/debugger-client/abstract-debugger-client.js +1 -1
  129. package/dist/Runtime/debugger-client/abstract-debugger-client.js.map +2 -2
  130. package/dist/Runtime/debugger-client/hot-reloader.js +2 -1
  131. package/dist/Runtime/debugger-client/hot-reloader.js.map +2 -2
  132. package/dist/Runtime/debugger-client/minimal-debugger-client.js +2 -0
  133. package/dist/Runtime/debugger-client/minimal-debugger-client.js.map +7 -0
  134. package/dist/Runtime/debugger-client/websocket-debugger-client.js.map +2 -2
  135. package/dist/Runtime/debugger-client/window-message-debugger-client.js.map +2 -2
  136. package/dist/Runtime/events-tools/inputtools.js +1 -1
  137. package/dist/Runtime/events-tools/inputtools.js.map +2 -2
  138. package/dist/Runtime/events-tools/objecttools.js +1 -1
  139. package/dist/Runtime/events-tools/objecttools.js.map +2 -2
  140. package/dist/Runtime/gd.js +1 -1
  141. package/dist/Runtime/gd.js.map +2 -2
  142. package/dist/Runtime/howler-sound-manager/howler-sound-manager.js +1 -1
  143. package/dist/Runtime/howler-sound-manager/howler-sound-manager.js.map +2 -2
  144. package/dist/Runtime/inputmanager.js +1 -1
  145. package/dist/Runtime/inputmanager.js.map +2 -2
  146. package/dist/Runtime/pixi-renderers/CustomRuntimeObject2DPixiRenderer.js +1 -1
  147. package/dist/Runtime/pixi-renderers/CustomRuntimeObject2DPixiRenderer.js.map +2 -2
  148. package/dist/Runtime/pixi-renderers/pixi-filters-tools.js +1 -1
  149. package/dist/Runtime/pixi-renderers/pixi-filters-tools.js.map +2 -2
  150. package/dist/Runtime/pixi-renderers/runtimegame-pixi-renderer.js +1 -1
  151. package/dist/Runtime/pixi-renderers/runtimegame-pixi-renderer.js.map +2 -2
  152. package/dist/Runtime/pixi-renderers/spriteruntimeobject-pixi-renderer.js +1 -1
  153. package/dist/Runtime/pixi-renderers/spriteruntimeobject-pixi-renderer.js.map +2 -2
  154. package/dist/Runtime/runtimegame.js +1 -1
  155. package/dist/Runtime/runtimegame.js.map +2 -2
  156. package/dist/Runtime/runtimeobject.js +1 -1
  157. package/dist/Runtime/runtimeobject.js.map +2 -2
  158. package/dist/Runtime/runtimescene.js +1 -1
  159. package/dist/Runtime/runtimescene.js.map +2 -2
  160. package/dist/Runtime/scenestack.js +1 -1
  161. package/dist/Runtime/scenestack.js.map +2 -2
  162. package/dist/Runtime/spriteruntimeobject.js +1 -1
  163. package/dist/Runtime/spriteruntimeobject.js.map +2 -2
  164. package/dist/Runtime/types/project-data.d.ts +36 -8
  165. package/dist/Runtime/variablescontainer.js +1 -1
  166. package/dist/Runtime/variablescontainer.js.map +2 -2
  167. package/dist/lib/libGD.cjs +1 -1
  168. package/dist/lib/libGD.wasm +0 -0
  169. package/gd.d.ts +149 -75
  170. package/package.json +3 -3
@@ -1,2 +1,2 @@
1
- var gdjs;(function(x){const g=new x.Logger("Multiplayer");let L;(function(o){const M="lobbies-root-container",v="lobbies-frame-container",f="lobbies-close-container",I="lobbies-loader-container",N="lobbies-texts-container",C="lobbies-iframe-container",T="lobbies-iframe";let D=!0;const b=[];o.getDomElementContainer=n=>{const e=n.getGame().getRenderer().getDomElementContainer();return e||(g.error("No DOM element container found."),null)},o.getLobbiesRootContainer=n=>{const e=o.getDomElementContainer(n);return e?e.querySelector(`#${M}`):null},o.getLobbiesLoaderContainer=n=>{const e=o.getDomElementContainer(n);return e?e.querySelector(`#${I}`):null},o.getLobbiesIframeContainer=n=>{const e=o.getDomElementContainer(n);return e?e.querySelector(`#${C}`):null},o.getLobbiesCloseContainer=n=>{const e=o.getDomElementContainer(n);return e?e.querySelector(`#${f}`):null},o.getLobbiesTextsContainer=n=>{const e=o.getDomElementContainer(n);return e?e.querySelector(`#${N}`):null},o.getLobbiesIframe=n=>{const e=o.getDomElementContainer(n);return e?e.querySelector(`#${T}`):null},o.displayLobbies=function(n,e){const r=o.getDomElementContainer(n);if(!r)return;const s=document.createElement("div");s.id=M,s.style.position="relative",s.style.backgroundColor="rgba(14, 6, 45, 0.5)",s.style.opacity="1",s.style.width="100%",s.style.height="100%",s.style.zIndex="2",s.style.pointerEvents="all";const i=document.createElement("div");i.id=v,i.style.backgroundColor="#FFFFFF",i.style.position="absolute",i.style.top="16px",i.style.bottom="16px",i.style.left="16px",i.style.right="16px",i.style.borderRadius="8px",i.style.boxShadow="0px 4px 4px rgba(0, 0, 0, 0.25)",i.style.overflow="hidden";const t=document.createElement("div");t.id=f,t.style.cursor="pointer",t.style.display="flex",t.style.justifyContent="right",t.style.alignItems="center",t.style.zIndex="3",t.style.position="absolute",t.style.top="32px",t.style.right="32px",m(t,e);const d=document.createElement("img");d.setAttribute("width","15px"),d.setAttribute("src","data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOCIgaGVpZ2h0PSI4IiB2aWV3Qm94PSIwIDAgOCA4IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTcuODUzNTUgMC4xNDY0NDdDOC4wNDg4MiAwLjM0MTcwOSA4LjA0ODgyIDAuNjU4MjkxIDcuODUzNTUgMC44NTM1NTNMMC44NTM1NTMgNy44NTM1NUMwLjY1ODI5MSA4LjA0ODgyIDAuMzQxNzA5IDguMDQ4ODIgMC4xNDY0NDcgNy44NTM1NUMtMC4wNDg4MTU1IDcuNjU4MjkgLTAuMDQ4ODE1NSA3LjM0MTcxIDAuMTQ2NDQ3IDcuMTQ2NDVMNy4xNDY0NSAwLjE0NjQ0N0M3LjM0MTcxIC0wLjA0ODgxNTUgNy42NTgyOSAtMC4wNDg4MTU1IDcuODUzNTUgMC4xNDY0NDdaIiBmaWxsPSIjMUQxRDI2Ii8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMC4xNDY0NDcgMC4xNDY0NDdDMC4zNDE3MDkgLTAuMDQ4ODE1NSAwLjY1ODI5MSAtMC4wNDg4MTU1IDAuODUzNTUzIDAuMTQ2NDQ3TDcuODUzNTUgNy4xNDY0NUM4LjA0ODgyIDcuMzQxNzEgOC4wNDg4MiA3LjY1ODI5IDcuODUzNTUgNy44NTM1NUM3LjY1ODI5IDguMDQ4ODIgNy4zNDE3MSA4LjA0ODgyIDcuMTQ2NDUgNy44NTM1NUwwLjE0NjQ0NyAwLjg1MzU1M0MtMC4wNDg4MTU1IDAuNjU4MjkxIC0wLjA0ODgxNTUgMC4zNDE3MDkgMC4xNDY0NDcgMC4xNDY0NDdaIiBmaWxsPSIjMUQxRDI2Ii8+Cjwvc3ZnPgo="),t.appendChild(d),D||(t.style.visibility="hidden");const a=document.createElement("div");a.id=I,a.style.display="flex",a.style.flexDirection="column",a.style.height="100%",a.style.width="100%",a.style.justifyContent="center",a.style.alignItems="center";const l=document.createElement("img");l.setAttribute("width","28px"),l.setAttribute("src","data:image/svg+xml;base64,PHN2ZyBjbGFzcz0iYW5pbWF0ZS1zcGluIC1tbC0xIG1yLTMgaC01IHctNSB0ZXh0LXdoaXRlIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCAyNCAyNCI+CjxjaXJjbGUgb3BhY2l0eT0nMC4yNScgY3g9IjEyIiBjeT0iMTIiIHI9IjEwIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSI0Ij48L2NpcmNsZT4KPHBhdGggb3BhY2l0eT0nMC43NScgZmlsbD0iY3VycmVudENvbG9yIiBkPSJNNCAxMmE4IDggMCAwMTgtOFYwQzUuMzczIDAgMCA1LjM3MyAwIDEyaDR6bTIgNS4yOTFBNy45NjIgNy45NjIgMCAwMTQgMTJIMGMwIDMuMDQyIDEuMTM1IDUuODI0IDMgNy45MzhsMy0yLjY0N3oiPjwvcGF0aD4KPC9zdmc+"),l.style.marginTop="50px";try{l.animate([{transform:"rotate(0deg)"},{transform:"rotate(359deg)"}],{duration:3e3,iterations:1/0})}catch{g.warn("Animation not supported, loader will be fixed.")}a.appendChild(l);const c=document.createElement("div");c.id=C,c.style.display="flex",c.style.flexDirection="column",c.style.height="100%",c.style.width="100%",c.style.justifyContent="stretch",c.style.alignItems="stretch",c.style.display="none",i.appendChild(t),i.appendChild(a),i.appendChild(c),s.appendChild(i),r.appendChild(s)},o.displayIframeInsideLobbiesContainer=(n,e)=>{const r=o.getLobbiesIframeContainer(n),s=o.getLobbiesLoaderContainer(n),i=o.getLobbiesTextsContainer(n);if(!r||!s||!i){g.error("Lobbies containers not found.");return}const t=document.createElement("iframe");t.id=T,t.setAttribute("sandbox","allow-forms allow-modals allow-orientation-lock allow-popups allow-same-origin allow-scripts"),t.addEventListener("load",()=>{r.style.display="flex",s.style.display="none"}),t.addEventListener("loaderror",()=>{o.addReloadUrlToTextsContainer(()=>{r.removeChild(t),r.style.display="none",s.style.display="flex",o.displayIframeInsideLobbiesContainer(n,e)},i)}),t.src=e,t.style.flex="1",t.style.border="0",r.appendChild(t)},o.addTextsToLoadingContainer=(n,e,r)=>{const s=o.getLobbiesLoaderContainer(n);if(!s){g.error("Loader container not found.");return}const i=document.createElement("div");if(i.id=N,i.style.display="flex",i.style.flexDirection="column",i.style.width="100%",i.style.justifyContent="center",i.style.alignItems="center",i.style.position="relative",i.style.zIndex="3",i.style.fontSize="11pt",i.style.fontFamily='-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"',!e){const t=document.createElement("h1");t.innerText="Publish your game!",t.style.fontSize="20pt",t.style.fontWeight="bold";const d=document.createElement("p");d.innerText="GDevelop's lobbies are only available for published games.";const a=document.createElement("p");a.innerText="Click the button below to learn how to publish your game then try again.",i.appendChild(t),i.appendChild(d),i.appendChild(a),s.innerHTML="";const l=document.createElement("a");m(l,r),l.innerText="How to publish my game",l.style.color="#0078d4",l.style.textDecoration="none",l.style.textDecoration="underline",l.style.cursor="pointer",i.appendChild(l)}s.prepend(i)},o.addReloadUrlToTextsContainer=(n,e)=>{const r=document.createElement("a");m(r,n),r.innerText="Try again",r.style.color="#0078d4",r.style.textDecoration="none",r.style.textDecoration="underline",r.style.cursor="pointer",e.appendChild(r)},o.removeLobbiesContainer=function(n){const e=o.getLobbiesRootContainer(n);!e||e.remove()},o.changeLobbiesWindowCloseActionVisibility=function(n,e){D=e;const r=o.getLobbiesCloseContainer(n);!r||(r.style.visibility=e?"inherit":"hidden")},o.hideLobbiesCloseButtonTemporarily=function(n){if(!D)return;const e=o.getLobbiesCloseContainer(n);!e||(e.style.visibility="hidden",setTimeout(()=>{e.style.visibility="inherit"},1e4))},o.displayErrorNotification=function(n){o.showNotification(n,"An error occurred while displaying the game lobbies, please try again.","error")},o.displayPlayerLeftNotification=function(n,e){o.showNotification(n,`${e} left.`,"warning")},o.displayPlayerJoinedNotification=function(n,e){o.showNotification(n,`${e} joined.`,"success")},o.displayConnectionErrorNotification=function(n){o.showNotification(n,"Could not connect to other players.","error")};const E=function(n){const e=document.getElementById(n);if(!e){g.error("Notification not found.");return}const r=b.indexOf(n);r!==-1&&b.splice(r,1),e.remove();for(let s=0;s<b.length;s++){const i=document.getElementById(b[s]);if(!i){g.error("Notification not found.");continue}i.style.top=`${12+s*32}px`}};o.showNotification=function(n,e,r){if(b.length>5){const c=b.shift();if(!c){g.error("No oldest notification ID found.");return}E(c)}const s=`notification-${Math.random().toString(36).substring(7)}`,i=n.getGame().getRenderer().getDomElementContainer();if(!i){g.error("No DOM element container found.");return}const t=document.createElement("div");t.id=s,t.style.position="absolute",t.style.pointerEvents="all",t.style.backgroundColor=r==="success"?"#0E062D":r==="warning"?"#FFA500":"#FF0000",t.style.top=`${12+b.length*32}px`,t.style.right="16px",t.style.padding="6px 32px 6px 6px",t.style.zIndex="1",t.style.display="flex",t.style.flexDirection="row-reverse",t.style.justifyContent="space-between",t.style.alignItems="center",t.style.boxShadow="0px 4px 4px rgba(0, 0, 0, 0.25)",t.style.borderRadius="4px",t.style.fontSize="11pt",t.style.color="#FFFFFF",t.style.fontFamily='-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"';try{t.animate([{transform:"translateY(-30px)",opacity:0},{transform:"translateY(0px)",opacity:1}],{duration:700,easing:"ease-out"})}catch{g.warn("Animation not supported, div will be fixed.")}const d=document.createElement("p");d.id="loggedText",d.innerHTML=e,d.style.margin="0px",t.appendChild(d),i.appendChild(t),b.push(s);const a=700,l=5e3;setTimeout(()=>{try{t.animate([{transform:"translateY(0px)",opacity:1},{transform:"translateY(-30px)",opacity:0}],{duration:a,easing:"ease-in"})}catch{g.warn("Animation not supported, div will be fixed.")}},l),setTimeout(()=>{E(s)},l+a)};const m=function(n,e){n.addEventListener("touchstart",r=>{e()}),n.addEventListener("click",r=>{e()})}})(L=x.multiplayerComponents||(x.multiplayerComponents={}))})(gdjs||(gdjs={}));
1
+ var gdjs;(function(D){const d=new D.Logger("Multiplayer");let v;(function(n){const m="lobbies-root-container",w="lobbies-frame-container",I="lobbies-close-container",N="lobbies-loader-container",T="lobbies-texts-container",p="lobbies-iframe-container",C="lobbies-iframe";let M=!0;const b=[];n.getDomElementContainer=e=>{const t=e.getGame().getRenderer().getDomElementContainer();return t||(d.error("No DOM element container found."),null)},n.getLobbiesRootContainer=e=>{const t=n.getDomElementContainer(e);return t?t.querySelector(`#${m}`):null},n.getLobbiesLoaderContainer=e=>{const t=n.getDomElementContainer(e);return t?t.querySelector(`#${N}`):null},n.getLobbiesIframeContainer=e=>{const t=n.getDomElementContainer(e);return t?t.querySelector(`#${p}`):null},n.getLobbiesCloseContainer=e=>{const t=n.getDomElementContainer(e);return t?t.querySelector(`#${I}`):null},n.getLobbiesTextsContainer=e=>{const t=n.getDomElementContainer(e);return t?t.querySelector(`#${T}`):null},n.getLobbiesIframe=e=>{const t=n.getDomElementContainer(e);return t?t.querySelector(`#${C}`):null},n.displayLobbies=function(e,t){const o=n.getDomElementContainer(e);if(!o)return;const l=document.createElement("div");l.id=m,l.style.position="relative",l.style.backgroundColor="rgba(14, 6, 45, 0.5)",l.style.opacity="1",l.style.width="100%",l.style.height="100%",l.style.zIndex="2",l.style.pointerEvents="all";const i=document.createElement("div");i.id=w,i.style.backgroundColor="#FFFFFF",i.style.position="absolute",i.style.top="16px",i.style.bottom="16px",i.style.left="16px",i.style.right="16px",i.style.borderRadius="8px",i.style.boxShadow="0px 4px 4px rgba(0, 0, 0, 0.25)",i.style.overflow="hidden";const r=document.createElement("div");r.id=I,r.style.cursor="pointer",r.style.display="flex",r.style.justifyContent="right",r.style.alignItems="center",r.style.zIndex="3",r.style.position="absolute",r.style.top="32px",r.style.right="32px",f(r,t);const g=document.createElement("img");g.setAttribute("width","15px"),g.setAttribute("src","data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iOCIgaGVpZ2h0PSI4IiB2aWV3Qm94PSIwIDAgOCA4IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTcuODUzNTUgMC4xNDY0NDdDOC4wNDg4MiAwLjM0MTcwOSA4LjA0ODgyIDAuNjU4MjkxIDcuODUzNTUgMC44NTM1NTNMMC44NTM1NTMgNy44NTM1NUMwLjY1ODI5MSA4LjA0ODgyIDAuMzQxNzA5IDguMDQ4ODIgMC4xNDY0NDcgNy44NTM1NUMtMC4wNDg4MTU1IDcuNjU4MjkgLTAuMDQ4ODE1NSA3LjM0MTcxIDAuMTQ2NDQ3IDcuMTQ2NDVMNy4xNDY0NSAwLjE0NjQ0N0M3LjM0MTcxIC0wLjA0ODgxNTUgNy42NTgyOSAtMC4wNDg4MTU1IDcuODUzNTUgMC4xNDY0NDdaIiBmaWxsPSIjMUQxRDI2Ii8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMC4xNDY0NDcgMC4xNDY0NDdDMC4zNDE3MDkgLTAuMDQ4ODE1NSAwLjY1ODI5MSAtMC4wNDg4MTU1IDAuODUzNTUzIDAuMTQ2NDQ3TDcuODUzNTUgNy4xNDY0NUM4LjA0ODgyIDcuMzQxNzEgOC4wNDg4MiA3LjY1ODI5IDcuODUzNTUgNy44NTM1NUM3LjY1ODI5IDguMDQ4ODIgNy4zNDE3MSA4LjA0ODgyIDcuMTQ2NDUgNy44NTM1NUwwLjE0NjQ0NyAwLjg1MzU1M0MtMC4wNDg4MTU1IDAuNjU4MjkxIC0wLjA0ODgxNTUgMC4zNDE3MDkgMC4xNDY0NDcgMC4xNDY0NDdaIiBmaWxsPSIjMUQxRDI2Ii8+Cjwvc3ZnPgo="),r.appendChild(g),M||(r.style.visibility="hidden");const s=document.createElement("div");s.id=N,s.style.display="flex",s.style.flexDirection="column",s.style.height="100%",s.style.width="100%",s.style.justifyContent="center",s.style.alignItems="center";const a=document.createElement("img");a.setAttribute("width","28px"),a.setAttribute("src","data:image/svg+xml;base64,PHN2ZyBjbGFzcz0iYW5pbWF0ZS1zcGluIC1tbC0xIG1yLTMgaC01IHctNSB0ZXh0LXdoaXRlIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCAyNCAyNCI+CjxjaXJjbGUgb3BhY2l0eT0nMC4yNScgY3g9IjEyIiBjeT0iMTIiIHI9IjEwIiBzdHJva2U9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSI0Ij48L2NpcmNsZT4KPHBhdGggb3BhY2l0eT0nMC43NScgZmlsbD0iY3VycmVudENvbG9yIiBkPSJNNCAxMmE4IDggMCAwMTgtOFYwQzUuMzczIDAgMCA1LjM3MyAwIDEyaDR6bTIgNS4yOTFBNy45NjIgNy45NjIgMCAwMTQgMTJIMGMwIDMuMDQyIDEuMTM1IDUuODI0IDMgNy45MzhsMy0yLjY0N3oiPjwvcGF0aD4KPC9zdmc+"),a.style.marginTop="50px";try{a.animate([{transform:"rotate(0deg)"},{transform:"rotate(359deg)"}],{duration:3e3,iterations:1/0})}catch{d.warn("Animation not supported, loader will be fixed.")}s.appendChild(a);const c=document.createElement("div");c.id=p,c.style.display="flex",c.style.flexDirection="column",c.style.height="100%",c.style.width="100%",c.style.justifyContent="stretch",c.style.alignItems="stretch",c.style.display="none",i.appendChild(r),i.appendChild(s),i.appendChild(c),l.appendChild(i),o.appendChild(l)},n.displayIframeInsideLobbiesContainer=(e,t)=>{const o=n.getLobbiesIframeContainer(e),l=n.getLobbiesLoaderContainer(e),i=n.getLobbiesTextsContainer(e);if(!o||!l||!i){d.error("Lobbies containers not found.");return}const r=document.createElement("iframe");r.id=C,r.setAttribute("sandbox","allow-forms allow-modals allow-orientation-lock allow-popups allow-same-origin allow-scripts"),r.addEventListener("load",()=>{o.style.display="flex",l.style.display="none"}),r.addEventListener("loaderror",()=>{n.addReloadUrlToTextsContainer(()=>{o.removeChild(r),o.style.display="none",l.style.display="flex",n.displayIframeInsideLobbiesContainer(e,t)},i)}),r.src=t,r.style.flex="1",r.style.border="0",o.appendChild(r)},n.addTextsToLoadingContainer=(e,t,o)=>{const l=n.getLobbiesLoaderContainer(e);if(!l){d.error("Loader container not found.");return}const i=document.createElement("div");if(i.id=T,i.style.display="flex",i.style.flexDirection="column",i.style.width="100%",i.style.justifyContent="center",i.style.alignItems="center",i.style.position="relative",i.style.zIndex="3",i.style.fontSize="11pt",i.style.fontFamily='-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"',!t){const r=document.createElement("h1");r.innerText="Publish your game!",r.style.fontSize="20pt",r.style.fontWeight="bold";const g=document.createElement("p");g.innerText="GDevelop's lobbies are only available for published games.";const s=document.createElement("p");s.innerText="Click the button below to learn how to publish your game then try again.",i.appendChild(r),i.appendChild(g),i.appendChild(s),l.innerHTML="";const a=document.createElement("a");f(a,o),a.innerText="How to publish my game",a.style.color="#0078d4",a.style.textDecoration="none",a.style.textDecoration="underline",a.style.cursor="pointer",i.appendChild(a)}l.prepend(i)},n.addReloadUrlToTextsContainer=(e,t)=>{const o=document.createElement("a");f(o,e),o.innerText="Try again",o.style.color="#0078d4",o.style.textDecoration="none",o.style.textDecoration="underline",o.style.cursor="pointer",t.appendChild(o)},n.removeLobbiesContainer=function(e){const t=n.getLobbiesRootContainer(e);!t||t.remove()},n.changeLobbiesWindowCloseActionVisibility=function(e,t){M=t;const o=n.getLobbiesCloseContainer(e);!o||(o.style.visibility=t?"inherit":"hidden")},n.hideLobbiesCloseButtonTemporarily=function(e){if(!M)return;const t=n.getLobbiesCloseContainer(e);!t||(t.style.visibility="hidden",setTimeout(()=>{t.style.visibility="inherit"},1e4))},n.displayErrorNotification=function(e){n.showNotification({runtimeScene:e,content:"An error occurred while displaying the game lobbies, please try again.",type:"error"})},n.displayPlayerLeftNotification=function(e,t){n.showNotification({runtimeScene:e,content:`${t} left.`,type:"warning"})},n.displayPlayerJoinedNotification=function(e,t){n.showNotification({runtimeScene:e,content:`${t} joined.`,type:"success"})},n.displayConnectionErrorNotification=function(e){n.showNotification({runtimeScene:e,content:"Could not connect to other players.",type:"error"})},n.displayHostMigrationNotification=function(e){n.showNotification({runtimeScene:e,content:"Migrating host...",type:"warning",id:"migrating-host",persist:!0})},n.showHostMigrationFinishedNotification=function(e){x("migrating-host"),n.showNotification({runtimeScene:e,content:"Host migrated!",type:"success"})},n.showHostMigrationFailedNotification=function(e){x("migrating-host"),n.showNotification({runtimeScene:e,content:"Host migration failed.",type:"error"})};const x=function(e){const t=document.getElementById(e);if(!t){d.warn(`Notification ${e} not found. skipping`);return}const o=b.indexOf(e);o!==-1&&b.splice(o,1),t.remove();for(let l=o;l<b.length;l++){const i=document.getElementById(b[l]);if(!i){d.error("Notification not found.");continue}i.style.top=`${12+l*32}px`}};n.showNotification=function({runtimeScene:e,content:t,type:o,id:l,persist:i}){if(b.length>5){const j=b.shift();if(!j){d.error("No oldest notification ID found.");return}x(j)}const r=l||`notification-${Math.random().toString(36).substring(7)}`,g=e.getGame().getRenderer().getDomElementContainer();if(!g){d.error("No DOM element container found.");return}const s=document.createElement("div");s.id=r,s.style.position="absolute",s.style.pointerEvents="all",s.style.backgroundColor=o==="success"?"#0E062D":o==="warning"?"#FFA500":"#FF0000",s.style.top=`${12+b.length*32}px`,s.style.right="16px",s.style.padding="6px 32px 6px 6px",s.style.zIndex="1",s.style.display="flex",s.style.flexDirection="row-reverse",s.style.justifyContent="space-between",s.style.alignItems="center",s.style.boxShadow="0px 4px 4px rgba(0, 0, 0, 0.25)",s.style.borderRadius="4px",s.style.fontSize="11pt",s.style.color="#FFFFFF",s.style.fontFamily='-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"';try{s.animate([{transform:"translateY(-30px)",opacity:0},{transform:"translateY(0px)",opacity:1}],{duration:700,easing:"ease-out"})}catch{d.warn("Animation not supported, div will be fixed.")}const a=document.createElement("p");if(a.id="loggedText",a.innerHTML=t,a.style.margin="0px",s.appendChild(a),g.appendChild(s),b.push(r),i)return;const c=700,L=3e3;setTimeout(()=>{try{s.animate([{transform:"translateY(0px)",opacity:1},{transform:"translateY(-30px)",opacity:0}],{duration:c,easing:"ease-in"})}catch{d.warn("Animation not supported, div will be fixed.")}},L),setTimeout(()=>{x(r)},L+c)};const f=function(e,t){e.addEventListener("touchstart",o=>{t()}),e.addEventListener("click",o=>{t()})}})(v=D.multiplayerComponents||(D.multiplayerComponents={}))})(gdjs||(gdjs={}));
2
2
  //# sourceMappingURL=multiplayercomponents.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../GDevelop/Extensions/Multiplayer/multiplayercomponents.ts"],
4
- "sourcesContent": ["namespace gdjs {\n const logger = new gdjs.Logger('Multiplayer');\n export namespace multiplayerComponents {\n const lobbiesRootContainerId = 'lobbies-root-container';\n const lobbiesFrameContainerId = 'lobbies-frame-container';\n const lobbiesCloseContainerId = 'lobbies-close-container';\n const lobbiesLoaderContainerId = 'lobbies-loader-container';\n const lobbiesTextsContainerId = 'lobbies-texts-container';\n const lobbiesIframeContainerId = 'lobbies-iframe-container';\n const lobbiesIframeId = 'lobbies-iframe';\n\n let canLobbyBeClosed = true;\n\n const notificationContainerIds: string[] = [];\n\n export const getDomElementContainer = (\n runtimeScene: gdjs.RuntimeScene\n ): HTMLDivElement | null => {\n // Find the DOM element container.\n const domElementContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domElementContainer) {\n logger.error('No DOM element container found.');\n return null;\n }\n return domElementContainer;\n };\n\n export const getLobbiesRootContainer = (\n runtimeScene: gdjs.RuntimeScene\n ): HTMLDivElement | null => {\n const domElementContainer = getDomElementContainer(runtimeScene);\n if (!domElementContainer) {\n return null;\n }\n // Find the root container with its ID.\n const lobbiesRootContainer = domElementContainer.querySelector(\n `#${lobbiesRootContainerId}`\n ) as HTMLDivElement | null;\n return lobbiesRootContainer;\n };\n\n export const getLobbiesLoaderContainer = (\n runtimeScene: gdjs.RuntimeScene\n ): HTMLDivElement | null => {\n const domElementContainer = getDomElementContainer(runtimeScene);\n if (!domElementContainer) {\n return null;\n }\n\n // Find the loader container with its ID.\n const lobbiesLoaderContainer = domElementContainer.querySelector(\n `#${lobbiesLoaderContainerId}`\n ) as HTMLDivElement | null;\n return lobbiesLoaderContainer;\n };\n\n export const getLobbiesIframeContainer = (\n runtimeScene: gdjs.RuntimeScene\n ): HTMLDivElement | null => {\n const domElementContainer = getDomElementContainer(runtimeScene);\n if (!domElementContainer) {\n return null;\n }\n\n // Find the iframe container with its ID.\n const lobbiesIframeContainer = domElementContainer.querySelector(\n `#${lobbiesIframeContainerId}`\n ) as HTMLDivElement | null;\n return lobbiesIframeContainer;\n };\n\n export const getLobbiesCloseContainer = (\n runtimeScene: gdjs.RuntimeScene\n ): HTMLDivElement | null => {\n const domElementContainer = getDomElementContainer(runtimeScene);\n if (!domElementContainer) {\n return null;\n }\n\n // Find the close container with its ID.\n const lobbiesCloseContainer = domElementContainer.querySelector(\n `#${lobbiesCloseContainerId}`\n ) as HTMLDivElement | null;\n return lobbiesCloseContainer;\n };\n\n export const getLobbiesTextsContainer = (\n runtimeScene: gdjs.RuntimeScene\n ): HTMLDivElement | null => {\n const domElementContainer = getDomElementContainer(runtimeScene);\n if (!domElementContainer) {\n return null;\n }\n\n // Find the text container with its ID.\n const lobbiesTextContainer = domElementContainer.querySelector(\n `#${lobbiesTextsContainerId}`\n ) as HTMLDivElement | null;\n return lobbiesTextContainer;\n };\n\n export const getLobbiesIframe = (\n runtimeScene: gdjs.RuntimeScene\n ): HTMLIFrameElement | null => {\n const domElementContainer = getDomElementContainer(runtimeScene);\n if (!domElementContainer) {\n return null;\n }\n\n // Find the iframe with its ID.\n const lobbiesIframe = domElementContainer.querySelector(\n `#${lobbiesIframeId}`\n ) as HTMLIFrameElement | null;\n return lobbiesIframe;\n };\n\n /**\n * Creates a DOM element that will contain the loader or a message if the game is not registered,\n * and adds it to the dom container.\n */\n export const displayLobbies = function (\n runtimeScene: gdjs.RuntimeScene,\n onCloseLobbiesContainer: () => void\n ) {\n const domElementContainer = getDomElementContainer(runtimeScene);\n if (!domElementContainer) {\n return;\n }\n\n const rootContainer = document.createElement('div');\n rootContainer.id = lobbiesRootContainerId;\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 = lobbiesFrameContainerId;\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.overflow = 'hidden';\n\n const _closeContainer: HTMLDivElement = document.createElement('div');\n _closeContainer.id = lobbiesCloseContainerId;\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 _closeContainer.style.position = 'absolute';\n _closeContainer.style.top = '32px';\n _closeContainer.style.right = '32px';\n addTouchAndClickEventListeners(_closeContainer, onCloseLobbiesContainer);\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 if (!canLobbyBeClosed) {\n _closeContainer.style.visibility = 'hidden';\n }\n\n const loaderContainer: HTMLDivElement = document.createElement('div');\n loaderContainer.id = lobbiesLoaderContainerId;\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.id = lobbiesIframeContainerId;\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 // Display the lobbies window right away, to show a loader\n // while the call for game registration is happening.\n domElementContainer.appendChild(rootContainer);\n };\n\n export const displayIframeInsideLobbiesContainer = (\n runtimeScene: gdjs.RuntimeScene,\n url: string\n ) => {\n const iframeContainer = getLobbiesIframeContainer(runtimeScene);\n const loaderContainer = getLobbiesLoaderContainer(runtimeScene);\n const textsContainer = getLobbiesTextsContainer(runtimeScene);\n\n if (!iframeContainer || !loaderContainer || !textsContainer) {\n logger.error('Lobbies containers not found.');\n return;\n }\n\n const iframe = document.createElement('iframe');\n iframe.id = lobbiesIframeId;\n\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 addReloadUrlToTextsContainer(() => {\n // Try again.\n iframeContainer.removeChild(iframe);\n iframeContainer.style.display = 'none';\n loaderContainer.style.display = 'flex';\n displayIframeInsideLobbiesContainer(runtimeScene, url);\n }, textsContainer);\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 container\n * especially if the game is not registered.\n */\n export const addTextsToLoadingContainer = (\n runtimeScene: gdjs.RuntimeScene,\n isGameRegistered: boolean,\n wikiOpenAction: () => void\n ) => {\n const loaderContainer = getLobbiesLoaderContainer(runtimeScene);\n if (!loaderContainer) {\n logger.error('Loader container not found.');\n return;\n }\n\n const textsContainer: HTMLDivElement = document.createElement('div');\n textsContainer.id = lobbiesTextsContainerId;\n textsContainer.style.display = 'flex';\n textsContainer.style.flexDirection = 'column';\n textsContainer.style.width = '100%';\n textsContainer.style.justifyContent = 'center';\n textsContainer.style.alignItems = 'center';\n textsContainer.style.position = 'relative';\n textsContainer.style.zIndex = '3';\n textsContainer.style.fontSize = '11pt';\n textsContainer.style.fontFamily =\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\"';\n\n if (!isGameRegistered) {\n const title = document.createElement('h1');\n title.innerText = 'Publish your game!';\n title.style.fontSize = '20pt';\n title.style.fontWeight = 'bold';\n const text1 = document.createElement('p');\n text1.innerText =\n \"GDevelop's lobbies are only available for published games.\";\n const text2 = document.createElement('p');\n text2.innerText =\n 'Click the button below to learn how to publish your game then try again.';\n textsContainer.appendChild(title);\n textsContainer.appendChild(text1);\n textsContainer.appendChild(text2);\n\n // Remove the loader and add the wiki link.\n loaderContainer.innerHTML = '';\n\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 textsContainer.appendChild(link);\n }\n\n loaderContainer.prepend(textsContainer);\n };\n\n /**\n * Helper to add a reload link in case the window hasn't opened properly.\n */\n export const addReloadUrlToTextsContainer = (\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 * Hides the lobbies container.\n */\n export const removeLobbiesContainer = function (\n runtimeScene: gdjs.RuntimeScene\n ) {\n const rootContainer = getLobbiesRootContainer(runtimeScene);\n if (!rootContainer) {\n return;\n }\n\n rootContainer.remove();\n };\n\n export const changeLobbiesWindowCloseActionVisibility = function (\n runtimeScene: gdjs.RuntimeScene,\n canClose: boolean\n ) {\n canLobbyBeClosed = canClose;\n\n const closeContainer = getLobbiesCloseContainer(runtimeScene);\n if (!closeContainer) {\n return;\n }\n\n closeContainer.style.visibility = canClose ? 'inherit' : 'hidden';\n };\n\n export const hideLobbiesCloseButtonTemporarily = function (\n runtimeScene: gdjs.RuntimeScene\n ) {\n if (!canLobbyBeClosed) return;\n\n const closeContainer = getLobbiesCloseContainer(runtimeScene);\n if (!closeContainer) {\n return;\n }\n\n closeContainer.style.visibility = 'hidden';\n\n // There is a risk a player leaves the lobby before the end of the countdown,\n // so we show the close container again after 5 seconds in case this happens,\n // to allow the player to leave the lobby.\n setTimeout(() => {\n closeContainer.style.visibility = 'inherit';\n }, 10000);\n };\n\n /**\n * Create, display, and hide an error notification.\n */\n export const displayErrorNotification = function (\n runtimeScene: gdjs.RuntimeScene\n ) {\n showNotification(\n runtimeScene,\n 'An error occurred while displaying the game lobbies, please try again.',\n 'error'\n );\n };\n\n /**\n * Create, display, and hide a notification when a player leaves the game.\n */\n export const displayPlayerLeftNotification = function (\n runtimeScene: gdjs.RuntimeScene,\n playerName: string\n ) {\n showNotification(runtimeScene, `${playerName} left.`, 'warning');\n };\n\n /**\n * Create, display, and hide a notification when a player joins the game.\n */\n export const displayPlayerJoinedNotification = function (\n runtimeScene: gdjs.RuntimeScene,\n playerName: string\n ) {\n showNotification(runtimeScene, `${playerName} joined.`, 'success');\n };\n\n /**\n * Create, display, and hide a notification when an error happens on connection.\n */\n export const displayConnectionErrorNotification = function (\n runtimeScene: gdjs.RuntimeScene\n ) {\n showNotification(\n runtimeScene,\n 'Could not connect to other players.',\n 'error'\n );\n };\n\n const removeNotificationAndShiftOthers = function (\n notificationContainerId: string\n ) {\n const notification = document.getElementById(notificationContainerId);\n if (!notification) {\n logger.error('Notification not found.');\n return;\n }\n const index = notificationContainerIds.indexOf(notificationContainerId);\n if (index !== -1) {\n notificationContainerIds.splice(index, 1);\n }\n notification.remove();\n\n // Shift the other notifications up.\n for (let i = 0; i < notificationContainerIds.length; i++) {\n const notification = document.getElementById(\n notificationContainerIds[i]\n );\n if (!notification) {\n logger.error('Notification not found.');\n continue;\n }\n notification.style.top = `${12 + i * 32}px`;\n }\n };\n\n /**\n * Helper to show a notification to the user, that disappears automatically.\n */\n export const showNotification = function (\n runtimeScene: gdjs.RuntimeScene,\n content: string,\n type: 'success' | 'warning' | 'error'\n ) {\n // When we show a notification, we add it below the other ones.\n // We also remove the oldest one if there are too many > 5.\n if (notificationContainerIds.length > 5) {\n const oldestNotificationId = notificationContainerIds.shift();\n if (!oldestNotificationId) {\n logger.error('No oldest notification ID found.');\n return;\n }\n\n removeNotificationAndShiftOthers(oldestNotificationId);\n }\n\n // We generate a random ID for the notification, so they can stack.\n const id = `notification-${Math.random().toString(36).substring(7)}`;\n\n const domContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domContainer) {\n logger.error('No DOM element container found.');\n return;\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'\n ? '#0E062D'\n : type === 'warning'\n ? '#FFA500'\n : '#FF0000';\n // Space the notifications vertically, based on how many there are.\n divContainer.style.top = `${12 + notificationContainerIds.length * 32}px`;\n divContainer.style.right = '16px';\n divContainer.style.padding = '6px 32px 6px 6px';\n // Use zIndex 1 to make sure it is below the iframe.\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 domContainer.appendChild(divContainer);\n notificationContainerIds.push(id);\n\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 removeNotificationAndShiftOthers(id);\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
- "mappings": "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",
4
+ "sourcesContent": ["namespace gdjs {\n const logger = new gdjs.Logger('Multiplayer');\n export namespace multiplayerComponents {\n const lobbiesRootContainerId = 'lobbies-root-container';\n const lobbiesFrameContainerId = 'lobbies-frame-container';\n const lobbiesCloseContainerId = 'lobbies-close-container';\n const lobbiesLoaderContainerId = 'lobbies-loader-container';\n const lobbiesTextsContainerId = 'lobbies-texts-container';\n const lobbiesIframeContainerId = 'lobbies-iframe-container';\n const lobbiesIframeId = 'lobbies-iframe';\n\n let canLobbyBeClosed = true;\n\n const notificationContainerIds: string[] = [];\n\n export const getDomElementContainer = (\n runtimeScene: gdjs.RuntimeScene\n ): HTMLDivElement | null => {\n // Find the DOM element container.\n const domElementContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domElementContainer) {\n logger.error('No DOM element container found.');\n return null;\n }\n return domElementContainer;\n };\n\n export const getLobbiesRootContainer = (\n runtimeScene: gdjs.RuntimeScene\n ): HTMLDivElement | null => {\n const domElementContainer = getDomElementContainer(runtimeScene);\n if (!domElementContainer) {\n return null;\n }\n // Find the root container with its ID.\n const lobbiesRootContainer = domElementContainer.querySelector(\n `#${lobbiesRootContainerId}`\n ) as HTMLDivElement | null;\n return lobbiesRootContainer;\n };\n\n export const getLobbiesLoaderContainer = (\n runtimeScene: gdjs.RuntimeScene\n ): HTMLDivElement | null => {\n const domElementContainer = getDomElementContainer(runtimeScene);\n if (!domElementContainer) {\n return null;\n }\n\n // Find the loader container with its ID.\n const lobbiesLoaderContainer = domElementContainer.querySelector(\n `#${lobbiesLoaderContainerId}`\n ) as HTMLDivElement | null;\n return lobbiesLoaderContainer;\n };\n\n export const getLobbiesIframeContainer = (\n runtimeScene: gdjs.RuntimeScene\n ): HTMLDivElement | null => {\n const domElementContainer = getDomElementContainer(runtimeScene);\n if (!domElementContainer) {\n return null;\n }\n\n // Find the iframe container with its ID.\n const lobbiesIframeContainer = domElementContainer.querySelector(\n `#${lobbiesIframeContainerId}`\n ) as HTMLDivElement | null;\n return lobbiesIframeContainer;\n };\n\n export const getLobbiesCloseContainer = (\n runtimeScene: gdjs.RuntimeScene\n ): HTMLDivElement | null => {\n const domElementContainer = getDomElementContainer(runtimeScene);\n if (!domElementContainer) {\n return null;\n }\n\n // Find the close container with its ID.\n const lobbiesCloseContainer = domElementContainer.querySelector(\n `#${lobbiesCloseContainerId}`\n ) as HTMLDivElement | null;\n return lobbiesCloseContainer;\n };\n\n export const getLobbiesTextsContainer = (\n runtimeScene: gdjs.RuntimeScene\n ): HTMLDivElement | null => {\n const domElementContainer = getDomElementContainer(runtimeScene);\n if (!domElementContainer) {\n return null;\n }\n\n // Find the text container with its ID.\n const lobbiesTextContainer = domElementContainer.querySelector(\n `#${lobbiesTextsContainerId}`\n ) as HTMLDivElement | null;\n return lobbiesTextContainer;\n };\n\n export const getLobbiesIframe = (\n runtimeScene: gdjs.RuntimeScene\n ): HTMLIFrameElement | null => {\n const domElementContainer = getDomElementContainer(runtimeScene);\n if (!domElementContainer) {\n return null;\n }\n\n // Find the iframe with its ID.\n const lobbiesIframe = domElementContainer.querySelector(\n `#${lobbiesIframeId}`\n ) as HTMLIFrameElement | null;\n return lobbiesIframe;\n };\n\n /**\n * Creates a DOM element that will contain the loader or a message if the game is not registered,\n * and adds it to the dom container.\n */\n export const displayLobbies = function (\n runtimeScene: gdjs.RuntimeScene,\n onCloseLobbiesContainer: () => void\n ) {\n const domElementContainer = getDomElementContainer(runtimeScene);\n if (!domElementContainer) {\n return;\n }\n\n const rootContainer = document.createElement('div');\n rootContainer.id = lobbiesRootContainerId;\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 = lobbiesFrameContainerId;\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.overflow = 'hidden';\n\n const _closeContainer: HTMLDivElement = document.createElement('div');\n _closeContainer.id = lobbiesCloseContainerId;\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 _closeContainer.style.position = 'absolute';\n _closeContainer.style.top = '32px';\n _closeContainer.style.right = '32px';\n addTouchAndClickEventListeners(_closeContainer, onCloseLobbiesContainer);\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 if (!canLobbyBeClosed) {\n _closeContainer.style.visibility = 'hidden';\n }\n\n const loaderContainer: HTMLDivElement = document.createElement('div');\n loaderContainer.id = lobbiesLoaderContainerId;\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.id = lobbiesIframeContainerId;\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 // Display the lobbies window right away, to show a loader\n // while the call for game registration is happening.\n domElementContainer.appendChild(rootContainer);\n };\n\n export const displayIframeInsideLobbiesContainer = (\n runtimeScene: gdjs.RuntimeScene,\n url: string\n ) => {\n const iframeContainer = getLobbiesIframeContainer(runtimeScene);\n const loaderContainer = getLobbiesLoaderContainer(runtimeScene);\n const textsContainer = getLobbiesTextsContainer(runtimeScene);\n\n if (!iframeContainer || !loaderContainer || !textsContainer) {\n logger.error('Lobbies containers not found.');\n return;\n }\n\n const iframe = document.createElement('iframe');\n iframe.id = lobbiesIframeId;\n\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 addReloadUrlToTextsContainer(() => {\n // Try again.\n iframeContainer.removeChild(iframe);\n iframeContainer.style.display = 'none';\n loaderContainer.style.display = 'flex';\n displayIframeInsideLobbiesContainer(runtimeScene, url);\n }, textsContainer);\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 container\n * especially if the game is not registered.\n */\n export const addTextsToLoadingContainer = (\n runtimeScene: gdjs.RuntimeScene,\n isGameRegistered: boolean,\n wikiOpenAction: () => void\n ) => {\n const loaderContainer = getLobbiesLoaderContainer(runtimeScene);\n if (!loaderContainer) {\n logger.error('Loader container not found.');\n return;\n }\n\n const textsContainer: HTMLDivElement = document.createElement('div');\n textsContainer.id = lobbiesTextsContainerId;\n textsContainer.style.display = 'flex';\n textsContainer.style.flexDirection = 'column';\n textsContainer.style.width = '100%';\n textsContainer.style.justifyContent = 'center';\n textsContainer.style.alignItems = 'center';\n textsContainer.style.position = 'relative';\n textsContainer.style.zIndex = '3';\n textsContainer.style.fontSize = '11pt';\n textsContainer.style.fontFamily =\n '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\"';\n\n if (!isGameRegistered) {\n const title = document.createElement('h1');\n title.innerText = 'Publish your game!';\n title.style.fontSize = '20pt';\n title.style.fontWeight = 'bold';\n const text1 = document.createElement('p');\n text1.innerText =\n \"GDevelop's lobbies are only available for published games.\";\n const text2 = document.createElement('p');\n text2.innerText =\n 'Click the button below to learn how to publish your game then try again.';\n textsContainer.appendChild(title);\n textsContainer.appendChild(text1);\n textsContainer.appendChild(text2);\n\n // Remove the loader and add the wiki link.\n loaderContainer.innerHTML = '';\n\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 textsContainer.appendChild(link);\n }\n\n loaderContainer.prepend(textsContainer);\n };\n\n /**\n * Helper to add a reload link in case the window hasn't opened properly.\n */\n export const addReloadUrlToTextsContainer = (\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 * Hides the lobbies container.\n */\n export const removeLobbiesContainer = function (\n runtimeScene: gdjs.RuntimeScene\n ) {\n const rootContainer = getLobbiesRootContainer(runtimeScene);\n if (!rootContainer) {\n return;\n }\n\n rootContainer.remove();\n };\n\n export const changeLobbiesWindowCloseActionVisibility = function (\n runtimeScene: gdjs.RuntimeScene,\n canClose: boolean\n ) {\n canLobbyBeClosed = canClose;\n\n const closeContainer = getLobbiesCloseContainer(runtimeScene);\n if (!closeContainer) {\n return;\n }\n\n closeContainer.style.visibility = canClose ? 'inherit' : 'hidden';\n };\n\n export const hideLobbiesCloseButtonTemporarily = function (\n runtimeScene: gdjs.RuntimeScene\n ) {\n if (!canLobbyBeClosed) return;\n\n const closeContainer = getLobbiesCloseContainer(runtimeScene);\n if (!closeContainer) {\n return;\n }\n\n closeContainer.style.visibility = 'hidden';\n\n // There is a risk a player leaves the lobby before the end of the countdown,\n // so we show the close container again after 5 seconds in case this happens,\n // to allow the player to leave the lobby.\n setTimeout(() => {\n closeContainer.style.visibility = 'inherit';\n }, 10000);\n };\n\n /**\n * Create, display, and hide an error notification.\n */\n export const displayErrorNotification = function (\n runtimeScene: gdjs.RuntimeScene\n ) {\n showNotification({\n runtimeScene,\n content:\n 'An error occurred while displaying the game lobbies, please try again.',\n type: 'error',\n });\n };\n\n /**\n * Create, display, and hide a notification when a player leaves the game.\n */\n export const displayPlayerLeftNotification = function (\n runtimeScene: gdjs.RuntimeScene,\n playerName: string\n ) {\n showNotification({\n runtimeScene,\n content: `${playerName} left.`,\n type: 'warning',\n });\n };\n\n /**\n * Create, display, and hide a notification when a player joins the game.\n */\n export const displayPlayerJoinedNotification = function (\n runtimeScene: gdjs.RuntimeScene,\n playerName: string\n ) {\n showNotification({\n runtimeScene,\n content: `${playerName} joined.`,\n type: 'success',\n });\n };\n\n /**\n * Create, display, and hide a notification when an error happens on connection.\n */\n export const displayConnectionErrorNotification = function (\n runtimeScene: gdjs.RuntimeScene\n ) {\n showNotification({\n runtimeScene,\n content: 'Could not connect to other players.',\n type: 'error',\n });\n };\n\n /**\n * Create, display, and hide a notification when a player leaves the game.\n */\n export const displayHostMigrationNotification = function (\n runtimeScene: gdjs.RuntimeScene\n ) {\n showNotification({\n runtimeScene,\n content: `Migrating host...`,\n type: 'warning',\n id: 'migrating-host',\n persist: true,\n });\n };\n\n export const showHostMigrationFinishedNotification = function (\n runtimeScene: gdjs.RuntimeScene\n ) {\n removeNotificationAndShiftOthers('migrating-host');\n showNotification({\n runtimeScene,\n content: `Host migrated!`,\n type: 'success',\n });\n };\n\n export const showHostMigrationFailedNotification = function (\n runtimeScene: gdjs.RuntimeScene\n ) {\n removeNotificationAndShiftOthers('migrating-host');\n showNotification({\n runtimeScene,\n content: `Host migration failed.`,\n type: 'error',\n });\n };\n\n const removeNotificationAndShiftOthers = function (\n notificationContainerId: string\n ) {\n const notification = document.getElementById(notificationContainerId);\n if (!notification) {\n logger.warn(\n `Notification ${notificationContainerId} not found. skipping`\n );\n return;\n }\n const index = notificationContainerIds.indexOf(notificationContainerId);\n if (index !== -1) {\n notificationContainerIds.splice(index, 1);\n }\n notification.remove();\n\n // Shift the notifications that are below the one that was removed up.\n for (let i = index; i < notificationContainerIds.length; i++) {\n const notification = document.getElementById(\n notificationContainerIds[i]\n );\n if (!notification) {\n logger.error('Notification not found.');\n continue;\n }\n notification.style.top = `${12 + i * 32}px`;\n }\n };\n\n /**\n * Helper to show a notification to the user, that disappears automatically.\n */\n export const showNotification = function ({\n runtimeScene,\n content,\n type,\n id,\n persist,\n }: {\n runtimeScene: gdjs.RuntimeScene;\n content: string;\n type: 'success' | 'warning' | 'error';\n id?: string;\n persist?: boolean;\n }) {\n // When we show a notification, we add it below the other ones.\n // We also remove the oldest one if there are too many > 5.\n if (notificationContainerIds.length > 5) {\n const oldestNotificationId = notificationContainerIds.shift();\n if (!oldestNotificationId) {\n logger.error('No oldest notification ID found.');\n return;\n }\n\n removeNotificationAndShiftOthers(oldestNotificationId);\n }\n\n // We generate a random ID for the notification, so they can stack.\n const notificationId =\n id || `notification-${Math.random().toString(36).substring(7)}`;\n\n const domContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domContainer) {\n logger.error('No DOM element container found.');\n return;\n }\n const divContainer = document.createElement('div');\n\n divContainer.id = notificationId;\n divContainer.style.position = 'absolute';\n divContainer.style.pointerEvents = 'all';\n divContainer.style.backgroundColor =\n type === 'success'\n ? '#0E062D'\n : type === 'warning'\n ? '#FFA500'\n : '#FF0000';\n // Space the notifications vertically, based on how many there are.\n divContainer.style.top = `${12 + notificationContainerIds.length * 32}px`;\n divContainer.style.right = '16px';\n divContainer.style.padding = '6px 32px 6px 6px';\n // Use zIndex 1 to make sure it is below the iframe.\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 domContainer.appendChild(divContainer);\n notificationContainerIds.push(notificationId);\n\n if (persist) {\n return;\n }\n\n const animationTime = 700;\n const notificationTime = 3000;\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 removeNotificationAndShiftOthers(notificationId);\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
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,2 +1,2 @@
1
- var gdjs;(function(a){const d=new a.Logger("Multiplayer"),m=new a.Logger("Multiplayer - Debug"),y=window.performance&&typeof window.performance.now=="function"?window.performance.now.bind(window.performance):Date.now;class g extends a.RuntimeBehavior{constructor(t,n,e){super(t,n,e);this.playerNumber=0;this._lastObjectSyncTimestamp=0;this._objectMaxTickRate=60;this._lastBasicObjectSyncTimestamp=0;this._objectBasicInfoTickRate=5;this._numberOfForcedBasicObjectUpdates=0;this._lastVariablesSyncTimestamp=0;this._variablesTickRate=1;this._numberOfForcedVariablesUpdates=0;this._lastEffectsSyncTimestamp=0;this._effectsTickRate=1;this._numberOfForcedEffectsUpdates=0;this._lastLogTimestamp=0;this._logTickRate=1;this._clock=0;this._destroyInstanceTimeoutId=null;this._timeBeforeDestroyingObjectWithoutNetworkIdInMs=500;this._sendDataToPeersWithIncreasedClock=async(t,n)=>{this._clock++,n._clock=this._clock;const e=a.multiplayerPeerJsHelper.getAllPeers();await a.multiplayerMessageManager.sendDataTo(e,t,n)};this.playerNumber=n.playerNumber==="Host"?0:parseInt(n.playerNumber,10),this.actionOnPlayerDisconnect=n.actionOnPlayerDisconnect,this._destroyInstanceTimeoutId=setTimeout(()=>{const r=this.owner.getRuntimeScene().networkId;!e.networkId&&a.multiplayer.isLobbyGameRunning()&&r&&(m.info(`Lobby game is running on a synced scene and object ${e.getName()} has not been assigned a networkId after a short delay, destroying it.`),e.deleteFromScene(t))},this._timeBeforeDestroyingObjectWithoutNetworkIdInMs)}_isOwnerAsPlayerOrHost(){const t=a.multiplayer.getCurrentPlayerNumber();return t===this.playerNumber||t===1&&this.playerNumber===0}_hasObjectBeenSyncedWithinMaxRate(){return y()-this._lastObjectSyncTimestamp<1e3/this._objectMaxTickRate}_hasObjectBasicInfoBeenSyncedRecently(){return y()-this._lastBasicObjectSyncTimestamp<1e3/this._objectBasicInfoTickRate}_haveVariablesBeenSyncedRecently(){return y()-this._lastVariablesSyncTimestamp<1e3/this._variablesTickRate}_haveEffectsBeenSyncedRecently(){return y()-this._lastEffectsSyncTimestamp<1e3/this._effectsTickRate}_getOrCreateInstanceNetworkId(){if(!this.owner.networkId){const t=a.makeUuid().substring(0,8);this.owner.networkId=t}return this.owner.networkId}_isBasicObjectNetworkSyncDataDifferentFromLastSync(t){return this._lastSentBasicObjectSyncData?JSON.stringify(t)!==JSON.stringify(this._lastSentBasicObjectSyncData):!0}_areVariablesDifferentFromLastSync(t){return this._lastSentVariableSyncData?JSON.stringify(t)!==JSON.stringify(this._lastSentVariableSyncData):!0}_areEffectsDifferentFromLastSync(t){if(!this._lastSentEffectSyncData)return!0;for(const n in t){if(!t.hasOwnProperty(n))continue;const e=t[n],r=e.ena,i=e.fc,c=this._lastSentEffectSyncData[n];if(!c||c.ena!==r)return!0;for(const s in i){if(!i.hasOwnProperty(s))continue;const o=i[s];if(c.fc[s]!==o)return!0}}return!1}doStepPostEvents(){if(!a.multiplayer.isLobbyGameRunning())return;if(this.playerNumber!==0&&!a.multiplayerMessageManager.isPlayerConnected(this.playerNumber)){m.info(`Player number ${this.playerNumber} does not exist in the lobby at the moment. Destroying the object.`),this.owner.deleteFromScene(this.owner.getInstanceContainer());return}if(!this._isOwnerAsPlayerOrHost()||this._hasObjectBeenSyncedWithinMaxRate())return;const t=this._getOrCreateInstanceNetworkId(),n=this.owner.getName(),e=this.owner.getNetworkSyncData(),r=this._isBasicObjectNetworkSyncDataDifferentFromLastSync({x:e.x,y:e.y,z:e.z,zo:e.zo,a:e.a,hid:e.hid,lay:e.lay,if:e.if,pfx:e.pfx,pfy:e.pfy}),i=!this._hasObjectBasicInfoBeenSyncedRecently()||r||this._numberOfForcedBasicObjectUpdates>0;if(r&&(this._numberOfForcedBasicObjectUpdates=3),!i)return;const c=e.var&&this._areVariablesDifferentFromLastSync(e.var),s=!this._haveVariablesBeenSyncedRecently()||c||this._numberOfForcedVariablesUpdates>0;c&&(this._numberOfForcedVariablesUpdates=3),s||delete e.var;const o=e.eff&&this._areEffectsDifferentFromLastSync(e.eff),l=!this._haveEffectsBeenSyncedRecently()||o||this._numberOfForcedEffectsUpdates>0;o&&(this._numberOfForcedEffectsUpdates=3),l||delete e.eff;const f=this.owner.getRuntimeScene().networkId;if(!f)return;const{messageName:h,messageData:b}=a.multiplayerMessageManager.createUpdateInstanceMessage({objectOwner:this.playerNumber,objectName:n,instanceNetworkId:t,objectNetworkSyncData:e,sceneNetworkId:f});this._sendDataToPeersWithIncreasedClock(h,b);const u=y();this._lastObjectSyncTimestamp=u,i&&(this._lastBasicObjectSyncTimestamp=u,this._lastSentBasicObjectSyncData={x:e.x,y:e.y,zo:e.zo,a:e.a,hid:e.hid,lay:e.lay,if:e.if,pfx:e.pfx,pfy:e.pfy},this._numberOfForcedBasicObjectUpdates=Math.max(this._numberOfForcedBasicObjectUpdates-1,0)),s&&(this._lastVariablesSyncTimestamp=u,this._lastSentVariableSyncData=e.var,this._numberOfForcedVariablesUpdates=Math.max(this._numberOfForcedVariablesUpdates-1,0)),l&&(this._lastEffectsSyncTimestamp=u,this._lastSentEffectSyncData=e.eff,this._numberOfForcedEffectsUpdates=Math.max(this._numberOfForcedEffectsUpdates-1,0))}onDestroy(){if(this._destroyInstanceTimeoutId&&(clearTimeout(this._destroyInstanceTimeoutId),this._destroyInstanceTimeoutId=null),!a.multiplayer.isLobbyGameRunning()||!this._isOwnerAsPlayerOrHost()&&!a.multiplayer.isPlayerHost())return;const t=this.owner.networkId,n=this.owner.getName();if(!t){m.info(`Destroying object ${n} without networkId, no need to send a message.`);return}const e=this.owner.getRuntimeScene().networkId;if(!e)return;const{messageName:r,messageData:i}=a.multiplayerMessageManager.createUpdateInstanceMessage({objectOwner:this.playerNumber,objectName:n,instanceNetworkId:t,objectNetworkSyncData:this.owner.getNetworkSyncData(),sceneNetworkId:e});this._sendDataToPeersWithIncreasedClock(r,i);const c=a.multiplayerPeerJsHelper.getAllPeers(),{messageName:s,messageData:o}=a.multiplayerMessageManager.createDestroyInstanceMessage({objectOwner:this.playerNumber,objectName:n,instanceNetworkId:t,sceneNetworkId:e}),l=a.multiplayerMessageManager.createInstanceDestroyedMessageNameFromDestroyInstanceMessage(s);a.multiplayerMessageManager.addExpectedMessageAcknowledgement({originalMessageName:s,originalData:{...o,_clock:this._clock+1},expectedMessageName:l,otherPeerIds:c,shouldCancelMessageIfTimesOut:!1}),this._sendDataToPeersWithIncreasedClock(s,o)}setPlayerObjectOwnership(t){if(m.info(`Setting ownership of object ${this.owner.getName()} (networkId: ${this.owner.networkId} to player ${t}.`),t<0){d.error("Invalid player number ("+t+") when setting ownership of an object.");return}const n=this.playerNumber;this.playerNumber=t;const e=a.multiplayer.getCurrentPlayerNumber();if(!a.multiplayer.isLobbyGameRunning())return;let r=this.owner.networkId;if(!r){if(m.info("Object has no networkId, we change the ownership locally, but it will not be synchronized yet if we are not the owner."),t!==e)return;m.info("We are the new owner, creating a networkId for the object."),r=this._getOrCreateInstanceNetworkId()}const i=this.owner.getRuntimeScene().networkId;if(!i)return;const c=this.owner.getName(),{messageName:s,messageData:o}=a.multiplayerMessageManager.createChangeInstanceOwnerMessage({objectOwner:n,objectName:c,instanceNetworkId:r,newObjectOwner:t,instanceX:this.owner.getX(),instanceY:this.owner.getY(),sceneNetworkId:i});if(t===e){const l=a.multiplayerPeerJsHelper.getAllPeers(),f=a.multiplayerMessageManager.createInstanceOwnerChangedMessageNameFromChangeInstanceOwnerMessage(s);a.multiplayerMessageManager.addExpectedMessageAcknowledgement({originalMessageName:s,originalData:{...o,_clock:this._clock+1},expectedMessageName:f,otherPeerIds:l,shouldCancelMessageIfTimesOut:e!==1})}if(m.info("Sending change owner message",s),this._sendDataToPeersWithIncreasedClock(s,o),t===e){m.info("Sending update message to move the object immediately.");const l=this.owner.getNetworkSyncData(),{messageName:f,messageData:h}=a.multiplayerMessageManager.createUpdateInstanceMessage({objectOwner:this.playerNumber,objectName:c,instanceNetworkId:r,objectNetworkSyncData:l,sceneNetworkId:i});this._sendDataToPeersWithIncreasedClock(f,h)}}getPlayerObjectOwnership(){return this.playerNumber}isObjectOwnedByCurrentPlayer(){return this._isOwnerAsPlayerOrHost()}removeObjectOwnership(){this.setPlayerObjectOwnership(0)}takeObjectOwnership(){this.setPlayerObjectOwnership(a.multiplayer.getCurrentPlayerNumber())}getActionOnPlayerDisconnect(){return this.actionOnPlayerDisconnect}enableBehaviorSynchronization(t,n){const e=this.owner.getBehavior(t);if(!e){d.error(`Behavior ${t} does not exist on object ${this.owner.getName()}.`);return}e.enableSynchronization(n)}}a.MultiplayerObjectRuntimeBehavior=g,a.registerBehavior("Multiplayer::MultiplayerObjectBehavior",a.MultiplayerObjectRuntimeBehavior)})(gdjs||(gdjs={}));
1
+ var gdjs;(function(a){const d=new a.Logger("Multiplayer"),m=new a.Logger("Multiplayer - Debug"),f=window.performance&&typeof window.performance.now=="function"?window.performance.now.bind(window.performance):Date.now;class g extends a.RuntimeBehavior{constructor(t,n,e){super(t,n,e);this.playerNumber=0;this._lastObjectSyncTimestamp=0;this._lastBasicObjectSyncTimestamp=0;this._objectBasicInfoSyncRate=5;this._numberOfForcedBasicObjectUpdates=0;this._lastVariablesSyncTimestamp=0;this._variablesSyncRate=1;this._numberOfForcedVariablesUpdates=0;this._lastEffectsSyncTimestamp=0;this._effectsSyncRate=1;this._numberOfForcedEffectsUpdates=0;this._lastLogTimestamp=0;this._logSyncRate=1;this._clock=0;this._destroyInstanceTimeoutId=null;this._timeBeforeDestroyingObjectWithoutNetworkIdInMs=500;this._sendDataToPeersWithIncreasedClock=async(t,n)=>{this._clock++,n._clock=this._clock;const e=a.multiplayerPeerJsHelper.getAllPeers();await a.multiplayerMessageManager.sendDataTo(e,t,n)};this.playerNumber=n.playerNumber==="Host"?0:parseInt(n.playerNumber,10),this.actionOnPlayerDisconnect=n.actionOnPlayerDisconnect,this._destroyInstanceTimeoutId=setTimeout(()=>{const r=this.owner.getRuntimeScene().networkId;!e.networkId&&a.multiplayer.isLobbyGameRunning()&&r&&(m.info(`Lobby game is running on a synced scene and object ${e.getName()} has not been assigned a networkId after a short delay, destroying it.`),e.deleteFromScene(t))},this._timeBeforeDestroyingObjectWithoutNetworkIdInMs)}_isOwnerAsPlayerOrHost(){const t=a.multiplayer.getCurrentPlayerNumber(),n=a.multiplayer.isCurrentPlayerHost();return t===this.playerNumber||n&&this.playerNumber===0}_hasObjectBeenSyncedWithinMaxRate(){const t=a.multiplayer.getObjectsSynchronizationRate();return f()-this._lastObjectSyncTimestamp<1e3/t}_hasObjectBasicInfoBeenSyncedRecently(){return f()-this._lastBasicObjectSyncTimestamp<1e3/this._objectBasicInfoSyncRate}_haveVariablesBeenSyncedRecently(){return f()-this._lastVariablesSyncTimestamp<1e3/this._variablesSyncRate}_haveEffectsBeenSyncedRecently(){return f()-this._lastEffectsSyncTimestamp<1e3/this._effectsSyncRate}_getOrCreateInstanceNetworkId(){if(!this.owner.networkId){const t=a.makeUuid().substring(0,8);this.owner.networkId=t}return this.owner.networkId}_isBasicObjectNetworkSyncDataDifferentFromLastSync(t){return this._lastSentBasicObjectSyncData?JSON.stringify(t)!==JSON.stringify(this._lastSentBasicObjectSyncData):!0}_areVariablesDifferentFromLastSync(t){return this._lastSentVariableSyncData?JSON.stringify(t)!==JSON.stringify(this._lastSentVariableSyncData):!0}_areEffectsDifferentFromLastSync(t){if(!this._lastSentEffectSyncData)return!0;for(const n in t){if(!t.hasOwnProperty(n))continue;const e=t[n],r=e.ena,i=e.fc,c=this._lastSentEffectSyncData[n];if(!c||c.ena!==r)return!0;for(const s in i){if(!i.hasOwnProperty(s))continue;const o=i[s];if(c.fc[s]!==o)return!0}}return!1}doStepPostEvents(){if(!a.multiplayer.isLobbyGameRunning())return;if(this.playerNumber!==0&&!a.multiplayerMessageManager.isPlayerConnected(this.playerNumber)){m.info(`Player number ${this.playerNumber} does not exist in the lobby at the moment. Destroying the object.`),this.owner.deleteFromScene(this.owner.getInstanceContainer());return}if(!this._isOwnerAsPlayerOrHost()||this._hasObjectBeenSyncedWithinMaxRate())return;const t=this._getOrCreateInstanceNetworkId(),n=this.owner.getName(),e=this.owner.getNetworkSyncData(),r=this._isBasicObjectNetworkSyncDataDifferentFromLastSync({x:e.x,y:e.y,z:e.z,zo:e.zo,a:e.a,hid:e.hid,lay:e.lay,if:e.if,pfx:e.pfx,pfy:e.pfy}),i=!this._hasObjectBasicInfoBeenSyncedRecently()||r||this._numberOfForcedBasicObjectUpdates>0;if(r&&(this._numberOfForcedBasicObjectUpdates=3),!i)return;const c=e.var&&this._areVariablesDifferentFromLastSync(e.var),s=!this._haveVariablesBeenSyncedRecently()||c||this._numberOfForcedVariablesUpdates>0;c&&(this._numberOfForcedVariablesUpdates=3),s||delete e.var;const o=e.eff&&this._areEffectsDifferentFromLastSync(e.eff),l=!this._haveEffectsBeenSyncedRecently()||o||this._numberOfForcedEffectsUpdates>0;o&&(this._numberOfForcedEffectsUpdates=3),l||delete e.eff;const y=this.owner.getRuntimeScene().networkId;if(!y)return;const{messageName:h,messageData:b}=a.multiplayerMessageManager.createUpdateInstanceMessage({objectOwner:this.playerNumber,objectName:n,instanceNetworkId:t,objectNetworkSyncData:e,sceneNetworkId:y});this._sendDataToPeersWithIncreasedClock(h,b);const u=f();this._lastObjectSyncTimestamp=u,i&&(this._lastBasicObjectSyncTimestamp=u,this._lastSentBasicObjectSyncData={x:e.x,y:e.y,zo:e.zo,a:e.a,hid:e.hid,lay:e.lay,if:e.if,pfx:e.pfx,pfy:e.pfy},this._numberOfForcedBasicObjectUpdates=Math.max(this._numberOfForcedBasicObjectUpdates-1,0)),s&&(this._lastVariablesSyncTimestamp=u,this._lastSentVariableSyncData=e.var,this._numberOfForcedVariablesUpdates=Math.max(this._numberOfForcedVariablesUpdates-1,0)),l&&(this._lastEffectsSyncTimestamp=u,this._lastSentEffectSyncData=e.eff,this._numberOfForcedEffectsUpdates=Math.max(this._numberOfForcedEffectsUpdates-1,0))}onDestroy(){if(this._destroyInstanceTimeoutId&&(clearTimeout(this._destroyInstanceTimeoutId),this._destroyInstanceTimeoutId=null),!a.multiplayer.isLobbyGameRunning()||!this._isOwnerAsPlayerOrHost()&&!a.multiplayer.isCurrentPlayerHost())return;const t=this.owner.networkId,n=this.owner.getName();if(!t){m.info(`Destroying object ${n} without networkId, no need to send a message.`);return}const e=this.owner.getRuntimeScene().networkId;if(!e)return;const{messageName:r,messageData:i}=a.multiplayerMessageManager.createUpdateInstanceMessage({objectOwner:this.playerNumber,objectName:n,instanceNetworkId:t,objectNetworkSyncData:this.owner.getNetworkSyncData(),sceneNetworkId:e});this._sendDataToPeersWithIncreasedClock(r,i);const c=a.multiplayerPeerJsHelper.getAllPeers(),{messageName:s,messageData:o}=a.multiplayerMessageManager.createDestroyInstanceMessage({objectOwner:this.playerNumber,objectName:n,instanceNetworkId:t,sceneNetworkId:e}),l=a.multiplayerMessageManager.createInstanceDestroyedMessageNameFromDestroyInstanceMessage(s);a.multiplayerMessageManager.addExpectedMessageAcknowledgement({originalMessageName:s,originalData:{...o,_clock:this._clock+1},expectedMessageName:l,otherPeerIds:c,shouldCancelMessageIfTimesOut:!1}),this._sendDataToPeersWithIncreasedClock(s,o)}setPlayerObjectOwnership(t){if(m.info(`Setting ownership of object ${this.owner.getName()} (networkId: ${this.owner.networkId} to player ${t}.`),t<0){d.error("Invalid player number ("+t+") when setting ownership of an object.");return}const n=this.playerNumber;this.playerNumber=t;const e=a.multiplayer.getCurrentPlayerNumber();if(!a.multiplayer.isLobbyGameRunning())return;let r=this.owner.networkId;if(!r){if(m.info("Object has no networkId, we change the ownership locally, but it will not be synchronized yet if we are not the owner."),t!==e)return;m.info("We are the new owner, creating a networkId for the object."),r=this._getOrCreateInstanceNetworkId()}const i=this.owner.getRuntimeScene().networkId;if(!i)return;const c=this.owner.getName(),{messageName:s,messageData:o}=a.multiplayerMessageManager.createChangeInstanceOwnerMessage({objectOwner:n,objectName:c,instanceNetworkId:r,newObjectOwner:t,instanceX:this.owner.getX(),instanceY:this.owner.getY(),sceneNetworkId:i});if(t===e){const l=a.multiplayerPeerJsHelper.getAllPeers(),y=a.multiplayerMessageManager.createInstanceOwnerChangedMessageNameFromChangeInstanceOwnerMessage(s);a.multiplayerMessageManager.addExpectedMessageAcknowledgement({originalMessageName:s,originalData:{...o,_clock:this._clock+1},expectedMessageName:y,otherPeerIds:l,shouldCancelMessageIfTimesOut:!a.multiplayer.isCurrentPlayerHost()})}if(m.info("Sending change owner message",s),this._sendDataToPeersWithIncreasedClock(s,o),t===e){m.info("Sending update message to move the object immediately.");const l=this.owner.getNetworkSyncData(),{messageName:y,messageData:h}=a.multiplayerMessageManager.createUpdateInstanceMessage({objectOwner:this.playerNumber,objectName:c,instanceNetworkId:r,objectNetworkSyncData:l,sceneNetworkId:i});this._sendDataToPeersWithIncreasedClock(y,h)}}getPlayerObjectOwnership(){return this.playerNumber}isObjectOwnedByCurrentPlayer(){return this._isOwnerAsPlayerOrHost()}removeObjectOwnership(){this.setPlayerObjectOwnership(0)}takeObjectOwnership(){this.setPlayerObjectOwnership(a.multiplayer.getCurrentPlayerNumber())}getActionOnPlayerDisconnect(){return this.actionOnPlayerDisconnect}enableBehaviorSynchronization(t,n){const e=this.owner.getBehavior(t);if(!e){d.error(`Behavior ${t} does not exist on object ${this.owner.getName()}.`);return}e.enableSynchronization(n)}}a.MultiplayerObjectRuntimeBehavior=g,a.registerBehavior("Multiplayer::MultiplayerObjectBehavior",a.MultiplayerObjectRuntimeBehavior)})(gdjs||(gdjs={}));
2
2
  //# sourceMappingURL=multiplayerobjectruntimebehavior.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../GDevelop/Extensions/Multiplayer/multiplayerobjectruntimebehavior.ts"],
4
- "sourcesContent": ["/*\n GDevelop - Multiplayer Object Behavior Extension\n Copyright (c) 2013-2016 Florian Rival (Florian.Rival@gmail.com)\n*/\n\nnamespace gdjs {\n const logger = new gdjs.Logger('Multiplayer');\n const debugLogger = new gdjs.Logger('Multiplayer - Debug');\n const getTimeNow =\n window.performance && typeof window.performance.now === 'function'\n ? window.performance.now.bind(window.performance)\n : Date.now;\n\n /**\n * The MultiplayerObjectRuntimeBehavior represents a behavior that can be added to objects\n * to make them synchronized over the network.\n */\n export class MultiplayerObjectRuntimeBehavior extends gdjs.RuntimeBehavior {\n // Which player is the owner of the object.\n // If 0, then the object is not owned by any player, so the host is the owner.\n playerNumber: number = 0;\n\n // The action to be executed when the player disconnects.\n actionOnPlayerDisconnect: string;\n\n // The last time the object has been synchronized.\n // This is to avoid synchronizing the object too often, see _objectMaxTickRate.\n _lastObjectSyncTimestamp: number = 0;\n // The number of times per second the object should be synchronized if it keeps changing.\n _objectMaxTickRate: number = 60;\n\n // The last time the basic object info has been synchronized.\n _lastBasicObjectSyncTimestamp: number = 0;\n // The number of times per second the object basic info should be synchronized when it doesn't change.\n _objectBasicInfoTickRate: number = 5;\n // The last data sent to synchronize the basic info of the object.\n _lastSentBasicObjectSyncData: BasicObjectNetworkSyncData | undefined;\n // When we know that the basic info of the object has been updated, we can force sending them\n // on the max tickrate for a number of times to ensure they are received, without the need of an acknowledgment.\n _numberOfForcedBasicObjectUpdates: number = 0;\n\n // The last time the variables have been synchronized.\n _lastVariablesSyncTimestamp: number = 0;\n // The number of times per second the variables should be synchronized.\n _variablesTickRate: number = 1;\n // The last data sent to synchronize the variables.\n _lastSentVariableSyncData: VariableNetworkSyncData[] | undefined;\n // When we know that the variables have been updated, we can force sending them\n // on the same tickrate as the object update for a number of times\n // to ensure they are received, without the need of an acknowledgment.\n _numberOfForcedVariablesUpdates: number = 0;\n\n // The last time the effects have been synchronized.\n _lastEffectsSyncTimestamp: number = 0;\n // The number of times per second the effects should be synchronized.\n _effectsTickRate: number = 1;\n // The last data sent to synchronize the effects.\n _lastSentEffectSyncData:\n | { [effectName: string]: EffectNetworkSyncData }\n | undefined;\n // When we know that the effects have been updated, we can force sending them\n // on the same tickrate as the object update for a number of times\n // to ensure they are received, without the need of an acknowledgment.\n _numberOfForcedEffectsUpdates: number = 0;\n\n // To avoid seeing too many logs.\n _lastLogTimestamp: number = 0;\n _logTickRate: number = 1;\n // Clock to be incremented every time we send a message, to ensure they are ordered\n // and old messages are ignored.\n _clock: number = 0;\n _destroyInstanceTimeoutId: NodeJS.Timeout | null = null;\n _timeBeforeDestroyingObjectWithoutNetworkIdInMs = 500;\n\n constructor(\n instanceContainer: gdjs.RuntimeInstanceContainer,\n behaviorData,\n owner: RuntimeObject\n ) {\n super(instanceContainer, behaviorData, owner);\n this.playerNumber =\n behaviorData.playerNumber === 'Host'\n ? 0\n : parseInt(behaviorData.playerNumber, 10);\n this.actionOnPlayerDisconnect = behaviorData.actionOnPlayerDisconnect;\n\n // When a synchronized object is created, we assume it will be assigned a networkId quickly if:\n // - It is a new object created by the current player. -> will be assigned a networkId when sending the update message.\n // - It is an object created by another player. -> will be assigned a networkId when receiving the update message.\n // There is a small risk that the object is created by us after we receive an update message from the host,\n // ending up with 2 objects created, one with a networkId (from the host) and one without (from us).\n // To handle this case and avoid having an object not synchronized, we set a timeout to destroy the object\n // if it has not been assigned a networkId after a short delay.\n this._destroyInstanceTimeoutId = setTimeout(() => {\n const sceneNetworkId = this.owner.getRuntimeScene().networkId;\n if (\n !owner.networkId &&\n gdjs.multiplayer.isLobbyGameRunning() &&\n sceneNetworkId\n ) {\n debugLogger.info(\n `Lobby game is running on a synced scene and object ${owner.getName()} has not been assigned a networkId after a short delay, destroying it.`\n );\n owner.deleteFromScene(instanceContainer);\n }\n }, this._timeBeforeDestroyingObjectWithoutNetworkIdInMs);\n }\n\n private _sendDataToPeersWithIncreasedClock = async (\n messageName: string,\n data: Object\n ) => {\n this._clock++;\n data['_clock'] = this._clock;\n const connectedPeerIds = gdjs.multiplayerPeerJsHelper.getAllPeers();\n await gdjs.multiplayerMessageManager.sendDataTo(\n connectedPeerIds,\n messageName,\n data\n );\n };\n\n private _isOwnerAsPlayerOrHost() {\n const currentPlayerNumber = gdjs.multiplayer.getCurrentPlayerNumber();\n\n const isOwnerOfObject =\n currentPlayerNumber === this.playerNumber || // Player as owner.\n (currentPlayerNumber === 1 && this.playerNumber === 0); // Host as owner.\n\n return isOwnerOfObject;\n }\n\n private _hasObjectBeenSyncedWithinMaxRate() {\n return (\n getTimeNow() - this._lastObjectSyncTimestamp <\n 1000 / this._objectMaxTickRate\n );\n }\n\n private _hasObjectBasicInfoBeenSyncedRecently() {\n return (\n getTimeNow() - this._lastBasicObjectSyncTimestamp <\n 1000 / this._objectBasicInfoTickRate\n );\n }\n\n private _haveVariablesBeenSyncedRecently() {\n return (\n getTimeNow() - this._lastVariablesSyncTimestamp <\n 1000 / this._variablesTickRate\n );\n }\n\n private _haveEffectsBeenSyncedRecently() {\n return (\n getTimeNow() - this._lastEffectsSyncTimestamp <\n 1000 / this._effectsTickRate\n );\n }\n\n // private _logToConsoleWithThrottle(message: string) {\n // if (getTimeNow() - this._lastLogTimestamp > 1000 / this._logTickRate) {\n // logger.info(message);\n // this._lastLogTimestamp = getTimeNow();\n // }\n // }\n\n private _getOrCreateInstanceNetworkId() {\n if (!this.owner.networkId) {\n // No ID for this object, let's generate one so it can be identified by other players.\n // Keep it short to avoid sending too much data.\n const newID = gdjs.makeUuid().substring(0, 8);\n this.owner.networkId = newID;\n }\n\n return this.owner.networkId;\n }\n\n private _isBasicObjectNetworkSyncDataDifferentFromLastSync(\n basicObjectNetworkSyncData: BasicObjectNetworkSyncData\n ) {\n if (!this._lastSentBasicObjectSyncData) {\n return true;\n }\n\n // Compare the json of the basicObjectNetworkSyncData to check if they are different.\n // This is not the most efficient way to do it, but it's simple and should work.\n const haveBasicObjectNetworkSyncDataChanged =\n JSON.stringify(basicObjectNetworkSyncData) !==\n JSON.stringify(this._lastSentBasicObjectSyncData);\n\n return haveBasicObjectNetworkSyncDataChanged;\n }\n\n private _areVariablesDifferentFromLastSync(\n variablesSyncData: VariableNetworkSyncData[]\n ) {\n if (!this._lastSentVariableSyncData) {\n return true;\n }\n\n // Compare the json of the variables to check if they are different.\n // This is not the most efficient way to do it, but it's simple and should work.\n const haveVariableSyncDataChanged =\n JSON.stringify(variablesSyncData) !==\n JSON.stringify(this._lastSentVariableSyncData);\n\n return haveVariableSyncDataChanged;\n }\n\n private _areEffectsDifferentFromLastSync(effectsSyncData: {\n [effectName: string]: EffectNetworkSyncData;\n }) {\n if (!this._lastSentEffectSyncData) {\n return true;\n }\n\n for (const effectName in effectsSyncData) {\n if (!effectsSyncData.hasOwnProperty(effectName)) {\n continue;\n }\n\n const effectSyncData = effectsSyncData[effectName];\n const effectEnabled = effectSyncData.ena;\n const effectFilterCreator = effectSyncData.fc;\n\n const effectInLastSync = this._lastSentEffectSyncData[effectName];\n\n if (!effectInLastSync || effectInLastSync.ena !== effectEnabled) {\n return true;\n }\n\n for (const parameterName in effectFilterCreator) {\n if (!effectFilterCreator.hasOwnProperty(parameterName)) {\n continue;\n }\n\n const parameterValue = effectFilterCreator[parameterName];\n const lastParameterValueSent = effectInLastSync.fc[parameterName];\n if (lastParameterValueSent !== parameterValue) {\n return true;\n }\n }\n }\n\n return false;\n }\n\n doStepPostEvents() {\n // Before doing anything, check if the game is running, if not, return.\n if (!gdjs.multiplayer.isLobbyGameRunning()) {\n return;\n }\n\n // If game is running and the object belongs to a player who is not connected, destroy the object.\n // As the game may create objects before the lobby game starts, we don't want to destroy them if it's not running.\n if (\n this.playerNumber !== 0 && // Host is always connected.\n !gdjs.multiplayerMessageManager.isPlayerConnected(this.playerNumber)\n ) {\n debugLogger.info(\n `Player number ${this.playerNumber} does not exist in the lobby at the moment. Destroying the object.`\n );\n this.owner.deleteFromScene(this.owner.getInstanceContainer());\n return;\n }\n\n if (!this._isOwnerAsPlayerOrHost()) {\n return;\n }\n\n // If the object has been synchronized recently at the max rate, then return.\n // This is to avoid sending data on every frame, which would be too much.\n if (this._hasObjectBeenSyncedWithinMaxRate()) {\n return;\n }\n\n const instanceNetworkId = this._getOrCreateInstanceNetworkId();\n const objectName = this.owner.getName();\n const objectNetworkSyncData = this.owner.getNetworkSyncData();\n\n // this._logToConsoleWithThrottle(\n // `Synchronizing object ${this.owner.getName()} (instance ${\n // this.owner.networkId\n // }) with player ${this.playerNumber} and data ${JSON.stringify(\n // objectNetworkSyncData\n // )}`\n // );\n\n const areBasicObjectNetworkSyncDataDifferent = this._isBasicObjectNetworkSyncDataDifferentFromLastSync(\n {\n x: objectNetworkSyncData.x,\n y: objectNetworkSyncData.y,\n z: objectNetworkSyncData.z,\n zo: objectNetworkSyncData.zo,\n a: objectNetworkSyncData.a,\n hid: objectNetworkSyncData.hid,\n lay: objectNetworkSyncData.lay,\n if: objectNetworkSyncData.if,\n pfx: objectNetworkSyncData.pfx,\n pfy: objectNetworkSyncData.pfy,\n }\n );\n const shouldSyncObjectBasicInfo =\n !this._hasObjectBasicInfoBeenSyncedRecently() ||\n areBasicObjectNetworkSyncDataDifferent ||\n this._numberOfForcedBasicObjectUpdates > 0;\n if (areBasicObjectNetworkSyncDataDifferent) {\n this._numberOfForcedBasicObjectUpdates = 3;\n }\n if (!shouldSyncObjectBasicInfo) {\n // If the basic info has not changed, assume we don't need to sync the whole object data at a high rate.\n // TODO: allow sending the variables, behaviors and effects still?\n return;\n }\n\n const areVariablesDifferent =\n objectNetworkSyncData.var &&\n this._areVariablesDifferentFromLastSync(objectNetworkSyncData.var);\n const shouldSyncVariables =\n !this._haveVariablesBeenSyncedRecently() ||\n areVariablesDifferent ||\n this._numberOfForcedVariablesUpdates > 0;\n if (areVariablesDifferent) {\n this._numberOfForcedVariablesUpdates = 3;\n }\n if (!shouldSyncVariables) {\n delete objectNetworkSyncData.var;\n }\n\n const areEffectsDifferent =\n objectNetworkSyncData.eff &&\n this._areEffectsDifferentFromLastSync(objectNetworkSyncData.eff);\n const shoundSyncEffects =\n !this._haveEffectsBeenSyncedRecently() ||\n areEffectsDifferent ||\n this._numberOfForcedEffectsUpdates > 0;\n if (areEffectsDifferent) {\n this._numberOfForcedEffectsUpdates = 3;\n }\n if (!shoundSyncEffects) {\n delete objectNetworkSyncData.eff;\n }\n\n const sceneNetworkId = this.owner.getRuntimeScene().networkId;\n if (!sceneNetworkId) {\n // No networkId for the scene yet, it will be set soon, let's not sync the object yet.\n return;\n }\n\n const {\n messageName: updateMessageName,\n messageData: updateMessageData,\n } = gdjs.multiplayerMessageManager.createUpdateInstanceMessage({\n objectOwner: this.playerNumber,\n objectName,\n instanceNetworkId,\n objectNetworkSyncData,\n sceneNetworkId,\n });\n this._sendDataToPeersWithIncreasedClock(\n updateMessageName,\n updateMessageData\n );\n\n const now = getTimeNow();\n\n this._lastObjectSyncTimestamp = now;\n if (shouldSyncObjectBasicInfo) {\n this._lastBasicObjectSyncTimestamp = now;\n this._lastSentBasicObjectSyncData = {\n x: objectNetworkSyncData.x,\n y: objectNetworkSyncData.y,\n zo: objectNetworkSyncData.zo,\n a: objectNetworkSyncData.a,\n hid: objectNetworkSyncData.hid,\n lay: objectNetworkSyncData.lay,\n if: objectNetworkSyncData.if,\n pfx: objectNetworkSyncData.pfx,\n pfy: objectNetworkSyncData.pfy,\n };\n this._numberOfForcedBasicObjectUpdates = Math.max(\n this._numberOfForcedBasicObjectUpdates - 1,\n 0\n );\n }\n if (shouldSyncVariables) {\n this._lastVariablesSyncTimestamp = now;\n this._lastSentVariableSyncData = objectNetworkSyncData.var;\n this._numberOfForcedVariablesUpdates = Math.max(\n this._numberOfForcedVariablesUpdates - 1,\n 0\n );\n }\n if (shoundSyncEffects) {\n this._lastEffectsSyncTimestamp = now;\n this._lastSentEffectSyncData = objectNetworkSyncData.eff;\n this._numberOfForcedEffectsUpdates = Math.max(\n this._numberOfForcedEffectsUpdates - 1,\n 0\n );\n }\n }\n\n onDestroy() {\n if (this._destroyInstanceTimeoutId) {\n clearTimeout(this._destroyInstanceTimeoutId);\n this._destroyInstanceTimeoutId = null;\n }\n\n // If the lobby game is not running, no need to send a message to destroy the object.\n if (!gdjs.multiplayer.isLobbyGameRunning()) {\n return;\n }\n\n // For destruction of objects, we allow the host to destroy the object even if it is not the owner.\n // This is particularly helpful when a player disconnects, so the host can destroy the object they were owning.\n if (!this._isOwnerAsPlayerOrHost() && !gdjs.multiplayer.isPlayerHost()) {\n return;\n }\n\n const instanceNetworkId = this.owner.networkId;\n const objectName = this.owner.getName();\n\n // If it had no networkId, then it was not synchronized and we don't need to send a message.\n if (!instanceNetworkId) {\n debugLogger.info(\n `Destroying object ${objectName} without networkId, no need to send a message.`\n );\n return;\n }\n\n const sceneNetworkId = this.owner.getRuntimeScene().networkId;\n if (!sceneNetworkId) {\n // No networkId for the scene yet, it will be set soon, let's not sync the object yet.\n return;\n }\n\n // Ensure we send a final update before the object is destroyed, if it had a networkId.\n const {\n messageName: updateMessageName,\n messageData: updateMessageData,\n } = gdjs.multiplayerMessageManager.createUpdateInstanceMessage({\n objectOwner: this.playerNumber,\n objectName,\n instanceNetworkId,\n objectNetworkSyncData: this.owner.getNetworkSyncData(),\n sceneNetworkId,\n });\n this._sendDataToPeersWithIncreasedClock(\n updateMessageName,\n updateMessageData\n );\n\n // Before sending the destroy message, we set up the object representing the peers\n // that we need an acknowledgment from.\n // If we are player 1, we are connected to everyone, so we expect an acknowledgment from everyone.\n // If we are another player, we are only connected to player 1, so we expect an acknowledgment from player 1.\n // In both cases, this represents the list of peers the current user is connected to.\n const otherPeerIds = gdjs.multiplayerPeerJsHelper.getAllPeers();\n const {\n messageName: destroyMessageName,\n messageData: destroyMessageData,\n } = gdjs.multiplayerMessageManager.createDestroyInstanceMessage({\n objectOwner: this.playerNumber,\n objectName,\n instanceNetworkId,\n sceneNetworkId,\n });\n const destroyedMessageName = gdjs.multiplayerMessageManager.createInstanceDestroyedMessageNameFromDestroyInstanceMessage(\n destroyMessageName\n );\n gdjs.multiplayerMessageManager.addExpectedMessageAcknowledgement({\n originalMessageName: destroyMessageName,\n originalData: {\n ...destroyMessageData,\n // As the method `sendDataToPeersWithIncreasedClock` will increment the clock,\n // we increment it here to ensure we can resend the same message if we don't receive an acknowledgment.\n _clock: this._clock + 1,\n },\n expectedMessageName: destroyedMessageName,\n otherPeerIds,\n // Destruction of objects are not reverted, as they will eventually be recreated by an update message.\n shouldCancelMessageIfTimesOut: false,\n });\n\n this._sendDataToPeersWithIncreasedClock(\n destroyMessageName,\n destroyMessageData\n );\n }\n\n setPlayerObjectOwnership(newObjectPlayerNumber: number) {\n debugLogger.info(\n `Setting ownership of object ${this.owner.getName()} (networkId: ${\n this.owner.networkId\n } to player ${newObjectPlayerNumber}.`\n );\n if (newObjectPlayerNumber < 0) {\n logger.error(\n 'Invalid player number (' +\n newObjectPlayerNumber +\n ') when setting ownership of an object.'\n );\n return;\n }\n\n // Update the ownership locally, so the object can be used immediately.\n // This is a prediction to allow snappy interactions.\n // If we are player 1 or host, we will have the ownership immediately anyway.\n // If we are another player, we will have the ownership as soon as the host acknowledges the change.\n // If the host does not send an acknowledgment, we will revert the ownership.\n const previousObjectPlayerNumber = this.playerNumber;\n this.playerNumber = newObjectPlayerNumber;\n const currentPlayerNumber = gdjs.multiplayer.getCurrentPlayerNumber();\n\n // If the lobby game is not running, do not try to update the ownership over the network,\n // as the game may create & update objects before the lobby game starts.\n if (!gdjs.multiplayer.isLobbyGameRunning()) {\n return;\n }\n\n let instanceNetworkId = this.owner.networkId;\n if (!instanceNetworkId) {\n debugLogger.info(\n 'Object has no networkId, we change the ownership locally, but it will not be synchronized yet if we are not the owner.'\n );\n if (newObjectPlayerNumber !== currentPlayerNumber) {\n // If we are not the new owner, we should not send a message to the host to change the ownership.\n // Just return and wait to receive an update message to reconcile this object.\n return;\n }\n // If we don't have a networkId, we need to create one now that we are the owner.\n // We are probably in a case where we created the object and then changed the ownership.\n debugLogger.info(\n 'We are the new owner, creating a networkId for the object.'\n );\n instanceNetworkId = this._getOrCreateInstanceNetworkId();\n }\n\n const sceneNetworkId = this.owner.getRuntimeScene().networkId;\n if (!sceneNetworkId) {\n // No networkId for the scene yet, it will be set soon, let's not sync the object yet.\n return;\n }\n\n const objectName = this.owner.getName();\n\n // When changing the ownership of an object with a networkId, we send a message to the host to ensure it is aware of the change,\n // and can either accept it and broadcast it to other players, or reject it and do nothing with it.\n // We expect an acknowledgment from the host, if not, we will retry and eventually revert the ownership.\n const {\n messageName,\n messageData,\n } = gdjs.multiplayerMessageManager.createChangeInstanceOwnerMessage({\n objectOwner: previousObjectPlayerNumber,\n objectName,\n instanceNetworkId,\n newObjectOwner: newObjectPlayerNumber,\n instanceX: this.owner.getX(),\n instanceY: this.owner.getY(),\n sceneNetworkId,\n });\n // Before sending the changeOwner message, if we are becoming the new owner,\n // we want to ensure this message is acknowledged, by everyone we're connected to.\n // If we are player 1, we are connected to everyone, so we expect an acknowledgment from everyone.\n // If we are another player, we are only connected to player 1, so we expect an acknowledgment from player 1.\n // In both cases, this represents the list of peers the current user is connected to.\n if (newObjectPlayerNumber === currentPlayerNumber) {\n const otherPeerIds = gdjs.multiplayerPeerJsHelper.getAllPeers();\n const changeOwnerAcknowledgedMessageName = gdjs.multiplayerMessageManager.createInstanceOwnerChangedMessageNameFromChangeInstanceOwnerMessage(\n messageName\n );\n gdjs.multiplayerMessageManager.addExpectedMessageAcknowledgement({\n originalMessageName: messageName,\n originalData: {\n ...messageData,\n _clock: this._clock + 1, // Will be incremented by the time the message is sent.\n },\n expectedMessageName: changeOwnerAcknowledgedMessageName,\n otherPeerIds,\n // If we are not the host, we should revert the ownership if the host does not acknowledge the change.\n shouldCancelMessageIfTimesOut: currentPlayerNumber !== 1,\n });\n }\n\n debugLogger.info('Sending change owner message', messageName);\n this._sendDataToPeersWithIncreasedClock(messageName, messageData);\n\n // If we are the new owner, also send directly an update of the position,\n // so that the object is immediately moved on the screen and we don't wait for the next tick.\n if (newObjectPlayerNumber === currentPlayerNumber) {\n debugLogger.info(\n 'Sending update message to move the object immediately.'\n );\n const objectNetworkSyncData = this.owner.getNetworkSyncData();\n const {\n messageName: updateMessageName,\n messageData: updateMessageData,\n } = gdjs.multiplayerMessageManager.createUpdateInstanceMessage({\n objectOwner: this.playerNumber,\n objectName,\n instanceNetworkId,\n objectNetworkSyncData,\n sceneNetworkId,\n });\n this._sendDataToPeersWithIncreasedClock(\n updateMessageName,\n updateMessageData\n );\n }\n }\n\n getPlayerObjectOwnership(): number {\n return this.playerNumber;\n }\n\n isObjectOwnedByCurrentPlayer(): boolean {\n return this._isOwnerAsPlayerOrHost();\n }\n\n removeObjectOwnership() {\n // 0 means the host is the owner.\n this.setPlayerObjectOwnership(0);\n }\n\n takeObjectOwnership() {\n this.setPlayerObjectOwnership(gdjs.multiplayer.getCurrentPlayerNumber());\n }\n\n getActionOnPlayerDisconnect() {\n return this.actionOnPlayerDisconnect;\n }\n\n enableBehaviorSynchronization(behaviorName: string, enable: boolean) {\n const behavior = this.owner.getBehavior(behaviorName);\n if (!behavior) {\n logger.error(\n `Behavior ${behaviorName} does not exist on object ${this.owner.getName()}.`\n );\n return;\n }\n\n behavior.enableSynchronization(enable);\n }\n }\n gdjs.registerBehavior(\n 'Multiplayer::MultiplayerObjectBehavior',\n gdjs.MultiplayerObjectRuntimeBehavior\n );\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/*\n GDevelop - Multiplayer Object Behavior Extension\n Copyright (c) 2013-2016 Florian Rival (Florian.Rival@gmail.com)\n*/\n\nnamespace gdjs {\n const logger = new gdjs.Logger('Multiplayer');\n const debugLogger = new gdjs.Logger('Multiplayer - Debug');\n const getTimeNow =\n window.performance && typeof window.performance.now === 'function'\n ? window.performance.now.bind(window.performance)\n : Date.now;\n\n /**\n * The MultiplayerObjectRuntimeBehavior represents a behavior that can be added to objects\n * to make them synchronized over the network.\n */\n export class MultiplayerObjectRuntimeBehavior extends gdjs.RuntimeBehavior {\n // Which player is the owner of the object.\n // If 0, then the object is not owned by any player, so the host is the owner.\n playerNumber: number = 0;\n\n // The action to be executed when the player disconnects.\n actionOnPlayerDisconnect: string;\n\n // The last time the object has been synchronized.\n // This is to avoid synchronizing the object too often, see _objectMaxSyncRate.\n _lastObjectSyncTimestamp: number = 0;\n\n // The last time the basic object info has been synchronized.\n _lastBasicObjectSyncTimestamp: number = 0;\n // The number of times per second the object basic info should be synchronized when it doesn't change.\n _objectBasicInfoSyncRate: number = 5;\n // The last data sent to synchronize the basic info of the object.\n _lastSentBasicObjectSyncData: BasicObjectNetworkSyncData | undefined;\n // When we know that the basic info of the object has been updated, we can force sending them\n // on the max SyncRate for a number of times to ensure they are received, without the need of an acknowledgment.\n _numberOfForcedBasicObjectUpdates: number = 0;\n\n // The last time the variables have been synchronized.\n _lastVariablesSyncTimestamp: number = 0;\n // The number of times per second the variables should be synchronized.\n _variablesSyncRate: number = 1;\n // The last data sent to synchronize the variables.\n _lastSentVariableSyncData: VariableNetworkSyncData[] | undefined;\n // When we know that the variables have been updated, we can force sending them\n // on the same syncRate as the object update for a number of times\n // to ensure they are received, without the need of an acknowledgment.\n _numberOfForcedVariablesUpdates: number = 0;\n\n // The last time the effects have been synchronized.\n _lastEffectsSyncTimestamp: number = 0;\n // The number of times per second the effects should be synchronized.\n _effectsSyncRate: number = 1;\n // The last data sent to synchronize the effects.\n _lastSentEffectSyncData:\n | { [effectName: string]: EffectNetworkSyncData }\n | undefined;\n // When we know that the effects have been updated, we can force sending them\n // on the same syncRate as the object update for a number of times\n // to ensure they are received, without the need of an acknowledgment.\n _numberOfForcedEffectsUpdates: number = 0;\n\n // To avoid seeing too many logs.\n _lastLogTimestamp: number = 0;\n _logSyncRate: number = 1;\n // Clock to be incremented every time we send a message, to ensure they are ordered\n // and old messages are ignored.\n _clock: number = 0;\n _destroyInstanceTimeoutId: NodeJS.Timeout | null = null;\n _timeBeforeDestroyingObjectWithoutNetworkIdInMs = 500;\n\n constructor(\n instanceContainer: gdjs.RuntimeInstanceContainer,\n behaviorData,\n owner: RuntimeObject\n ) {\n super(instanceContainer, behaviorData, owner);\n this.playerNumber =\n behaviorData.playerNumber === 'Host'\n ? 0\n : parseInt(behaviorData.playerNumber, 10);\n this.actionOnPlayerDisconnect = behaviorData.actionOnPlayerDisconnect;\n\n // When a synchronized object is created, we assume it will be assigned a networkId quickly if:\n // - It is a new object created by the current player. -> will be assigned a networkId when sending the update message.\n // - It is an object created by another player. -> will be assigned a networkId when receiving the update message.\n // There is a small risk that the object is created by us after we receive an update message from the host,\n // ending up with 2 objects created, one with a networkId (from the host) and one without (from us).\n // To handle this case and avoid having an object not synchronized, we set a timeout to destroy the object\n // if it has not been assigned a networkId after a short delay.\n this._destroyInstanceTimeoutId = setTimeout(() => {\n const sceneNetworkId = this.owner.getRuntimeScene().networkId;\n if (\n !owner.networkId &&\n gdjs.multiplayer.isLobbyGameRunning() &&\n sceneNetworkId\n ) {\n debugLogger.info(\n `Lobby game is running on a synced scene and object ${owner.getName()} has not been assigned a networkId after a short delay, destroying it.`\n );\n owner.deleteFromScene(instanceContainer);\n }\n }, this._timeBeforeDestroyingObjectWithoutNetworkIdInMs);\n }\n\n private _sendDataToPeersWithIncreasedClock = async (\n messageName: string,\n data: Object\n ) => {\n this._clock++;\n data['_clock'] = this._clock;\n const connectedPeerIds = gdjs.multiplayerPeerJsHelper.getAllPeers();\n await gdjs.multiplayerMessageManager.sendDataTo(\n connectedPeerIds,\n messageName,\n data\n );\n };\n\n private _isOwnerAsPlayerOrHost() {\n const currentPlayerNumber = gdjs.multiplayer.getCurrentPlayerNumber();\n const isHost = gdjs.multiplayer.isCurrentPlayerHost();\n\n const isOwnerOfObject =\n currentPlayerNumber === this.playerNumber || // Player as owner.\n (isHost && this.playerNumber === 0); // Host as owner.\n\n return isOwnerOfObject;\n }\n\n private _hasObjectBeenSyncedWithinMaxRate() {\n const objectMaxSyncRate = gdjs.multiplayer.getObjectsSynchronizationRate();\n return (\n getTimeNow() - this._lastObjectSyncTimestamp < 1000 / objectMaxSyncRate\n );\n }\n\n private _hasObjectBasicInfoBeenSyncedRecently() {\n return (\n getTimeNow() - this._lastBasicObjectSyncTimestamp <\n 1000 / this._objectBasicInfoSyncRate\n );\n }\n\n private _haveVariablesBeenSyncedRecently() {\n return (\n getTimeNow() - this._lastVariablesSyncTimestamp <\n 1000 / this._variablesSyncRate\n );\n }\n\n private _haveEffectsBeenSyncedRecently() {\n return (\n getTimeNow() - this._lastEffectsSyncTimestamp <\n 1000 / this._effectsSyncRate\n );\n }\n\n // private _logToConsoleWithThrottle(message: string) {\n // if (getTimeNow() - this._lastLogTimestamp > 1000 / this._logSyncRate) {\n // logger.info(message);\n // this._lastLogTimestamp = getTimeNow();\n // }\n // }\n\n private _getOrCreateInstanceNetworkId() {\n if (!this.owner.networkId) {\n // No ID for this object, let's generate one so it can be identified by other players.\n // Keep it short to avoid sending too much data.\n const newID = gdjs.makeUuid().substring(0, 8);\n this.owner.networkId = newID;\n }\n\n return this.owner.networkId;\n }\n\n private _isBasicObjectNetworkSyncDataDifferentFromLastSync(\n basicObjectNetworkSyncData: BasicObjectNetworkSyncData\n ) {\n if (!this._lastSentBasicObjectSyncData) {\n return true;\n }\n\n // Compare the json of the basicObjectNetworkSyncData to check if they are different.\n // This is not the most efficient way to do it, but it's simple and should work.\n const haveBasicObjectNetworkSyncDataChanged =\n JSON.stringify(basicObjectNetworkSyncData) !==\n JSON.stringify(this._lastSentBasicObjectSyncData);\n\n return haveBasicObjectNetworkSyncDataChanged;\n }\n\n private _areVariablesDifferentFromLastSync(\n variablesSyncData: VariableNetworkSyncData[]\n ) {\n if (!this._lastSentVariableSyncData) {\n return true;\n }\n\n // Compare the json of the variables to check if they are different.\n // This is not the most efficient way to do it, but it's simple and should work.\n const haveVariableSyncDataChanged =\n JSON.stringify(variablesSyncData) !==\n JSON.stringify(this._lastSentVariableSyncData);\n\n return haveVariableSyncDataChanged;\n }\n\n private _areEffectsDifferentFromLastSync(effectsSyncData: {\n [effectName: string]: EffectNetworkSyncData;\n }) {\n if (!this._lastSentEffectSyncData) {\n return true;\n }\n\n for (const effectName in effectsSyncData) {\n if (!effectsSyncData.hasOwnProperty(effectName)) {\n continue;\n }\n\n const effectSyncData = effectsSyncData[effectName];\n const effectEnabled = effectSyncData.ena;\n const effectFilterCreator = effectSyncData.fc;\n\n const effectInLastSync = this._lastSentEffectSyncData[effectName];\n\n if (!effectInLastSync || effectInLastSync.ena !== effectEnabled) {\n return true;\n }\n\n for (const parameterName in effectFilterCreator) {\n if (!effectFilterCreator.hasOwnProperty(parameterName)) {\n continue;\n }\n\n const parameterValue = effectFilterCreator[parameterName];\n const lastParameterValueSent = effectInLastSync.fc[parameterName];\n if (lastParameterValueSent !== parameterValue) {\n return true;\n }\n }\n }\n\n return false;\n }\n\n doStepPostEvents() {\n // Before doing anything, check if the game is running, if not, return.\n if (!gdjs.multiplayer.isLobbyGameRunning()) {\n return;\n }\n\n // If game is running and the object belongs to a player who is not connected, destroy the object.\n // As the game may create objects before the lobby game starts, we don't want to destroy them if it's not running.\n if (\n this.playerNumber !== 0 && // Host is always connected.\n !gdjs.multiplayerMessageManager.isPlayerConnected(this.playerNumber)\n ) {\n debugLogger.info(\n `Player number ${this.playerNumber} does not exist in the lobby at the moment. Destroying the object.`\n );\n this.owner.deleteFromScene(this.owner.getInstanceContainer());\n return;\n }\n\n if (!this._isOwnerAsPlayerOrHost()) {\n return;\n }\n\n // If the object has been synchronized recently at the max rate, then return.\n // This is to avoid sending data on every frame, which would be too much.\n if (this._hasObjectBeenSyncedWithinMaxRate()) {\n return;\n }\n\n const instanceNetworkId = this._getOrCreateInstanceNetworkId();\n const objectName = this.owner.getName();\n const objectNetworkSyncData = this.owner.getNetworkSyncData();\n\n // this._logToConsoleWithThrottle(\n // `Synchronizing object ${this.owner.getName()} (instance ${\n // this.owner.networkId\n // }) with player ${this.playerNumber} and data ${JSON.stringify(\n // objectNetworkSyncData\n // )}`\n // );\n\n const areBasicObjectNetworkSyncDataDifferent = this._isBasicObjectNetworkSyncDataDifferentFromLastSync(\n {\n x: objectNetworkSyncData.x,\n y: objectNetworkSyncData.y,\n z: objectNetworkSyncData.z,\n zo: objectNetworkSyncData.zo,\n a: objectNetworkSyncData.a,\n hid: objectNetworkSyncData.hid,\n lay: objectNetworkSyncData.lay,\n if: objectNetworkSyncData.if,\n pfx: objectNetworkSyncData.pfx,\n pfy: objectNetworkSyncData.pfy,\n }\n );\n const shouldSyncObjectBasicInfo =\n !this._hasObjectBasicInfoBeenSyncedRecently() ||\n areBasicObjectNetworkSyncDataDifferent ||\n this._numberOfForcedBasicObjectUpdates > 0;\n if (areBasicObjectNetworkSyncDataDifferent) {\n this._numberOfForcedBasicObjectUpdates = 3;\n }\n if (!shouldSyncObjectBasicInfo) {\n // If the basic info has not changed, assume we don't need to sync the whole object data at a high rate.\n // TODO: allow sending the variables, behaviors and effects still?\n return;\n }\n\n const areVariablesDifferent =\n objectNetworkSyncData.var &&\n this._areVariablesDifferentFromLastSync(objectNetworkSyncData.var);\n const shouldSyncVariables =\n !this._haveVariablesBeenSyncedRecently() ||\n areVariablesDifferent ||\n this._numberOfForcedVariablesUpdates > 0;\n if (areVariablesDifferent) {\n this._numberOfForcedVariablesUpdates = 3;\n }\n if (!shouldSyncVariables) {\n delete objectNetworkSyncData.var;\n }\n\n const areEffectsDifferent =\n objectNetworkSyncData.eff &&\n this._areEffectsDifferentFromLastSync(objectNetworkSyncData.eff);\n const shoundSyncEffects =\n !this._haveEffectsBeenSyncedRecently() ||\n areEffectsDifferent ||\n this._numberOfForcedEffectsUpdates > 0;\n if (areEffectsDifferent) {\n this._numberOfForcedEffectsUpdates = 3;\n }\n if (!shoundSyncEffects) {\n delete objectNetworkSyncData.eff;\n }\n\n const sceneNetworkId = this.owner.getRuntimeScene().networkId;\n if (!sceneNetworkId) {\n // No networkId for the scene yet, it will be set soon, let's not sync the object yet.\n return;\n }\n\n const {\n messageName: updateMessageName,\n messageData: updateMessageData,\n } = gdjs.multiplayerMessageManager.createUpdateInstanceMessage({\n objectOwner: this.playerNumber,\n objectName,\n instanceNetworkId,\n objectNetworkSyncData,\n sceneNetworkId,\n });\n this._sendDataToPeersWithIncreasedClock(\n updateMessageName,\n updateMessageData\n );\n\n const now = getTimeNow();\n\n this._lastObjectSyncTimestamp = now;\n if (shouldSyncObjectBasicInfo) {\n this._lastBasicObjectSyncTimestamp = now;\n this._lastSentBasicObjectSyncData = {\n x: objectNetworkSyncData.x,\n y: objectNetworkSyncData.y,\n zo: objectNetworkSyncData.zo,\n a: objectNetworkSyncData.a,\n hid: objectNetworkSyncData.hid,\n lay: objectNetworkSyncData.lay,\n if: objectNetworkSyncData.if,\n pfx: objectNetworkSyncData.pfx,\n pfy: objectNetworkSyncData.pfy,\n };\n this._numberOfForcedBasicObjectUpdates = Math.max(\n this._numberOfForcedBasicObjectUpdates - 1,\n 0\n );\n }\n if (shouldSyncVariables) {\n this._lastVariablesSyncTimestamp = now;\n this._lastSentVariableSyncData = objectNetworkSyncData.var;\n this._numberOfForcedVariablesUpdates = Math.max(\n this._numberOfForcedVariablesUpdates - 1,\n 0\n );\n }\n if (shoundSyncEffects) {\n this._lastEffectsSyncTimestamp = now;\n this._lastSentEffectSyncData = objectNetworkSyncData.eff;\n this._numberOfForcedEffectsUpdates = Math.max(\n this._numberOfForcedEffectsUpdates - 1,\n 0\n );\n }\n }\n\n onDestroy() {\n if (this._destroyInstanceTimeoutId) {\n clearTimeout(this._destroyInstanceTimeoutId);\n this._destroyInstanceTimeoutId = null;\n }\n\n // If the lobby game is not running, no need to send a message to destroy the object.\n if (!gdjs.multiplayer.isLobbyGameRunning()) {\n return;\n }\n\n // For destruction of objects, we allow the host to destroy the object even if it is not the owner.\n // This is particularly helpful when a player disconnects, so the host can destroy the object they were owning.\n if (\n !this._isOwnerAsPlayerOrHost() &&\n !gdjs.multiplayer.isCurrentPlayerHost()\n ) {\n return;\n }\n\n const instanceNetworkId = this.owner.networkId;\n const objectName = this.owner.getName();\n\n // If it had no networkId, then it was not synchronized and we don't need to send a message.\n if (!instanceNetworkId) {\n debugLogger.info(\n `Destroying object ${objectName} without networkId, no need to send a message.`\n );\n return;\n }\n\n const sceneNetworkId = this.owner.getRuntimeScene().networkId;\n if (!sceneNetworkId) {\n // No networkId for the scene yet, it will be set soon, let's not sync the object yet.\n return;\n }\n\n // Ensure we send a final update before the object is destroyed, if it had a networkId.\n const {\n messageName: updateMessageName,\n messageData: updateMessageData,\n } = gdjs.multiplayerMessageManager.createUpdateInstanceMessage({\n objectOwner: this.playerNumber,\n objectName,\n instanceNetworkId,\n objectNetworkSyncData: this.owner.getNetworkSyncData(),\n sceneNetworkId,\n });\n this._sendDataToPeersWithIncreasedClock(\n updateMessageName,\n updateMessageData\n );\n\n // Before sending the destroy message, we set up the object representing the peers\n // that we need an acknowledgment from.\n // If we are the host, we are connected to everyone, so we expect an acknowledgment from everyone.\n // If we are another player, we are only connected to the host, so we expect an acknowledgment from the host.\n // In both cases, this represents the list of peers the current user is connected to.\n const otherPeerIds = gdjs.multiplayerPeerJsHelper.getAllPeers();\n const {\n messageName: destroyMessageName,\n messageData: destroyMessageData,\n } = gdjs.multiplayerMessageManager.createDestroyInstanceMessage({\n objectOwner: this.playerNumber,\n objectName,\n instanceNetworkId,\n sceneNetworkId,\n });\n const destroyedMessageName = gdjs.multiplayerMessageManager.createInstanceDestroyedMessageNameFromDestroyInstanceMessage(\n destroyMessageName\n );\n gdjs.multiplayerMessageManager.addExpectedMessageAcknowledgement({\n originalMessageName: destroyMessageName,\n originalData: {\n ...destroyMessageData,\n // As the method `sendDataToPeersWithIncreasedClock` will increment the clock,\n // we increment it here to ensure we can resend the same message if we don't receive an acknowledgment.\n _clock: this._clock + 1,\n },\n expectedMessageName: destroyedMessageName,\n otherPeerIds,\n // Destruction of objects are not reverted, as they will eventually be recreated by an update message.\n shouldCancelMessageIfTimesOut: false,\n });\n\n this._sendDataToPeersWithIncreasedClock(\n destroyMessageName,\n destroyMessageData\n );\n }\n\n setPlayerObjectOwnership(newObjectPlayerNumber: number) {\n debugLogger.info(\n `Setting ownership of object ${this.owner.getName()} (networkId: ${\n this.owner.networkId\n } to player ${newObjectPlayerNumber}.`\n );\n if (newObjectPlayerNumber < 0) {\n logger.error(\n 'Invalid player number (' +\n newObjectPlayerNumber +\n ') when setting ownership of an object.'\n );\n return;\n }\n\n // Update the ownership locally, so the object can be used immediately.\n // This is a prediction to allow snappy interactions.\n // If we are host, we will have the ownership immediately anyway.\n // If we are another player, we will have the ownership as soon as the host acknowledges the change.\n // If the host does not send an acknowledgment, we will revert the ownership.\n const previousObjectPlayerNumber = this.playerNumber;\n this.playerNumber = newObjectPlayerNumber;\n const currentPlayerNumber = gdjs.multiplayer.getCurrentPlayerNumber();\n\n // If the lobby game is not running, do not try to update the ownership over the network,\n // as the game may create & update objects before the lobby game starts.\n if (!gdjs.multiplayer.isLobbyGameRunning()) {\n return;\n }\n\n let instanceNetworkId = this.owner.networkId;\n if (!instanceNetworkId) {\n debugLogger.info(\n 'Object has no networkId, we change the ownership locally, but it will not be synchronized yet if we are not the owner.'\n );\n if (newObjectPlayerNumber !== currentPlayerNumber) {\n // If we are not the new owner, we should not send a message to the host to change the ownership.\n // Just return and wait to receive an update message to reconcile this object.\n return;\n }\n // If we don't have a networkId, we need to create one now that we are the owner.\n // We are probably in a case where we created the object and then changed the ownership.\n debugLogger.info(\n 'We are the new owner, creating a networkId for the object.'\n );\n instanceNetworkId = this._getOrCreateInstanceNetworkId();\n }\n\n const sceneNetworkId = this.owner.getRuntimeScene().networkId;\n if (!sceneNetworkId) {\n // No networkId for the scene yet, it will be set soon, let's not sync the object yet.\n return;\n }\n\n const objectName = this.owner.getName();\n\n // When changing the ownership of an object with a networkId, we send a message to the host to ensure it is aware of the change,\n // and can either accept it and broadcast it to other players, or reject it and do nothing with it.\n // We expect an acknowledgment from the host, if not, we will retry and eventually revert the ownership.\n const {\n messageName,\n messageData,\n } = gdjs.multiplayerMessageManager.createChangeInstanceOwnerMessage({\n objectOwner: previousObjectPlayerNumber,\n objectName,\n instanceNetworkId,\n newObjectOwner: newObjectPlayerNumber,\n instanceX: this.owner.getX(),\n instanceY: this.owner.getY(),\n sceneNetworkId,\n });\n // Before sending the changeOwner message, if we are becoming the new owner,\n // we want to ensure this message is acknowledged, by everyone we're connected to.\n // If we are the host, we are connected to everyone, so we expect an acknowledgment from everyone.\n // If we are another player, we are only connected to the host, so we expect an acknowledgment from the host.\n // In both cases, this represents the list of peers the current user is connected to.\n if (newObjectPlayerNumber === currentPlayerNumber) {\n const otherPeerIds = gdjs.multiplayerPeerJsHelper.getAllPeers();\n const changeOwnerAcknowledgedMessageName = gdjs.multiplayerMessageManager.createInstanceOwnerChangedMessageNameFromChangeInstanceOwnerMessage(\n messageName\n );\n gdjs.multiplayerMessageManager.addExpectedMessageAcknowledgement({\n originalMessageName: messageName,\n originalData: {\n ...messageData,\n _clock: this._clock + 1, // Will be incremented by the time the message is sent.\n },\n expectedMessageName: changeOwnerAcknowledgedMessageName,\n otherPeerIds,\n // If we are not the host, we should revert the ownership if the host does not acknowledge the change.\n shouldCancelMessageIfTimesOut: !gdjs.multiplayer.isCurrentPlayerHost(),\n });\n }\n\n debugLogger.info('Sending change owner message', messageName);\n this._sendDataToPeersWithIncreasedClock(messageName, messageData);\n\n // If we are the new owner, also send directly an update of the position,\n // so that the object is immediately moved on the screen and we don't wait for the next tick.\n if (newObjectPlayerNumber === currentPlayerNumber) {\n debugLogger.info(\n 'Sending update message to move the object immediately.'\n );\n const objectNetworkSyncData = this.owner.getNetworkSyncData();\n const {\n messageName: updateMessageName,\n messageData: updateMessageData,\n } = gdjs.multiplayerMessageManager.createUpdateInstanceMessage({\n objectOwner: this.playerNumber,\n objectName,\n instanceNetworkId,\n objectNetworkSyncData,\n sceneNetworkId,\n });\n this._sendDataToPeersWithIncreasedClock(\n updateMessageName,\n updateMessageData\n );\n }\n }\n\n getPlayerObjectOwnership(): number {\n return this.playerNumber;\n }\n\n isObjectOwnedByCurrentPlayer(): boolean {\n return this._isOwnerAsPlayerOrHost();\n }\n\n removeObjectOwnership() {\n // 0 means the host is the owner.\n this.setPlayerObjectOwnership(0);\n }\n\n takeObjectOwnership() {\n this.setPlayerObjectOwnership(gdjs.multiplayer.getCurrentPlayerNumber());\n }\n\n getActionOnPlayerDisconnect() {\n return this.actionOnPlayerDisconnect;\n }\n\n enableBehaviorSynchronization(behaviorName: string, enable: boolean) {\n const behavior = this.owner.getBehavior(behaviorName);\n if (!behavior) {\n logger.error(\n `Behavior ${behaviorName} does not exist on object ${this.owner.getName()}.`\n );\n return;\n }\n\n behavior.enableSynchronization(enable);\n }\n }\n gdjs.registerBehavior(\n 'Multiplayer::MultiplayerObjectBehavior',\n gdjs.MultiplayerObjectRuntimeBehavior\n );\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,2 +1,2 @@
1
- var gdjs;(function(t){const a=new t.Logger("Multiplayer");let _;(function(n){n.disableMultiplayerForTesting=!1,n._isReadyToSendOrReceiveGameUpdateMessages=!1;let I=null,T=!1,R=!1,k=!1;n._isLobbyGameRunning=!1;let E=!1,m=null,f=null,C=null,i=null,A=null,G=null;const H=1e4,$=3e4,B=5;let v=!1;n.playerNumber=null,t.registerRuntimeScenePreEventsCallback(e=>{v=e.getGame().isUsingGDevelopDevelopmentEnvironment(),!n.disableMultiplayerForTesting&&(t.multiplayerMessageManager.handleHeartbeatsToSend(),t.multiplayerMessageManager.handleJustDisconnectedPeers(e),t.multiplayerMessageManager.handleChangeInstanceOwnerMessagesReceived(e),t.multiplayerMessageManager.handleUpdateInstanceMessagesReceived(e),t.multiplayerMessageManager.handleCustomMessagesReceived(),t.multiplayerMessageManager.handleAcknowledgeMessagesReceived(),t.multiplayerMessageManager.resendClearOrCancelAcknowledgedMessages(e),t.multiplayerMessageManager.handleChangeVariableOwnerMessagesReceived(e),n._isLobbyGameRunning&&t.multiplayerMessageManager.handleSavedUpdateMessages(e),t.multiplayerMessageManager.handleUpdateGameMessagesReceived(e),t.multiplayerMessageManager.handleUpdateSceneMessagesReceived(e))}),t.registerRuntimeScenePostEventsCallback(e=>{n.disableMultiplayerForTesting||(z(e),K(e),t.multiplayerMessageManager.handleHeartbeatsReceived(),t.multiplayerMessageManager.handleDestroyInstanceMessagesReceived(e),t.multiplayerVariablesManager.handleChangeVariableOwnerMessagesToSend(),t.multiplayerMessageManager.handleUpdateGameMessagesToSend(e),t.multiplayerMessageManager.handleUpdateSceneMessagesToSend(e))}),t.registerRuntimeScenePostEventsCallback(()=>{n.disableMultiplayerForTesting||(k=!1,E=!1)});const V=({runtimeGame:e,gameId:o})=>{const s="https://gd.games",r=new URL(`${s}/games/${o}/lobbies${m?`/${m}`:""}`);r.searchParams.set("gameVersion",e.getGameData().properties.version),e.getAdditionalOptions().nativeMobileApp&&r.searchParams.set("nativeMobileApp","true"),r.searchParams.set("isPreview",e.isPreview()?"true":"false"),v&&r.searchParams.set("dev","true"),f&&r.searchParams.set("connectionId",f),n.playerNumber&&r.searchParams.set("positionInLobby",n.playerNumber.toString());const d=t.playerAuthentication.getUserId();d&&r.searchParams.set("playerId",d);const c=t.playerAuthentication.getUserToken();return c&&r.searchParams.set("playerToken",c),r.searchParams.set("multiplayerVersion","2"),r.toString()};n.hasLobbyGameJustStarted=()=>k,n.isLobbyGameRunning=()=>n._isLobbyGameRunning,n.isReadyToSendOrReceiveGameUpdateMessages=()=>n._isReadyToSendOrReceiveGameUpdateMessages,n.hasLobbyGameJustEnded=()=>E,n.getPlayersInLobbyCount=()=>t.multiplayerMessageManager.getNumberOfConnectedPlayers(),n.isPlayerConnected=e=>t.multiplayerMessageManager.isPlayerConnected(e),n.getCurrentPlayerNumber=()=>n.playerNumber||0,n.isPlayerHost=()=>n.playerNumber===1,n.getPlayerUsername=e=>t.multiplayerMessageManager.getPlayerUsername(e),n.getCurrentPlayerUsername=()=>{const e=n.getCurrentPlayerNumber();return n.getPlayerUsername(e)};const z=e=>{const o=t.multiplayerMessageManager.getLatestPlayerWhoJustLeft();if(o){const s=n.getPlayerUsername(o);t.multiplayerComponents.displayPlayerLeftNotification(e,s),t.multiplayerMessageManager.removePlayerWhoJustLeft(),W()}},K=e=>{const o=t.multiplayerMessageManager.getLatestPlayerWhoJustJoined();if(o){const s=n.getPlayerUsername(o);t.multiplayerComponents.displayPlayerJoinedNotification(e,s)}t.multiplayerMessageManager.removePlayerWhoJustJoined()},j=(e,o,s=0)=>{const d=`${v?"https://api-dev.gdevelop.io":"https://api.gdevelop.io"}/game/public-game/${o}`;return fetch(d,{method:"HEAD"}).then(c=>c.status!==200?(a.warn(`Error while fetching the game: ${c.status} ${c.statusText}`),c.status===404||s>2?!1:j(e,o,s+1)):!0,c=>(a.error("Error while fetching game:",c),!1))},Y=function(e,o){if(f){a.info("Already connected to a lobby.");return}i&&(a.warn("Already connected to a lobby. Closing the previous one."),i.close(),f=null,n.playerNumber=null,m=null,i=null);const s=t.projectData.properties.projectUuid,r=t.playerAuthentication.getUserId(),d=t.playerAuthentication.getUserToken();if(!s){a.error("Cannot open lobbies if the project has no ID.");return}if(!r||!d){a.warn("Cannot open lobbies if the player is not connected.");return}const c=v?"wss://api-ws-dev.gdevelop.io/play":"wss://api-ws.gdevelop.io/play",g=new URL(c);g.searchParams.set("gameId",s),g.searchParams.set("lobbyId",o),g.searchParams.set("playerId",r),g.searchParams.set("connectionType","lobby"),g.searchParams.set("playerGameToken",d),i=new WebSocket(g.toString()),i.onopen=()=>{if(a.info("Connected to the lobby."),A=setInterval(()=>{i&&i.send(JSON.stringify({action:"heartbeat",connectionType:"lobby"}))},H),i){i.send(JSON.stringify({action:"getConnectionId"}));const l=e.getGame().getPlatformInfo();i.send(JSON.stringify({action:"sessionInformation",connectionType:"lobby",isCordova:l.isCordova,devicePlatform:l.devicePlatform,navigatorPlatform:l.navigatorPlatform,hasTouch:l.hasTouch,supportedCompressionMethods:l.supportedCompressionMethods}))}},i.onmessage=l=>{if(l.data){const p=JSON.parse(l.data);switch(p.type){case"connectionId":{const b=p.data,u=b.connectionId,y=b.positionInLobby,ge=b.validIceServers||[],pe=b.brokerServerConfig;if(!u||!y){a.error("No connectionId or position received"),t.multiplayerComponents.displayErrorNotification(e),i&&i.close();return}q({runtimeScene:e,connectionId:u,positionInLobby:y,lobbyId:o,playerId:r,playerToken:d,validIceServers:ge,brokerServerConfig:pe});break}case"lobbyUpdated":{const u=p.data.positionInLobby;Q({runtimeScene:e,positionInLobby:u});break}case"gameCountdownStarted":{const b=p.data,u=b.compressionMethod||"none",y=b.secondsToStart||B;X({runtimeScene:e,compressionMethod:u,secondsToStart:y});break}case"gameStarted":{const u=p.data.heartbeatInterval||$;Z({runtimeScene:e,heartbeatInterval:u});break}case"peerId":{const b=p.data;if(!b){a.error("No message received");return}const u=b.peerId,y=b.compressionMethod;if(!u||!y){a.error("Malformed message received");return}ee({peerId:u,compressionMethod:y});break}}}},i.onclose=()=>{if(a.info("Disconnected from the lobby. Either manually or game started."),f=null,i=null,A&&clearInterval(A),n._isLobbyGameRunning)return;const l=t.multiplayerComponents.getLobbiesIframe(e);!l||!l.contentWindow||l.contentWindow.postMessage({id:"lobbyLeft"},"*")}},q=function({runtimeScene:e,connectionId:o,positionInLobby:s,lobbyId:r,playerId:d,playerToken:c,validIceServers:g,brokerServerConfig:l}){if(g.length)for(const b of g)t.multiplayerPeerJsHelper.useCustomICECandidate(b.urls,b.username,b.credential);l?t.multiplayerPeerJsHelper.useCustomBrokerServer(l.hostname,l.port,l.path,l.key,l.secure):t.multiplayerPeerJsHelper.useDefaultBrokerServer(),f=o,n.playerNumber=s,m=r;const p=t.multiplayerComponents.getLobbiesIframe(e);if(!p||!p.contentWindow){a.error("The lobbies iframe is not opened, cannot send the join message.");return}p.contentWindow.postMessage({id:"lobbyJoined",lobbyId:r,playerId:d,playerToken:c,connectionId:f,positionInLobby:s},"https://gd.games")},O=function(){i&&i.close(),f=null,n.playerNumber=null,m=null,i=null},Q=function({runtimeScene:e,positionInLobby:o}){n.playerNumber=o;const s=t.multiplayerComponents.getLobbiesIframe(e);!s||!s.contentWindow||s.contentWindow.postMessage({id:"lobbyUpdated",positionInLobby:o},"*")},X=function({runtimeScene:e,compressionMethod:o,secondsToStart:s}){t.multiplayerPeerJsHelper.setCompressionMethod(o),n.getCurrentPlayerNumber()===1&&se();const r=t.multiplayerComponents.getLobbiesIframe(e);if(!r||!r.contentWindow){a.info("The lobbies iframe is not opened, not sending message.");return}r.contentWindow.postMessage({id:"gameCountdownStarted",secondsToStart:s},"*"),t.multiplayerComponents.hideLobbiesCloseButtonTemporarily(e)},W=async function(){const e=t.projectData.properties.projectUuid,o=t.playerAuthentication.getUserId(),s=t.playerAuthentication.getUserToken();if(!e||!o||!s||!m){a.error("Cannot keep the lobby playing without the game ID or player ID.");return}const r=v?"https://api-dev.gdevelop.io":"https://api.gdevelop.io",d={"Content-Type":"application/json"};let c=`${r}/play/game/${e}/public-lobby/${m}/action/heartbeat`;d.Authorization=`player-game-token ${s}`,c+=`?playerId=${o}`;const g=t.multiplayerMessageManager.getConnectedPlayers();try{await fetch(c,{method:"POST",headers:d,body:JSON.stringify({players:g})})}catch(l){a.error("Error while sending heartbeat, retrying:",l);try{await fetch(c,{method:"POST",headers:d,body:JSON.stringify({players:g})})}catch(p){a.error("Error while sending heartbeat a second time. Giving up:",p)}}},Z=function({runtimeScene:e,heartbeatInterval:o}){const s=t.multiplayerPeerJsHelper.getAllPeers();if(!n.isPlayerHost()&&s.length===0){t.multiplayerComponents.displayConnectionErrorNotification(e),O(),n.removeLobbiesContainer(e),N(e);return}n.isPlayerHost()&&(G=setInterval(async()=>{await W()},o)),a.info("Lobby game has started."),t.multiplayerMessageManager.handleSavedUpdateMessages(e),n._isReadyToSendOrReceiveGameUpdateMessages=!0,k=!0,n._isLobbyGameRunning=!0,n.removeLobbiesContainer(e),i&&i.close(),N(e)};n.handleLobbyGameEnded=function(){a.info("Lobby game has ended."),E=!0,n._isLobbyGameRunning=!1,m=null,n.playerNumber=null,n._isReadyToSendOrReceiveGameUpdateMessages=!1,G&&clearInterval(G),t.multiplayerPeerJsHelper.disconnectFromAllPeers(),t.multiplayerMessageManager.clearAllMessagesTempData()};const ee=function({peerId:e,compressionMethod:o}){t.multiplayerPeerJsHelper.setCompressionMethod(o);const s=t.multiplayerPeerJsHelper.getCurrentId();if(!s){a.error("No peerId found, the player does not seem connected to the broker server.");return}if(s===e){a.info("Received our own peerId, ignoring.");return}t.multiplayerPeerJsHelper.connect(e)},te=function(){if(!i){a.error("No connection to send the start countdown message. Are you connected to a lobby?");return}i.send(JSON.stringify({action:"startGameCountdown",connectionType:"lobby"}))},ne=function(){if(!i){a.error("No connection to send the start countdown message. Are you connected to a lobby?");return}i.send(JSON.stringify({action:"startGame",connectionType:"lobby"})),n._isReadyToSendOrReceiveGameUpdateMessages=!0},oe=function(){if(!i){a.error("No connection to send the start countdown message. Are you connected to a lobby?");return}i.send(JSON.stringify({action:"joinGame",connectionType:"lobby"}))};n.markConnectionAsConnected=function(){!i||i.send(JSON.stringify({action:"updateConnection",connectionType:"lobby",status:"connected"}))},n.endLobbyGame=async function(){if(!n.isLobbyGameRunning())return;if(!n.isPlayerHost()){a.error("Only the host can end the game.");return}n._isLobbyGameRunning=!1,a.info("Ending the lobby game."),t.multiplayerMessageManager.sendEndGameMessage();const e=t.projectData.properties.projectUuid,o=t.playerAuthentication.getUserId(),s=t.playerAuthentication.getUserToken();if(!e||!o||!s||!m){a.error("Cannot end the lobby without the game ID or player ID.");return}const r=v?"https://api-dev.gdevelop.io":"https://api.gdevelop.io",d={"Content-Type":"application/json"};let c=`${r}/play/game/${e}/public-lobby/${m}/action/end`;d.Authorization=`player-game-token ${s}`,c+=`?playerId=${o}`;try{await fetch(c,{method:"POST",headers:d,body:JSON.stringify({gameId:e,lobbyId:m})})}catch(g){a.error("Error while ending the game:",g)}n.handleLobbyGameEnded()};const se=function(){if(!i){a.error("No connection to send the message. Are you connected to a lobby?");return}const e=t.multiplayerPeerJsHelper.getCurrentId();if(!e){a.error("No peerId found, the player doesn't seem connected to the broker server.");return}i.send(JSON.stringify({action:"sendPeerId",connectionType:"lobby",peerId:e}))},ae=function(e,o,{checkOrigin:s}){if(!(s&&!["https://gd.games","http://localhost:4000"].includes(o.origin))){if(!o.data.id)throw new Error("Malformed message");switch(o.data.id){case"lobbiesListenerReady":{re(e);break}case"joinLobby":{if(!o.data.lobbyId)throw new Error("Malformed message.");Y(e,o.data.lobbyId);break}case"startGameCountdown":{te();break}case"startGame":{ne();break}case"leaveLobby":{O();break}case"joinGame":{oe();break}}}},D=function(e,o){a.error(o),n.removeLobbiesContainer(e),N(e)},re=e=>{const o=t.multiplayerComponents.getLobbiesIframe(e);if(!o||!o.contentWindow)return;const s=e.getGame().getPlatformInfo();o.contentWindow.postMessage({id:"sessionInformation",isCordova:s.isCordova,devicePlatform:s.devicePlatform,navigatorPlatform:s.navigatorPlatform,hasTouch:s.hasTouch},"*")},ie=(e,o)=>{const s=V({runtimeGame:e.getGame(),gameId:o});C=r=>{ae(e,r,{checkOrigin:!0})},window.addEventListener("message",C,!0),t.multiplayerComponents.displayIframeInsideLobbiesContainer(e,s)};n.openLobbiesWindow=async e=>{if(n.isLobbiesWindowOpen(e)||t.playerAuthentication.isAuthenticationWindowOpen())return;const o=t.projectData.properties.projectUuid;if(!o){D(e,"The game ID is missing, the lobbies window cannot be opened.");return}if(T||R)return;if(!e.getGame().getRenderer().getDomElementContainer()){D(e,"The div element covering the game couldn't be found, the lobbies window cannot be displayed.");return}const r=()=>{n.removeLobbiesContainer(e)},d=t.playerAuthentication.getUserId(),c=t.playerAuthentication.getUserToken();if(!d||!c){R=!0;const{status:p}=await t.playerAuthentication.openAuthenticationWindow(e).promise;R=!1,p==="logged"&&n.openLobbiesWindow(e);return}if(t.multiplayerComponents.displayLobbies(e,r),I===null){T=!0;try{I=await j(e.getGame(),o)}catch(p){I=!1,a.error("Error while checking if the game is registered:",p),D(e,"Error while checking if the game is registered.");return}finally{T=!1}}const g=e.getGame().getRenderer().getElectron(),l=g?()=>g.shell.openExternal("https://wiki.gdevelop.io/gdevelop5/publishing/web"):()=>window.open("https://wiki.gdevelop.io/gdevelop5/publishing/web","_blank");t.multiplayerComponents.addTextsToLoadingContainer(e,I,l),I&&ie(e,o)},n.isLobbiesWindowOpen=function(e){return!!t.multiplayerComponents.getLobbiesRootContainer(e)},n.showLobbiesCloseButton=function(e,o){t.multiplayerComponents.changeLobbiesWindowCloseActionVisibility(e,o)},n.removeLobbiesContainer=function(e){de(),t.multiplayerComponents.removeLobbiesContainer(e)};const de=function(){C&&(window.removeEventListener("message",C,!0),C=null)},N=function(e){const o=e.getGame().getRenderer().getCanvas();o&&o.focus()};n.leaveGameLobby=async()=>{O(),n.handleLobbyGameEnded()}})(_=t.multiplayer||(t.multiplayer={}))})(gdjs||(gdjs={}));
1
+ var gdjs;(function(t){const r=new t.Logger("Multiplayer"),j=window.performance&&typeof window.performance.now=="function"?window.performance.now.bind(window.performance):Date.now,k=async({relativeUrl:H,method:R,body:P,dev:_})=>{const T=t.playerAuthentication.getUserId(),L=t.playerAuthentication.getUserToken();if(!T||!L)throw r.warn("Cannot fetch as a player if the player is not connected."),new Error("Cannot fetch as a player if the player is not connected.");const f=_?"https://api-dev.gdevelop.io":"https://api.gdevelop.io",A=new URL(`${f}${H}`);A.searchParams.set("playerId",T);const p=A.toString(),m={"Content-Type":"application/json",Authorization:`player-game-token ${L}`},E=await fetch(p,{method:R,headers:m,body:P});if(!E.ok)throw new Error(`Error while fetching as a player: ${E.status} ${E.statusText}`);const b=await E.text();if(b!=="OK")try{return JSON.parse(b)}catch(U){throw new Error(`Error while parsing the response: ${U}`)}};let re;(function(n){n.disableMultiplayerForTesting=!1,n._isReadyToSendOrReceiveGameUpdateMessages=!1;let P=null,_=!1,T=!1,L=!1;n._isLobbyGameRunning=!1;let A=!1,p=null,m=null,E=!1,b=null,U=null,$=!1,x=null,D=null,i=null,B=null,G=null;const ae=1e4,Q=3e4;let F=Q;const ie=1e3,ce=1e4,le=1e3,de=1e4;let S=null;const ge=12e3;n.DEFAULT_OBJECT_MAX_SYNC_RATE=30,n._objectMaxSyncRate=n.DEFAULT_OBJECT_MAX_SYNC_RATE;let w=!1;n.playerNumber=null,n.hostPeerId=null,t.registerRuntimeScenePreEventsCallback(e=>{w=e.getGame().isUsingGDevelopDevelopmentEnvironment(),!n.disableMultiplayerForTesting&&(t.multiplayerMessageManager.handleHeartbeatsToSend(),t.multiplayerMessageManager.handleJustDisconnectedPeers(e),t.multiplayerMessageManager.handleChangeInstanceOwnerMessagesReceived(e),t.multiplayerMessageManager.handleUpdateInstanceMessagesReceived(e),t.multiplayerMessageManager.handleCustomMessagesReceived(),t.multiplayerMessageManager.handleAcknowledgeMessagesReceived(),t.multiplayerMessageManager.resendClearOrCancelAcknowledgedMessages(e),t.multiplayerMessageManager.handleChangeVariableOwnerMessagesReceived(e),n._isLobbyGameRunning&&t.multiplayerMessageManager.handleSavedUpdateMessages(e),t.multiplayerMessageManager.handleUpdateGameMessagesReceived(e),t.multiplayerMessageManager.handleUpdateSceneMessagesReceived(e))}),t.registerRuntimeScenePostEventsCallback(e=>{n.disableMultiplayerForTesting||(he(e),ue(e),t.multiplayerMessageManager.handleHeartbeatsReceived(),t.multiplayerMessageManager.handleEndGameMessagesReceived(),t.multiplayerMessageManager.handleResumeGameMessagesReceived(e),t.multiplayerMessageManager.handleDestroyInstanceMessagesReceived(e),t.multiplayerVariablesManager.handleChangeVariableOwnerMessagesToSend(),t.multiplayerMessageManager.handleUpdateGameMessagesToSend(e),t.multiplayerMessageManager.handleUpdateSceneMessagesToSend(e))}),t.registerRuntimeScenePostEventsCallback(()=>{n.disableMultiplayerForTesting||(L=!1,A=!1)});const be=({runtimeGame:e,gameId:o})=>{const s="https://gd.games",a=new URL(`${s}/games/${o}/lobbies${p?`/${p}`:""}`);a.searchParams.set("gameVersion",e.getGameData().properties.version),e.getAdditionalOptions().nativeMobileApp&&a.searchParams.set("nativeMobileApp","true"),a.searchParams.set("isPreview",e.isPreview()?"true":"false"),w&&a.searchParams.set("dev","true"),m&&a.searchParams.set("connectionId",m),n.playerNumber&&a.searchParams.set("positionInLobby",n.playerNumber.toString());const g=t.playerAuthentication.getUserId();g&&a.searchParams.set("playerId",g);const d=t.playerAuthentication.getUserToken();return d&&a.searchParams.set("playerToken",d),a.searchParams.set("multiplayerVersion","2"),a.toString()};n.setObjectsSynchronizationRate=e=>{e<1||e>60?(r.warn(`Invalid rate ${e} for object synchronization. Defaulting to ${n.DEFAULT_OBJECT_MAX_SYNC_RATE}.`),n._objectMaxSyncRate=n.DEFAULT_OBJECT_MAX_SYNC_RATE):n._objectMaxSyncRate=e},n.getObjectsSynchronizationRate=()=>n._objectMaxSyncRate,n.hasLobbyGameJustStarted=()=>L,n.isLobbyGameRunning=()=>n._isLobbyGameRunning,n.isReadyToSendOrReceiveGameUpdateMessages=()=>n._isReadyToSendOrReceiveGameUpdateMessages,n.hasLobbyGameJustEnded=()=>A,n.getPlayersInLobbyCount=()=>t.multiplayerMessageManager.getNumberOfConnectedPlayers(),n.isPlayerConnected=e=>t.multiplayerMessageManager.isPlayerConnected(e),n.getCurrentPlayerNumber=()=>n.playerNumber||0,n.isCurrentPlayerHost=()=>!!n.hostPeerId&&n.hostPeerId===t.multiplayerPeerJsHelper.getCurrentId(),n.isMigratingHost=()=>!!$,n.endLobbyWhenHostLeaves=e=>{E=e},n.shouldEndLobbyWhenHostLeaves=()=>E,n.getPlayerUsername=e=>t.multiplayerMessageManager.getPlayerUsername(e),n.getCurrentPlayerUsername=()=>{const e=n.getCurrentPlayerNumber();return n.getPlayerUsername(e)};const he=e=>{const o=t.multiplayerMessageManager.getLatestPlayerWhoJustLeft();if(o){const s=n.getPlayerUsername(o);t.multiplayerComponents.displayPlayerLeftNotification(e,s),t.multiplayerMessageManager.removePlayerWhoJustLeft(),n.isCurrentPlayerHost()&&n.isReadyToSendOrReceiveGameUpdateMessages()&&O()}},ue=e=>{const o=t.multiplayerMessageManager.getLatestPlayerWhoJustJoined();if(o){const s=n.getPlayerUsername(o);t.multiplayerComponents.displayPlayerJoinedNotification(e,s),n.isCurrentPlayerHost()&&n.isReadyToSendOrReceiveGameUpdateMessages()&&O()}t.multiplayerMessageManager.removePlayerWhoJustJoined()},ne=(e,o,s=0)=>{const g=`${w?"https://api-dev.gdevelop.io":"https://api.gdevelop.io"}/game/public-game/${o}`;return fetch(g,{method:"HEAD"}).then(d=>d.status!==200?(r.warn(`Error while fetching the game: ${d.status} ${d.statusText}`),d.status===404||s>2?!1:ne(e,o,s+1)):!0,d=>(r.error("Error while fetching game:",d),!1))},fe=function(e,o){if(m){r.info("Already connected to a lobby.");return}i&&(r.warn("Already connected to a lobby. Closing the previous one."),i.close(),m=null,n.playerNumber=null,n.hostPeerId=null,p=null,i=null);const s=t.projectData.properties.projectUuid,a=t.playerAuthentication.getUserId(),g=t.playerAuthentication.getUserToken();if(!s){r.error("Cannot open lobbies if the project has no ID.");return}if(!a||!g){r.warn("Cannot open lobbies if the player is not connected.");return}const d=w?"wss://api-ws-dev.gdevelop.io/play":"wss://api-ws.gdevelop.io/play",c=new URL(d);c.searchParams.set("gameId",s),c.searchParams.set("lobbyId",o),c.searchParams.set("playerId",a),c.searchParams.set("connectionType","lobby"),c.searchParams.set("playerGameToken",g),i=new WebSocket(c.toString()),i.onopen=()=>{if(r.info("Connected to the lobby."),B=setInterval(()=>{i&&i.send(JSON.stringify({action:"heartbeat",connectionType:"lobby"}))},ae),i){i.send(JSON.stringify({action:"getConnectionId"}));const l=e.getGame().getPlatformInfo();i.send(JSON.stringify({action:"sessionInformation",connectionType:"lobby",isCordova:l.isCordova,devicePlatform:l.devicePlatform,navigatorPlatform:l.navigatorPlatform,hasTouch:l.hasTouch,supportedCompressionMethods:l.supportedCompressionMethods}))}},i.onmessage=l=>{if(l.data){const h=JSON.parse(l.data);switch(h.type){case"connectionId":{const u=h.data,v=u.connectionId,J=u.positionInLobby,ke=u.validIceServers||[],He=u.brokerServerConfig;if(!v||!J){r.error("No connectionId or position received"),t.multiplayerComponents.displayErrorNotification(e),i&&i.close();return}me({runtimeScene:e,connectionId:v,positionInLobby:J,lobbyId:o,playerId:a,playerToken:g,validIceServers:ke,brokerServerConfig:He});break}case"lobbyUpdated":{const v=h.data.positionInLobby;ye({runtimeScene:e,positionInLobby:v});break}case"gameCountdownStarted":{const v=h.data.compressionMethod||"none";we({runtimeScene:e,compressionMethod:v});break}case"gameStarted":{F=h.data.heartbeatInterval||Q,Me({runtimeScene:e});break}case"peerId":{const u=h.data;if(!u){r.error("No message received");return}const v=u.peerId,J=u.compressionMethod;if(!v||!J){r.error("Malformed message received");return}Ce({peerId:v,compressionMethod:J});break}}}},i.onclose=()=>{if(n._isLobbyGameRunning||r.info("Disconnected from the lobby."),m=null,i=null,B&&clearInterval(B),n._isLobbyGameRunning)return;const l=t.multiplayerComponents.getLobbiesIframe(e);!l||!l.contentWindow||l.contentWindow.postMessage({id:"lobbyLeft"},"*")}},me=function({runtimeScene:e,connectionId:o,positionInLobby:s,lobbyId:a,playerId:g,playerToken:d,validIceServers:c,brokerServerConfig:l}){if(c.length)for(const u of c)t.multiplayerPeerJsHelper.useCustomICECandidate(u.urls,u.username,u.credential);l?t.multiplayerPeerJsHelper.useCustomBrokerServer(l.hostname,l.port,l.path,l.key,l.secure):t.multiplayerPeerJsHelper.useDefaultBrokerServer(),m=o,n.playerNumber=s,p=a;const h=t.multiplayerComponents.getLobbiesIframe(e);if(!h||!h.contentWindow){r.error("The lobbies iframe is not opened, cannot send the join message.");return}h.contentWindow.postMessage({id:"lobbyJoined",lobbyId:a,playerId:g,playerToken:d,connectionId:m,positionInLobby:s},"https://gd.games")},z=function(){i&&i.close(),m=null,n.playerNumber=null,n.hostPeerId=null,p=null,i=null},ye=function({runtimeScene:e,positionInLobby:o}){n.playerNumber=o;const s=t.multiplayerComponents.getLobbiesIframe(e);!s||!s.contentWindow||s.contentWindow.postMessage({id:"lobbyUpdated",positionInLobby:o},"*")},we=function({runtimeScene:e,compressionMethod:o}){t.multiplayerPeerJsHelper.setCompressionMethod(o),n.getCurrentPlayerNumber()===1&&Ee();const s=t.multiplayerComponents.getLobbiesIframe(e);if(!s||!s.contentWindow){r.info("The lobbies iframe is not opened, not sending message.");return}s.contentWindow.postMessage({id:"gameCountdownStarted"},"*"),t.multiplayerComponents.hideLobbiesCloseButtonTemporarily(e)},O=async function(){const e=t.projectData.properties.projectUuid;if(!e||!p){r.error("Cannot keep the lobby playing without the game ID or lobby ID.");return}const o=`/play/game/${e}/public-lobby/${p}/action/heartbeat`,s=t.multiplayerMessageManager.getConnectedPlayers();try{await k({relativeUrl:o,method:"POST",body:JSON.stringify({players:s}),dev:w})}catch(a){r.error("Error while sending heartbeat, retrying:",a);try{await k({relativeUrl:o,method:"POST",body:JSON.stringify({players:s}),dev:w})}catch(g){r.error("Error while sending heartbeat a second time. Giving up:",g)}}},Me=function({runtimeScene:e}){const o=t.multiplayerPeerJsHelper.getAllPeers();if(!n.isCurrentPlayerHost()&&o.length===0){t.multiplayerComponents.displayConnectionErrorNotification(e),z(),n.removeLobbiesContainer(e),X(e);return}n.isCurrentPlayerHost()&&(G=setInterval(async()=>{await O()},F)),r.info("Lobby game has started."),t.multiplayerMessageManager.handleSavedUpdateMessages(e),n._isReadyToSendOrReceiveGameUpdateMessages=!0,L=!0,n._isLobbyGameRunning=!0,n.removeLobbiesContainer(e),i&&i.close(),X(e)};n.handleLobbyGameEnded=function(){r.info("Lobby game has ended."),A=!0,n._isLobbyGameRunning=!1,p=null,n.playerNumber=null,n.hostPeerId=null,n._isReadyToSendOrReceiveGameUpdateMessages=!1,G&&(clearInterval(G),G=null),t.multiplayerPeerJsHelper.disconnectFromAllPeers(),t.multiplayerMessageManager.clearAllMessagesTempData()};const Ce=function({peerId:e,compressionMethod:o}){t.multiplayerPeerJsHelper.setCompressionMethod(o);const s=t.multiplayerPeerJsHelper.getCurrentId();if(!s){r.error("No peerId found, the player does not seem connected to the broker server.");return}if(s===e){r.info("Received our own peerId, ignoring.");return}n.hostPeerId=e,t.multiplayerPeerJsHelper.connect(e)},Ie=function(){if(!i){r.error("No connection to send the start countdown message. Are you connected to a lobby?");return}i.send(JSON.stringify({action:"startGameCountdown",connectionType:"lobby"}))},ve=function(){if(!i){r.error("No connection to send the start countdown message. Are you connected to a lobby?");return}i.send(JSON.stringify({action:"startGame",connectionType:"lobby"})),n._isReadyToSendOrReceiveGameUpdateMessages=!0},Pe=function(){if(!i){r.error("No connection to send the start countdown message. Are you connected to a lobby?");return}i.send(JSON.stringify({action:"joinGame",connectionType:"lobby"}))};n.markConnectionAsConnected=function(){!i||i.send(JSON.stringify({action:"updateConnection",connectionType:"lobby",status:"connected",peerId:t.multiplayerPeerJsHelper.getCurrentId()}))};const I=function(e){b=null,U=null,x=null,S&&(clearTimeout(S),S=null),$=!1,n.hostPeerId?t.multiplayerComponents.showHostMigrationFinishedNotification(e):t.multiplayerComponents.showHostMigrationFailedNotification(e)};n.resumeGame=async function(e){n.isCurrentPlayerHost()&&(t.multiplayerMessageManager.sendResumeGameMessage(),await O(),G=setInterval(async()=>{await O()},F)),I(e)};const oe=async function({runtimeScene:e}){if(!b||!U)return;try{const s=`/play/game/${b.gameId}/public-lobby/${b.lobbyId}/lobby-change-host-request?peerId=${t.multiplayerPeerJsHelper.getCurrentId()}`;b=await k({relativeUrl:s,method:"GET",dev:w})}catch(s){r.error("Error while trying to retrieve the lobby change host request:",s),n.handleLobbyGameEnded(),I(e);return}if(!b)throw new Error("No lobby change host request received.");const o=b.newHostPeerId;if(!o){if(r.info("No new host picked yet."),j()-U>ce){r.error("Timeout while waiting for the lobby host change. Giving up."),n.handleLobbyGameEnded(),I(e);return}r.info("Retrying..."),setTimeout(()=>{oe({runtimeScene:e})},ie);return}try{const s=b.newLobbyId,a=b.newPlayers;if(!s||!a){r.error("Change host request is incomplete. Cannot change host."),n.handleLobbyGameEnded(),I(e);return}n.hostPeerId=o,x=j(),p=s,o===t.multiplayerPeerJsHelper.getCurrentId()?(r.info(`We are the new host. Switching to lobby ${s} and awaiting for ${a.length-1} player(s) to connect.`),await se({runtimeScene:e})):(r.info(`Connecting to new host and switching lobby to ${s}.`),t.multiplayerPeerJsHelper.connect(o),S=setTimeout(()=>{r.error("Timeout while waiting for the game to resume. Leaving the lobby."),n.handleLobbyGameEnded(),I(e)},ge))}catch(s){r.error("Error while trying to change host:",s),n.handleLobbyGameEnded(),I(e)}},se=async function({runtimeScene:e}){if(!b)return;const o=b.newPlayers;if(!o){r.error("No expected players in the lobby change host request."),n.handleLobbyGameEnded(),I(e);return}const s=o.map(c=>c.playerNumber);t.multiplayerMessageManager.getConnectedPlayers().map(c=>c.playerNumber).filter(c=>!s.includes(c)).map(c=>{r.info(`Player ${c} left during the host migration. Marking as disconnected.`),t.multiplayerMessageManager.markPlayerAsDisconnected({runtimeScene:e,playerNumber:c})});const d=s.filter(c=>c!==n.playerNumber&&!t.multiplayerMessageManager.hasReceivedHeartbeatFromPlayer(c));if(d.length===0){r.info("All expected players are connected. Resuming the game."),await n.resumeGame(e);return}if(x&&j()-x>de&&d.length>0){r.error(`Timeout while waiting for players ${d.join(", ")} to connect. Assume they disconnected.`),d.map(c=>{t.multiplayerMessageManager.markPlayerAsDisconnected({runtimeScene:e,playerNumber:c})}),await n.resumeGame(e);return}setTimeout(()=>{se({runtimeScene:e})},le)};n.handleHostDisconnected=async function({runtimeScene:e}){if(!n._isLobbyGameRunning)return;b&&(n.handleLobbyGameEnded(),I(e));const o=t.projectData.properties.projectUuid;if(!o||!p){r.error("Cannot ask for a host change without the game ID or lobby ID.");return}try{$=!0,t.multiplayerComponents.displayHostMigrationNotification(e);const s=`/play/game/${o}/public-lobby/${p}/lobby-change-host-request`,a=t.multiplayerMessageManager.getPlayersInfo(),g=Object.keys(a).map(l=>({playerNumber:parseInt(l,10),playerId:a[l].playerId,ping:a[l].ping})),d=JSON.stringify({playersInfo:g,peerId:t.multiplayerPeerJsHelper.getCurrentId()});b=await k({relativeUrl:s,method:"POST",body:d,dev:w}),U=j(),await oe({runtimeScene:e})}catch(s){r.error("Error while trying to change host:",s),n.handleLobbyGameEnded(),I(e)}},n.endLobbyGame=async function(){if(!n.isLobbyGameRunning())return;if(!n.isCurrentPlayerHost()){r.error("Only the host can end the game.");return}n._isLobbyGameRunning=!1,r.info("Ending the lobby game."),t.multiplayerMessageManager.sendEndGameMessage();const e=t.projectData.properties.projectUuid;if(!e||!p){r.error("Cannot end the lobby without the game ID or lobby ID.");return}const o=`/play/game/${e}/public-lobby/${p}/action/end`;try{await k({relativeUrl:o,method:"POST",body:JSON.stringify({}),dev:w})}catch(s){r.error("Error while ending the game:",s)}n.handleLobbyGameEnded()};const Ee=function(){if(!i){r.error("No connection to send the message. Are you connected to a lobby?");return}const e=t.multiplayerPeerJsHelper.getCurrentId();if(!e){r.error("No peerId found, the player doesn't seem connected to the broker server.");return}i.send(JSON.stringify({action:"sendPeerId",connectionType:"lobby",peerId:e})),n.hostPeerId=e},Re=function(e,o,{checkOrigin:s}){if(!(s&&!["https://gd.games","http://localhost:4000"].includes(o.origin))){if(!o.data.id)throw new Error("Malformed message");switch(o.data.id){case"lobbiesListenerReady":{Te(e);break}case"joinLobby":{if(!o.data.lobbyId)throw new Error("Malformed message.");fe(e,o.data.lobbyId);break}case"startGameCountdown":{Ie();break}case"startGame":{ve();break}case"leaveLobby":{z();break}case"joinGame":{Pe();break}}}},K=function(e,o){r.error(o),n.removeLobbiesContainer(e),X(e)},Te=e=>{const o=t.multiplayerComponents.getLobbiesIframe(e);if(!o||!o.contentWindow)return;const s=e.getGame().getPlatformInfo();o.contentWindow.postMessage({id:"sessionInformation",isCordova:s.isCordova,devicePlatform:s.devicePlatform,navigatorPlatform:s.navigatorPlatform,hasTouch:s.hasTouch},"*")},Le=(e,o)=>{const s=be({runtimeGame:e.getGame(),gameId:o});D=a=>{Re(e,a,{checkOrigin:!0})},window.addEventListener("message",D,!0),t.multiplayerComponents.displayIframeInsideLobbiesContainer(e,s)};n.openLobbiesWindow=async e=>{if(n.isLobbiesWindowOpen(e)||t.playerAuthentication.isAuthenticationWindowOpen())return;const o=t.projectData.properties.projectUuid;if(!o){K(e,"The game ID is missing, the lobbies window cannot be opened.");return}if(_||T)return;if(!e.getGame().getRenderer().getDomElementContainer()){K(e,"The div element covering the game couldn't be found, the lobbies window cannot be displayed.");return}const a=()=>{n.removeLobbiesContainer(e)},g=t.playerAuthentication.getUserId(),d=t.playerAuthentication.getUserToken();if(!g||!d){T=!0;const{status:h}=await t.playerAuthentication.openAuthenticationWindow(e).promise;T=!1,h==="logged"&&n.openLobbiesWindow(e);return}if(t.multiplayerComponents.displayLobbies(e,a),P===null){_=!0;try{P=await ne(e.getGame(),o)}catch(h){P=!1,r.error("Error while checking if the game is registered:",h),K(e,"Error while checking if the game is registered.");return}finally{_=!1}}const c=e.getGame().getRenderer().getElectron(),l=c?()=>c.shell.openExternal("https://wiki.gdevelop.io/gdevelop5/publishing/web"):()=>window.open("https://wiki.gdevelop.io/gdevelop5/publishing/web","_blank");t.multiplayerComponents.addTextsToLoadingContainer(e,P,l),P&&Le(e,o)},n.isLobbiesWindowOpen=function(e){return!!t.multiplayerComponents.getLobbiesRootContainer(e)},n.showLobbiesCloseButton=function(e,o){t.multiplayerComponents.changeLobbiesWindowCloseActionVisibility(e,o)},n.removeLobbiesContainer=function(e){Ne(),t.multiplayerComponents.removeLobbiesContainer(e)};const Ne=function(){D&&(window.removeEventListener("message",D,!0),D=null)},X=function(e){const o=e.getGame().getRenderer().getCanvas();o&&o.focus()};n.leaveGameLobby=async()=>{z(),n.handleLobbyGameEnded()}})(re=t.multiplayer||(t.multiplayer={}))})(gdjs||(gdjs={}));
2
2
  //# sourceMappingURL=multiplayertools.js.map