@@ -76,6 +76,10 @@ const updateOptions: IUpdateOptions = {
7676 downloadStarted : quitAllProcesses ,
7777} ;
7878
79+ /**
80+ * NTP Check to adapt on thai calendar system
81+ * https://snack.planetarium.dev/kor/2020/02/thai-in-2562/
82+ */
7983client
8084 . syncTime ( )
8185 . then ( ( time ) => {
@@ -94,6 +98,10 @@ client
9498 console . error ( error ) ;
9599 } ) ;
96100
101+ /**
102+ * Prevent launcher run concurrently and manage deep link event when running launcher already exists
103+ * To prevent other conditional logic making deep link behavior irregular this should be called as early as possible.
104+ */
97105if ( ! app . requestSingleInstanceLock ( ) ) {
98106 app . quit ( ) ;
99107} else {
@@ -108,6 +116,12 @@ if (!app.requestSingleInstanceLock()) {
108116
109117 cleanUp ( ) ;
110118
119+ /** Install rosetta on AArch64
120+ * native ARM64 launcher complicates distribution and build (both game and launcher),
121+ * only x86 binary is served for both game and launcher.
122+ * Rosetta installation is crucial step since 'missing rosetta error' is silent on both main and renderer,
123+ * which makes very hard to debug.
124+ */
111125 if ( process . platform === "darwin" && process . arch == "arm64" ) {
112126 exec ( "/usr/bin/arch -arch x86_64 uname -m" , ( error ) => {
113127 if ( error ) {
@@ -129,6 +143,7 @@ if (!app.requestSingleInstanceLock()) {
129143
130144async function initializeConfig ( ) {
131145 try {
146+ // Start of config.json fetch flow, can be mutated safely until finalization
132147 const res = await axios ( REMOTE_CONFIG_URL ) ;
133148 const remoteConfig : IConfig = res . data ;
134149
@@ -151,11 +166,16 @@ async function initializeConfig() {
151166 const data = await fetch ( remoteConfig . PlanetRegistryUrl ) ;
152167
153168 registry = await data . json ( ) ;
169+
170+ /** Planet Registry Failsafe
171+ * if registry not exists or failed to fetch, throw 'parse failure'
172+ * if registry fetched but the format is invalid, throw 'registry empty'
173+ * if registry fetched correctly but matching entry with ID in config.json not exists, use first planet available from parsed data.
174+ */
154175 if ( registry === undefined ) throw Error ( "Failed to parse registry." ) ;
155176 if ( ! Array . isArray ( registry ) || registry . length <= 0 ) {
156177 throw Error ( "Registry is empty or invalid. No planets found." ) ;
157178 }
158-
159179 const planet =
160180 registry . find ( ( v ) => v . id === remoteConfig . Planet ) ??
161181 ( ( ) => {
@@ -178,15 +198,11 @@ async function initializeConfig() {
178198 return ;
179199 }
180200
181- // Replace config
182201 console . log ( "Replace config with remote config:" , remoteConfig ) ;
183202 remoteConfig . Locale = getConfig ( "Locale" ) ;
184- remoteConfig . PlayerUpdateRetryCount = getConfig (
185- "PlayerUpdateRetryCount" ,
186- 0 ,
187- ) ;
188203 remoteConfig . TrayOnClose = getConfig ( "TrayOnClose" , true ) ;
189204
205+ // config finalized at this point
190206 configStore . store = remoteConfig ;
191207 console . log ( "Initialize config complete" ) ;
192208 } catch ( error ) {
@@ -201,6 +217,7 @@ async function initializeConfig() {
201217async function initializeApp ( ) {
202218 console . log ( "initializeApp" ) ;
203219
220+ // set default protocol to OS, so that launcher can be executed via protocol even if launcher is off.
204221 const isProtocolSet = app . setAsDefaultProtocolClient (
205222 "ninechronicles-launcher" ,
206223 process . execPath ,
@@ -211,15 +228,18 @@ async function initializeApp() {
211228 console . log ( "isProtocolSet" , isProtocolSet ) ;
212229
213230 app . on ( "ready" , async ( ) => {
231+ // electron-remote initialization.
232+ // As this impose security considerations, we should remove this ASAP.
214233 remoteInitialize ( ) ;
215234
235+ // Renderer is initialized at this very moment.
216236 win = await createV2Window ( ) ;
217237 await initGeoBlocking ( ) ;
218238
219239 process . on ( "uncaughtException" , async ( error ) => {
220240 if ( error . message . includes ( "system error -86" ) ) {
221241 console . error ( "System error -86 error occurred:" , error ) ;
222-
242+ // system error -86 : unknown arch, missing rosetta, failed to execute x86 program.
223243 if ( win ) {
224244 await dialog
225245 . showMessageBox ( win , {
@@ -251,8 +271,8 @@ async function initializeApp() {
251271 setV2Quitting ( ! getConfig ( "TrayOnClose" ) ) ;
252272
253273 if ( useUpdate ) {
254- appUpdaterInstance = new AppUpdater ( win , baseUrl , updateOptions ) ;
255- initCheckForUpdateWorker ( win , appUpdaterInstance ) ;
274+ appUpdaterInstance = new AppUpdater ( win , baseUrl , updateOptions ) ; // Launcher Updater
275+ initCheckForUpdateWorker ( win , appUpdaterInstance ) ; // Game Updater
256276 }
257277
258278 webEnable ( win . webContents ) ;
@@ -285,18 +305,11 @@ function initializeIpc() {
285305 }
286306
287307 if ( utils . getExecutePath ( ) === "PLAYER_UPDATE" ) {
288- configStore . set (
289- // Update Retry Counter
290- "PlayerUpdateRetryCount" ,
291- configStore . get ( "PlayerUpdateRetryCount" ) + 1 ,
292- ) ;
293308 return manualPlayerUpdate ( ) ;
294309 }
295310
296311 const node = utils . execute ( utils . getExecutePath ( ) , info . args ) ;
297- if ( node !== null ) {
298- configStore . set ( "PlayerUpdateRetryCount" , 0 ) ;
299- }
312+
300313 node . on ( "close" , ( code ) => {
301314 // Code 21: ERROR_NOT_READY
302315 if ( code === 21 ) {
@@ -320,7 +333,6 @@ function initializeIpc() {
320333 ipcMain . handle ( "execute launcher update" , async ( event ) => {
321334 if ( appUpdaterInstance === null ) throw Error ( "appUpdaterInstance is null" ) ;
322335 setV2Quitting ( true ) ;
323- configStore . set ( "PlayerUpdateRetryCount" , 0 ) ;
324336 await appUpdaterInstance . execute ( ) ;
325337 } ) ;
326338
@@ -336,29 +348,6 @@ function initializeIpc() {
336348 }
337349 } ) ;
338350
339- ipcMain . on ( "get-aws-sink-cloudwatch-guid" , async ( event ) => {
340- const localAppData = process . env . localappdata ;
341- if ( process . platform === "win32" && localAppData !== undefined ) {
342- const guidPath = path . join (
343- localAppData ,
344- "planetarium" ,
345- ".aws_sink_cloudwatch_guid" ,
346- ) ;
347-
348- if ( fs . existsSync ( guidPath ) ) {
349- event . returnValue = await fs . promises . readFile ( guidPath , {
350- encoding : "utf-8" ,
351- } ) ;
352- } else {
353- event . returnValue = null ;
354- }
355-
356- return ;
357- }
358-
359- event . returnValue = "Not supported platform." ;
360- } ) ;
361-
362351 ipcMain . on ( "online-status-changed" , ( event , status : "online" | "offline" ) => {
363352 console . log ( `online-status-changed: ${ status } ` ) ;
364353 if ( status === "offline" ) {
@@ -367,13 +356,16 @@ function initializeIpc() {
367356 } ) ;
368357
369358 ipcMain . handle ( "get-planetary-info" , async ( ) => {
359+ // Synchronously wait until registry / remote node initialized
360+ // This should return, otherwise entry point of renderer will stuck in white screen.
370361 while ( ! registry || ! remoteNode ) {
371362 await utils . sleep ( 100 ) ;
372363 }
373364 return [ registry , remoteNode ] ;
374365 } ) ;
375366
376367 ipcMain . handle ( "check-geoblock" , async ( ) => {
368+ // synchronously wait until 'await initGeoBlocking();' finished
377369 while ( ! geoBlock ) {
378370 await utils . sleep ( 100 ) ;
379371 }
@@ -483,6 +475,7 @@ function initCheckForUpdateWorker(
483475 throw new NotSupportedPlatformError ( process . platform ) ;
484476 }
485477
478+ // Fork separated update checker worker process
486479 const checkForUpdateWorker = fork (
487480 path . join ( __dirname , "./checkForUpdateWorker.js" ) ,
488481 [ ] ,
@@ -552,6 +545,8 @@ async function initGeoBlocking() {
552545 return geoBlock . country ;
553546 } catch ( error ) {
554547 console . error ( "Failed to fetch geo data:" , error ) ;
548+ // Fallback to latest result stored in renderer-side local storage.
549+ // defaults to the most strict condition if both remote and local value not exists.
555550 win ?. webContents
556551 . executeJavaScript ( 'localStorage.getItem("country")' )
557552 . then ( ( result ) => {
0 commit comments