gdcore-tools 2.0.0-beta6 → 2.0.0-beta8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Runtime/Cordova/config.xml +4 -0
- package/dist/Runtime/Cordova/package.json +12 -20
- package/dist/Runtime/CustomRuntimeObject.js +1 -1
- package/dist/Runtime/CustomRuntimeObject.js.map +2 -2
- package/dist/Runtime/CustomRuntimeObjectInstanceContainer.js +1 -1
- package/dist/Runtime/CustomRuntimeObjectInstanceContainer.js.map +2 -2
- package/dist/Runtime/Extensions/3D/A_RuntimeObject3D.js +1 -1
- package/dist/Runtime/Extensions/3D/A_RuntimeObject3D.js.map +2 -2
- package/dist/Runtime/Extensions/3D/AmbientLight.js +1 -1
- package/dist/Runtime/Extensions/3D/AmbientLight.js.map +2 -2
- package/dist/Runtime/Extensions/3D/CustomRuntimeObject3D.js +1 -1
- package/dist/Runtime/Extensions/3D/CustomRuntimeObject3D.js.map +2 -2
- package/dist/Runtime/Extensions/3D/DirectionalLight.js +1 -1
- package/dist/Runtime/Extensions/3D/DirectionalLight.js.map +2 -2
- package/dist/Runtime/Extensions/3D/ExponentialFog.js +1 -1
- package/dist/Runtime/Extensions/3D/ExponentialFog.js.map +2 -2
- package/dist/Runtime/Extensions/3D/HemisphereLight.js +1 -1
- package/dist/Runtime/Extensions/3D/HemisphereLight.js.map +2 -2
- package/dist/Runtime/Extensions/3D/JsExtension.js +419 -228
- package/dist/Runtime/Extensions/3D/LinearFog.js +1 -1
- package/dist/Runtime/Extensions/3D/LinearFog.js.map +2 -2
- package/dist/Runtime/Extensions/3D/Model3DRuntimeObject.js +1 -1
- package/dist/Runtime/Extensions/3D/Model3DRuntimeObject.js.map +2 -2
- package/dist/Runtime/Extensions/AdvancedWindow/electron-advancedwindowtools.js +1 -1
- package/dist/Runtime/Extensions/AdvancedWindow/electron-advancedwindowtools.js.map +2 -2
- package/dist/Runtime/Extensions/AnchorBehavior/anchorruntimebehavior.js +1 -1
- package/dist/Runtime/Extensions/AnchorBehavior/anchorruntimebehavior.js.map +2 -2
- package/dist/Runtime/Extensions/BBText/JsExtension.js +45 -42
- package/dist/Runtime/Extensions/BBText/bbtextruntimeobject.js +1 -1
- package/dist/Runtime/Extensions/BBText/bbtextruntimeobject.js.map +2 -2
- package/dist/Runtime/Extensions/BitmapText/JsExtension.js +40 -49
- package/dist/Runtime/Extensions/BitmapText/bitmaptextruntimeobject.js +1 -1
- package/dist/Runtime/Extensions/BitmapText/bitmaptextruntimeobject.js.map +2 -2
- package/dist/Runtime/Extensions/Effects/bevel-pixi-filter.js +1 -1
- package/dist/Runtime/Extensions/Effects/bevel-pixi-filter.js.map +2 -2
- package/dist/Runtime/Extensions/Effects/color-replace-pixi-filter.js +1 -1
- package/dist/Runtime/Extensions/Effects/color-replace-pixi-filter.js.map +2 -2
- package/dist/Runtime/Extensions/Effects/drop-shadow-pixi-filter.js +1 -1
- package/dist/Runtime/Extensions/Effects/drop-shadow-pixi-filter.js.map +2 -2
- package/dist/Runtime/Extensions/Effects/glow-pixi-filter.js +1 -1
- package/dist/Runtime/Extensions/Effects/glow-pixi-filter.js.map +2 -2
- package/dist/Runtime/Extensions/Effects/outline-pixi-filter.js +1 -1
- package/dist/Runtime/Extensions/Effects/outline-pixi-filter.js.map +2 -2
- package/dist/Runtime/Extensions/ExampleJsExtension/JsExtension.js +18 -21
- package/dist/Runtime/Extensions/Firebase/B_firebasetools/C_firebasetools.js +1 -1
- package/dist/Runtime/Extensions/Firebase/B_firebasetools/C_firebasetools.js.map +2 -2
- package/dist/Runtime/Extensions/JsExtensionTypes.d.ts +8 -2
- package/dist/Runtime/Extensions/Leaderboards/leaderboardstools.js +1 -1
- package/dist/Runtime/Extensions/Leaderboards/leaderboardstools.js.map +2 -2
- package/dist/Runtime/Extensions/Lighting/JsExtension.js +50 -38
- package/dist/Runtime/Extensions/Lighting/lightruntimeobject-pixi-renderer.js +1 -1
- package/dist/Runtime/Extensions/Lighting/lightruntimeobject-pixi-renderer.js.map +2 -2
- package/dist/Runtime/Extensions/Multiplayer/JsExtension.js +15 -0
- package/dist/Runtime/Extensions/Multiplayer/multiplayerobjectruntimebehavior.js +1 -1
- package/dist/Runtime/Extensions/Multiplayer/multiplayerobjectruntimebehavior.js.map +2 -2
- package/dist/Runtime/Extensions/Multiplayer/multiplayertools.js.map +2 -2
- package/dist/Runtime/Extensions/PanelSpriteObject/panelspriteruntimeobject.js +1 -1
- package/dist/Runtime/Extensions/PanelSpriteObject/panelspriteruntimeobject.js.map +2 -2
- package/dist/Runtime/Extensions/ParticleSystem/particleemitterobject-pixi-renderer.js +1 -1
- package/dist/Runtime/Extensions/ParticleSystem/particleemitterobject-pixi-renderer.js.map +2 -2
- package/dist/Runtime/Extensions/ParticleSystem/particleemitterobject.js +1 -1
- package/dist/Runtime/Extensions/ParticleSystem/particleemitterobject.js.map +2 -2
- package/dist/Runtime/Extensions/Physics2Behavior/JsExtension.js +76 -24
- package/dist/Runtime/Extensions/Physics2Behavior/physics2runtimebehavior.js +1 -1
- package/dist/Runtime/Extensions/Physics2Behavior/physics2runtimebehavior.js.map +2 -2
- package/dist/Runtime/Extensions/PlayerAuthentication/playerauthenticationtools.js +1 -1
- package/dist/Runtime/Extensions/PlayerAuthentication/playerauthenticationtools.js.map +2 -2
- package/dist/Runtime/Extensions/PrimitiveDrawing/shapepainterruntimeobject.js +1 -1
- package/dist/Runtime/Extensions/PrimitiveDrawing/shapepainterruntimeobject.js.map +2 -2
- package/dist/Runtime/Extensions/Spine/JsExtension.js +45 -36
- package/dist/Runtime/Extensions/Spine/spineruntimeobject.js +1 -1
- package/dist/Runtime/Extensions/Spine/spineruntimeobject.js.map +2 -2
- package/dist/Runtime/Extensions/TextInput/JsExtension.js +52 -55
- package/dist/Runtime/Extensions/TextInput/textinputruntimeobject-pixi-renderer.js +1 -1
- package/dist/Runtime/Extensions/TextInput/textinputruntimeobject-pixi-renderer.js.map +2 -2
- package/dist/Runtime/Extensions/TextInput/textinputruntimeobject.js +1 -1
- package/dist/Runtime/Extensions/TextInput/textinputruntimeobject.js.map +2 -2
- package/dist/Runtime/Extensions/TextObject/textruntimeobject-pixi-renderer.js +1 -1
- package/dist/Runtime/Extensions/TextObject/textruntimeobject-pixi-renderer.js.map +2 -2
- package/dist/Runtime/Extensions/TextObject/textruntimeobject.js +1 -1
- package/dist/Runtime/Extensions/TextObject/textruntimeobject.js.map +2 -2
- package/dist/Runtime/Extensions/TileMap/JsExtension.js +430 -261
- package/dist/Runtime/Extensions/TileMap/TileMapRuntimeManager.js +1 -1
- package/dist/Runtime/Extensions/TileMap/TileMapRuntimeManager.js.map +2 -2
- package/dist/Runtime/Extensions/TileMap/collision/TransformedTileMap.js +1 -1
- package/dist/Runtime/Extensions/TileMap/collision/TransformedTileMap.js.map +2 -2
- package/dist/Runtime/Extensions/TileMap/helper/TileMapHelper.js +1 -1
- package/dist/Runtime/Extensions/TileMap/helper/TileMapHelper.js.map +1 -1
- package/dist/Runtime/Extensions/TileMap/helper/dts/model/TileMapModel.d.ts.map +1 -1
- package/dist/Runtime/Extensions/TileMap/helper/dts/render/TileMapPixiHelper.d.ts +1 -0
- package/dist/Runtime/Extensions/TileMap/helper/dts/render/TileMapPixiHelper.d.ts.map +1 -1
- package/dist/Runtime/Extensions/TileMap/simpletilemapruntimeobject.js +1 -1
- package/dist/Runtime/Extensions/TileMap/simpletilemapruntimeobject.js.map +2 -2
- package/dist/Runtime/Extensions/TileMap/tilemapcollisionmaskruntimeobject.js +1 -1
- package/dist/Runtime/Extensions/TileMap/tilemapcollisionmaskruntimeobject.js.map +2 -2
- package/dist/Runtime/Extensions/TileMap/tilemapruntimeobject.js +1 -1
- package/dist/Runtime/Extensions/TileMap/tilemapruntimeobject.js.map +2 -2
- package/dist/Runtime/Extensions/TiledSpriteObject/tiledspriteruntimeobject.js +1 -1
- package/dist/Runtime/Extensions/TiledSpriteObject/tiledspriteruntimeobject.js.map +2 -2
- package/dist/Runtime/Extensions/Video/JsExtension.js +35 -44
- package/dist/Runtime/Extensions/Video/videoruntimeobject.js +1 -1
- package/dist/Runtime/Extensions/Video/videoruntimeobject.js.map +2 -2
- package/dist/Runtime/ResourceLoader.js +1 -1
- package/dist/Runtime/ResourceLoader.js.map +2 -2
- package/dist/Runtime/RuntimeInstanceContainer.js.map +2 -2
- package/dist/Runtime/debugger-client/hot-reloader.js +2 -2
- package/dist/Runtime/debugger-client/hot-reloader.js.map +2 -2
- package/dist/Runtime/events-tools/inputtools.js +1 -1
- package/dist/Runtime/events-tools/inputtools.js.map +2 -2
- package/dist/Runtime/events-tools/objecttools.js +1 -1
- package/dist/Runtime/events-tools/objecttools.js.map +2 -2
- package/dist/Runtime/gd.js +1 -1
- package/dist/Runtime/gd.js.map +2 -2
- package/dist/Runtime/howler-sound-manager/howler-sound-manager.js +1 -1
- package/dist/Runtime/howler-sound-manager/howler-sound-manager.js.map +2 -2
- package/dist/Runtime/inputmanager.js +1 -1
- package/dist/Runtime/inputmanager.js.map +2 -2
- package/dist/Runtime/pixi-renderers/CustomRuntimeObject2DPixiRenderer.js +1 -1
- package/dist/Runtime/pixi-renderers/CustomRuntimeObject2DPixiRenderer.js.map +2 -2
- package/dist/Runtime/pixi-renderers/pixi-filters-tools.js +1 -1
- package/dist/Runtime/pixi-renderers/pixi-filters-tools.js.map +2 -2
- package/dist/Runtime/pixi-renderers/runtimegame-pixi-renderer.js +1 -1
- package/dist/Runtime/pixi-renderers/runtimegame-pixi-renderer.js.map +2 -2
- package/dist/Runtime/pixi-renderers/spriteruntimeobject-pixi-renderer.js +1 -1
- package/dist/Runtime/pixi-renderers/spriteruntimeobject-pixi-renderer.js.map +2 -2
- package/dist/Runtime/runtimegame.js +1 -1
- package/dist/Runtime/runtimegame.js.map +1 -1
- package/dist/Runtime/runtimeobject.js +1 -1
- package/dist/Runtime/runtimeobject.js.map +2 -2
- package/dist/Runtime/spriteruntimeobject.js +1 -1
- package/dist/Runtime/spriteruntimeobject.js.map +2 -2
- package/dist/Runtime/types/project-data.d.ts +5 -0
- package/dist/lib/libGD.cjs +1 -1
- package/dist/lib/libGD.d.cts +5 -0
- package/dist/lib/libGD.wasm +0 -0
- package/dist/loaders.cjs +3 -1
- package/dist/loaders.d.cts +2 -0
- package/gd.d.ts +74 -30
- package/package.json +3 -3
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var gdjs;(function(d){const a=new d.Logger("Player Authentication"),u=d.playerAuthenticationComponents;let Q;(function(r){let w=null,A=null,p=null,M=!1,b=!1,D=null,y=null,k=null,W=null,f=null,l=null,j=null,x=null,I=null,v=null,g=null;const X=e=>{F(e)==="web"&&($(),I=t=>{_({runtimeScene:e,event:t,checkOrigin:!0})},window.addEventListener("message",I,!0),a.info("Notifying parent window that player authentication is ready."),window.parent.postMessage({id:"playerAuthReady"},"*"),j=setTimeout(()=>{a.info("Removing automatic games platform authentication listener."),$()},3e3))},Y=e=>{const t=e.getGame().getAdditionalOptions();if(t&&t.isPreview){const n=t.playerId,o=t.playerToken,i=t.playerUsername;n&&o&&(a.info(`Automatically logging in the player with ID ${n} as it's a preview.`),S({userId:n,username:i||null,userToken:o}),K(e))}};d.registerRuntimeScenePostEventsCallback(()=>{M=!1}),d.registerFirstRuntimeSceneLoadedCallback(e=>{Y(e),X(e)});const O=e=>`${e}_authenticatedUser`,U=({runtimeGame:e,gameId:t,connectionId:n})=>`https://gd.games/auth?gameId=${t}${n?`&connectionId=${n}`:""}${e.isUsingGDevelopDevelopmentEnvironment()?"&dev=true":""}&allowLoginProviders=true`,F=e=>e.getGame().getRenderer().getElectron()?"electron":Z(e)?"web-iframe":typeof cordova!="undefined"?"cordova-websocket":"web",Z=e=>{const t=e.getGame().getAdditionalOptions();return t&&t.isPreview&&t.allowAuthenticationUsingIframeForPreview};r.isAuthenticated=()=>(b||R(),p!==null),r.hasLoggedIn=()=>M,r.getUsername=()=>(b||R(),w||""),r.getUserToken=()=>(b||R(),p||null),r.getUserId=()=>(b||R(),A||"");const B=(e,t,n=0)=>{const i=`${e.isUsingGDevelopDevelopmentEnvironment()?"https://api-dev.gdevelop.io":"https://api.gdevelop.io"}/game/public-game/${t}`;return fetch(i,{method:"HEAD"}).then(s=>s.status!==200?(a.warn(`Error while fetching the game: ${s.status} ${s.statusText}`),s.status===404||n>2?!1:B(e,t,n+1)):!0,s=>(a.error("Error while fetching game:",s),!1))};r.logout=e=>{w=null,p=null,A=null;const t=d.projectData.properties.projectUuid;if(!t){a.error("Missing game id in project properties.");return}window.localStorage.removeItem(O(t)),G(e),r.removeAuthenticationBanner(e);const n=e.getGame().getRenderer().getDomElementContainer();if(!n){h(e,"The div element covering the game couldn't be found, the authentication banner cannot be displayed.");return}u.displayLoggedOutNotification(n)};const R=()=>{const e=d.projectData.properties.projectUuid;if(!e){a.error("Missing game id in project properties.");return}try{const t=window.localStorage.getItem(O(e));if(!t){b=!0;return}const n=JSON.parse(t);w=n.username,A=n.userId,p=n.userToken,b=!0}catch(t){a.warn("Unable to read authentication details from localStorage. Player authentication will not be available.",t)}},G=e=>{if(r.removeAuthenticationContainer(e),J(),g&&(a.info("Closing authentication websocket connection."),g.close(),g=null),D&&(D.close(),D=null),typeof SafariViewController!="undefined")try{SafariViewController.hide()}catch{a.info("Could not hide login window. Waiting for user to do it.")}},S=({username:e,userId:t,userToken:n})=>{e||a.warn("The authenticated player does not have a username"),w=e,A=t,p=n,M=!0;const o=d.projectData.properties.projectUuid;if(!o){a.error("Missing game id in project properties.");return}try{window.localStorage.setItem(O(o),JSON.stringify({username:w,userId:A,userToken:p}))}catch(i){a.warn("Unable to save the authentication details to localStorage. Player authentication will not be available.",i)}};r.login=({runtimeScene:e,userId:t,username:n,userToken:o})=>{S({userId:t,username:n,userToken:o}),G(e),r.removeAuthenticationBanner(e);const i=e.getGame().getRenderer().getDomElementContainer();if(!i){h(e,"The div element covering the game couldn't be found, the authentication banner cannot be displayed.");return}u.displayLoggedInNotification(i,w||"Anonymous")};const _=function({runtimeScene:e,event:t,checkOrigin:n,onDone:o}){if(!(n&&!["https://liluo.io","https://gd.games"].includes(t.origin))){if(!t.data.id)throw new Error("Malformed message");switch(t.data.id){case"authenticationResult":{if(!(t.data.body&&t.data.body.token))throw new Error("Malformed message.");r.login({runtimeScene:e,userId:t.data.body.userId,username:t.data.body.username,userToken:t.data.body.token}),P(e),o&&o("logged");break}case"alreadyAuthenticated":{if(!(t.data.body&&t.data.body.token))throw new Error("Malformed message.");S({userId:t.data.body.userId,username:t.data.body.username,userToken:t.data.body.token}),$(),K(e);break}}}},h=function(e,t){a.error(t),G(e);const n=e.getGame().getRenderer().getDomElementContainer();if(!n){h(e,"The div element covering the game couldn't be found, the authentication banner cannot be displayed.");return}u.displayErrorNotification(n),P(e)},ee=e=>{J();const t=15*60*1e3;x=setTimeout(()=>{a.info("Authentication window did not send message in time. Closing it."),G(e),P(e)},t)},J=()=>{x&&clearTimeout(x)},V=function(e){const t=()=>{r.removeAuthenticationBanner(e)},n=()=>{r.openAuthenticationWindow(e)};return p?u.computeAuthenticatedBanner(n,t,w):u.computeNotAuthenticatedBanner(n,t)};r.displayAuthenticationBanner=function(e){if(l){l.style.opacity="1";return}b||R();const t=e.getGame().getRenderer().getDomElementContainer();if(!t){h(e,"The div element covering the game couldn't be found, the authentication banner cannot be displayed.");return}l=V(e),t.appendChild(l)};const K=function(e){if(!l)return;const t=e.getGame().getRenderer().getDomElementContainer();if(!t){h(e,"The div element covering the game couldn't be found, the authentication banner cannot be displayed.");return}const n=l;l=V(e),t.replaceChild(l,n)},q=(e,t,n)=>new Promise(o=>{let i=!1;const s=e.getGame().isUsingGDevelopDevelopmentEnvironment()?`wss://api-ws-dev.gdevelop.io/play?gameId=${t}&connectionType=login`:`wss://api-ws.gdevelop.io/play?gameId=${t}&connectionType=login`;g=new WebSocket(s),g.onopen=()=>{a.info("Opened authentication websocket connection."),g&&g.send(JSON.stringify({action:"getConnectionId"}))},g.onerror=()=>{a.info("Error in authentication websocket connection."),i||(i=!0,o("errored")),h(e,"Error while connecting to the authentication server.")},g.onclose=()=>{a.info("Closing authentication websocket connection."),i||(i=!0,o("dismissed"))},g.onmessage=c=>{if(c.data){const C=JSON.parse(c.data);switch(C.type){case"authenticationResult":{const m=C.data;r.login({runtimeScene:e,userId:m.userId,username:m.username,userToken:m.token}),P(e),i=!0,o("logged");break}case"connectionId":{const E=C.data.connectionId;if(!E){a.error("No WebSocket connectionId received"),i=!0,o("errored");return}a.info("WebSocket connectionId received."),n({connectionId:E,resolve:o});break}}}}}),ne=(e,t)=>q(e,t,({connectionId:n})=>{const o=U({runtimeGame:e.getGame(),gameId:t,connectionId:n}),i=e.getGame().getRenderer().getElectron(),s=()=>i.shell.openExternal(o);s(),f&&u.addAuthenticationUrlToTextsContainer(s,f)}),oe=(e,t)=>q(e,t,({connectionId:n,resolve:o})=>{const i=U({runtimeGame:e.getGame(),gameId:t,connectionId:n});SafariViewController.isAvailable(function(s){s?SafariViewController.show({url:i,hidden:!1,animated:!0,transition:"slide",enterReaderModeIfAvailable:!1,barColor:"#000000",tintColor:"#ffffff",controlTintColor:"#ffffff"},function(c){c.event==="closed"&&o("dismissed")},function(c){a.log("Error opening webview: "+JSON.stringify(c)),o("errored")}):(a.error("Plugin SafariViewController is not available"),o("errored"))})}),ie=(e,t)=>new Promise(n=>{const o=U({runtimeGame:e.getGame(),gameId:t});let i=!1;v=E=>{_({runtimeScene:e,event:E,checkOrigin:!0,onDone:L=>{i||(i=!0,n(L))}})},window.addEventListener("message",v,!0);const s=screen.width/2-500/2,c=screen.height/2-600/2,C=`left=${s},top=${c},width=500,height=600`,m=()=>{D=window.open(o,"authentication",C)};m(),f&&u.addAuthenticationUrlToTextsContainer(m,f)}),ae=(e,t)=>new Promise(n=>{if(!W||!k||!f){a.error("Can't open an authentication iframe - no iframe container, loader container or text container was opened for it.");return}const o=U({runtimeGame:e.getGame(),gameId:t});v=i=>{_({runtimeScene:e,event:i,checkOrigin:!0,onDone:n})},window.addEventListener("message",v,!0),u.displayIframeInsideAuthenticationContainer(W,k,f,o)});r.openAuthenticationWindow=e=>new d.PromiseTask(new Promise(t=>{const n=e.getGame().getRenderer().getDomElementContainer();if(!n){h(e,"The div element covering the game couldn't be found, the authentication window cannot be displayed."),t({status:"errored"});return}const o=d.projectData.properties.projectUuid;if(!o){h(e,"The game ID is missing, the authentication window cannot be opened."),t({status:"errored"});return}let i=!1;const s=()=>{G(e),r.displayAuthenticationBanner(e),i=!0,t({status:"dismissed"})};l&&(l.style.opacity="0");const c=F(e),{rootContainer:C,loaderContainer:m,iframeContainer:E}=u.computeAuthenticationContainer(s);y=C,k=m,W=E,n.appendChild(y),(async()=>{const L=await B(e.getGame(),o);if(k){const z=e.getGame().getRenderer().getElectron(),de=z?()=>z.shell.openExternal("https://wiki.gdevelop.io/gdevelop5/publishing/web"):null;f=u.addAuthenticationTextsToLoadingContainer(k,c,L,de)}if(!L)return;ee(e);let T;switch(c){case"electron":T=await ne(e,o);break;case"cordova-websocket":T=await oe(e,o);break;case"web-iframe":T=await ae(e,o);break;case"web":default:T=await ie(e,o);break}i||(T==="dismissed"&&s(),t({status:T}))})()})),r.isAuthenticationWindowOpen=function(){return!!y},r.removeAuthenticationContainer=function(e){le();const t=e.getGame().getRenderer().getDomElementContainer();if(!t){a.info("The div element covering the game couldn't be found, the authentication must be already closed.");return}y&&t.removeChild(y),y=null,k=null,W=null,f=null};const le=function(){v&&(window.removeEventListener("message",v,!0),v=null)},$=function(){I&&(window.removeEventListener("message",I,!0),I=null),j&&(clearTimeout(j),j=null)};r.removeAuthenticationBanner=function(e){if(!l){a.info("The authentication banner couldn't be found, the authentication banner must be already closed.");return}const t=e.getGame().getRenderer().getDomElementContainer();if(!t){a.info("The div element covering the game couldn't be found, the authentication must be already closed.");return}t.removeChild(l),l=null};const P=function(e){const t=e.getGame().getRenderer().getCanvas();t&&t.focus()}})(Q=d.playerAuthentication||(d.playerAuthentication={}))})(gdjs||(gdjs={}));
|
|
1
|
+
var gdjs;(function(u){const r=new u.Logger("Player Authentication"),g=u.playerAuthenticationComponents;let X;(function(s){let p=null,T=null,b=null,O=!1,v=!1,D=null,A=null,k=null,W=null,f=null,d=null,j=null,S=null,I=null,C=null,m=null;const Y=e=>{B(e)==="web"&&(N(),I=t=>{_({runtimeScene:e,event:t,checkOrigin:!0})},window.addEventListener("message",I,!0),r.info("Notifying parent window that player authentication is ready."),window.parent.postMessage({id:"playerAuthReady"},"*"),j=setTimeout(()=>{r.info("Removing automatic games platform authentication listener."),N()},3e3))},Z=e=>{const t=e.getGame().getAdditionalOptions();if(t&&t.isPreview){const n=t.playerId,i=t.playerToken,o=t.playerUsername;n&&i&&(r.info(`Automatically logging in the player with ID ${n} as it's a preview.`),x({userId:n,username:o||null,userToken:i}),q(e))}};u.registerRuntimeScenePostEventsCallback(()=>{O=!1}),u.registerFirstRuntimeSceneLoadedCallback(e=>{Z(e),Y(e)});const M=e=>`${e}_authenticatedUser`,U=({runtimeGame:e,gameId:t,connectionId:n,authWindowOptions:i})=>{const o="https://gd.games",a=new URLSearchParams;if(a.set("gameId",t),n&&a.set("connectionId",n),e.isUsingGDevelopDevelopmentEnvironment()&&a.set("dev","true"),a.set("allowLoginProviders","true"),i)for(const[l,c]of Object.entries(i))a.set(l,c.toString());return`${o}/auth?${a.toString()}`},B=e=>e.getGame().getRenderer().getElectron()?"electron":ee(e)?"web-iframe":typeof cordova!="undefined"?"cordova-websocket":"web",ee=e=>{const t=e.getGame().getAdditionalOptions();return t&&t.isPreview&&t.allowAuthenticationUsingIframeForPreview};s.isAuthenticated=()=>(v||R(),b!==null),s.hasLoggedIn=()=>O,s.getUsername=()=>(v||R(),p||""),s.getUserToken=()=>(v||R(),b||null),s.getUserId=()=>(v||R(),T||"");const H=(e,t,n=0)=>{const o=`${e.isUsingGDevelopDevelopmentEnvironment()?"https://api-dev.gdevelop.io":"https://api.gdevelop.io"}/game/public-game/${t}`;return fetch(o,{method:"HEAD"}).then(a=>a.status!==200?(r.warn(`Error while fetching the game: ${a.status} ${a.statusText}`),a.status===404||n>2?!1:H(e,t,n+1)):!0,a=>(r.error("Error while fetching game:",a),!1))};s.logout=e=>{p=null,b=null,T=null;const t=u.projectData.properties.projectUuid;if(!t){r.error("Missing game id in project properties.");return}window.localStorage.removeItem(M(t)),G(e),s.removeAuthenticationBanner(e);const n=e.getGame().getRenderer().getDomElementContainer();if(!n){h(e,"The div element covering the game couldn't be found, the authentication banner cannot be displayed.");return}g.displayLoggedOutNotification(n)};const R=()=>{const e=u.projectData.properties.projectUuid;if(!e){r.error("Missing game id in project properties.");return}try{const t=window.localStorage.getItem(M(e));if(!t){v=!0;return}const n=JSON.parse(t);p=n.username,T=n.userId,b=n.userToken,v=!0}catch(t){r.warn("Unable to read authentication details from localStorage. Player authentication will not be available.",t)}},G=e=>{if(s.removeAuthenticationContainer(e),V(),m&&(r.info("Closing authentication websocket connection."),m.close(),m=null),D&&(D.close(),D=null),typeof SafariViewController!="undefined")try{SafariViewController.hide()}catch{r.info("Could not hide login window. Waiting for user to do it.")}},x=({username:e,userId:t,userToken:n})=>{e||r.warn("The authenticated player does not have a username"),p=e,T=t,b=n,O=!0;const i=u.projectData.properties.projectUuid;if(!i){r.error("Missing game id in project properties.");return}try{window.localStorage.setItem(M(i),JSON.stringify({username:p,userId:T,userToken:b}))}catch(o){r.warn("Unable to save the authentication details to localStorage. Player authentication will not be available.",o)}};s.login=({runtimeScene:e,userId:t,username:n,userToken:i})=>{x({userId:t,username:n,userToken:i}),G(e),s.removeAuthenticationBanner(e);const o=e.getGame().getRenderer().getDomElementContainer();if(!o){h(e,"The div element covering the game couldn't be found, the authentication banner cannot be displayed.");return}g.displayLoggedInNotification(o,p||"Anonymous")};const _=function({runtimeScene:e,event:t,checkOrigin:n,onDone:i}){if(!(n&&!["https://liluo.io","https://gd.games"].includes(t.origin))){if(!t.data.id)throw new Error("Malformed message");switch(t.data.id){case"authenticationResult":{if(!(t.data.body&&t.data.body.token))throw new Error("Malformed message.");s.login({runtimeScene:e,userId:t.data.body.userId,username:t.data.body.username,userToken:t.data.body.token}),P(e),i&&i("logged");break}case"alreadyAuthenticated":{if(!(t.data.body&&t.data.body.token))throw new Error("Malformed message.");x({userId:t.data.body.userId,username:t.data.body.username,userToken:t.data.body.token}),N(),q(e);break}}}},h=function(e,t){r.error(t),G(e);const n=e.getGame().getRenderer().getDomElementContainer();if(!n){h(e,"The div element covering the game couldn't be found, the authentication banner cannot be displayed.");return}g.displayErrorNotification(n),P(e)},te=e=>{V();const t=15*60*1e3;S=setTimeout(()=>{r.info("Authentication window did not send message in time. Closing it."),G(e),P(e)},t)},V=()=>{S&&clearTimeout(S)},K=function(e){const t=()=>{s.removeAuthenticationBanner(e)},n=()=>{s.openAuthenticationWindow(e)};return b?g.computeAuthenticatedBanner(n,t,p):g.computeNotAuthenticatedBanner(n,t)};s.displayAuthenticationBanner=function(e){if(d){d.style.opacity="1";return}v||R();const t=e.getGame().getRenderer().getDomElementContainer();if(!t){h(e,"The div element covering the game couldn't be found, the authentication banner cannot be displayed.");return}d=K(e),t.appendChild(d)};const q=function(e){if(!d)return;const t=e.getGame().getRenderer().getDomElementContainer();if(!t){h(e,"The div element covering the game couldn't be found, the authentication banner cannot be displayed.");return}const n=d;d=K(e),t.replaceChild(d,n)},z=(e,t,n)=>new Promise(i=>{let o=!1;const a=e.getGame().isUsingGDevelopDevelopmentEnvironment()?`wss://api-ws-dev.gdevelop.io/play?gameId=${t}&connectionType=login`:`wss://api-ws.gdevelop.io/play?gameId=${t}&connectionType=login`;m=new WebSocket(a),m.onopen=()=>{r.info("Opened authentication websocket connection."),m&&m.send(JSON.stringify({action:"getConnectionId"}))},m.onerror=()=>{r.info("Error in authentication websocket connection."),o||(o=!0,i("errored")),h(e,"Error while connecting to the authentication server.")},m.onclose=()=>{r.info("Closing authentication websocket connection."),o||(o=!0,i("dismissed"))},m.onmessage=l=>{if(l.data){const c=JSON.parse(l.data);switch(c.type){case"authenticationResult":{const w=c.data;s.login({runtimeScene:e,userId:w.userId,username:w.username,userToken:w.token}),P(e),o=!0,i("logged");break}case"connectionId":{const y=c.data.connectionId;if(!y){r.error("No WebSocket connectionId received"),o=!0,i("errored");return}r.info("WebSocket connectionId received."),n({connectionId:y,resolve:i});break}}}}}),oe=(e,t,n)=>z(e,t,({connectionId:i})=>{const o=U({runtimeGame:e.getGame(),gameId:t,connectionId:i,authWindowOptions:n}),a=e.getGame().getRenderer().getElectron(),l=()=>a.shell.openExternal(o);l(),f&&g.addAuthenticationUrlToTextsContainer(l,f)}),ie=(e,t,n)=>z(e,t,({connectionId:i,resolve:o})=>{const a=U({runtimeGame:e.getGame(),gameId:t,connectionId:i,authWindowOptions:n});SafariViewController.isAvailable(function(l){l?SafariViewController.show({url:a,hidden:!1,animated:!0,transition:"slide",enterReaderModeIfAvailable:!1,barColor:"#000000",tintColor:"#ffffff",controlTintColor:"#ffffff"},function(c){c.event==="closed"&&o("dismissed")},function(c){r.log("Error opening webview: "+JSON.stringify(c)),o("errored")}):(r.error("Plugin SafariViewController is not available"),o("errored"))})}),ae=(e,t,n)=>new Promise(i=>{const o=U({runtimeGame:e.getGame(),gameId:t,authWindowOptions:n});let a=!1;C=F=>{_({runtimeScene:e,event:F,checkOrigin:!0,onDone:L=>{a||(a=!0,i(L))}})},window.addEventListener("message",C,!0);const l=screen.width/2-500/2,c=screen.height/2-600/2,w=`left=${l},top=${c},width=500,height=600`,y=()=>{D=window.open(o,"authentication",w)};y(),f&&g.addAuthenticationUrlToTextsContainer(y,f)}),re=(e,t,n)=>new Promise(i=>{if(!W||!k||!f){r.error("Can't open an authentication iframe - no iframe container, loader container or text container was opened for it.");return}const o=U({runtimeGame:e.getGame(),gameId:t,authWindowOptions:n});C=a=>{_({runtimeScene:e,event:a,checkOrigin:!0,onDone:i})},window.addEventListener("message",C,!0),g.displayIframeInsideAuthenticationContainer(W,k,f,o)});s.openAuthenticationWindow=(e,t={disableGuestLogin:!1})=>new u.PromiseTask(new Promise(n=>{const i=e.getGame().getRenderer().getDomElementContainer();if(!i){h(e,"The div element covering the game couldn't be found, the authentication window cannot be displayed."),n({status:"errored"});return}const o=u.projectData.properties.projectUuid;if(!o){h(e,"The game ID is missing, the authentication window cannot be opened."),n({status:"errored"});return}let a=!1;const l=()=>{G(e),s.displayAuthenticationBanner(e),a=!0,n({status:"dismissed"})};d&&(d.style.opacity="0");const c=B(e),{rootContainer:w,loaderContainer:y,iframeContainer:F}=g.computeAuthenticationContainer(l);A=w,k=y,W=F,i.appendChild(A),(async()=>{const L=await H(e.getGame(),o);if(k){const Q=e.getGame().getRenderer().getElectron(),de=Q?()=>Q.shell.openExternal("https://wiki.gdevelop.io/gdevelop5/publishing/web"):null;f=g.addAuthenticationTextsToLoadingContainer(k,c,L,de)}if(!L)return;te(e);let E;switch(c){case"electron":E=await oe(e,o,t);break;case"cordova-websocket":E=await ie(e,o,t);break;case"web-iframe":E=await re(e,o,t);break;case"web":default:E=await ae(e,o,t);break}a||(E==="dismissed"&&l(),n({status:E}))})()})),s.isAuthenticationWindowOpen=function(){return!!A},s.removeAuthenticationContainer=function(e){ce();const t=e.getGame().getRenderer().getDomElementContainer();if(!t){r.info("The div element covering the game couldn't be found, the authentication must be already closed.");return}A&&t.removeChild(A),A=null,k=null,W=null,f=null};const ce=function(){C&&(window.removeEventListener("message",C,!0),C=null)},N=function(){I&&(window.removeEventListener("message",I,!0),I=null),j&&(clearTimeout(j),j=null)};s.removeAuthenticationBanner=function(e){if(!d){r.info("The authentication banner couldn't be found, the authentication banner must be already closed.");return}const t=e.getGame().getRenderer().getDomElementContainer();if(!t){r.info("The div element covering the game couldn't be found, the authentication must be already closed.");return}t.removeChild(d),d=null};const P=function(e){const t=e.getGame().getRenderer().getCanvas();t&&t.focus()}})(X=u.playerAuthentication||(u.playerAuthentication={}))})(gdjs||(gdjs={}));
|
|
2
2
|
//# sourceMappingURL=playerauthenticationtools.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../GDevelop/Extensions/PlayerAuthentication/playerauthenticationtools.ts"],
|
|
4
|
-
"sourcesContent": ["namespace gdjs {\n declare var cordova: any;\n declare var SafariViewController: any;\n\n const logger = new gdjs.Logger('Player Authentication');\n const authComponents = gdjs.playerAuthenticationComponents;\n // TODO EBO Replace runtimeScene to instanceContainer.\n export namespace playerAuthentication {\n // Authentication information.\n let _username: string | null = null;\n let _userId: string | null = null;\n let _userToken: string | null = null;\n let _justLoggedIn = false;\n\n let _checkedLocalStorage: boolean = false;\n\n // Authentication display\n let _authenticationWindow: Window | null = null; // For Web.\n let _authenticationRootContainer: HTMLDivElement | null = null;\n let _authenticationLoaderContainer: HTMLDivElement | null = null;\n let _authenticationIframeContainer: HTMLDivElement | null = null;\n let _authenticationTextContainer: HTMLDivElement | null = null;\n let _authenticationBanner: HTMLDivElement | null = null;\n let _automaticGamesPlatformAuthenticationTimeoutId: NodeJS.Timeout | null = null;\n let _authenticationTimeoutId: NodeJS.Timeout | null = null;\n\n // Communication methods.\n let _automaticGamesPlatformAuthenticationCallback:\n | ((event: MessageEvent) => void)\n | null = null;\n let _authenticationMessageCallback:\n | ((event: MessageEvent) => void)\n | null = null;\n let _websocket: WebSocket | null = null;\n\n type AuthenticationWindowStatus = 'logged' | 'errored' | 'dismissed';\n\n const handleAutomaticGamesPlatformAuthentication = (\n runtimeScene: gdjs.RuntimeScene\n ) => {\n if (getPlayerAuthPlatform(runtimeScene) !== 'web') {\n // Automatic authentication is only valid when the game is hosted on GDevelop games platform.\n return;\n }\n\n removeAutomaticGamesPlatformAuthenticationCallback(); // Remove any callback that could have been registered before.\n _automaticGamesPlatformAuthenticationCallback = (event: MessageEvent) => {\n receiveAuthenticationMessage({\n runtimeScene,\n event,\n checkOrigin: true,\n });\n };\n window.addEventListener(\n 'message',\n _automaticGamesPlatformAuthenticationCallback,\n true\n );\n logger.info(\n 'Notifying parent window that player authentication is ready.'\n );\n window.parent.postMessage(\n {\n id: 'playerAuthReady',\n },\n '*' // We could restrict to GDevelop games platform but it's not necessary as the message is not sensitive, and it allows easy debugging.\n );\n // If no answer after 3 seconds, assume that the game is not embedded in GDevelop games platform, and remove the listener.\n _automaticGamesPlatformAuthenticationTimeoutId = setTimeout(() => {\n logger.info(\n 'Removing automatic games platform authentication listener.'\n );\n removeAutomaticGamesPlatformAuthenticationCallback();\n }, 3000);\n };\n\n const handleAutomaticPreviewAuthentication = (\n runtimeScene: gdjs.RuntimeScene\n ) => {\n const runtimeGameOptions = runtimeScene.getGame().getAdditionalOptions();\n if (runtimeGameOptions && runtimeGameOptions.isPreview) {\n // If the game is a preview, and the user is already authenticated, we can log them in automatically.\n const playerId = runtimeGameOptions.playerId;\n const playerToken = runtimeGameOptions.playerToken;\n const playerUsername = runtimeGameOptions.playerUsername;\n if (playerId && playerToken) {\n logger.info(\n `Automatically logging in the player with ID ${playerId} as it's a preview.`\n );\n saveAuthKeyToStorage({\n userId: playerId,\n username: playerUsername || null,\n userToken: playerToken,\n });\n refreshAuthenticationBannerIfAny(runtimeScene);\n }\n }\n };\n\n // Ensure that the condition \"just logged in\" is valid only for one frame.\n gdjs.registerRuntimeScenePostEventsCallback(() => {\n _justLoggedIn = false;\n });\n\n // If the extension is used, register an eventlistener to know if the user is\n // logged in while playing the game on GDevelop games platform.\n // Then send a message to the parent iframe to say that the player auth is ready.\n gdjs.registerFirstRuntimeSceneLoadedCallback(\n (runtimeScene: RuntimeScene) => {\n handleAutomaticPreviewAuthentication(runtimeScene);\n handleAutomaticGamesPlatformAuthentication(runtimeScene);\n }\n );\n\n const getLocalStorageKey = (gameId: string) =>\n `${gameId}_authenticatedUser`;\n\n const getAuthWindowUrl = ({\n runtimeGame,\n gameId,\n connectionId,\n }: {\n runtimeGame: gdjs.RuntimeGame;\n gameId: string;\n connectionId?: string;\n }) => {\n // Uncomment to test the case of a failing loading:\n // return 'https://gd.games.wronglink';\n\n return `https://gd.games/auth?gameId=${gameId}${\n connectionId ? `&connectionId=${connectionId}` : ''\n }${\n runtimeGame.isUsingGDevelopDevelopmentEnvironment() ? '&dev=true' : ''\n }&allowLoginProviders=true`;\n };\n\n /**\n * Get the platform running the game, which changes how the authentication\n * window is opened.\n */\n const getPlayerAuthPlatform = (\n runtimeScene: RuntimeScene\n ): 'electron' | 'cordova-websocket' | 'web-iframe' | 'web' => {\n const runtimeGame = runtimeScene.getGame();\n const electron = runtimeGame.getRenderer().getElectron();\n if (electron) {\n // This can be a:\n // - Preview in GDevelop desktop app.\n // - Desktop game running on Electron.\n return 'electron';\n }\n\n // This can be a:\n // - Preview in GDevelop mobile app (iOS only)\n if (shouldAuthenticationUseIframe(runtimeScene)) return 'web-iframe';\n\n if (typeof cordova !== 'undefined') {\n // The game is an Android or an iOS app.\n return 'cordova-websocket';\n }\n\n // This can be a:\n // - Preview in GDevelop web-app\n // - Preview in Gdevelop mobile app (Android only)\n // - Web game (gd.games or any website/server) accessed via a desktop browser...\n // - Or a web game accessed via a mobile browser (Android/iOS).\n return 'web';\n };\n\n /**\n * Check if, in some exceptional cases, we allow authentication\n * to be done through a iframe.\n * This is usually discouraged as the user can't verify that the authentication\n * window is a genuine one. It's only to be used in trusted contexts (e.g:\n * preview in the GDevelop mobile app).\n */\n const shouldAuthenticationUseIframe = (runtimeScene: RuntimeScene) => {\n const runtimeGameOptions = runtimeScene.getGame().getAdditionalOptions();\n return (\n runtimeGameOptions &&\n runtimeGameOptions.isPreview &&\n runtimeGameOptions.allowAuthenticationUsingIframeForPreview\n );\n };\n\n /**\n * Returns true if a user token is present in the local storage.\n */\n export const isAuthenticated = () => {\n if (!_checkedLocalStorage) {\n readAuthenticatedUserFromLocalStorage();\n }\n return _userToken !== null;\n };\n\n /**\n * Returns true if the user just logged in.\n * Useful to update username or trigger messages in the game.\n */\n export const hasLoggedIn = () => _justLoggedIn;\n\n /**\n * Returns the username from the local storage.\n */\n export const getUsername = () => {\n if (!_checkedLocalStorage) {\n readAuthenticatedUserFromLocalStorage();\n }\n return _username || '';\n };\n\n /**\n * Returns the user token from the local storage.\n */\n export const getUserToken = () => {\n if (!_checkedLocalStorage) {\n readAuthenticatedUserFromLocalStorage();\n }\n return _userToken || null;\n };\n\n /**\n * Returns the username from the local storage.\n */\n export const getUserId = () => {\n if (!_checkedLocalStorage) {\n readAuthenticatedUserFromLocalStorage();\n }\n return _userId || '';\n };\n\n /**\n * Returns true if the game is registered, false otherwise.\n * Useful to display a message to the user to register the game before logging in.\n */\n const checkIfGameIsRegistered = (\n runtimeGame: gdjs.RuntimeGame,\n gameId: string,\n tries: number = 0\n ): Promise<boolean> => {\n const rootApi = runtimeGame.isUsingGDevelopDevelopmentEnvironment()\n ? 'https://api-dev.gdevelop.io'\n : 'https://api.gdevelop.io';\n const url = `${rootApi}/game/public-game/${gameId}`;\n return fetch(url, { method: 'HEAD' }).then(\n (response) => {\n if (response.status !== 200) {\n logger.warn(\n `Error while fetching the game: ${response.status} ${response.statusText}`\n );\n\n // If the response is not 404, it may be a timeout, so retry a few times.\n if (response.status === 404 || tries > 2) {\n return false;\n }\n\n return checkIfGameIsRegistered(runtimeGame, gameId, tries + 1);\n }\n return true;\n },\n (err) => {\n logger.error('Error while fetching game:', err);\n return false;\n }\n );\n };\n\n /**\n * Remove the user information from the local storage.\n */\n export const logout = (runtimeScene: RuntimeScene) => {\n _username = null;\n _userToken = null;\n _userId = null;\n\n const gameId = gdjs.projectData.properties.projectUuid;\n if (!gameId) {\n logger.error('Missing game id in project properties.');\n return;\n }\n window.localStorage.removeItem(getLocalStorageKey(gameId));\n cleanUpAuthWindowAndTimeouts(runtimeScene);\n removeAuthenticationBanner(runtimeScene);\n const domElementContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domElementContainer) {\n handleAuthenticationError(\n runtimeScene,\n \"The div element covering the game couldn't be found, the authentication banner cannot be displayed.\"\n );\n return;\n }\n authComponents.displayLoggedOutNotification(domElementContainer);\n };\n\n /**\n * Retrieves the user information from the local storage, and store\n * them in the extension variables.\n */\n const readAuthenticatedUserFromLocalStorage = () => {\n const gameId = gdjs.projectData.properties.projectUuid;\n if (!gameId) {\n logger.error('Missing game id in project properties.');\n return;\n }\n try {\n const authenticatedUserStorageItem = window.localStorage.getItem(\n getLocalStorageKey(gameId)\n );\n if (!authenticatedUserStorageItem) {\n _checkedLocalStorage = true;\n return;\n }\n const authenticatedUser = JSON.parse(authenticatedUserStorageItem);\n\n _username = authenticatedUser.username;\n _userId = authenticatedUser.userId;\n _userToken = authenticatedUser.userToken;\n _checkedLocalStorage = true;\n } catch (err) {\n logger.warn(\n 'Unable to read authentication details from localStorage. Player authentication will not be available.',\n err\n );\n }\n };\n\n /**\n * Helper to be called on login or error.\n * Removes all the UI and timeouts.\n */\n const cleanUpAuthWindowAndTimeouts = (runtimeScene: RuntimeScene) => {\n removeAuthenticationContainer(runtimeScene);\n clearAuthenticationWindowTimeout();\n\n // If there is a websocket communication (electron, cordova), close it.\n if (_websocket) {\n logger.info('Closing authentication websocket connection.');\n _websocket.close();\n _websocket = null;\n }\n // If a new window was opened (web), close it.\n if (_authenticationWindow) {\n _authenticationWindow.close();\n _authenticationWindow = null;\n }\n\n // If cordova (native mobile app), hide external window.\n // TODO: calling hide does nothing on Android, the plugin should be updated to handle the action `hide`.\n if (typeof SafariViewController !== 'undefined') {\n try {\n SafariViewController.hide();\n } catch (error) {\n logger.info(\n 'Could not hide login window. Waiting for user to do it.'\n );\n }\n }\n };\n\n const saveAuthKeyToStorage = ({\n username,\n userId,\n userToken,\n }: {\n username: string | null;\n userId: string;\n userToken: string;\n }) => {\n if (!username) {\n logger.warn('The authenticated player does not have a username');\n }\n _username = username;\n _userId = userId;\n _userToken = userToken;\n _justLoggedIn = true;\n\n const gameId = gdjs.projectData.properties.projectUuid;\n if (!gameId) {\n logger.error('Missing game id in project properties.');\n return;\n }\n try {\n window.localStorage.setItem(\n getLocalStorageKey(gameId),\n JSON.stringify({\n username: _username,\n userId: _userId,\n userToken: _userToken,\n })\n );\n } catch (err) {\n logger.warn(\n 'Unable to save the authentication details to localStorage. Player authentication will not be available.',\n err\n );\n }\n };\n\n export const login = ({\n runtimeScene,\n userId,\n username,\n userToken,\n }: {\n runtimeScene: gdjs.RuntimeScene;\n userId: string;\n username: string | null;\n userToken: string;\n }) => {\n saveAuthKeyToStorage({ userId, username, userToken });\n cleanUpAuthWindowAndTimeouts(runtimeScene);\n removeAuthenticationBanner(runtimeScene);\n\n const domElementContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domElementContainer) {\n handleAuthenticationError(\n runtimeScene,\n \"The div element covering the game couldn't be found, the authentication banner cannot be displayed.\"\n );\n return;\n }\n authComponents.displayLoggedInNotification(\n domElementContainer,\n _username || 'Anonymous'\n );\n };\n\n /**\n * Reads the event sent by the authentication window and\n * display the appropriate banner.\n */\n const receiveAuthenticationMessage = function ({\n runtimeScene,\n event,\n checkOrigin,\n onDone,\n }: {\n runtimeScene: gdjs.RuntimeScene;\n event: MessageEvent;\n checkOrigin: boolean;\n onDone?: (status: 'logged' | 'errored' | 'dismissed') => void;\n }) {\n const allowedOrigins = ['https://liluo.io', 'https://gd.games'];\n\n // Check origin of message.\n if (checkOrigin && !allowedOrigins.includes(event.origin)) {\n // Automatic authentication message ignored: wrong origin. Return silently.\n return;\n }\n // Check that message is not malformed.\n if (!event.data.id) {\n throw new Error('Malformed message');\n }\n\n // Handle message.\n switch (event.data.id) {\n case 'authenticationResult': {\n if (!(event.data.body && event.data.body.token)) {\n throw new Error('Malformed message.');\n }\n\n login({\n runtimeScene,\n userId: event.data.body.userId,\n username: event.data.body.username,\n userToken: event.data.body.token,\n });\n focusOnGame(runtimeScene);\n if (onDone) onDone('logged');\n break;\n }\n case 'alreadyAuthenticated': {\n if (!(event.data.body && event.data.body.token)) {\n throw new Error('Malformed message.');\n }\n\n saveAuthKeyToStorage({\n userId: event.data.body.userId,\n username: event.data.body.username,\n userToken: event.data.body.token,\n });\n removeAutomaticGamesPlatformAuthenticationCallback();\n refreshAuthenticationBannerIfAny(runtimeScene);\n break;\n }\n }\n };\n\n /**\n * Handle any error that can occur as part of the authentication process.\n */\n const handleAuthenticationError = function (\n runtimeScene: gdjs.RuntimeScene,\n message: string\n ) {\n logger.error(message);\n cleanUpAuthWindowAndTimeouts(runtimeScene);\n\n const domElementContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domElementContainer) {\n handleAuthenticationError(\n runtimeScene,\n \"The div element covering the game couldn't be found, the authentication banner cannot be displayed.\"\n );\n return;\n }\n authComponents.displayErrorNotification(domElementContainer);\n focusOnGame(runtimeScene);\n };\n\n /**\n * If after 5min, no message has been received from the authentication window,\n * show a notification and remove the authentication container.\n */\n const startAuthenticationWindowTimeout = (\n runtimeScene: gdjs.RuntimeScene\n ) => {\n clearAuthenticationWindowTimeout();\n const time = 15 * 60 * 1000; // 15 minutes, in case the user needs time to authenticate.\n _authenticationTimeoutId = setTimeout(() => {\n logger.info(\n 'Authentication window did not send message in time. Closing it.'\n );\n cleanUpAuthWindowAndTimeouts(runtimeScene);\n focusOnGame(runtimeScene);\n }, time);\n };\n\n /**\n * Clear all existing authentication timeouts.\n * Useful when:\n * - a new authentication starts\n * - the authentication succeeded\n * - the authentication window is closed\n */\n const clearAuthenticationWindowTimeout = () => {\n if (_authenticationTimeoutId) clearTimeout(_authenticationTimeoutId);\n };\n\n /**\n * Helper to create the authentication banner based on the authentication status.\n */\n const createAuthenticationBanner = function (\n runtimeScene: gdjs.RuntimeScene\n ): HTMLDivElement {\n const onDismissBanner = () => {\n removeAuthenticationBanner(runtimeScene);\n };\n const onOpenAuthenticationWindow = () => {\n openAuthenticationWindow(runtimeScene);\n };\n\n return _userToken\n ? authComponents.computeAuthenticatedBanner(\n onOpenAuthenticationWindow,\n onDismissBanner,\n _username\n )\n : authComponents.computeNotAuthenticatedBanner(\n onOpenAuthenticationWindow,\n onDismissBanner\n );\n };\n\n /**\n * Action to display the banner to the user, depending on their authentication status.\n */\n export const displayAuthenticationBanner = function (\n runtimeScene: gdjs.RuntimeScene\n ) {\n if (_authenticationBanner) {\n // Banner already displayed, ensure it's visible.\n _authenticationBanner.style.opacity = '1';\n return;\n }\n if (!_checkedLocalStorage) {\n readAuthenticatedUserFromLocalStorage();\n }\n\n const domElementContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domElementContainer) {\n handleAuthenticationError(\n runtimeScene,\n \"The div element covering the game couldn't be found, the authentication banner cannot be displayed.\"\n );\n return;\n }\n\n _authenticationBanner = createAuthenticationBanner(runtimeScene);\n domElementContainer.appendChild(_authenticationBanner);\n };\n\n /**\n * Helper to recompute the authentication banner.\n * This is useful if the user is already logged on GDevelop games platform\n * and we want to display the banner with the username.\n */\n const refreshAuthenticationBannerIfAny = function (\n runtimeScene: gdjs.RuntimeScene\n ) {\n if (!_authenticationBanner) return;\n const domElementContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domElementContainer) {\n handleAuthenticationError(\n runtimeScene,\n \"The div element covering the game couldn't be found, the authentication banner cannot be displayed.\"\n );\n return;\n }\n const oldAuthenticationBanner = _authenticationBanner;\n _authenticationBanner = createAuthenticationBanner(runtimeScene);\n domElementContainer.replaceChild(\n _authenticationBanner,\n oldAuthenticationBanner\n );\n };\n\n const setupWebsocketForAuthenticationWindow = (\n runtimeScene: gdjs.RuntimeScene,\n gameId: string,\n onOpenAuthenticationWindow: (options: {\n connectionId: string;\n resolve: (AuthenticationWindowStatus) => void;\n }) => void\n ) =>\n new Promise<AuthenticationWindowStatus>((resolve) => {\n let hasFinishedAlready = false;\n const wsPlayApi = runtimeScene\n .getGame()\n .isUsingGDevelopDevelopmentEnvironment()\n ? `wss://api-ws-dev.gdevelop.io/play?gameId=${gameId}&connectionType=login`\n : `wss://api-ws.gdevelop.io/play?gameId=${gameId}&connectionType=login`;\n _websocket = new WebSocket(wsPlayApi);\n _websocket.onopen = () => {\n logger.info('Opened authentication websocket connection.');\n // When socket is open, ask for the connectionId, so that we can open the authentication window.\n if (_websocket) {\n _websocket.send(JSON.stringify({ action: 'getConnectionId' }));\n }\n };\n _websocket.onerror = () => {\n logger.info('Error in authentication websocket connection.');\n if (!hasFinishedAlready) {\n hasFinishedAlready = true;\n resolve('errored');\n }\n handleAuthenticationError(\n runtimeScene,\n 'Error while connecting to the authentication server.'\n );\n };\n _websocket.onclose = () => {\n logger.info('Closing authentication websocket connection.');\n if (!hasFinishedAlready) {\n hasFinishedAlready = true;\n resolve('dismissed');\n }\n };\n _websocket.onmessage = (event) => {\n if (event.data) {\n const messageContent = JSON.parse(event.data);\n switch (messageContent.type) {\n case 'authenticationResult': {\n const messageData = messageContent.data;\n\n login({\n runtimeScene,\n userId: messageData.userId,\n username: messageData.username,\n userToken: messageData.token,\n });\n focusOnGame(runtimeScene);\n\n hasFinishedAlready = true;\n resolve('logged');\n break;\n }\n case 'connectionId': {\n const messageData = messageContent.data;\n const connectionId = messageData.connectionId;\n if (!connectionId) {\n logger.error('No WebSocket connectionId received');\n hasFinishedAlready = true;\n resolve('errored');\n return;\n }\n\n logger.info('WebSocket connectionId received.');\n onOpenAuthenticationWindow({ connectionId, resolve });\n break;\n }\n }\n }\n };\n });\n\n /**\n * Helper to handle authentication window on Electron.\n * We open a new window, and create a websocket to know when the user is logged in.\n */\n const openAuthenticationWindowForElectron = (\n runtimeScene: gdjs.RuntimeScene,\n gameId: string\n ) =>\n setupWebsocketForAuthenticationWindow(\n runtimeScene,\n gameId,\n ({ connectionId }) => {\n const targetUrl = getAuthWindowUrl({\n runtimeGame: runtimeScene.getGame(),\n gameId,\n connectionId,\n });\n\n const electron = runtimeScene.getGame().getRenderer().getElectron();\n const openWindow = () => electron.shell.openExternal(targetUrl);\n\n openWindow();\n\n // Add the link to the window in case a popup blocker is preventing the window from opening.\n if (_authenticationTextContainer) {\n authComponents.addAuthenticationUrlToTextsContainer(\n openWindow,\n _authenticationTextContainer\n );\n }\n }\n );\n\n /**\n * Helper to handle authentication window on Cordova on iOS and Android.\n * We open an external window, and listen to the websocket to know when the user is logged in.\n */\n const openAuthenticationWindowForCordovaWithWebSocket = (\n runtimeScene: gdjs.RuntimeScene,\n gameId: string\n ) =>\n setupWebsocketForAuthenticationWindow(\n runtimeScene,\n gameId,\n ({ connectionId, resolve }) => {\n const targetUrl = getAuthWindowUrl({\n runtimeGame: runtimeScene.getGame(),\n gameId,\n connectionId,\n });\n\n SafariViewController.isAvailable(function (available: boolean) {\n if (available) {\n SafariViewController.show(\n {\n url: targetUrl,\n hidden: false,\n animated: true,\n transition: 'slide',\n enterReaderModeIfAvailable: false,\n barColor: '#000000',\n tintColor: '#ffffff',\n controlTintColor: '#ffffff',\n },\n function (result: any) {\n // Other events are `opened` and `loaded`.\n if (result.event === 'closed') {\n resolve('dismissed');\n }\n },\n function (error: any) {\n logger.log('Error opening webview: ' + JSON.stringify(error));\n resolve('errored');\n }\n );\n } else {\n logger.error('Plugin SafariViewController is not available');\n resolve('errored');\n }\n });\n }\n );\n\n /**\n * Helper to handle authentication window on web.\n * We open a new window, and listen to messages posted back to the game window.\n */\n const openAuthenticationWindowForWeb = (\n runtimeScene: gdjs.RuntimeScene,\n gameId: string\n ) =>\n new Promise<AuthenticationWindowStatus>((resolve) => {\n // If we're on a browser, open a new window.\n const targetUrl = getAuthWindowUrl({\n runtimeGame: runtimeScene.getGame(),\n gameId,\n });\n\n // Listen to messages posted by the authentication window, so that we can\n // know when the user is authenticated.\n let isDoneAlready = false;\n _authenticationMessageCallback = (event: MessageEvent) => {\n receiveAuthenticationMessage({\n runtimeScene,\n event,\n checkOrigin: true,\n onDone: (status) => {\n if (isDoneAlready) return;\n isDoneAlready = true;\n resolve(status);\n },\n });\n };\n window.addEventListener(\n 'message',\n _authenticationMessageCallback,\n true\n );\n\n const left = screen.width / 2 - 500 / 2;\n const top = screen.height / 2 - 600 / 2;\n const windowFeatures = `left=${left},top=${top},width=500,height=600`;\n const openWindow = () => {\n _authenticationWindow = window.open(\n targetUrl,\n 'authentication',\n windowFeatures\n );\n };\n\n openWindow();\n\n // Add the link to the window in case a popup blocker is preventing the window from opening.\n if (_authenticationTextContainer) {\n authComponents.addAuthenticationUrlToTextsContainer(\n openWindow,\n _authenticationTextContainer\n );\n }\n });\n\n /**\n * Helper to handle authentication iframe on web.\n * We open an iframe, and listen to messages posted back to the game window.\n */\n const openAuthenticationIframeForWeb = (\n runtimeScene: gdjs.RuntimeScene,\n gameId: string\n ) =>\n new Promise<AuthenticationWindowStatus>((resolve) => {\n if (\n !_authenticationIframeContainer ||\n !_authenticationLoaderContainer ||\n !_authenticationTextContainer\n ) {\n logger.error(\n \"Can't open an authentication iframe - no iframe container, loader container or text container was opened for it.\"\n );\n return;\n }\n\n const targetUrl = getAuthWindowUrl({\n runtimeGame: runtimeScene.getGame(),\n gameId,\n });\n\n // Listen to messages posted by the authentication window, so that we can\n // know when the user is authenticated.\n _authenticationMessageCallback = (event: MessageEvent) => {\n receiveAuthenticationMessage({\n runtimeScene,\n event,\n checkOrigin: true,\n onDone: resolve,\n });\n };\n window.addEventListener(\n 'message',\n _authenticationMessageCallback,\n true\n );\n\n authComponents.displayIframeInsideAuthenticationContainer(\n _authenticationIframeContainer,\n _authenticationLoaderContainer,\n _authenticationTextContainer,\n targetUrl\n );\n });\n\n /**\n * Action to display the authentication window to the user.\n */\n export const openAuthenticationWindow = (\n runtimeScene: gdjs.RuntimeScene\n ): gdjs.PromiseTask<{ status: 'logged' | 'errored' | 'dismissed' }> =>\n new gdjs.PromiseTask(\n new Promise((resolve) => {\n // Create the authentication container for the player to wait.\n const domElementContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domElementContainer) {\n handleAuthenticationError(\n runtimeScene,\n \"The div element covering the game couldn't be found, the authentication window cannot be displayed.\"\n );\n resolve({ status: 'errored' });\n return;\n }\n\n const _gameId = gdjs.projectData.properties.projectUuid;\n if (!_gameId) {\n handleAuthenticationError(\n runtimeScene,\n 'The game ID is missing, the authentication window cannot be opened.'\n );\n resolve({ status: 'errored' });\n return;\n }\n\n let isDismissedAlready = false;\n const onAuthenticationContainerDismissed = () => {\n cleanUpAuthWindowAndTimeouts(runtimeScene);\n displayAuthenticationBanner(runtimeScene);\n\n isDismissedAlready = true;\n resolve({ status: 'dismissed' });\n };\n\n // If the banner is displayed, hide it, so that it can be shown again if the user closes the window.\n if (_authenticationBanner) _authenticationBanner.style.opacity = '0';\n\n const playerAuthPlatform = getPlayerAuthPlatform(runtimeScene);\n const {\n rootContainer,\n loaderContainer,\n iframeContainer,\n } = authComponents.computeAuthenticationContainer(\n onAuthenticationContainerDismissed\n );\n _authenticationRootContainer = rootContainer;\n _authenticationLoaderContainer = loaderContainer;\n _authenticationIframeContainer = iframeContainer;\n\n // Display the authentication window right away, to show a loader\n // while the call for game registration is happening.\n domElementContainer.appendChild(_authenticationRootContainer);\n\n // If the game is registered, open the authentication window.\n // Otherwise, open the window indicating that the game is not registered.\n (async () => {\n const isGameRegistered = await checkIfGameIsRegistered(\n runtimeScene.getGame(),\n _gameId\n );\n\n if (_authenticationLoaderContainer) {\n const electron = runtimeScene\n .getGame()\n .getRenderer()\n .getElectron();\n const wikiOpenAction = electron\n ? () =>\n electron.shell.openExternal(\n 'https://wiki.gdevelop.io/gdevelop5/publishing/web'\n )\n : null; // Only show a link if we're on electron.\n\n _authenticationTextContainer = authComponents.addAuthenticationTextsToLoadingContainer(\n _authenticationLoaderContainer,\n playerAuthPlatform,\n isGameRegistered,\n wikiOpenAction\n );\n }\n if (!isGameRegistered) return;\n\n startAuthenticationWindowTimeout(runtimeScene);\n\n // Based on which platform the game is running, we open the authentication window\n // with a different window, with or without a websocket.\n let status: AuthenticationWindowStatus;\n switch (playerAuthPlatform) {\n case 'electron':\n // This can be a:\n // - Preview in GDevelop desktop app.\n // - Desktop game running on Electron.\n status = await openAuthenticationWindowForElectron(\n runtimeScene,\n _gameId\n );\n break;\n case 'cordova-websocket':\n // The game is an iOS app.\n status = await openAuthenticationWindowForCordovaWithWebSocket(\n runtimeScene,\n _gameId\n );\n break;\n case 'web-iframe':\n // This can be a:\n // - Preview in GDevelop mobile app (iOS only)\n status = await openAuthenticationIframeForWeb(\n runtimeScene,\n _gameId\n );\n break;\n case 'web':\n default:\n // This can be a:\n // - Preview in GDevelop web-app\n // - Preview in Gdevelop mobile app (Android only)\n // - Web game (gd.games or any website/server) accessed via a desktop browser...\n // - Or a web game accessed via a mobile browser (Android/iOS).\n status = await openAuthenticationWindowForWeb(\n runtimeScene,\n _gameId\n );\n break;\n }\n\n if (isDismissedAlready) return;\n if (status === 'dismissed') {\n onAuthenticationContainerDismissed();\n }\n\n resolve({ status });\n })();\n })\n );\n\n /**\n * Condition to check if the window is open, so that the game can be paused in the background.\n */\n export const isAuthenticationWindowOpen = function (): boolean {\n return !!_authenticationRootContainer;\n };\n\n /**\n * Remove the container displaying the authentication window and the callback.\n */\n export const removeAuthenticationContainer = function (\n runtimeScene: gdjs.RuntimeScene\n ) {\n removeAuthenticationCallbacks();\n const domElementContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domElementContainer) {\n logger.info(\n \"The div element covering the game couldn't be found, the authentication must be already closed.\"\n );\n return;\n }\n\n // Remove the authentication root container.\n if (_authenticationRootContainer) {\n domElementContainer.removeChild(_authenticationRootContainer);\n }\n\n _authenticationRootContainer = null;\n _authenticationLoaderContainer = null;\n _authenticationIframeContainer = null;\n _authenticationTextContainer = null;\n };\n\n /*\n * Remove the authentication callbacks from web or cordova.\n */\n const removeAuthenticationCallbacks = function () {\n // Remove the authentication callbacks.\n if (_authenticationMessageCallback) {\n window.removeEventListener(\n 'message',\n _authenticationMessageCallback,\n true\n );\n _authenticationMessageCallback = null;\n }\n };\n\n /*\n * Remove the automatic authentication callback when running on web.\n */\n const removeAutomaticGamesPlatformAuthenticationCallback = function () {\n if (_automaticGamesPlatformAuthenticationCallback) {\n window.removeEventListener(\n 'message',\n _automaticGamesPlatformAuthenticationCallback,\n true\n );\n _automaticGamesPlatformAuthenticationCallback = null;\n }\n if (_automaticGamesPlatformAuthenticationTimeoutId) {\n clearTimeout(_automaticGamesPlatformAuthenticationTimeoutId);\n _automaticGamesPlatformAuthenticationTimeoutId = null;\n }\n };\n\n /**\n * Remove the banner displaying the authentication status.\n */\n export const removeAuthenticationBanner = function (\n runtimeScene: gdjs.RuntimeScene\n ) {\n if (!_authenticationBanner) {\n logger.info(\n \"The authentication banner couldn't be found, the authentication banner must be already closed.\"\n );\n return;\n }\n const domElementContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domElementContainer) {\n logger.info(\n \"The div element covering the game couldn't be found, the authentication must be already closed.\"\n );\n return;\n }\n\n domElementContainer.removeChild(_authenticationBanner);\n _authenticationBanner = null;\n };\n\n /**\n * Focus on game canvas to allow user to interact with it.\n */\n const focusOnGame = function (runtimeScene: gdjs.RuntimeScene) {\n const gameCanvas = runtimeScene.getGame().getRenderer().getCanvas();\n if (gameCanvas) gameCanvas.focus();\n };\n }\n}\n"],
|
|
5
|
-
"mappings": "AAAA,GAAU,MAAV,UAAU,EAAV,CAIE,KAAM,GAAS,GAAI,GAAK,OAAO,yBACzB,EAAiB,EAAK,+BAErB,GAAU,GAAV,UAAU,EAAV,CAEL,GAAI,GAA2B,KAC3B,EAAyB,KACzB,EAA4B,KAC5B,EAAgB,GAEhB,EAAgC,GAGhC,EAAuC,KACvC,EAAsD,KACtD,EAAwD,KACxD,EAAwD,KACxD,EAAsD,KACtD,EAA+C,KAC/C,EAAwE,KACxE,EAAkD,KAGlD,EAEO,KACP,EAEO,KACP,EAA+B,
|
|
4
|
+
"sourcesContent": ["namespace gdjs {\n declare var cordova: any;\n declare var SafariViewController: any;\n\n const logger = new gdjs.Logger('Player Authentication');\n const authComponents = gdjs.playerAuthenticationComponents;\n // TODO EBO Replace runtimeScene to instanceContainer.\n export namespace playerAuthentication {\n // Authentication information.\n let _username: string | null = null;\n let _userId: string | null = null;\n let _userToken: string | null = null;\n let _justLoggedIn = false;\n\n let _checkedLocalStorage: boolean = false;\n\n // Authentication display\n let _authenticationWindow: Window | null = null; // For Web.\n let _authenticationRootContainer: HTMLDivElement | null = null;\n let _authenticationLoaderContainer: HTMLDivElement | null = null;\n let _authenticationIframeContainer: HTMLDivElement | null = null;\n let _authenticationTextContainer: HTMLDivElement | null = null;\n let _authenticationBanner: HTMLDivElement | null = null;\n let _automaticGamesPlatformAuthenticationTimeoutId: NodeJS.Timeout | null = null;\n let _authenticationTimeoutId: NodeJS.Timeout | null = null;\n\n // Communication methods.\n let _automaticGamesPlatformAuthenticationCallback:\n | ((event: MessageEvent) => void)\n | null = null;\n let _authenticationMessageCallback:\n | ((event: MessageEvent) => void)\n | null = null;\n let _websocket: WebSocket | null = null;\n\n type AuthenticationWindowStatus = 'logged' | 'errored' | 'dismissed';\n type AuthenticationWindowOptions = { disableGuestLogin: boolean };\n\n const handleAutomaticGamesPlatformAuthentication = (\n runtimeScene: gdjs.RuntimeScene\n ) => {\n if (getPlayerAuthPlatform(runtimeScene) !== 'web') {\n // Automatic authentication is only valid when the game is hosted on GDevelop games platform.\n return;\n }\n\n removeAutomaticGamesPlatformAuthenticationCallback(); // Remove any callback that could have been registered before.\n _automaticGamesPlatformAuthenticationCallback = (event: MessageEvent) => {\n receiveAuthenticationMessage({\n runtimeScene,\n event,\n checkOrigin: true,\n });\n };\n window.addEventListener(\n 'message',\n _automaticGamesPlatformAuthenticationCallback,\n true\n );\n logger.info(\n 'Notifying parent window that player authentication is ready.'\n );\n window.parent.postMessage(\n {\n id: 'playerAuthReady',\n },\n '*' // We could restrict to GDevelop games platform but it's not necessary as the message is not sensitive, and it allows easy debugging.\n );\n // If no answer after 3 seconds, assume that the game is not embedded in GDevelop games platform, and remove the listener.\n _automaticGamesPlatformAuthenticationTimeoutId = setTimeout(() => {\n logger.info(\n 'Removing automatic games platform authentication listener.'\n );\n removeAutomaticGamesPlatformAuthenticationCallback();\n }, 3000);\n };\n\n const handleAutomaticPreviewAuthentication = (\n runtimeScene: gdjs.RuntimeScene\n ) => {\n const runtimeGameOptions = runtimeScene.getGame().getAdditionalOptions();\n if (runtimeGameOptions && runtimeGameOptions.isPreview) {\n // If the game is a preview, and the user is already authenticated, we can log them in automatically.\n const playerId = runtimeGameOptions.playerId;\n const playerToken = runtimeGameOptions.playerToken;\n const playerUsername = runtimeGameOptions.playerUsername;\n if (playerId && playerToken) {\n logger.info(\n `Automatically logging in the player with ID ${playerId} as it's a preview.`\n );\n saveAuthKeyToStorage({\n userId: playerId,\n username: playerUsername || null,\n userToken: playerToken,\n });\n refreshAuthenticationBannerIfAny(runtimeScene);\n }\n }\n };\n\n // Ensure that the condition \"just logged in\" is valid only for one frame.\n gdjs.registerRuntimeScenePostEventsCallback(() => {\n _justLoggedIn = false;\n });\n\n // If the extension is used, register an eventlistener to know if the user is\n // logged in while playing the game on GDevelop games platform.\n // Then send a message to the parent iframe to say that the player auth is ready.\n gdjs.registerFirstRuntimeSceneLoadedCallback(\n (runtimeScene: RuntimeScene) => {\n handleAutomaticPreviewAuthentication(runtimeScene);\n handleAutomaticGamesPlatformAuthentication(runtimeScene);\n }\n );\n\n const getLocalStorageKey = (gameId: string) =>\n `${gameId}_authenticatedUser`;\n\n const getAuthWindowUrl = ({\n runtimeGame,\n gameId,\n connectionId,\n authWindowOptions,\n }: {\n runtimeGame: gdjs.RuntimeGame;\n gameId: string;\n connectionId?: string;\n authWindowOptions?: AuthenticationWindowOptions;\n }) => {\n // Uncomment to test the case of a failing loading:\n // return 'https://gd.games.wronglink';\n\n const baseUrl = 'https://gd.games';\n // const baseUrl = 'http://localhost:4000';\n\n const searchParams = new URLSearchParams();\n searchParams.set('gameId', gameId);\n if (connectionId) searchParams.set('connectionId', connectionId);\n if (runtimeGame.isUsingGDevelopDevelopmentEnvironment()) {\n searchParams.set('dev', 'true');\n }\n searchParams.set('allowLoginProviders', 'true');\n if (authWindowOptions) {\n for (const [key, value] of Object.entries(authWindowOptions)) {\n searchParams.set(key, value.toString());\n }\n }\n\n return `${baseUrl}/auth?${searchParams.toString()}`;\n };\n\n /**\n * Get the platform running the game, which changes how the authentication\n * window is opened.\n */\n const getPlayerAuthPlatform = (\n runtimeScene: RuntimeScene\n ): 'electron' | 'cordova-websocket' | 'web-iframe' | 'web' => {\n const runtimeGame = runtimeScene.getGame();\n const electron = runtimeGame.getRenderer().getElectron();\n if (electron) {\n // This can be a:\n // - Preview in GDevelop desktop app.\n // - Desktop game running on Electron.\n return 'electron';\n }\n\n // This can be a:\n // - Preview in GDevelop mobile app (iOS only)\n if (shouldAuthenticationUseIframe(runtimeScene)) return 'web-iframe';\n\n if (typeof cordova !== 'undefined') {\n // The game is an Android or an iOS app.\n return 'cordova-websocket';\n }\n\n // This can be a:\n // - Preview in GDevelop web-app\n // - Preview in Gdevelop mobile app (Android only)\n // - Web game (gd.games or any website/server) accessed via a desktop browser...\n // - Or a web game accessed via a mobile browser (Android/iOS).\n return 'web';\n };\n\n /**\n * Check if, in some exceptional cases, we allow authentication\n * to be done through a iframe.\n * This is usually discouraged as the user can't verify that the authentication\n * window is a genuine one. It's only to be used in trusted contexts (e.g:\n * preview in the GDevelop mobile app).\n */\n const shouldAuthenticationUseIframe = (runtimeScene: RuntimeScene) => {\n const runtimeGameOptions = runtimeScene.getGame().getAdditionalOptions();\n return (\n runtimeGameOptions &&\n runtimeGameOptions.isPreview &&\n runtimeGameOptions.allowAuthenticationUsingIframeForPreview\n );\n };\n\n /**\n * Returns true if a user token is present in the local storage.\n */\n export const isAuthenticated = () => {\n if (!_checkedLocalStorage) {\n readAuthenticatedUserFromLocalStorage();\n }\n return _userToken !== null;\n };\n\n /**\n * Returns true if the user just logged in.\n * Useful to update username or trigger messages in the game.\n */\n export const hasLoggedIn = () => _justLoggedIn;\n\n /**\n * Returns the username from the local storage.\n */\n export const getUsername = () => {\n if (!_checkedLocalStorage) {\n readAuthenticatedUserFromLocalStorage();\n }\n return _username || '';\n };\n\n /**\n * Returns the user token from the local storage.\n */\n export const getUserToken = () => {\n if (!_checkedLocalStorage) {\n readAuthenticatedUserFromLocalStorage();\n }\n return _userToken || null;\n };\n\n /**\n * Returns the username from the local storage.\n */\n export const getUserId = () => {\n if (!_checkedLocalStorage) {\n readAuthenticatedUserFromLocalStorage();\n }\n return _userId || '';\n };\n\n /**\n * Returns true if the game is registered, false otherwise.\n * Useful to display a message to the user to register the game before logging in.\n */\n const checkIfGameIsRegistered = (\n runtimeGame: gdjs.RuntimeGame,\n gameId: string,\n tries: number = 0\n ): Promise<boolean> => {\n const rootApi = runtimeGame.isUsingGDevelopDevelopmentEnvironment()\n ? 'https://api-dev.gdevelop.io'\n : 'https://api.gdevelop.io';\n const url = `${rootApi}/game/public-game/${gameId}`;\n return fetch(url, { method: 'HEAD' }).then(\n (response) => {\n if (response.status !== 200) {\n logger.warn(\n `Error while fetching the game: ${response.status} ${response.statusText}`\n );\n\n // If the response is not 404, it may be a timeout, so retry a few times.\n if (response.status === 404 || tries > 2) {\n return false;\n }\n\n return checkIfGameIsRegistered(runtimeGame, gameId, tries + 1);\n }\n return true;\n },\n (err) => {\n logger.error('Error while fetching game:', err);\n return false;\n }\n );\n };\n\n /**\n * Remove the user information from the local storage.\n */\n export const logout = (runtimeScene: RuntimeScene) => {\n _username = null;\n _userToken = null;\n _userId = null;\n\n const gameId = gdjs.projectData.properties.projectUuid;\n if (!gameId) {\n logger.error('Missing game id in project properties.');\n return;\n }\n window.localStorage.removeItem(getLocalStorageKey(gameId));\n cleanUpAuthWindowAndTimeouts(runtimeScene);\n removeAuthenticationBanner(runtimeScene);\n const domElementContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domElementContainer) {\n handleAuthenticationError(\n runtimeScene,\n \"The div element covering the game couldn't be found, the authentication banner cannot be displayed.\"\n );\n return;\n }\n authComponents.displayLoggedOutNotification(domElementContainer);\n };\n\n /**\n * Retrieves the user information from the local storage, and store\n * them in the extension variables.\n */\n const readAuthenticatedUserFromLocalStorage = () => {\n const gameId = gdjs.projectData.properties.projectUuid;\n if (!gameId) {\n logger.error('Missing game id in project properties.');\n return;\n }\n try {\n const authenticatedUserStorageItem = window.localStorage.getItem(\n getLocalStorageKey(gameId)\n );\n if (!authenticatedUserStorageItem) {\n _checkedLocalStorage = true;\n return;\n }\n const authenticatedUser = JSON.parse(authenticatedUserStorageItem);\n\n _username = authenticatedUser.username;\n _userId = authenticatedUser.userId;\n _userToken = authenticatedUser.userToken;\n _checkedLocalStorage = true;\n } catch (err) {\n logger.warn(\n 'Unable to read authentication details from localStorage. Player authentication will not be available.',\n err\n );\n }\n };\n\n /**\n * Helper to be called on login or error.\n * Removes all the UI and timeouts.\n */\n const cleanUpAuthWindowAndTimeouts = (runtimeScene: RuntimeScene) => {\n removeAuthenticationContainer(runtimeScene);\n clearAuthenticationWindowTimeout();\n\n // If there is a websocket communication (electron, cordova), close it.\n if (_websocket) {\n logger.info('Closing authentication websocket connection.');\n _websocket.close();\n _websocket = null;\n }\n // If a new window was opened (web), close it.\n if (_authenticationWindow) {\n _authenticationWindow.close();\n _authenticationWindow = null;\n }\n\n // If cordova (native mobile app), hide external window.\n // TODO: calling hide does nothing on Android, the plugin should be updated to handle the action `hide`.\n if (typeof SafariViewController !== 'undefined') {\n try {\n SafariViewController.hide();\n } catch (error) {\n logger.info(\n 'Could not hide login window. Waiting for user to do it.'\n );\n }\n }\n };\n\n const saveAuthKeyToStorage = ({\n username,\n userId,\n userToken,\n }: {\n username: string | null;\n userId: string;\n userToken: string;\n }) => {\n if (!username) {\n logger.warn('The authenticated player does not have a username');\n }\n _username = username;\n _userId = userId;\n _userToken = userToken;\n _justLoggedIn = true;\n\n const gameId = gdjs.projectData.properties.projectUuid;\n if (!gameId) {\n logger.error('Missing game id in project properties.');\n return;\n }\n try {\n window.localStorage.setItem(\n getLocalStorageKey(gameId),\n JSON.stringify({\n username: _username,\n userId: _userId,\n userToken: _userToken,\n })\n );\n } catch (err) {\n logger.warn(\n 'Unable to save the authentication details to localStorage. Player authentication will not be available.',\n err\n );\n }\n };\n\n export const login = ({\n runtimeScene,\n userId,\n username,\n userToken,\n }: {\n runtimeScene: gdjs.RuntimeScene;\n userId: string;\n username: string | null;\n userToken: string;\n }) => {\n saveAuthKeyToStorage({ userId, username, userToken });\n cleanUpAuthWindowAndTimeouts(runtimeScene);\n removeAuthenticationBanner(runtimeScene);\n\n const domElementContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domElementContainer) {\n handleAuthenticationError(\n runtimeScene,\n \"The div element covering the game couldn't be found, the authentication banner cannot be displayed.\"\n );\n return;\n }\n authComponents.displayLoggedInNotification(\n domElementContainer,\n _username || 'Anonymous'\n );\n };\n\n /**\n * Reads the event sent by the authentication window and\n * display the appropriate banner.\n */\n const receiveAuthenticationMessage = function ({\n runtimeScene,\n event,\n checkOrigin,\n onDone,\n }: {\n runtimeScene: gdjs.RuntimeScene;\n event: MessageEvent;\n checkOrigin: boolean;\n onDone?: (status: 'logged' | 'errored' | 'dismissed') => void;\n }) {\n const allowedOrigins = ['https://liluo.io', 'https://gd.games'];\n // const allowedOrigins = ['localhost:4000'];\n\n // Check origin of message.\n if (checkOrigin && !allowedOrigins.includes(event.origin)) {\n // Automatic authentication message ignored: wrong origin. Return silently.\n return;\n }\n // Check that message is not malformed.\n if (!event.data.id) {\n throw new Error('Malformed message');\n }\n\n // Handle message.\n switch (event.data.id) {\n case 'authenticationResult': {\n if (!(event.data.body && event.data.body.token)) {\n throw new Error('Malformed message.');\n }\n\n login({\n runtimeScene,\n userId: event.data.body.userId,\n username: event.data.body.username,\n userToken: event.data.body.token,\n });\n focusOnGame(runtimeScene);\n if (onDone) onDone('logged');\n break;\n }\n case 'alreadyAuthenticated': {\n if (!(event.data.body && event.data.body.token)) {\n throw new Error('Malformed message.');\n }\n\n saveAuthKeyToStorage({\n userId: event.data.body.userId,\n username: event.data.body.username,\n userToken: event.data.body.token,\n });\n removeAutomaticGamesPlatformAuthenticationCallback();\n refreshAuthenticationBannerIfAny(runtimeScene);\n break;\n }\n }\n };\n\n /**\n * Handle any error that can occur as part of the authentication process.\n */\n const handleAuthenticationError = function (\n runtimeScene: gdjs.RuntimeScene,\n message: string\n ) {\n logger.error(message);\n cleanUpAuthWindowAndTimeouts(runtimeScene);\n\n const domElementContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domElementContainer) {\n handleAuthenticationError(\n runtimeScene,\n \"The div element covering the game couldn't be found, the authentication banner cannot be displayed.\"\n );\n return;\n }\n authComponents.displayErrorNotification(domElementContainer);\n focusOnGame(runtimeScene);\n };\n\n /**\n * If after 5min, no message has been received from the authentication window,\n * show a notification and remove the authentication container.\n */\n const startAuthenticationWindowTimeout = (\n runtimeScene: gdjs.RuntimeScene\n ) => {\n clearAuthenticationWindowTimeout();\n const time = 15 * 60 * 1000; // 15 minutes, in case the user needs time to authenticate.\n _authenticationTimeoutId = setTimeout(() => {\n logger.info(\n 'Authentication window did not send message in time. Closing it.'\n );\n cleanUpAuthWindowAndTimeouts(runtimeScene);\n focusOnGame(runtimeScene);\n }, time);\n };\n\n /**\n * Clear all existing authentication timeouts.\n * Useful when:\n * - a new authentication starts\n * - the authentication succeeded\n * - the authentication window is closed\n */\n const clearAuthenticationWindowTimeout = () => {\n if (_authenticationTimeoutId) clearTimeout(_authenticationTimeoutId);\n };\n\n /**\n * Helper to create the authentication banner based on the authentication status.\n */\n const createAuthenticationBanner = function (\n runtimeScene: gdjs.RuntimeScene\n ): HTMLDivElement {\n const onDismissBanner = () => {\n removeAuthenticationBanner(runtimeScene);\n };\n const onOpenAuthenticationWindow = () => {\n openAuthenticationWindow(runtimeScene);\n };\n\n return _userToken\n ? authComponents.computeAuthenticatedBanner(\n onOpenAuthenticationWindow,\n onDismissBanner,\n _username\n )\n : authComponents.computeNotAuthenticatedBanner(\n onOpenAuthenticationWindow,\n onDismissBanner\n );\n };\n\n /**\n * Action to display the banner to the user, depending on their authentication status.\n */\n export const displayAuthenticationBanner = function (\n runtimeScene: gdjs.RuntimeScene\n ) {\n if (_authenticationBanner) {\n // Banner already displayed, ensure it's visible.\n _authenticationBanner.style.opacity = '1';\n return;\n }\n if (!_checkedLocalStorage) {\n readAuthenticatedUserFromLocalStorage();\n }\n\n const domElementContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domElementContainer) {\n handleAuthenticationError(\n runtimeScene,\n \"The div element covering the game couldn't be found, the authentication banner cannot be displayed.\"\n );\n return;\n }\n\n _authenticationBanner = createAuthenticationBanner(runtimeScene);\n domElementContainer.appendChild(_authenticationBanner);\n };\n\n /**\n * Helper to recompute the authentication banner.\n * This is useful if the user is already logged on GDevelop games platform\n * and we want to display the banner with the username.\n */\n const refreshAuthenticationBannerIfAny = function (\n runtimeScene: gdjs.RuntimeScene\n ) {\n if (!_authenticationBanner) return;\n const domElementContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domElementContainer) {\n handleAuthenticationError(\n runtimeScene,\n \"The div element covering the game couldn't be found, the authentication banner cannot be displayed.\"\n );\n return;\n }\n const oldAuthenticationBanner = _authenticationBanner;\n _authenticationBanner = createAuthenticationBanner(runtimeScene);\n domElementContainer.replaceChild(\n _authenticationBanner,\n oldAuthenticationBanner\n );\n };\n\n const setupWebsocketForAuthenticationWindow = (\n runtimeScene: gdjs.RuntimeScene,\n gameId: string,\n onOpenAuthenticationWindow: (options: {\n connectionId: string;\n resolve: (AuthenticationWindowStatus) => void;\n }) => void\n ) =>\n new Promise<AuthenticationWindowStatus>((resolve) => {\n let hasFinishedAlready = false;\n const wsPlayApi = runtimeScene\n .getGame()\n .isUsingGDevelopDevelopmentEnvironment()\n ? `wss://api-ws-dev.gdevelop.io/play?gameId=${gameId}&connectionType=login`\n : `wss://api-ws.gdevelop.io/play?gameId=${gameId}&connectionType=login`;\n _websocket = new WebSocket(wsPlayApi);\n _websocket.onopen = () => {\n logger.info('Opened authentication websocket connection.');\n // When socket is open, ask for the connectionId, so that we can open the authentication window.\n if (_websocket) {\n _websocket.send(JSON.stringify({ action: 'getConnectionId' }));\n }\n };\n _websocket.onerror = () => {\n logger.info('Error in authentication websocket connection.');\n if (!hasFinishedAlready) {\n hasFinishedAlready = true;\n resolve('errored');\n }\n handleAuthenticationError(\n runtimeScene,\n 'Error while connecting to the authentication server.'\n );\n };\n _websocket.onclose = () => {\n logger.info('Closing authentication websocket connection.');\n if (!hasFinishedAlready) {\n hasFinishedAlready = true;\n resolve('dismissed');\n }\n };\n _websocket.onmessage = (event) => {\n if (event.data) {\n const messageContent = JSON.parse(event.data);\n switch (messageContent.type) {\n case 'authenticationResult': {\n const messageData = messageContent.data;\n\n login({\n runtimeScene,\n userId: messageData.userId,\n username: messageData.username,\n userToken: messageData.token,\n });\n focusOnGame(runtimeScene);\n\n hasFinishedAlready = true;\n resolve('logged');\n break;\n }\n case 'connectionId': {\n const messageData = messageContent.data;\n const connectionId = messageData.connectionId;\n if (!connectionId) {\n logger.error('No WebSocket connectionId received');\n hasFinishedAlready = true;\n resolve('errored');\n return;\n }\n\n logger.info('WebSocket connectionId received.');\n onOpenAuthenticationWindow({ connectionId, resolve });\n break;\n }\n }\n }\n };\n });\n\n /**\n * Helper to handle authentication window on Electron.\n * We open a new window, and create a websocket to know when the user is logged in.\n */\n const openAuthenticationWindowForElectron = (\n runtimeScene: gdjs.RuntimeScene,\n gameId: string,\n authWindowOptions: AuthenticationWindowOptions\n ) =>\n setupWebsocketForAuthenticationWindow(\n runtimeScene,\n gameId,\n ({ connectionId }) => {\n const targetUrl = getAuthWindowUrl({\n runtimeGame: runtimeScene.getGame(),\n gameId,\n connectionId,\n authWindowOptions,\n });\n\n const electron = runtimeScene.getGame().getRenderer().getElectron();\n const openWindow = () => electron.shell.openExternal(targetUrl);\n\n openWindow();\n\n // Add the link to the window in case a popup blocker is preventing the window from opening.\n if (_authenticationTextContainer) {\n authComponents.addAuthenticationUrlToTextsContainer(\n openWindow,\n _authenticationTextContainer\n );\n }\n }\n );\n\n /**\n * Helper to handle authentication window on Cordova on iOS and Android.\n * We open an external window, and listen to the websocket to know when the user is logged in.\n */\n const openAuthenticationWindowForCordovaWithWebSocket = (\n runtimeScene: gdjs.RuntimeScene,\n gameId: string,\n authWindowOptions: AuthenticationWindowOptions\n ) =>\n setupWebsocketForAuthenticationWindow(\n runtimeScene,\n gameId,\n ({ connectionId, resolve }) => {\n const targetUrl = getAuthWindowUrl({\n runtimeGame: runtimeScene.getGame(),\n gameId,\n connectionId,\n authWindowOptions,\n });\n\n SafariViewController.isAvailable(function (available: boolean) {\n if (available) {\n SafariViewController.show(\n {\n url: targetUrl,\n hidden: false,\n animated: true,\n transition: 'slide',\n enterReaderModeIfAvailable: false,\n barColor: '#000000',\n tintColor: '#ffffff',\n controlTintColor: '#ffffff',\n },\n function (result: any) {\n // Other events are `opened` and `loaded`.\n if (result.event === 'closed') {\n resolve('dismissed');\n }\n },\n function (error: any) {\n logger.log('Error opening webview: ' + JSON.stringify(error));\n resolve('errored');\n }\n );\n } else {\n logger.error('Plugin SafariViewController is not available');\n resolve('errored');\n }\n });\n }\n );\n\n /**\n * Helper to handle authentication window on web.\n * We open a new window, and listen to messages posted back to the game window.\n */\n const openAuthenticationWindowForWeb = (\n runtimeScene: gdjs.RuntimeScene,\n gameId: string,\n authWindowOptions: AuthenticationWindowOptions\n ) =>\n new Promise<AuthenticationWindowStatus>((resolve) => {\n // If we're on a browser, open a new window.\n const targetUrl = getAuthWindowUrl({\n runtimeGame: runtimeScene.getGame(),\n gameId,\n authWindowOptions,\n });\n\n // Listen to messages posted by the authentication window, so that we can\n // know when the user is authenticated.\n let isDoneAlready = false;\n _authenticationMessageCallback = (event: MessageEvent) => {\n receiveAuthenticationMessage({\n runtimeScene,\n event,\n checkOrigin: true,\n onDone: (status) => {\n if (isDoneAlready) return;\n isDoneAlready = true;\n resolve(status);\n },\n });\n };\n window.addEventListener(\n 'message',\n _authenticationMessageCallback,\n true\n );\n\n const left = screen.width / 2 - 500 / 2;\n const top = screen.height / 2 - 600 / 2;\n const windowFeatures = `left=${left},top=${top},width=500,height=600`;\n const openWindow = () => {\n _authenticationWindow = window.open(\n targetUrl,\n 'authentication',\n windowFeatures\n );\n };\n\n openWindow();\n\n // Add the link to the window in case a popup blocker is preventing the window from opening.\n if (_authenticationTextContainer) {\n authComponents.addAuthenticationUrlToTextsContainer(\n openWindow,\n _authenticationTextContainer\n );\n }\n });\n\n /**\n * Helper to handle authentication iframe on web.\n * We open an iframe, and listen to messages posted back to the game window.\n */\n const openAuthenticationIframeForWeb = (\n runtimeScene: gdjs.RuntimeScene,\n gameId: string,\n authWindowOptions: AuthenticationWindowOptions\n ) =>\n new Promise<AuthenticationWindowStatus>((resolve) => {\n if (\n !_authenticationIframeContainer ||\n !_authenticationLoaderContainer ||\n !_authenticationTextContainer\n ) {\n logger.error(\n \"Can't open an authentication iframe - no iframe container, loader container or text container was opened for it.\"\n );\n return;\n }\n\n const targetUrl = getAuthWindowUrl({\n runtimeGame: runtimeScene.getGame(),\n gameId,\n authWindowOptions,\n });\n\n // Listen to messages posted by the authentication window, so that we can\n // know when the user is authenticated.\n _authenticationMessageCallback = (event: MessageEvent) => {\n receiveAuthenticationMessage({\n runtimeScene,\n event,\n checkOrigin: true,\n onDone: resolve,\n });\n };\n window.addEventListener(\n 'message',\n _authenticationMessageCallback,\n true\n );\n\n authComponents.displayIframeInsideAuthenticationContainer(\n _authenticationIframeContainer,\n _authenticationLoaderContainer,\n _authenticationTextContainer,\n targetUrl\n );\n });\n\n /**\n * Action to display the authentication window to the user.\n */\n export const openAuthenticationWindow = (\n runtimeScene: gdjs.RuntimeScene,\n authWindowOptions: AuthenticationWindowOptions = {\n disableGuestLogin: false,\n }\n ): gdjs.PromiseTask<{ status: 'logged' | 'errored' | 'dismissed' }> =>\n new gdjs.PromiseTask(\n new Promise((resolve) => {\n // Create the authentication container for the player to wait.\n const domElementContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domElementContainer) {\n handleAuthenticationError(\n runtimeScene,\n \"The div element covering the game couldn't be found, the authentication window cannot be displayed.\"\n );\n resolve({ status: 'errored' });\n return;\n }\n\n const _gameId = gdjs.projectData.properties.projectUuid;\n if (!_gameId) {\n handleAuthenticationError(\n runtimeScene,\n 'The game ID is missing, the authentication window cannot be opened.'\n );\n resolve({ status: 'errored' });\n return;\n }\n\n let isDismissedAlready = false;\n const onAuthenticationContainerDismissed = () => {\n cleanUpAuthWindowAndTimeouts(runtimeScene);\n displayAuthenticationBanner(runtimeScene);\n\n isDismissedAlready = true;\n resolve({ status: 'dismissed' });\n };\n\n // If the banner is displayed, hide it, so that it can be shown again if the user closes the window.\n if (_authenticationBanner) _authenticationBanner.style.opacity = '0';\n\n const playerAuthPlatform = getPlayerAuthPlatform(runtimeScene);\n const {\n rootContainer,\n loaderContainer,\n iframeContainer,\n } = authComponents.computeAuthenticationContainer(\n onAuthenticationContainerDismissed\n );\n _authenticationRootContainer = rootContainer;\n _authenticationLoaderContainer = loaderContainer;\n _authenticationIframeContainer = iframeContainer;\n\n // Display the authentication window right away, to show a loader\n // while the call for game registration is happening.\n domElementContainer.appendChild(_authenticationRootContainer);\n\n // If the game is registered, open the authentication window.\n // Otherwise, open the window indicating that the game is not registered.\n (async () => {\n const isGameRegistered = await checkIfGameIsRegistered(\n runtimeScene.getGame(),\n _gameId\n );\n\n if (_authenticationLoaderContainer) {\n const electron = runtimeScene\n .getGame()\n .getRenderer()\n .getElectron();\n const wikiOpenAction = electron\n ? () =>\n electron.shell.openExternal(\n 'https://wiki.gdevelop.io/gdevelop5/publishing/web'\n )\n : null; // Only show a link if we're on electron.\n\n _authenticationTextContainer = authComponents.addAuthenticationTextsToLoadingContainer(\n _authenticationLoaderContainer,\n playerAuthPlatform,\n isGameRegistered,\n wikiOpenAction\n );\n }\n if (!isGameRegistered) return;\n\n startAuthenticationWindowTimeout(runtimeScene);\n\n // Based on which platform the game is running, we open the authentication window\n // with a different window, with or without a websocket.\n let status: AuthenticationWindowStatus;\n switch (playerAuthPlatform) {\n case 'electron':\n // This can be a:\n // - Preview in GDevelop desktop app.\n // - Desktop game running on Electron.\n status = await openAuthenticationWindowForElectron(\n runtimeScene,\n _gameId,\n authWindowOptions\n );\n break;\n case 'cordova-websocket':\n // The game is an iOS app.\n status = await openAuthenticationWindowForCordovaWithWebSocket(\n runtimeScene,\n _gameId,\n authWindowOptions\n );\n break;\n case 'web-iframe':\n // This can be a:\n // - Preview in GDevelop mobile app (iOS only)\n status = await openAuthenticationIframeForWeb(\n runtimeScene,\n _gameId,\n authWindowOptions\n );\n break;\n case 'web':\n default:\n // This can be a:\n // - Preview in GDevelop web-app\n // - Preview in Gdevelop mobile app (Android only)\n // - Web game (gd.games or any website/server) accessed via a desktop browser...\n // - Or a web game accessed via a mobile browser (Android/iOS).\n status = await openAuthenticationWindowForWeb(\n runtimeScene,\n _gameId,\n authWindowOptions\n );\n break;\n }\n\n if (isDismissedAlready) return;\n if (status === 'dismissed') {\n onAuthenticationContainerDismissed();\n }\n\n resolve({ status });\n })();\n })\n );\n\n /**\n * Condition to check if the window is open, so that the game can be paused in the background.\n */\n export const isAuthenticationWindowOpen = function (): boolean {\n return !!_authenticationRootContainer;\n };\n\n /**\n * Remove the container displaying the authentication window and the callback.\n */\n export const removeAuthenticationContainer = function (\n runtimeScene: gdjs.RuntimeScene\n ) {\n removeAuthenticationCallbacks();\n const domElementContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domElementContainer) {\n logger.info(\n \"The div element covering the game couldn't be found, the authentication must be already closed.\"\n );\n return;\n }\n\n // Remove the authentication root container.\n if (_authenticationRootContainer) {\n domElementContainer.removeChild(_authenticationRootContainer);\n }\n\n _authenticationRootContainer = null;\n _authenticationLoaderContainer = null;\n _authenticationIframeContainer = null;\n _authenticationTextContainer = null;\n };\n\n /*\n * Remove the authentication callbacks from web or cordova.\n */\n const removeAuthenticationCallbacks = function () {\n // Remove the authentication callbacks.\n if (_authenticationMessageCallback) {\n window.removeEventListener(\n 'message',\n _authenticationMessageCallback,\n true\n );\n _authenticationMessageCallback = null;\n }\n };\n\n /*\n * Remove the automatic authentication callback when running on web.\n */\n const removeAutomaticGamesPlatformAuthenticationCallback = function () {\n if (_automaticGamesPlatformAuthenticationCallback) {\n window.removeEventListener(\n 'message',\n _automaticGamesPlatformAuthenticationCallback,\n true\n );\n _automaticGamesPlatformAuthenticationCallback = null;\n }\n if (_automaticGamesPlatformAuthenticationTimeoutId) {\n clearTimeout(_automaticGamesPlatformAuthenticationTimeoutId);\n _automaticGamesPlatformAuthenticationTimeoutId = null;\n }\n };\n\n /**\n * Remove the banner displaying the authentication status.\n */\n export const removeAuthenticationBanner = function (\n runtimeScene: gdjs.RuntimeScene\n ) {\n if (!_authenticationBanner) {\n logger.info(\n \"The authentication banner couldn't be found, the authentication banner must be already closed.\"\n );\n return;\n }\n const domElementContainer = runtimeScene\n .getGame()\n .getRenderer()\n .getDomElementContainer();\n if (!domElementContainer) {\n logger.info(\n \"The div element covering the game couldn't be found, the authentication must be already closed.\"\n );\n return;\n }\n\n domElementContainer.removeChild(_authenticationBanner);\n _authenticationBanner = null;\n };\n\n /**\n * Focus on game canvas to allow user to interact with it.\n */\n const focusOnGame = function (runtimeScene: gdjs.RuntimeScene) {\n const gameCanvas = runtimeScene.getGame().getRenderer().getCanvas();\n if (gameCanvas) gameCanvas.focus();\n };\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var gdjs;(function(
|
|
1
|
+
var gdjs;(function(s){const a=class extends s.RuntimeObject{constructor(e,t){super(e,t);this._scaleX=1;this._scaleY=1;this._blendMode=0;this._flippedX=!1;this._flippedY=!1;this._customCenter=null;this._customCollisionMask=null;this._fillColor=typeof t.fillColor=="string"?s.rgbOrHexStringToNumber(t.fillColor):parseInt(s.rgbToHex(t.fillColor.r,t.fillColor.g,t.fillColor.b),16),this._outlineColor=typeof t.outlineColor=="string"?s.rgbOrHexStringToNumber(t.outlineColor):parseInt(s.rgbToHex(t.outlineColor.r,t.outlineColor.g,t.outlineColor.b),16),this._fillOpacity=t.fillOpacity,this._outlineOpacity=t.outlineOpacity,this._outlineSize=t.outlineSize,this._useAbsoluteCoordinates=t.absoluteCoordinates,this._clearBetweenFrames=t.clearBetweenFrames,this._antialiasing=t.antialiasing,this._renderer=new s.ShapePainterRuntimeObjectRenderer(this,e),this.onCreated()}getRendererObject(){return this._renderer.getRendererObject()}updateFromObjectData(e,t){return typeof t.fillColor=="string"&&e.fillColor!==t.fillColor&&this.setFillColor(t.fillColor),typeof e.fillColor!="string"&&typeof t.fillColor!="string"&&(e.fillColor.r!==t.fillColor.r||e.fillColor.g!==t.fillColor.g||e.fillColor.b!==t.fillColor.b)&&this.setFillColor(""+t.fillColor.r+";"+t.fillColor.g+";"+t.fillColor.b),typeof t.outlineColor=="string"&&e.outlineColor!==t.outlineColor&&this.setOutlineColor(t.outlineColor),typeof e.outlineColor!="string"&&typeof t.outlineColor!="string"&&(e.outlineColor.r!==t.outlineColor.r||e.outlineColor.g!==t.outlineColor.g||e.outlineColor.b!==t.outlineColor.b)&&this.setOutlineColor(""+t.outlineColor.r+";"+t.outlineColor.g+";"+t.outlineColor.b),e.fillOpacity!==t.fillOpacity&&this.setFillOpacity(t.fillOpacity),e.outlineOpacity!==t.outlineOpacity&&this.setOutlineOpacity(t.outlineOpacity),e.outlineSize!==t.outlineSize&&this.setOutlineSize(t.outlineSize),e.absoluteCoordinates!==t.absoluteCoordinates&&(this._useAbsoluteCoordinates=t.absoluteCoordinates,this._renderer.updatePositionX(),this._renderer.updatePositionY(),this._renderer.updateAngle(),this._renderer.updateScaleX(),this._renderer.updateScaleY()),e.clearBetweenFrames!==t.clearBetweenFrames&&(this._clearBetweenFrames=t.clearBetweenFrames),e.antialiasing!==t.antialiasing&&this.setAntialiasing(t.antialiasing),!0}extraInitializationFromInitialInstance(e){e.flippedX&&this.flipX(e.flippedX),e.flippedY&&this.flipY(e.flippedY)}stepBehaviorsPreEvents(e){this._clearBetweenFrames&&this.clear(),super.stepBehaviorsPreEvents(e)}onDestroyed(){super.onDestroyed(),this._renderer.destroy()}clear(){this._renderer.clear()}getVisibilityAABB(){return this._useAbsoluteCoordinates?null:this.getAABB()}drawRectangle(e,t,i,o){this._renderer.drawRectangle(e,t,i,o)}drawCircle(e,t,i){this._renderer.drawCircle(e,t,i)}drawLine(e,t,i,o,r){this._renderer.drawLine(e,t,i,o,r)}drawLineV2(e,t,i,o,r){this._renderer.drawLineV2(e,t,i,o,r)}drawEllipse(e,t,i,o){this._renderer.drawEllipse(e,t,i,o)}drawFilletRectangle(e,t,i,o,r){this._renderer.drawFilletRectangle(e,t,i,o,r)}drawRoundedRectangle(e,t,i,o,r){this._renderer.drawRoundedRectangle(e,t,i,o,r)}drawChamferRectangle(e,t,i,o,r){this._renderer.drawChamferRectangle(e,t,i,o,r)}drawTorus(e,t,i,o,r,l){this._renderer.drawTorus(e,t,i,o,r,l)}drawRegularPolygon(e,t,i,o,r){this._renderer.drawRegularPolygon(e,t,i,o,r)}drawStar(e,t,i,o,r,l){this._renderer.drawStar(e,t,i,o,r,l)}drawArc(e,t,i,o,r,l,n){this._renderer.drawArc(e,t,i,o,r,l,n)}drawBezierCurve(e,t,i,o,r,l,n,f){this._renderer.drawBezierCurve(e,t,i,o,r,l,n,f)}drawQuadraticCurve(e,t,i,o,r,l){this._renderer.drawQuadraticCurve(e,t,i,o,r,l)}beginFillPath(e,t){this._renderer.beginFillPath(),this._renderer.drawPathMoveTo(e,t)}endFillPath(){this._renderer.endFillPath()}drawPathMoveTo(e,t){this._renderer.drawPathMoveTo(e,t)}drawPathLineTo(e,t){this._renderer.drawPathLineTo(e,t)}drawPathBezierCurveTo(e,t,i,o,r,l){this._renderer.drawPathBezierCurveTo(e,t,i,o,r,l)}drawPathArc(e,t,i,o,r,l){this._renderer.drawPathArc(e,t,i,o,r,l)}drawPathQuadraticCurveTo(e,t,i,o){this._renderer.drawPathQuadraticCurveTo(e,t,i,o)}closePath(){this._renderer.closePath()}setClearBetweenFrames(e){this._clearBetweenFrames=e}isClearedBetweenFrames(){return this._clearBetweenFrames}setAntialiasing(e){this._antialiasing=e,this._renderer.updateAntialiasing()}getAntialiasing(){return this._antialiasing}checkAntialiasing(e){return this._antialiasing===e}setCoordinatesRelative(e){this._useAbsoluteCoordinates=!e}areCoordinatesRelative(){return!this._useAbsoluteCoordinates}setFillColor(e){this._fillColor=s.rgbOrHexStringToNumber(e)}getFillColorR(){return s.hexNumberToRGB(this._fillColor).r}getFillColorG(){return s.hexNumberToRGB(this._fillColor).g}getFillColorB(){return s.hexNumberToRGB(this._fillColor).b}setOutlineColor(e){this._outlineColor=s.rgbOrHexStringToNumber(e),this._renderer.updateOutline()}getOutlineColorR(){return s.hexNumberToRGB(this._outlineColor).r}getOutlineColorG(){return s.hexNumberToRGB(this._outlineColor).g}getOutlineColorB(){return s.hexNumberToRGB(this._outlineColor).b}setOutlineSize(e){this._outlineSize=e,this._renderer.updateOutline()}getOutlineSize(){return this._outlineSize}setFillOpacity(e){this._fillOpacity=e}getFillOpacity(){return this._fillOpacity}setOutlineOpacity(e){this._outlineOpacity=e,this._renderer.updateOutline()}getOutlineOpacity(){return this._outlineOpacity}setX(e){e!==this.x&&(super.setX(e),this._renderer.updatePositionX())}setY(e){e!==this.y&&(super.setY(e),this._renderer.updatePositionY())}setAngle(e){e!==this.angle&&(super.setAngle(e),this._renderer.updateAngle(),this.invalidateHitboxes())}setRotationCenter(e,t){this._customCenter||(this._customCenter=[0,0]),this._customCenter[0]=e,this._customCenter[1]=t,this._renderer.updateRotationCenter()}getRotationCenterX(){return this._customCenter?this._customCenter[0]:this._renderer.getUnscaledWidth()/2-this._renderer.getFrameRelativeOriginX()}getRotationCenterY(){return this._customCenter?this._customCenter[1]:this._renderer.getUnscaledHeight()/2-this._renderer.getFrameRelativeOriginY()}getCenterX(){return this._customCenter?this._customCenter[0]*Math.abs(this._scaleX)+this.getX()-this.getDrawableX():super.getCenterX()}getCenterY(){return this._customCenter?this._customCenter[1]*Math.abs(this._scaleY)+this.getY()-this.getDrawableY():super.getCenterY()}setWidth(e){const t=this._renderer.getUnscaledWidth();t!==0&&this.setScaleX(e/t)}setHeight(e){const t=this._renderer.getUnscaledHeight();t!==0&&this.setScaleY(e/t)}setSize(e,t){this.setWidth(e),this.setHeight(t)}setScale(e){this.setScaleX(e),this.setScaleY(e)}setScaleX(e){e<0&&(e=0),e!==Math.abs(this._scaleX)&&(this._scaleX=e*(this._flippedX?-1:1),this._renderer.updateScaleX(),this.invalidateHitboxes())}setScaleY(e){e<0&&(e=0),e!==Math.abs(this._scaleY)&&(this._scaleY=e*(this._flippedY?-1:1),this._renderer.updateScaleY(),this.invalidateHitboxes())}flipX(e){e!==this._flippedX&&(this._scaleX*=-1,this._flippedX=e,this._renderer.updateScaleX(),this.invalidateHitboxes())}flipY(e){e!==this._flippedY&&(this._scaleY*=-1,this._flippedY=e,this._renderer.updateScaleY(),this.invalidateHitboxes())}isFlippedX(){return this._flippedX}isFlippedY(){return this._flippedY}getScale(){const e=Math.abs(this._scaleX),t=Math.abs(this._scaleY);return e===t?e:Math.sqrt(e*t)}getScaleY(){return Math.abs(this._scaleY)}getScaleX(){return Math.abs(this._scaleX)}invalidateBounds(){this.invalidateHitboxes()}getDrawableX(){return this._renderer.getDrawableX()}getDrawableY(){return this._renderer.getDrawableY()}getWidth(){return this._renderer.getWidth()}getHeight(){return this._renderer.getHeight()}updatePreRender(e){this._renderer.updatePreRender()}transformToDrawing(e,t){const i=a._pointForTransformation;return i[0]=e,i[1]=t,this._renderer.transformToDrawing(i)}transformToScene(e,t){const i=a._pointForTransformation;return i[0]=e,i[1]=t,this._renderer.transformToScene(i)}transformToDrawingX(e,t){return this.transformToDrawing(e,t)[0]}transformToDrawingY(e,t){return this.transformToDrawing(e,t)[1]}transformToSceneX(e,t){return this.transformToScene(e,t)[0]}transformToSceneY(e,t){return this.transformToScene(e,t)[1]}setRectangularCollisionMask(e,t,i,o){if(!this._customCollisionMask){const l=new s.Polygon;l.vertices.push([0,0]),l.vertices.push([0,0]),l.vertices.push([0,0]),l.vertices.push([0,0]),this._customCollisionMask=[l]}const r=this._customCollisionMask[0].vertices;r[0][0]=e,r[0][1]=t,r[1][0]=i,r[1][1]=t,r[2][0]=i,r[2][1]=o,r[3][0]=e,r[3][1]=o,this.invalidateHitboxes()}updateHitBoxes(){this.hitBoxes=this._defaultHitBoxes;const e=this.getWidth(),t=this.getHeight(),i=this.getCenterX(),o=this.getCenterY(),r=this.hitBoxes[0].vertices;if(this._customCollisionMask){const l=this._customCollisionMask[0].vertices;for(let n=0;n<4;n++){const f=this.transformToScene(l[n][0],l[n][1]);r[n][0]=f[0],r[n][1]=f[1]}}else i===e/2&&o===t/2?(r[0][0]=-i,r[0][1]=-o,r[1][0]=+i,r[1][1]=-o,r[2][0]=+i,r[2][1]=+o,r[3][0]=-i,r[3][1]=+o):(r[0][0]=0-i,r[0][1]=0-o,r[1][0]=e-i,r[1][1]=0-o,r[2][0]=e-i,r[2][1]=t-o,r[3][0]=0-i,r[3][1]=t-o),this._useAbsoluteCoordinates||this.hitBoxes[0].rotate(s.toRad(this.getAngle())),this.hitBoxes[0].move(this.getDrawableX()+i,this.getDrawableY()+o)}};let h=a;h._pointForTransformation=[0,0],s.ShapePainterRuntimeObject=h,s.registerObject("PrimitiveDrawing::Drawer",s.ShapePainterRuntimeObject),h.supportsReinitialization=!1})(gdjs||(gdjs={}));
|
|
2
2
|
//# sourceMappingURL=shapepainterruntimeobject.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../GDevelop/Extensions/PrimitiveDrawing/shapepainterruntimeobject.ts"],
|
|
4
|
-
"sourcesContent": ["/*\n * GDevelop JS Platform\n * 2013 Florian Rival (Florian.Rival@gmail.com)\n */\nnamespace gdjs {\n /** Represents a color in RGB Format */\n export type RGBColor = {\n /** The Red component of the color, from 0 to 255. */\n r: integer;\n /** The Green component of the color, from 0 to 255. */\n g: integer;\n /** The Blue component of the color, from 0 to 255. */\n b: integer;\n };\n\n export type Antialiasing = 'none' | 'low' | 'medium' | 'high';\n\n /** Initial properties for a for {@link gdjs.ShapePainterRuntimeObject}. */\n export type ShapePainterObjectDataType = {\n /** The color (in RGB format) of the inner part of the painted shape */\n fillColor: RGBColor;\n /** The color (in RGB format) of the outline of the painted shape */\n outlineColor: RGBColor;\n /** The opacity of the inner part of the painted shape, from 0 to 255 */\n fillOpacity: float;\n /** The opacity of the outline of the painted shape, from 0 to 255 */\n outlineOpacity: float;\n /** The size of the outline of the painted shape, in pixels. */\n outlineSize: float;\n /** Use absolute coordinates? */\n absoluteCoordinates: boolean;\n /** Clear the previous render before the next draw? */\n clearBetweenFrames: boolean;\n /** The type of anti-aliasing to apply at rendering. */\n antialiasing: Antialiasing;\n };\n\n export type ShapePainterObjectData = ObjectData & ShapePainterObjectDataType;\n\n /**\n * The ShapePainterRuntimeObject allows to draw graphics shapes on screen.\n */\n export class ShapePainterRuntimeObject\n extends gdjs.RuntimeObject\n implements gdjs.Resizable, gdjs.Scalable, gdjs.Flippable {\n _scaleX: number = 1;\n _scaleY: number = 1;\n _blendMode: number = 0;\n _flippedX: boolean = false;\n _flippedY: boolean = false;\n _customCenter: FloatPoint | null = null;\n _customCollisionMask: Polygon[] | null = null;\n\n _fillColor: integer;\n _outlineColor: integer;\n _fillOpacity: float;\n _outlineOpacity: float;\n _outlineSize: float;\n _useAbsoluteCoordinates: boolean;\n _clearBetweenFrames: boolean;\n _antialiasing: Antialiasing;\n _renderer: gdjs.ShapePainterRuntimeObjectRenderer;\n\n private static readonly _pointForTransformation: FloatPoint = [0, 0];\n\n /**\n * @param instanceContainer The container the object belongs to.\n * @param shapePainterObjectData The initial properties of the object\n */\n constructor(\n instanceContainer: gdjs.RuntimeInstanceContainer,\n shapePainterObjectData: ShapePainterObjectData\n ) {\n super(instanceContainer, shapePainterObjectData);\n this._fillColor = parseInt(\n gdjs.rgbToHex(\n shapePainterObjectData.fillColor.r,\n shapePainterObjectData.fillColor.g,\n shapePainterObjectData.fillColor.b\n ),\n 16\n );\n this._outlineColor = parseInt(\n gdjs.rgbToHex(\n shapePainterObjectData.outlineColor.r,\n shapePainterObjectData.outlineColor.g,\n shapePainterObjectData.outlineColor.b\n ),\n 16\n );\n this._fillOpacity = shapePainterObjectData.fillOpacity;\n this._outlineOpacity = shapePainterObjectData.outlineOpacity;\n this._outlineSize = shapePainterObjectData.outlineSize;\n this._useAbsoluteCoordinates = shapePainterObjectData.absoluteCoordinates;\n this._clearBetweenFrames = shapePainterObjectData.clearBetweenFrames;\n this._antialiasing = shapePainterObjectData.antialiasing;\n this._renderer = new gdjs.ShapePainterRuntimeObjectRenderer(\n this,\n instanceContainer\n );\n\n // *ALWAYS* call `this.onCreated()` at the very end of your object constructor.\n this.onCreated();\n }\n\n getRendererObject() {\n return this._renderer.getRendererObject();\n }\n\n updateFromObjectData(\n oldObjectData: ShapePainterObjectData,\n newObjectData: ShapePainterObjectData\n ): boolean {\n if (\n oldObjectData.fillColor.r !== newObjectData.fillColor.r ||\n oldObjectData.fillColor.g !== newObjectData.fillColor.g ||\n oldObjectData.fillColor.b !== newObjectData.fillColor.b\n ) {\n this.setFillColor(\n '' +\n newObjectData.fillColor.r +\n ';' +\n newObjectData.fillColor.g +\n ';' +\n newObjectData.fillColor.b\n );\n }\n if (\n oldObjectData.outlineColor.r !== newObjectData.outlineColor.r ||\n oldObjectData.outlineColor.g !== newObjectData.outlineColor.g ||\n oldObjectData.outlineColor.b !== newObjectData.outlineColor.b\n ) {\n this.setOutlineColor(\n '' +\n newObjectData.outlineColor.r +\n ';' +\n newObjectData.outlineColor.g +\n ';' +\n newObjectData.outlineColor.b\n );\n }\n if (oldObjectData.fillOpacity !== newObjectData.fillOpacity) {\n this.setFillOpacity(newObjectData.fillOpacity);\n }\n if (oldObjectData.outlineOpacity !== newObjectData.outlineOpacity) {\n this.setOutlineOpacity(newObjectData.outlineOpacity);\n }\n if (oldObjectData.outlineSize !== newObjectData.outlineSize) {\n this.setOutlineSize(newObjectData.outlineSize);\n }\n if (\n oldObjectData.absoluteCoordinates !== newObjectData.absoluteCoordinates\n ) {\n this._useAbsoluteCoordinates = newObjectData.absoluteCoordinates;\n this._renderer.updatePositionX();\n this._renderer.updatePositionY();\n this._renderer.updateAngle();\n this._renderer.updateScaleX();\n this._renderer.updateScaleY();\n }\n if (\n oldObjectData.clearBetweenFrames !== newObjectData.clearBetweenFrames\n ) {\n this._clearBetweenFrames = newObjectData.clearBetweenFrames;\n }\n return true;\n }\n\n stepBehaviorsPreEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {\n //We redefine stepBehaviorsPreEvents just to clear the graphics before running events.\n if (this._clearBetweenFrames) {\n this.clear();\n }\n super.stepBehaviorsPreEvents(instanceContainer);\n }\n\n onDestroyed(): void {\n super.onDestroyed();\n this._renderer.destroy();\n }\n\n /**\n * Clear the graphics.\n */\n clear() {\n this._renderer.clear();\n }\n\n getVisibilityAABB() {\n return this._useAbsoluteCoordinates ? null : this.getAABB();\n }\n\n drawRectangle(x1: float, y1: float, x2: float, y2: float) {\n this._renderer.drawRectangle(x1, y1, x2, y2);\n }\n\n drawCircle(x: float, y: float, radius: float) {\n this._renderer.drawCircle(x, y, radius);\n }\n\n drawLine(x1: float, y1: float, x2: float, y2: float, thickness: float) {\n this._renderer.drawLine(x1, y1, x2, y2, thickness);\n }\n\n drawLineV2(x1: float, y1: float, x2: float, y2: float, thickness: float) {\n this._renderer.drawLineV2(x1, y1, x2, y2, thickness);\n }\n\n drawEllipse(centerX: float, centerY: float, width: float, height: float) {\n this._renderer.drawEllipse(centerX, centerY, width, height);\n }\n\n drawFilletRectangle(\n startX1: float,\n startY1: float,\n endX2: float,\n endY2: float,\n fillet: float\n ) {\n this._renderer.drawFilletRectangle(\n startX1,\n startY1,\n endX2,\n endY2,\n fillet\n );\n }\n\n drawRoundedRectangle(\n startX1: float,\n startY1: float,\n endX2: float,\n endY2: float,\n radius: float\n ) {\n this._renderer.drawRoundedRectangle(\n startX1,\n startY1,\n endX2,\n endY2,\n radius\n );\n }\n\n drawChamferRectangle(\n startX1: float,\n startY1: float,\n endX2: float,\n endY2: float,\n chamfer: float\n ) {\n this._renderer.drawChamferRectangle(\n startX1,\n startY1,\n endX2,\n endY2,\n chamfer\n );\n }\n\n drawTorus(\n centerX: float,\n centerY: float,\n innerRadius: float,\n outerRadius: float,\n startArc: float,\n endArc: float\n ) {\n this._renderer.drawTorus(\n centerX,\n centerY,\n innerRadius,\n outerRadius,\n startArc,\n endArc\n );\n }\n\n drawRegularPolygon(\n centerX: float,\n centerY: float,\n sides: float,\n radius: float,\n rotation: float\n ) {\n this._renderer.drawRegularPolygon(\n centerX,\n centerY,\n sides,\n radius,\n rotation\n );\n }\n\n drawStar(\n centerX: float,\n centerY: float,\n points: float,\n radius: float,\n innerRadius: float,\n rotation: float\n ) {\n this._renderer.drawStar(\n centerX,\n centerY,\n points,\n radius,\n innerRadius,\n rotation\n );\n }\n\n drawArc(\n centerX: float,\n centerY: float,\n radius: float,\n startAngle: float,\n endAngle: float,\n anticlockwise: boolean,\n closePath: boolean\n ) {\n this._renderer.drawArc(\n centerX,\n centerY,\n radius,\n startAngle,\n endAngle,\n anticlockwise,\n closePath\n );\n }\n\n drawBezierCurve(\n x1: float,\n y1: float,\n cpX: float,\n cpY: float,\n cpX2: float,\n cpY2: float,\n x2: float,\n y2: float\n ) {\n this._renderer.drawBezierCurve(x1, y1, cpX, cpY, cpX2, cpY2, x2, y2);\n }\n\n drawQuadraticCurve(\n x1: float,\n y1: float,\n cpX: float,\n cpY: float,\n x2: float,\n y2: float\n ) {\n this._renderer.drawQuadraticCurve(x1, y1, cpX, cpY, x2, y2);\n }\n\n beginFillPath(x1: float, y1: float) {\n this._renderer.beginFillPath();\n this._renderer.drawPathMoveTo(x1, y1);\n }\n\n endFillPath() {\n this._renderer.endFillPath();\n }\n\n drawPathMoveTo(x1: float, y1: float) {\n this._renderer.drawPathMoveTo(x1, y1);\n }\n\n drawPathLineTo(x1: float, y1: float) {\n this._renderer.drawPathLineTo(x1, y1);\n }\n\n drawPathBezierCurveTo(\n cpX: float,\n cpY: float,\n cpX2: float,\n cpY2: float,\n toX: float,\n toY: float\n ) {\n this._renderer.drawPathBezierCurveTo(cpX, cpY, cpX2, cpY2, toX, toY);\n }\n\n drawPathArc(\n cx: float,\n cy: float,\n radius: float,\n startAngle: float,\n endAngle: float,\n anticlockwise: boolean\n ) {\n this._renderer.drawPathArc(\n cx,\n cy,\n radius,\n startAngle,\n endAngle,\n anticlockwise\n );\n }\n\n drawPathQuadraticCurveTo(cpX: float, cpY: float, toX: float, toY: float) {\n this._renderer.drawPathQuadraticCurveTo(cpX, cpY, toX, toY);\n }\n\n closePath() {\n this._renderer.closePath();\n }\n\n setClearBetweenFrames(value: boolean): void {\n this._clearBetweenFrames = value;\n }\n\n isClearedBetweenFrames(): boolean {\n return this._clearBetweenFrames;\n }\n\n setAntialiasing(value: Antialiasing): void {\n this._antialiasing = value;\n this._renderer.updateAntialiasing();\n }\n\n getAntialiasing(): Antialiasing {\n return this._antialiasing;\n }\n\n checkAntialiasing(valueToCompare: Antialiasing): boolean {\n return this._antialiasing === valueToCompare;\n }\n\n setCoordinatesRelative(value: boolean): void {\n this._useAbsoluteCoordinates = !value;\n }\n\n areCoordinatesRelative(): boolean {\n return !this._useAbsoluteCoordinates;\n }\n\n /**\n *\n * @param rgbColor semicolon separated decimal values\n */\n setFillColor(rgbColor: string): void {\n const colors = rgbColor.split(';');\n if (colors.length < 3) {\n return;\n }\n this._fillColor = parseInt(\n gdjs.rgbToHex(\n parseInt(colors[0], 10),\n parseInt(colors[1], 10),\n parseInt(colors[2], 10)\n ),\n 16\n );\n }\n\n getFillColorR(): integer {\n return gdjs.hexNumberToRGB(this._fillColor).r;\n }\n getFillColorG(): integer {\n return gdjs.hexNumberToRGB(this._fillColor).g;\n }\n getFillColorB(): integer {\n return gdjs.hexNumberToRGB(this._fillColor).b;\n }\n\n /**\n *\n * @param rgbColor semicolon separated decimal values\n */\n setOutlineColor(rgbColor: string): void {\n const colors = rgbColor.split(';');\n if (colors.length < 3) {\n return;\n }\n this._outlineColor = parseInt(\n gdjs.rgbToHex(\n parseInt(colors[0], 10),\n parseInt(colors[1], 10),\n parseInt(colors[2], 10)\n ),\n 16\n );\n this._renderer.updateOutline();\n }\n\n getOutlineColorR(): integer {\n return gdjs.hexNumberToRGB(this._outlineColor).r;\n }\n getOutlineColorG(): integer {\n return gdjs.hexNumberToRGB(this._outlineColor).g;\n }\n getOutlineColorB(): integer {\n return gdjs.hexNumberToRGB(this._outlineColor).b;\n }\n\n setOutlineSize(size: float): void {\n this._outlineSize = size;\n this._renderer.updateOutline();\n }\n\n getOutlineSize() {\n return this._outlineSize;\n }\n\n /**\n *\n * @param opacity from 0 to 255\n */\n setFillOpacity(opacity: float): void {\n this._fillOpacity = opacity;\n }\n\n /**\n *\n * @returns an opacity value from 0 to 255.\n */\n getFillOpacity() {\n return this._fillOpacity;\n }\n\n /**\n *\n * @param opacity from 0 to 255\n */\n setOutlineOpacity(opacity: float): void {\n this._outlineOpacity = opacity;\n this._renderer.updateOutline();\n }\n\n /**\n *\n * @returns an opacity value from 0 to 255.\n */\n getOutlineOpacity() {\n return this._outlineOpacity;\n }\n\n setX(x: float): void {\n if (x === this.x) {\n return;\n }\n super.setX(x);\n this._renderer.updatePositionX();\n }\n\n setY(y: float): void {\n if (y === this.y) {\n return;\n }\n super.setY(y);\n this._renderer.updatePositionY();\n }\n\n setAngle(angle: float): void {\n if (angle === this.angle) {\n return;\n }\n super.setAngle(angle);\n this._renderer.updateAngle();\n this.invalidateHitboxes();\n }\n\n /**\n * The center of rotation is defined relatively\n * to the drawing origin (the object position).\n * This avoids the center to move on the drawing\n * when new shapes push the bounds.\n *\n * When no custom center is defined, it will move\n * to stay at the center of the drawable bounds.\n *\n * @param x coordinate of the custom center\n * @param y coordinate of the custom center\n */\n setRotationCenter(x: float, y: float): void {\n if (!this._customCenter) {\n this._customCenter = [0, 0];\n }\n this._customCenter[0] = x;\n this._customCenter[1] = y;\n this._renderer.updateRotationCenter();\n }\n\n /**\n * @returns The center X relatively to the drawing origin\n * (whereas `getCenterX()` is relative to the top left drawable bound and scaled).\n */\n getRotationCenterX(): float {\n return this._customCenter\n ? this._customCenter[0]\n : this._renderer.getUnscaledWidth() / 2 -\n this._renderer.getFrameRelativeOriginX();\n }\n\n /**\n * @returns The center Y relatively to the drawing origin\n * (whereas `getCenterY()` is relative to the top left drawable bound and scaled).\n */\n getRotationCenterY(): float {\n return this._customCenter\n ? this._customCenter[1]\n : this._renderer.getUnscaledHeight() / 2 -\n this._renderer.getFrameRelativeOriginY();\n }\n\n getCenterX(): float {\n if (!this._customCenter) {\n return super.getCenterX();\n }\n return (\n this._customCenter[0] * Math.abs(this._scaleX) +\n this.getX() -\n this.getDrawableX()\n );\n }\n\n getCenterY(): float {\n if (!this._customCenter) {\n return super.getCenterY();\n }\n return (\n this._customCenter[1] * Math.abs(this._scaleY) +\n this.getY() -\n this.getDrawableY()\n );\n }\n\n setWidth(newWidth: float): void {\n const unscaledWidth = this._renderer.getUnscaledWidth();\n if (unscaledWidth !== 0) {\n this.setScaleX(newWidth / unscaledWidth);\n }\n }\n\n setHeight(newHeight: float): void {\n const unscaledHeight = this._renderer.getUnscaledHeight();\n if (unscaledHeight !== 0) {\n this.setScaleY(newHeight / unscaledHeight);\n }\n }\n\n setSize(newWidth: float, newHeight: float): void {\n this.setWidth(newWidth);\n this.setHeight(newHeight);\n }\n\n /**\n * Change the scale on X and Y axis of the object.\n *\n * @param newScale The new scale (must be greater than 0).\n */\n setScale(newScale: float): void {\n this.setScaleX(newScale);\n this.setScaleY(newScale);\n }\n\n /**\n * Change the scale on X axis of the object (changing its width).\n *\n * @param newScale The new scale (must be greater than 0).\n */\n setScaleX(newScale: float): void {\n if (newScale < 0) {\n newScale = 0;\n }\n if (newScale === Math.abs(this._scaleX)) {\n return;\n }\n this._scaleX = newScale * (this._flippedX ? -1 : 1);\n this._renderer.updateScaleX();\n this.invalidateHitboxes();\n }\n\n /**\n * Change the scale on Y axis of the object (changing its width).\n *\n * @param newScale The new scale (must be greater than 0).\n */\n setScaleY(newScale: float): void {\n if (newScale < 0) {\n newScale = 0;\n }\n if (newScale === Math.abs(this._scaleY)) {\n return;\n }\n this._scaleY = newScale * (this._flippedY ? -1 : 1);\n this._renderer.updateScaleY();\n this.invalidateHitboxes();\n }\n\n flipX(enable: boolean): void {\n if (enable !== this._flippedX) {\n this._scaleX *= -1;\n this._flippedX = enable;\n this._renderer.updateScaleX();\n this.invalidateHitboxes();\n }\n }\n\n flipY(enable: boolean): void {\n if (enable !== this._flippedY) {\n this._scaleY *= -1;\n this._flippedY = enable;\n this._renderer.updateScaleY();\n this.invalidateHitboxes();\n }\n }\n\n isFlippedX(): boolean {\n return this._flippedX;\n }\n\n isFlippedY(): boolean {\n return this._flippedY;\n }\n\n /**\n * Get the scale of the object (or the geometric mean of the X and Y scale in case they are different).\n *\n * @return the scale of the object (or the geometric mean of the X and Y scale in case they are different).\n */\n getScale(): number {\n const scaleX = Math.abs(this._scaleX);\n const scaleY = Math.abs(this._scaleY);\n return scaleX === scaleY ? scaleX : Math.sqrt(scaleX * scaleY);\n }\n\n /**\n * Get the scale of the object on Y axis.\n *\n * @return the scale of the object on Y axis\n */\n getScaleY(): float {\n return Math.abs(this._scaleY);\n }\n\n /**\n * Get the scale of the object on X axis.\n *\n * @return the scale of the object on X axis\n */\n getScaleX(): float {\n return Math.abs(this._scaleX);\n }\n\n invalidateBounds() {\n this.invalidateHitboxes();\n }\n\n getDrawableX(): float {\n return this._renderer.getDrawableX();\n }\n\n getDrawableY(): float {\n return this._renderer.getDrawableY();\n }\n\n getWidth(): float {\n return this._renderer.getWidth();\n }\n\n getHeight(): float {\n return this._renderer.getHeight();\n }\n\n updatePreRender(instanceContainer: gdjs.RuntimeInstanceContainer): void {\n this._renderer.updatePreRender();\n }\n\n transformToDrawing(x: float, y: float) {\n const point = ShapePainterRuntimeObject._pointForTransformation;\n point[0] = x;\n point[1] = y;\n return this._renderer.transformToDrawing(point);\n }\n\n transformToScene(x: float, y: float) {\n const point = ShapePainterRuntimeObject._pointForTransformation;\n point[0] = x;\n point[1] = y;\n return this._renderer.transformToScene(point);\n }\n\n transformToDrawingX(x: float, y: float) {\n return this.transformToDrawing(x, y)[0];\n }\n\n transformToDrawingY(x: float, y: float) {\n return this.transformToDrawing(x, y)[1];\n }\n\n transformToSceneX(x: float, y: float) {\n return this.transformToScene(x, y)[0];\n }\n\n transformToSceneY(x: float, y: float) {\n return this.transformToScene(x, y)[1];\n }\n\n setRectangularCollisionMask(\n left: float,\n top: float,\n right: float,\n bottom: float\n ) {\n if (!this._customCollisionMask) {\n const rectangle = new gdjs.Polygon();\n rectangle.vertices.push([0, 0]);\n rectangle.vertices.push([0, 0]);\n rectangle.vertices.push([0, 0]);\n rectangle.vertices.push([0, 0]);\n this._customCollisionMask = [rectangle];\n }\n const rectangle = this._customCollisionMask[0].vertices;\n\n rectangle[0][0] = left;\n rectangle[0][1] = top;\n\n rectangle[1][0] = right;\n rectangle[1][1] = top;\n\n rectangle[2][0] = right;\n rectangle[2][1] = bottom;\n\n rectangle[3][0] = left;\n rectangle[3][1] = bottom;\n\n this.invalidateHitboxes();\n }\n\n updateHitBoxes(): void {\n this.hitBoxes = this._defaultHitBoxes;\n const width = this.getWidth();\n const height = this.getHeight();\n const centerX = this.getCenterX();\n const centerY = this.getCenterY();\n const vertices = this.hitBoxes[0].vertices;\n if (this._customCollisionMask) {\n const customCollisionMaskVertices = this._customCollisionMask[0]\n .vertices;\n for (let i = 0; i < 4; i++) {\n const point = this.transformToScene(\n customCollisionMaskVertices[i][0],\n customCollisionMaskVertices[i][1]\n );\n vertices[i][0] = point[0];\n vertices[i][1] = point[1];\n }\n } else {\n if (centerX === width / 2 && centerY === height / 2) {\n vertices[0][0] = -centerX;\n vertices[0][1] = -centerY;\n vertices[1][0] = +centerX;\n vertices[1][1] = -centerY;\n vertices[2][0] = +centerX;\n vertices[2][1] = +centerY;\n vertices[3][0] = -centerX;\n vertices[3][1] = +centerY;\n } else {\n vertices[0][0] = 0 - centerX;\n vertices[0][1] = 0 - centerY;\n vertices[1][0] = width - centerX;\n vertices[1][1] = 0 - centerY;\n vertices[2][0] = width - centerX;\n vertices[2][1] = height - centerY;\n vertices[3][0] = 0 - centerX;\n vertices[3][1] = height - centerY;\n }\n if (!this._useAbsoluteCoordinates) {\n this.hitBoxes[0].rotate(gdjs.toRad(this.getAngle()));\n }\n this.hitBoxes[0].move(\n this.getDrawableX() + centerX,\n this.getDrawableY() + centerY\n );\n }\n }\n }\n gdjs.registerObject(\n 'PrimitiveDrawing::Drawer',\n gdjs.ShapePainterRuntimeObject\n );\n ShapePainterRuntimeObject.supportsReinitialization = false;\n}\n"],
|
|
5
|
-
"mappings": "AAIA,GAAU,MAAV,UAAU,EAAV,CAsCS,qBACG,GAAK,aAC4C,CAyBzD,YACE,EACA,EACA,CACA,MAAM,EAAmB,GA5B3B,aAAkB,EAClB,aAAkB,EAClB,gBAAqB,EACrB,eAAqB,GACrB,eAAqB,GACrB,mBAAmC,KACnC,0BAAyC,KAuBvC,KAAK,
|
|
4
|
+
"sourcesContent": ["/*\n * GDevelop JS Platform\n * 2013 Florian Rival (Florian.Rival@gmail.com)\n */\nnamespace gdjs {\n /** Represents a color in RGB Format */\n export type RGBColor = {\n /** The Red component of the color, from 0 to 255. */\n r: integer;\n /** The Green component of the color, from 0 to 255. */\n g: integer;\n /** The Blue component of the color, from 0 to 255. */\n b: integer;\n };\n\n export type Antialiasing = 'none' | 'low' | 'medium' | 'high';\n\n /** Initial properties for a for {@link gdjs.ShapePainterRuntimeObject}. */\n export type ShapePainterObjectDataType = {\n /** The color of the inner part of the painted shape */\n fillColor: RGBColor | string;\n /** The color of the outline of the painted shape */\n outlineColor: RGBColor | string;\n /** The opacity of the inner part of the painted shape, from 0 to 255 */\n fillOpacity: float;\n /** The opacity of the outline of the painted shape, from 0 to 255 */\n outlineOpacity: float;\n /** The size of the outline of the painted shape, in pixels. */\n outlineSize: float;\n /** Use absolute coordinates? */\n absoluteCoordinates: boolean;\n /** Clear the previous render before the next draw? */\n clearBetweenFrames: boolean;\n /** The type of anti-aliasing to apply at rendering. */\n antialiasing: Antialiasing;\n };\n\n export type ShapePainterObjectData = ObjectData & ShapePainterObjectDataType;\n\n /**\n * The ShapePainterRuntimeObject allows to draw graphics shapes on screen.\n */\n export class ShapePainterRuntimeObject\n extends gdjs.RuntimeObject\n implements gdjs.Resizable, gdjs.Scalable, gdjs.Flippable {\n _scaleX: number = 1;\n _scaleY: number = 1;\n _blendMode: number = 0;\n _flippedX: boolean = false;\n _flippedY: boolean = false;\n _customCenter: FloatPoint | null = null;\n _customCollisionMask: Polygon[] | null = null;\n\n _fillColor: integer;\n _outlineColor: integer;\n _fillOpacity: float;\n _outlineOpacity: float;\n _outlineSize: float;\n _useAbsoluteCoordinates: boolean;\n _clearBetweenFrames: boolean;\n _antialiasing: Antialiasing;\n _renderer: gdjs.ShapePainterRuntimeObjectRenderer;\n\n private static readonly _pointForTransformation: FloatPoint = [0, 0];\n\n /**\n * @param instanceContainer The container the object belongs to.\n * @param shapePainterObjectData The initial properties of the object\n */\n constructor(\n instanceContainer: gdjs.RuntimeInstanceContainer,\n shapePainterObjectData: ShapePainterObjectData\n ) {\n super(instanceContainer, shapePainterObjectData);\n this._fillColor =\n typeof shapePainterObjectData.fillColor === 'string'\n ? gdjs.rgbOrHexStringToNumber(shapePainterObjectData.fillColor)\n : parseInt(\n gdjs.rgbToHex(\n shapePainterObjectData.fillColor.r,\n shapePainterObjectData.fillColor.g,\n shapePainterObjectData.fillColor.b\n ),\n 16\n );\n this._outlineColor =\n typeof shapePainterObjectData.outlineColor === 'string'\n ? gdjs.rgbOrHexStringToNumber(shapePainterObjectData.outlineColor)\n : parseInt(\n gdjs.rgbToHex(\n shapePainterObjectData.outlineColor.r,\n shapePainterObjectData.outlineColor.g,\n shapePainterObjectData.outlineColor.b\n ),\n 16\n );\n this._fillOpacity = shapePainterObjectData.fillOpacity;\n this._outlineOpacity = shapePainterObjectData.outlineOpacity;\n this._outlineSize = shapePainterObjectData.outlineSize;\n this._useAbsoluteCoordinates = shapePainterObjectData.absoluteCoordinates;\n this._clearBetweenFrames = shapePainterObjectData.clearBetweenFrames;\n this._antialiasing = shapePainterObjectData.antialiasing;\n this._renderer = new gdjs.ShapePainterRuntimeObjectRenderer(\n this,\n instanceContainer\n );\n\n // *ALWAYS* call `this.onCreated()` at the very end of your object constructor.\n this.onCreated();\n }\n\n getRendererObject() {\n return this._renderer.getRendererObject();\n }\n\n updateFromObjectData(\n oldObjectData: ShapePainterObjectData,\n newObjectData: ShapePainterObjectData\n ): boolean {\n if (typeof newObjectData.fillColor === 'string') {\n if (oldObjectData.fillColor !== newObjectData.fillColor) {\n this.setFillColor(newObjectData.fillColor);\n }\n }\n if (\n typeof oldObjectData.fillColor !== 'string' &&\n typeof newObjectData.fillColor !== 'string' &&\n (oldObjectData.fillColor.r !== newObjectData.fillColor.r ||\n oldObjectData.fillColor.g !== newObjectData.fillColor.g ||\n oldObjectData.fillColor.b !== newObjectData.fillColor.b)\n ) {\n this.setFillColor(\n '' +\n newObjectData.fillColor.r +\n ';' +\n newObjectData.fillColor.g +\n ';' +\n newObjectData.fillColor.b\n );\n }\n if (typeof newObjectData.outlineColor === 'string') {\n if (oldObjectData.outlineColor !== newObjectData.outlineColor) {\n this.setOutlineColor(newObjectData.outlineColor);\n }\n }\n if (\n typeof oldObjectData.outlineColor !== 'string' &&\n typeof newObjectData.outlineColor !== 'string' &&\n (oldObjectData.outlineColor.r !== newObjectData.outlineColor.r ||\n oldObjectData.outlineColor.g !== newObjectData.outlineColor.g ||\n oldObjectData.outlineColor.b !== newObjectData.outlineColor.b)\n ) {\n this.setOutlineColor(\n '' +\n newObjectData.outlineColor.r +\n ';' +\n newObjectData.outlineColor.g +\n ';' +\n newObjectData.outlineColor.b\n );\n }\n if (oldObjectData.fillOpacity !== newObjectData.fillOpacity) {\n this.setFillOpacity(newObjectData.fillOpacity);\n }\n if (oldObjectData.outlineOpacity !== newObjectData.outlineOpacity) {\n this.setOutlineOpacity(newObjectData.outlineOpacity);\n }\n if (oldObjectData.outlineSize !== newObjectData.outlineSize) {\n this.setOutlineSize(newObjectData.outlineSize);\n }\n if (\n oldObjectData.absoluteCoordinates !== newObjectData.absoluteCoordinates\n ) {\n this._useAbsoluteCoordinates = newObjectData.absoluteCoordinates;\n this._renderer.updatePositionX();\n this._renderer.updatePositionY();\n this._renderer.updateAngle();\n this._renderer.updateScaleX();\n this._renderer.updateScaleY();\n }\n if (\n oldObjectData.clearBetweenFrames !== newObjectData.clearBetweenFrames\n ) {\n this._clearBetweenFrames = newObjectData.clearBetweenFrames;\n }\n if (oldObjectData.antialiasing !== newObjectData.antialiasing) {\n this.setAntialiasing(newObjectData.antialiasing);\n }\n\n return true;\n }\n\n /**\n * Initialize the extra parameters that could be set for an instance.\n * @param initialInstanceData The extra parameters\n */\n extraInitializationFromInitialInstance(initialInstanceData: InstanceData) {\n if (initialInstanceData.flippedX) {\n this.flipX(initialInstanceData.flippedX);\n }\n if (initialInstanceData.flippedY) {\n this.flipY(initialInstanceData.flippedY);\n }\n }\n\n stepBehaviorsPreEvents(instanceContainer: gdjs.RuntimeInstanceContainer) {\n //We redefine stepBehaviorsPreEvents just to clear the graphics before running events.\n if (this._clearBetweenFrames) {\n this.clear();\n }\n super.stepBehaviorsPreEvents(instanceContainer);\n }\n\n onDestroyed(): void {\n super.onDestroyed();\n this._renderer.destroy();\n }\n\n /**\n * Clear the graphics.\n */\n clear() {\n this._renderer.clear();\n }\n\n getVisibilityAABB() {\n return this._useAbsoluteCoordinates ? null : this.getAABB();\n }\n\n drawRectangle(x1: float, y1: float, x2: float, y2: float) {\n this._renderer.drawRectangle(x1, y1, x2, y2);\n }\n\n drawCircle(x: float, y: float, radius: float) {\n this._renderer.drawCircle(x, y, radius);\n }\n\n drawLine(x1: float, y1: float, x2: float, y2: float, thickness: float) {\n this._renderer.drawLine(x1, y1, x2, y2, thickness);\n }\n\n drawLineV2(x1: float, y1: float, x2: float, y2: float, thickness: float) {\n this._renderer.drawLineV2(x1, y1, x2, y2, thickness);\n }\n\n drawEllipse(centerX: float, centerY: float, width: float, height: float) {\n this._renderer.drawEllipse(centerX, centerY, width, height);\n }\n\n drawFilletRectangle(\n startX1: float,\n startY1: float,\n endX2: float,\n endY2: float,\n fillet: float\n ) {\n this._renderer.drawFilletRectangle(\n startX1,\n startY1,\n endX2,\n endY2,\n fillet\n );\n }\n\n drawRoundedRectangle(\n startX1: float,\n startY1: float,\n endX2: float,\n endY2: float,\n radius: float\n ) {\n this._renderer.drawRoundedRectangle(\n startX1,\n startY1,\n endX2,\n endY2,\n radius\n );\n }\n\n drawChamferRectangle(\n startX1: float,\n startY1: float,\n endX2: float,\n endY2: float,\n chamfer: float\n ) {\n this._renderer.drawChamferRectangle(\n startX1,\n startY1,\n endX2,\n endY2,\n chamfer\n );\n }\n\n drawTorus(\n centerX: float,\n centerY: float,\n innerRadius: float,\n outerRadius: float,\n startArc: float,\n endArc: float\n ) {\n this._renderer.drawTorus(\n centerX,\n centerY,\n innerRadius,\n outerRadius,\n startArc,\n endArc\n );\n }\n\n drawRegularPolygon(\n centerX: float,\n centerY: float,\n sides: float,\n radius: float,\n rotation: float\n ) {\n this._renderer.drawRegularPolygon(\n centerX,\n centerY,\n sides,\n radius,\n rotation\n );\n }\n\n drawStar(\n centerX: float,\n centerY: float,\n points: float,\n radius: float,\n innerRadius: float,\n rotation: float\n ) {\n this._renderer.drawStar(\n centerX,\n centerY,\n points,\n radius,\n innerRadius,\n rotation\n );\n }\n\n drawArc(\n centerX: float,\n centerY: float,\n radius: float,\n startAngle: float,\n endAngle: float,\n anticlockwise: boolean,\n closePath: boolean\n ) {\n this._renderer.drawArc(\n centerX,\n centerY,\n radius,\n startAngle,\n endAngle,\n anticlockwise,\n closePath\n );\n }\n\n drawBezierCurve(\n x1: float,\n y1: float,\n cpX: float,\n cpY: float,\n cpX2: float,\n cpY2: float,\n x2: float,\n y2: float\n ) {\n this._renderer.drawBezierCurve(x1, y1, cpX, cpY, cpX2, cpY2, x2, y2);\n }\n\n drawQuadraticCurve(\n x1: float,\n y1: float,\n cpX: float,\n cpY: float,\n x2: float,\n y2: float\n ) {\n this._renderer.drawQuadraticCurve(x1, y1, cpX, cpY, x2, y2);\n }\n\n beginFillPath(x1: float, y1: float) {\n this._renderer.beginFillPath();\n this._renderer.drawPathMoveTo(x1, y1);\n }\n\n endFillPath() {\n this._renderer.endFillPath();\n }\n\n drawPathMoveTo(x1: float, y1: float) {\n this._renderer.drawPathMoveTo(x1, y1);\n }\n\n drawPathLineTo(x1: float, y1: float) {\n this._renderer.drawPathLineTo(x1, y1);\n }\n\n drawPathBezierCurveTo(\n cpX: float,\n cpY: float,\n cpX2: float,\n cpY2: float,\n toX: float,\n toY: float\n ) {\n this._renderer.drawPathBezierCurveTo(cpX, cpY, cpX2, cpY2, toX, toY);\n }\n\n drawPathArc(\n cx: float,\n cy: float,\n radius: float,\n startAngle: float,\n endAngle: float,\n anticlockwise: boolean\n ) {\n this._renderer.drawPathArc(\n cx,\n cy,\n radius,\n startAngle,\n endAngle,\n anticlockwise\n );\n }\n\n drawPathQuadraticCurveTo(cpX: float, cpY: float, toX: float, toY: float) {\n this._renderer.drawPathQuadraticCurveTo(cpX, cpY, toX, toY);\n }\n\n closePath() {\n this._renderer.closePath();\n }\n\n setClearBetweenFrames(value: boolean): void {\n this._clearBetweenFrames = value;\n }\n\n isClearedBetweenFrames(): boolean {\n return this._clearBetweenFrames;\n }\n\n setAntialiasing(value: Antialiasing): void {\n this._antialiasing = value;\n this._renderer.updateAntialiasing();\n }\n\n getAntialiasing(): Antialiasing {\n return this._antialiasing;\n }\n\n checkAntialiasing(valueToCompare: Antialiasing): boolean {\n return this._antialiasing === valueToCompare;\n }\n\n setCoordinatesRelative(value: boolean): void {\n this._useAbsoluteCoordinates = !value;\n }\n\n areCoordinatesRelative(): boolean {\n return !this._useAbsoluteCoordinates;\n }\n\n setFillColor(color: string): void {\n this._fillColor = gdjs.rgbOrHexStringToNumber(color);\n }\n\n getFillColorR(): integer {\n return gdjs.hexNumberToRGB(this._fillColor).r;\n }\n getFillColorG(): integer {\n return gdjs.hexNumberToRGB(this._fillColor).g;\n }\n getFillColorB(): integer {\n return gdjs.hexNumberToRGB(this._fillColor).b;\n }\n\n setOutlineColor(rgbColor: string): void {\n this._outlineColor = gdjs.rgbOrHexStringToNumber(rgbColor);\n this._renderer.updateOutline();\n }\n\n getOutlineColorR(): integer {\n return gdjs.hexNumberToRGB(this._outlineColor).r;\n }\n getOutlineColorG(): integer {\n return gdjs.hexNumberToRGB(this._outlineColor).g;\n }\n getOutlineColorB(): integer {\n return gdjs.hexNumberToRGB(this._outlineColor).b;\n }\n\n setOutlineSize(size: float): void {\n this._outlineSize = size;\n this._renderer.updateOutline();\n }\n\n getOutlineSize() {\n return this._outlineSize;\n }\n\n /**\n *\n * @param opacity from 0 to 255\n */\n setFillOpacity(opacity: float): void {\n this._fillOpacity = opacity;\n }\n\n /**\n *\n * @returns an opacity value from 0 to 255.\n */\n getFillOpacity() {\n return this._fillOpacity;\n }\n\n /**\n *\n * @param opacity from 0 to 255\n */\n setOutlineOpacity(opacity: float): void {\n this._outlineOpacity = opacity;\n this._renderer.updateOutline();\n }\n\n /**\n *\n * @returns an opacity value from 0 to 255.\n */\n getOutlineOpacity() {\n return this._outlineOpacity;\n }\n\n setX(x: float): void {\n if (x === this.x) {\n return;\n }\n super.setX(x);\n this._renderer.updatePositionX();\n }\n\n setY(y: float): void {\n if (y === this.y) {\n return;\n }\n super.setY(y);\n this._renderer.updatePositionY();\n }\n\n setAngle(angle: float): void {\n if (angle === this.angle) {\n return;\n }\n super.setAngle(angle);\n this._renderer.updateAngle();\n this.invalidateHitboxes();\n }\n\n /**\n * The center of rotation is defined relatively\n * to the drawing origin (the object position).\n * This avoids the center to move on the drawing\n * when new shapes push the bounds.\n *\n * When no custom center is defined, it will move\n * to stay at the center of the drawable bounds.\n *\n * @param x coordinate of the custom center\n * @param y coordinate of the custom center\n */\n setRotationCenter(x: float, y: float): void {\n if (!this._customCenter) {\n this._customCenter = [0, 0];\n }\n this._customCenter[0] = x;\n this._customCenter[1] = y;\n this._renderer.updateRotationCenter();\n }\n\n /**\n * @returns The center X relatively to the drawing origin\n * (whereas `getCenterX()` is relative to the top left drawable bound and scaled).\n */\n getRotationCenterX(): float {\n return this._customCenter\n ? this._customCenter[0]\n : this._renderer.getUnscaledWidth() / 2 -\n this._renderer.getFrameRelativeOriginX();\n }\n\n /**\n * @returns The center Y relatively to the drawing origin\n * (whereas `getCenterY()` is relative to the top left drawable bound and scaled).\n */\n getRotationCenterY(): float {\n return this._customCenter\n ? this._customCenter[1]\n : this._renderer.getUnscaledHeight() / 2 -\n this._renderer.getFrameRelativeOriginY();\n }\n\n getCenterX(): float {\n if (!this._customCenter) {\n return super.getCenterX();\n }\n return (\n this._customCenter[0] * Math.abs(this._scaleX) +\n this.getX() -\n this.getDrawableX()\n );\n }\n\n getCenterY(): float {\n if (!this._customCenter) {\n return super.getCenterY();\n }\n return (\n this._customCenter[1] * Math.abs(this._scaleY) +\n this.getY() -\n this.getDrawableY()\n );\n }\n\n setWidth(newWidth: float): void {\n const unscaledWidth = this._renderer.getUnscaledWidth();\n if (unscaledWidth !== 0) {\n this.setScaleX(newWidth / unscaledWidth);\n }\n }\n\n setHeight(newHeight: float): void {\n const unscaledHeight = this._renderer.getUnscaledHeight();\n if (unscaledHeight !== 0) {\n this.setScaleY(newHeight / unscaledHeight);\n }\n }\n\n setSize(newWidth: float, newHeight: float): void {\n this.setWidth(newWidth);\n this.setHeight(newHeight);\n }\n\n /**\n * Change the scale on X and Y axis of the object.\n *\n * @param newScale The new scale (must be greater than 0).\n */\n setScale(newScale: float): void {\n this.setScaleX(newScale);\n this.setScaleY(newScale);\n }\n\n /**\n * Change the scale on X axis of the object (changing its width).\n *\n * @param newScale The new scale (must be greater than 0).\n */\n setScaleX(newScale: float): void {\n if (newScale < 0) {\n newScale = 0;\n }\n if (newScale === Math.abs(this._scaleX)) {\n return;\n }\n this._scaleX = newScale * (this._flippedX ? -1 : 1);\n this._renderer.updateScaleX();\n this.invalidateHitboxes();\n }\n\n /**\n * Change the scale on Y axis of the object (changing its width).\n *\n * @param newScale The new scale (must be greater than 0).\n */\n setScaleY(newScale: float): void {\n if (newScale < 0) {\n newScale = 0;\n }\n if (newScale === Math.abs(this._scaleY)) {\n return;\n }\n this._scaleY = newScale * (this._flippedY ? -1 : 1);\n this._renderer.updateScaleY();\n this.invalidateHitboxes();\n }\n\n flipX(enable: boolean): void {\n if (enable !== this._flippedX) {\n this._scaleX *= -1;\n this._flippedX = enable;\n this._renderer.updateScaleX();\n this.invalidateHitboxes();\n }\n }\n\n flipY(enable: boolean): void {\n if (enable !== this._flippedY) {\n this._scaleY *= -1;\n this._flippedY = enable;\n this._renderer.updateScaleY();\n this.invalidateHitboxes();\n }\n }\n\n isFlippedX(): boolean {\n return this._flippedX;\n }\n\n isFlippedY(): boolean {\n return this._flippedY;\n }\n\n /**\n * Get the scale of the object (or the geometric mean of the X and Y scale in case they are different).\n *\n * @return the scale of the object (or the geometric mean of the X and Y scale in case they are different).\n */\n getScale(): number {\n const scaleX = Math.abs(this._scaleX);\n const scaleY = Math.abs(this._scaleY);\n return scaleX === scaleY ? scaleX : Math.sqrt(scaleX * scaleY);\n }\n\n /**\n * Get the scale of the object on Y axis.\n *\n * @return the scale of the object on Y axis\n */\n getScaleY(): float {\n return Math.abs(this._scaleY);\n }\n\n /**\n * Get the scale of the object on X axis.\n *\n * @return the scale of the object on X axis\n */\n getScaleX(): float {\n return Math.abs(this._scaleX);\n }\n\n invalidateBounds() {\n this.invalidateHitboxes();\n }\n\n getDrawableX(): float {\n return this._renderer.getDrawableX();\n }\n\n getDrawableY(): float {\n return this._renderer.getDrawableY();\n }\n\n getWidth(): float {\n return this._renderer.getWidth();\n }\n\n getHeight(): float {\n return this._renderer.getHeight();\n }\n\n updatePreRender(instanceContainer: gdjs.RuntimeInstanceContainer): void {\n this._renderer.updatePreRender();\n }\n\n transformToDrawing(x: float, y: float) {\n const point = ShapePainterRuntimeObject._pointForTransformation;\n point[0] = x;\n point[1] = y;\n return this._renderer.transformToDrawing(point);\n }\n\n transformToScene(x: float, y: float) {\n const point = ShapePainterRuntimeObject._pointForTransformation;\n point[0] = x;\n point[1] = y;\n return this._renderer.transformToScene(point);\n }\n\n transformToDrawingX(x: float, y: float) {\n return this.transformToDrawing(x, y)[0];\n }\n\n transformToDrawingY(x: float, y: float) {\n return this.transformToDrawing(x, y)[1];\n }\n\n transformToSceneX(x: float, y: float) {\n return this.transformToScene(x, y)[0];\n }\n\n transformToSceneY(x: float, y: float) {\n return this.transformToScene(x, y)[1];\n }\n\n setRectangularCollisionMask(\n left: float,\n top: float,\n right: float,\n bottom: float\n ) {\n if (!this._customCollisionMask) {\n const rectangle = new gdjs.Polygon();\n rectangle.vertices.push([0, 0]);\n rectangle.vertices.push([0, 0]);\n rectangle.vertices.push([0, 0]);\n rectangle.vertices.push([0, 0]);\n this._customCollisionMask = [rectangle];\n }\n const rectangle = this._customCollisionMask[0].vertices;\n\n rectangle[0][0] = left;\n rectangle[0][1] = top;\n\n rectangle[1][0] = right;\n rectangle[1][1] = top;\n\n rectangle[2][0] = right;\n rectangle[2][1] = bottom;\n\n rectangle[3][0] = left;\n rectangle[3][1] = bottom;\n\n this.invalidateHitboxes();\n }\n\n updateHitBoxes(): void {\n this.hitBoxes = this._defaultHitBoxes;\n const width = this.getWidth();\n const height = this.getHeight();\n const centerX = this.getCenterX();\n const centerY = this.getCenterY();\n const vertices = this.hitBoxes[0].vertices;\n if (this._customCollisionMask) {\n const customCollisionMaskVertices = this._customCollisionMask[0]\n .vertices;\n for (let i = 0; i < 4; i++) {\n const point = this.transformToScene(\n customCollisionMaskVertices[i][0],\n customCollisionMaskVertices[i][1]\n );\n vertices[i][0] = point[0];\n vertices[i][1] = point[1];\n }\n } else {\n if (centerX === width / 2 && centerY === height / 2) {\n vertices[0][0] = -centerX;\n vertices[0][1] = -centerY;\n vertices[1][0] = +centerX;\n vertices[1][1] = -centerY;\n vertices[2][0] = +centerX;\n vertices[2][1] = +centerY;\n vertices[3][0] = -centerX;\n vertices[3][1] = +centerY;\n } else {\n vertices[0][0] = 0 - centerX;\n vertices[0][1] = 0 - centerY;\n vertices[1][0] = width - centerX;\n vertices[1][1] = 0 - centerY;\n vertices[2][0] = width - centerX;\n vertices[2][1] = height - centerY;\n vertices[3][0] = 0 - centerX;\n vertices[3][1] = height - centerY;\n }\n if (!this._useAbsoluteCoordinates) {\n this.hitBoxes[0].rotate(gdjs.toRad(this.getAngle()));\n }\n this.hitBoxes[0].move(\n this.getDrawableX() + centerX,\n this.getDrawableY() + centerY\n );\n }\n }\n }\n gdjs.registerObject(\n 'PrimitiveDrawing::Drawer',\n gdjs.ShapePainterRuntimeObject\n );\n ShapePainterRuntimeObject.supportsReinitialization = false;\n}\n"],
|
|
5
|
+
"mappings": "AAIA,GAAU,MAAV,UAAU,EAAV,CAsCS,qBACG,GAAK,aAC4C,CAyBzD,YACE,EACA,EACA,CACA,MAAM,EAAmB,GA5B3B,aAAkB,EAClB,aAAkB,EAClB,gBAAqB,EACrB,eAAqB,GACrB,eAAqB,GACrB,mBAAmC,KACnC,0BAAyC,KAuBvC,KAAK,WACH,MAAO,GAAuB,WAAc,SACxC,EAAK,uBAAuB,EAAuB,WACnD,SACE,EAAK,SACH,EAAuB,UAAU,EACjC,EAAuB,UAAU,EACjC,EAAuB,UAAU,GAEnC,IAER,KAAK,cACH,MAAO,GAAuB,cAAiB,SAC3C,EAAK,uBAAuB,EAAuB,cACnD,SACE,EAAK,SACH,EAAuB,aAAa,EACpC,EAAuB,aAAa,EACpC,EAAuB,aAAa,GAEtC,IAER,KAAK,aAAe,EAAuB,YAC3C,KAAK,gBAAkB,EAAuB,eAC9C,KAAK,aAAe,EAAuB,YAC3C,KAAK,wBAA0B,EAAuB,oBACtD,KAAK,oBAAsB,EAAuB,mBAClD,KAAK,cAAgB,EAAuB,aAC5C,KAAK,UAAY,GAAI,GAAK,kCACxB,KACA,GAIF,KAAK,YAGP,mBAAoB,CAClB,MAAO,MAAK,UAAU,oBAGxB,qBACE,EACA,EACS,CACT,MAAI,OAAO,GAAc,WAAc,UACjC,EAAc,YAAc,EAAc,WAC5C,KAAK,aAAa,EAAc,WAIlC,MAAO,GAAc,WAAc,UACnC,MAAO,GAAc,WAAc,UAClC,GAAc,UAAU,IAAM,EAAc,UAAU,GACrD,EAAc,UAAU,IAAM,EAAc,UAAU,GACtD,EAAc,UAAU,IAAM,EAAc,UAAU,IAExD,KAAK,aACH,GACE,EAAc,UAAU,EACxB,IACA,EAAc,UAAU,EACxB,IACA,EAAc,UAAU,GAG1B,MAAO,GAAc,cAAiB,UACpC,EAAc,eAAiB,EAAc,cAC/C,KAAK,gBAAgB,EAAc,cAIrC,MAAO,GAAc,cAAiB,UACtC,MAAO,GAAc,cAAiB,UACrC,GAAc,aAAa,IAAM,EAAc,aAAa,GAC3D,EAAc,aAAa,IAAM,EAAc,aAAa,GAC5D,EAAc,aAAa,IAAM,EAAc,aAAa,IAE9D,KAAK,gBACH,GACE,EAAc,aAAa,EAC3B,IACA,EAAc,aAAa,EAC3B,IACA,EAAc,aAAa,GAG7B,EAAc,cAAgB,EAAc,aAC9C,KAAK,eAAe,EAAc,aAEhC,EAAc,iBAAmB,EAAc,gBACjD,KAAK,kBAAkB,EAAc,gBAEnC,EAAc,cAAgB,EAAc,aAC9C,KAAK,eAAe,EAAc,aAGlC,EAAc,sBAAwB,EAAc,qBAEpD,MAAK,wBAA0B,EAAc,oBAC7C,KAAK,UAAU,kBACf,KAAK,UAAU,kBACf,KAAK,UAAU,cACf,KAAK,UAAU,eACf,KAAK,UAAU,gBAGf,EAAc,qBAAuB,EAAc,oBAEnD,MAAK,oBAAsB,EAAc,oBAEvC,EAAc,eAAiB,EAAc,cAC/C,KAAK,gBAAgB,EAAc,cAG9B,GAOT,uCAAuC,EAAmC,CACxE,AAAI,EAAoB,UACtB,KAAK,MAAM,EAAoB,UAE7B,EAAoB,UACtB,KAAK,MAAM,EAAoB,UAInC,uBAAuB,EAAkD,CAEvE,AAAI,KAAK,qBACP,KAAK,QAEP,MAAM,uBAAuB,GAG/B,aAAoB,CAClB,MAAM,cACN,KAAK,UAAU,UAMjB,OAAQ,CACN,KAAK,UAAU,QAGjB,mBAAoB,CAClB,MAAO,MAAK,wBAA0B,KAAO,KAAK,UAGpD,cAAc,EAAW,EAAW,EAAW,EAAW,CACxD,KAAK,UAAU,cAAc,EAAI,EAAI,EAAI,GAG3C,WAAW,EAAU,EAAU,EAAe,CAC5C,KAAK,UAAU,WAAW,EAAG,EAAG,GAGlC,SAAS,EAAW,EAAW,EAAW,EAAW,EAAkB,CACrE,KAAK,UAAU,SAAS,EAAI,EAAI,EAAI,EAAI,GAG1C,WAAW,EAAW,EAAW,EAAW,EAAW,EAAkB,CACvE,KAAK,UAAU,WAAW,EAAI,EAAI,EAAI,EAAI,GAG5C,YAAY,EAAgB,EAAgB,EAAc,EAAe,CACvE,KAAK,UAAU,YAAY,EAAS,EAAS,EAAO,GAGtD,oBACE,EACA,EACA,EACA,EACA,EACA,CACA,KAAK,UAAU,oBACb,EACA,EACA,EACA,EACA,GAIJ,qBACE,EACA,EACA,EACA,EACA,EACA,CACA,KAAK,UAAU,qBACb,EACA,EACA,EACA,EACA,GAIJ,qBACE,EACA,EACA,EACA,EACA,EACA,CACA,KAAK,UAAU,qBACb,EACA,EACA,EACA,EACA,GAIJ,UACE,EACA,EACA,EACA,EACA,EACA,EACA,CACA,KAAK,UAAU,UACb,EACA,EACA,EACA,EACA,EACA,GAIJ,mBACE,EACA,EACA,EACA,EACA,EACA,CACA,KAAK,UAAU,mBACb,EACA,EACA,EACA,EACA,GAIJ,SACE,EACA,EACA,EACA,EACA,EACA,EACA,CACA,KAAK,UAAU,SACb,EACA,EACA,EACA,EACA,EACA,GAIJ,QACE,EACA,EACA,EACA,EACA,EACA,EACA,EACA,CACA,KAAK,UAAU,QACb,EACA,EACA,EACA,EACA,EACA,EACA,GAIJ,gBACE,EACA,EACA,EACA,EACA,EACA,EACA,EACA,EACA,CACA,KAAK,UAAU,gBAAgB,EAAI,EAAI,EAAK,EAAK,EAAM,EAAM,EAAI,GAGnE,mBACE,EACA,EACA,EACA,EACA,EACA,EACA,CACA,KAAK,UAAU,mBAAmB,EAAI,EAAI,EAAK,EAAK,EAAI,GAG1D,cAAc,EAAW,EAAW,CAClC,KAAK,UAAU,gBACf,KAAK,UAAU,eAAe,EAAI,GAGpC,aAAc,CACZ,KAAK,UAAU,cAGjB,eAAe,EAAW,EAAW,CACnC,KAAK,UAAU,eAAe,EAAI,GAGpC,eAAe,EAAW,EAAW,CACnC,KAAK,UAAU,eAAe,EAAI,GAGpC,sBACE,EACA,EACA,EACA,EACA,EACA,EACA,CACA,KAAK,UAAU,sBAAsB,EAAK,EAAK,EAAM,EAAM,EAAK,GAGlE,YACE,EACA,EACA,EACA,EACA,EACA,EACA,CACA,KAAK,UAAU,YACb,EACA,EACA,EACA,EACA,EACA,GAIJ,yBAAyB,EAAY,EAAY,EAAY,EAAY,CACvE,KAAK,UAAU,yBAAyB,EAAK,EAAK,EAAK,GAGzD,WAAY,CACV,KAAK,UAAU,YAGjB,sBAAsB,EAAsB,CAC1C,KAAK,oBAAsB,EAG7B,wBAAkC,CAChC,MAAO,MAAK,oBAGd,gBAAgB,EAA2B,CACzC,KAAK,cAAgB,EACrB,KAAK,UAAU,qBAGjB,iBAAgC,CAC9B,MAAO,MAAK,cAGd,kBAAkB,EAAuC,CACvD,MAAO,MAAK,gBAAkB,EAGhC,uBAAuB,EAAsB,CAC3C,KAAK,wBAA0B,CAAC,EAGlC,wBAAkC,CAChC,MAAO,CAAC,KAAK,wBAGf,aAAa,EAAqB,CAChC,KAAK,WAAa,EAAK,uBAAuB,GAGhD,eAAyB,CACvB,MAAO,GAAK,eAAe,KAAK,YAAY,EAE9C,eAAyB,CACvB,MAAO,GAAK,eAAe,KAAK,YAAY,EAE9C,eAAyB,CACvB,MAAO,GAAK,eAAe,KAAK,YAAY,EAG9C,gBAAgB,EAAwB,CACtC,KAAK,cAAgB,EAAK,uBAAuB,GACjD,KAAK,UAAU,gBAGjB,kBAA4B,CAC1B,MAAO,GAAK,eAAe,KAAK,eAAe,EAEjD,kBAA4B,CAC1B,MAAO,GAAK,eAAe,KAAK,eAAe,EAEjD,kBAA4B,CAC1B,MAAO,GAAK,eAAe,KAAK,eAAe,EAGjD,eAAe,EAAmB,CAChC,KAAK,aAAe,EACpB,KAAK,UAAU,gBAGjB,gBAAiB,CACf,MAAO,MAAK,aAOd,eAAe,EAAsB,CACnC,KAAK,aAAe,EAOtB,gBAAiB,CACf,MAAO,MAAK,aAOd,kBAAkB,EAAsB,CACtC,KAAK,gBAAkB,EACvB,KAAK,UAAU,gBAOjB,mBAAoB,CAClB,MAAO,MAAK,gBAGd,KAAK,EAAgB,CACnB,AAAI,IAAM,KAAK,GAGf,OAAM,KAAK,GACX,KAAK,UAAU,mBAGjB,KAAK,EAAgB,CACnB,AAAI,IAAM,KAAK,GAGf,OAAM,KAAK,GACX,KAAK,UAAU,mBAGjB,SAAS,EAAoB,CAC3B,AAAI,IAAU,KAAK,OAGnB,OAAM,SAAS,GACf,KAAK,UAAU,cACf,KAAK,sBAeP,kBAAkB,EAAU,EAAgB,CAC1C,AAAK,KAAK,eACR,MAAK,cAAgB,CAAC,EAAG,IAE3B,KAAK,cAAc,GAAK,EACxB,KAAK,cAAc,GAAK,EACxB,KAAK,UAAU,uBAOjB,oBAA4B,CAC1B,MAAO,MAAK,cACR,KAAK,cAAc,GACnB,KAAK,UAAU,mBAAqB,EAClC,KAAK,UAAU,0BAOvB,oBAA4B,CAC1B,MAAO,MAAK,cACR,KAAK,cAAc,GACnB,KAAK,UAAU,oBAAsB,EACnC,KAAK,UAAU,0BAGvB,YAAoB,CAClB,MAAK,MAAK,cAIR,KAAK,cAAc,GAAK,KAAK,IAAI,KAAK,SACtC,KAAK,OACL,KAAK,eALE,MAAM,aASjB,YAAoB,CAClB,MAAK,MAAK,cAIR,KAAK,cAAc,GAAK,KAAK,IAAI,KAAK,SACtC,KAAK,OACL,KAAK,eALE,MAAM,aASjB,SAAS,EAAuB,CAC9B,KAAM,GAAgB,KAAK,UAAU,mBACrC,AAAI,IAAkB,GACpB,KAAK,UAAU,EAAW,GAI9B,UAAU,EAAwB,CAChC,KAAM,GAAiB,KAAK,UAAU,oBACtC,AAAI,IAAmB,GACrB,KAAK,UAAU,EAAY,GAI/B,QAAQ,EAAiB,EAAwB,CAC/C,KAAK,SAAS,GACd,KAAK,UAAU,GAQjB,SAAS,EAAuB,CAC9B,KAAK,UAAU,GACf,KAAK,UAAU,GAQjB,UAAU,EAAuB,CAI/B,AAHI,EAAW,GACb,GAAW,GAET,IAAa,KAAK,IAAI,KAAK,UAG/B,MAAK,QAAU,EAAY,MAAK,UAAY,GAAK,GACjD,KAAK,UAAU,eACf,KAAK,sBAQP,UAAU,EAAuB,CAI/B,AAHI,EAAW,GACb,GAAW,GAET,IAAa,KAAK,IAAI,KAAK,UAG/B,MAAK,QAAU,EAAY,MAAK,UAAY,GAAK,GACjD,KAAK,UAAU,eACf,KAAK,sBAGP,MAAM,EAAuB,CAC3B,AAAI,IAAW,KAAK,WAClB,MAAK,SAAW,GAChB,KAAK,UAAY,EACjB,KAAK,UAAU,eACf,KAAK,sBAIT,MAAM,EAAuB,CAC3B,AAAI,IAAW,KAAK,WAClB,MAAK,SAAW,GAChB,KAAK,UAAY,EACjB,KAAK,UAAU,eACf,KAAK,sBAIT,YAAsB,CACpB,MAAO,MAAK,UAGd,YAAsB,CACpB,MAAO,MAAK,UAQd,UAAmB,CACjB,KAAM,GAAS,KAAK,IAAI,KAAK,SACvB,EAAS,KAAK,IAAI,KAAK,SAC7B,MAAO,KAAW,EAAS,EAAS,KAAK,KAAK,EAAS,GAQzD,WAAmB,CACjB,MAAO,MAAK,IAAI,KAAK,SAQvB,WAAmB,CACjB,MAAO,MAAK,IAAI,KAAK,SAGvB,kBAAmB,CACjB,KAAK,qBAGP,cAAsB,CACpB,MAAO,MAAK,UAAU,eAGxB,cAAsB,CACpB,MAAO,MAAK,UAAU,eAGxB,UAAkB,CAChB,MAAO,MAAK,UAAU,WAGxB,WAAmB,CACjB,MAAO,MAAK,UAAU,YAGxB,gBAAgB,EAAwD,CACtE,KAAK,UAAU,kBAGjB,mBAAmB,EAAU,EAAU,CACrC,KAAM,GAAQ,EAA0B,wBACxC,SAAM,GAAK,EACX,EAAM,GAAK,EACJ,KAAK,UAAU,mBAAmB,GAG3C,iBAAiB,EAAU,EAAU,CACnC,KAAM,GAAQ,EAA0B,wBACxC,SAAM,GAAK,EACX,EAAM,GAAK,EACJ,KAAK,UAAU,iBAAiB,GAGzC,oBAAoB,EAAU,EAAU,CACtC,MAAO,MAAK,mBAAmB,EAAG,GAAG,GAGvC,oBAAoB,EAAU,EAAU,CACtC,MAAO,MAAK,mBAAmB,EAAG,GAAG,GAGvC,kBAAkB,EAAU,EAAU,CACpC,MAAO,MAAK,iBAAiB,EAAG,GAAG,GAGrC,kBAAkB,EAAU,EAAU,CACpC,MAAO,MAAK,iBAAiB,EAAG,GAAG,GAGrC,4BACE,EACA,EACA,EACA,EACA,CACA,GAAI,CAAC,KAAK,qBAAsB,CAC9B,KAAM,GAAY,GAAI,GAAK,QAC3B,EAAU,SAAS,KAAK,CAAC,EAAG,IAC5B,EAAU,SAAS,KAAK,CAAC,EAAG,IAC5B,EAAU,SAAS,KAAK,CAAC,EAAG,IAC5B,EAAU,SAAS,KAAK,CAAC,EAAG,IAC5B,KAAK,qBAAuB,CAAC,GAE/B,KAAM,GAAY,KAAK,qBAAqB,GAAG,SAE/C,EAAU,GAAG,GAAK,EAClB,EAAU,GAAG,GAAK,EAElB,EAAU,GAAG,GAAK,EAClB,EAAU,GAAG,GAAK,EAElB,EAAU,GAAG,GAAK,EAClB,EAAU,GAAG,GAAK,EAElB,EAAU,GAAG,GAAK,EAClB,EAAU,GAAG,GAAK,EAElB,KAAK,qBAGP,gBAAuB,CACrB,KAAK,SAAW,KAAK,iBACrB,KAAM,GAAQ,KAAK,WACb,EAAS,KAAK,YACd,EAAU,KAAK,aACf,EAAU,KAAK,aACf,EAAW,KAAK,SAAS,GAAG,SAClC,GAAI,KAAK,qBAAsB,CAC7B,KAAM,GAA8B,KAAK,qBAAqB,GAC3D,SACH,OAAS,GAAI,EAAG,EAAI,EAAG,IAAK,CAC1B,KAAM,GAAQ,KAAK,iBACjB,EAA4B,GAAG,GAC/B,EAA4B,GAAG,IAEjC,EAAS,GAAG,GAAK,EAAM,GACvB,EAAS,GAAG,GAAK,EAAM,QAGzB,AAAI,KAAY,EAAQ,GAAK,IAAY,EAAS,EAChD,GAAS,GAAG,GAAK,CAAC,EAClB,EAAS,GAAG,GAAK,CAAC,EAClB,EAAS,GAAG,GAAK,CAAC,EAClB,EAAS,GAAG,GAAK,CAAC,EAClB,EAAS,GAAG,GAAK,CAAC,EAClB,EAAS,GAAG,GAAK,CAAC,EAClB,EAAS,GAAG,GAAK,CAAC,EAClB,EAAS,GAAG,GAAK,CAAC,GAElB,GAAS,GAAG,GAAK,EAAI,EACrB,EAAS,GAAG,GAAK,EAAI,EACrB,EAAS,GAAG,GAAK,EAAQ,EACzB,EAAS,GAAG,GAAK,EAAI,EACrB,EAAS,GAAG,GAAK,EAAQ,EACzB,EAAS,GAAG,GAAK,EAAS,EAC1B,EAAS,GAAG,GAAK,EAAI,EACrB,EAAS,GAAG,GAAK,EAAS,GAEvB,KAAK,yBACR,KAAK,SAAS,GAAG,OAAO,EAAK,MAAM,KAAK,aAE1C,KAAK,SAAS,GAAG,KACf,KAAK,eAAiB,EACtB,KAAK,eAAiB,KAz0BvB,QAqBmB,AArBnB,EAqBmB,wBAAsC,CAAC,EAAG,GArB7D,EAAM,4BA80Bb,EAAK,eACH,2BACA,EAAK,2BAEP,EAA0B,yBAA2B,KAx3B7C",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|