2020-05-09 14:45:23 +00:00
import { GameState } from "../core/game_state" ;
import { createLogger } from "../core/logging" ;
2020-08-06 09:28:28 +00:00
import { findNiceValue } from "../core/utils" ;
2020-05-09 14:45:23 +00:00
import { cachebust } from "../core/cachebust" ;
import { PlatformWrapperImplBrowser } from "../platform/browser/wrapper" ;
2020-06-10 10:13:38 +00:00
import { T , autoDetectLanguageId , updateApplicationLanguage } from "../translations" ;
2020-05-21 11:55:45 +00:00
import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs" ;
2020-05-23 13:04:55 +00:00
import { CHANGELOG } from "../changelog" ;
2020-05-27 12:30:59 +00:00
import { globalConfig } from "../core/config" ;
2020-05-09 14:45:23 +00:00
const logger = createLogger ( "state/preload" ) ;
export class PreloadState extends GameState {
constructor ( ) {
super ( "PreloadState" ) ;
}
getInnerHTML ( ) {
return `
< div class = "loadingImage" > < / d i v >
< div class = "loadingStatus" >
< span class = "desc" > Booting < / s p a n >
< span class = "bar" >
< span class = "inner" style = "width: 0%" > < / s p a n >
< span class = "status" > 0 % < / s p a n >
< / s p a n >
< / d i v >
< / d i v >
` ;
}
getThemeMusic ( ) {
return null ;
}
getHasFadeIn ( ) {
return false ;
}
2020-07-22 02:15:16 +00:00
onEnter ( ) {
2020-05-09 14:45:23 +00:00
this . htmlElement . classList . add ( "prefab_LoadingState" ) ;
const elementsToRemove = [ "#loadingPreload" , "#fontPreload" ] ;
for ( let i = 0 ; i < elementsToRemove . length ; ++ i ) {
const elem = document . querySelector ( elementsToRemove [ i ] ) ;
if ( elem ) {
elem . remove ( ) ;
}
}
2020-05-21 11:55:45 +00:00
this . dialogs = new HUDModalDialogs ( null , this . app ) ;
const dialogsElement = document . body . querySelector ( ".modalDialogParent" ) ;
this . dialogs . initializeToElement ( dialogsElement ) ;
2020-05-09 14:45:23 +00:00
2020-07-22 02:15:16 +00:00
/** @type {HTMLElement} */
2020-05-09 14:45:23 +00:00
this . statusText = this . htmlElement . querySelector ( ".loadingStatus > .desc" ) ;
2020-07-22 02:15:16 +00:00
/** @type {HTMLElement} */
2020-05-09 14:45:23 +00:00
this . statusBar = this . htmlElement . querySelector ( ".loadingStatus > .bar > .inner" ) ;
2020-07-22 02:15:16 +00:00
/** @type {HTMLElement} */
2020-05-09 14:45:23 +00:00
this . statusBarText = this . htmlElement . querySelector ( ".loadingStatus > .bar > .status" ) ;
2020-07-22 02:15:16 +00:00
2020-05-09 14:45:23 +00:00
this . currentStatus = "booting" ;
this . currentIndex = 0 ;
this . startLoading ( ) ;
}
onLeave ( ) {
// this.dialogs.cleanup();
}
startLoading ( ) {
this . setStatus ( "Booting" )
. then ( ( ) => this . setStatus ( "Creating platform wrapper" ) )
. then ( ( ) => this . app . platformWrapper . initialize ( ) )
. then ( ( ) => this . setStatus ( "Initializing local storage" ) )
. then ( ( ) => {
const wrapper = this . app . platformWrapper ;
if ( wrapper instanceof PlatformWrapperImplBrowser ) {
try {
window . localStorage . setItem ( "local_storage_test" , "1" ) ;
window . localStorage . removeItem ( "local_storage_test" ) ;
} catch ( ex ) {
logger . error ( "Failed to read/write local storage:" , ex ) ;
return new Promise ( ( ) => {
alert ( ` Your brower does not support thirdparty cookies or you have disabled it in your security settings. \n \n
In Chrome this setting is called "Block third-party cookies and site data" . \ n \ n
Please allow third party cookies and then reload the page . ` );
// Never return
} ) ;
}
}
} )
. then ( ( ) => this . setStatus ( "Creating storage" ) )
. then ( ( ) => {
return this . app . storage . initialize ( ) ;
} )
2020-05-16 15:57:25 +00:00
. then ( ( ) => this . setStatus ( "Initializing libraries" ) )
. then ( ( ) => this . app . analytics . initialize ( ) )
. then ( ( ) => this . app . gameAnalytics . initialize ( ) )
2020-05-09 14:45:23 +00:00
. then ( ( ) => this . setStatus ( "Initializing settings" ) )
. then ( ( ) => {
return this . app . settings . initialize ( ) ;
} )
2020-05-21 11:05:21 +00:00
. then ( ( ) => {
// Initialize fullscreen
if ( this . app . platformWrapper . getSupportsFullscreen ( ) ) {
this . app . platformWrapper . setFullscreen ( this . app . settings . getIsFullScreen ( ) ) ;
}
} )
2020-06-10 10:13:38 +00:00
. then ( ( ) => this . setStatus ( "Initializing language" ) )
. then ( ( ) => {
if ( this . app . settings . getLanguage ( ) === "auto-detect" ) {
const language = autoDetectLanguageId ( ) ;
logger . log ( "Setting language to" , language ) ;
return this . app . settings . updateLanguage ( language ) ;
}
} )
. then ( ( ) => {
const language = this . app . settings . getLanguage ( ) ;
updateApplicationLanguage ( language ) ;
} )
2020-05-09 14:45:23 +00:00
. then ( ( ) => this . setStatus ( "Initializing sounds" ) )
. then ( ( ) => {
// Notice: We don't await the sounds loading itself
return this . app . sound . initialize ( ) ;
} )
. then ( ( ) => {
this . app . backgroundResourceLoader . startLoading ( ) ;
} )
. then ( ( ) => this . setStatus ( "Initializing savegame" ) )
. then ( ( ) => {
return this . app . savegameMgr . initialize ( ) . catch ( err => {
logger . error ( "Failed to initialize savegames:" , err ) ;
2020-06-13 08:11:18 +00:00
alert (
"Your savegames failed to load, it seems your data files got corrupted. I'm so sorry!\n\n(This can happen if your pc crashed while a game was saved).\n\nYou can try re-importing your savegames."
) ;
return this . app . savegameMgr . writeAsync ( ) ;
2020-05-09 14:45:23 +00:00
} ) ;
} )
. then ( ( ) => this . setStatus ( "Downloading resources" ) )
. then ( ( ) => {
return this . app . backgroundResourceLoader . getPromiseForBareGame ( ) ;
} )
2020-05-23 13:04:55 +00:00
. then ( ( ) => this . setStatus ( "Checking changelog" ) )
. then ( ( ) => {
2020-05-27 12:30:59 +00:00
if ( G _IS _DEV && globalConfig . debug . disableUpgradeNotification ) {
return ;
}
2020-05-23 13:04:55 +00:00
return this . app . storage
. readFileAsync ( "lastversion.bin" )
. catch ( err => {
logger . warn ( "Failed to read lastversion:" , err ) ;
return G _BUILD _VERSION ;
} )
. then ( version => {
2020-05-24 15:53:13 +00:00
logger . log ( "Last version:" , version , "App version:" , G _BUILD _VERSION ) ;
this . app . storage . writeFileAsync ( "lastversion.bin" , G _BUILD _VERSION ) ;
2020-05-23 13:04:55 +00:00
return version ;
} )
. then ( version => {
let changelogEntries = [ ] ;
logger . log ( "Last seen version:" , version ) ;
for ( let i = 0 ; i < CHANGELOG . length ; ++ i ) {
if ( CHANGELOG [ i ] . version === version ) {
break ;
}
changelogEntries . push ( CHANGELOG [ i ] ) ;
}
if ( changelogEntries . length === 0 ) {
return ;
}
let dialogHtml = T . dialogs . updateSummary . desc ;
for ( let i = 0 ; i < changelogEntries . length ; ++ i ) {
const entry = changelogEntries [ i ] ;
dialogHtml += `
< div class = "changelogDialogEntry" >
< span class = "version" > $ { entry . version } < / s p a n >
< span class = "date" > $ { entry . date } < / s p a n >
< ul class = "changes" >
$ { entry . entries . map ( text => ` <li> ${ text } </li> ` ) . join ( "" ) }
< / u l >
< / d i v >
` ;
}
return new Promise ( resolve => {
this . dialogs . showInfo ( T . dialogs . updateSummary . title , dialogHtml ) . ok . add ( resolve ) ;
} ) ;
} ) ;
} )
2020-05-09 14:45:23 +00:00
. then ( ( ) => this . setStatus ( "Launching" ) )
. then (
( ) => {
this . moveToState ( "MainMenuState" ) ;
} ,
err => {
this . showFailMessage ( err ) ;
}
) ;
}
setStatus ( text ) {
logger . log ( "✅ " + text ) ;
this . currentIndex += 1 ;
this . currentStatus = text ;
this . statusText . innerText = text ;
const numSteps = 10 ; // FIXME
const percentage = ( this . currentIndex / numSteps ) * 100.0 ;
this . statusBar . style . width = percentage + "%" ;
this . statusBarText . innerText = findNiceValue ( percentage ) + "%" ;
2020-05-19 09:24:51 +00:00
return Promise . resolve ( ) ;
2020-05-09 14:45:23 +00:00
}
showFailMessage ( text ) {
logger . error ( "App init failed:" , text ) ;
const email = "bugs@shapez.io" ;
const subElement = document . createElement ( "div" ) ;
subElement . classList . add ( "failureBox" ) ;
subElement . innerHTML = `
< div class = "logo" >
< img src = "${cachebust(" res / logo . png ")}" alt = "Shapez.io Logo" >
< / d i v >
< div class = "failureInner" >
< div class = "errorHeader" >
Failed to initialize application !
< / d i v >
< div class = "errorMessage" >
$ { this . currentStatus } failed : < br / >
$ { text }
< / d i v >
2020-07-22 02:15:16 +00:00
2020-05-09 14:45:23 +00:00
< div class = "supportHelp" >
Please send me an email with steps to reproduce and what you did before this happened :
< br / > < a class = "email" href = "mailto:${email}?subject=App%20does%20not%20launch" > $ { email } < / a >
< / d i v >
2020-07-22 02:15:16 +00:00
2020-05-09 14:45:23 +00:00
< div class = "lower" >
< button class = "resetApp styledButton" > Reset App < / b u t t o n >
< i > Build $ { G _BUILD _VERSION } @ $ { G _BUILD _COMMIT _HASH } < / i >
< / d i v >
< / d i v >
` ;
this . htmlElement . classList . add ( "failure" ) ;
this . htmlElement . appendChild ( subElement ) ;
const resetBtn = subElement . querySelector ( "button.resetApp" ) ;
this . trackClicks ( resetBtn , this . showResetConfirm ) ;
}
showResetConfirm ( ) {
if ( confirm ( "Are you sure you want to reset the app? This will delete all your savegames" ) ) {
this . resetApp ( ) ;
}
}
resetApp ( ) {
this . app . settings
. resetEverythingAsync ( )
. then ( ( ) => {
this . app . savegameMgr . resetEverythingAsync ( ) ;
} )
. then ( ( ) => {
this . app . settings . resetEverythingAsync ( ) ;
} )
. then ( ( ) => {
window . location . reload ( ) ;
} ) ;
}
}