Skip to content

Commit 17dae2e

Browse files
authored
Merge pull request #2513 from planetarium/feat/comment
Add comment for maintainability
2 parents 3189e98 + dfd57c0 commit 17dae2e

File tree

5 files changed

+48
-51
lines changed

5 files changed

+48
-51
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "NineChronicles",
33
"productName": "Nine Chronicles",
4-
"version": "2.7.6",
4+
"version": "2.7.7",
55
"description": "Game Launcher for Nine Chronicles",
66
"author": "Planetarium <engineering@planetariumhq.com>",
77
"license": "GPL-3.0",

src/interfaces/config.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ export interface IConfig {
2929
TrayOnClose: boolean;
3030
Planet: string;
3131
PlanetRegistryUrl: string;
32-
PlayerUpdateRetryCount: number;
3332
PatrolRewardServiceUrl: string;
3433
MeadPledgePortalUrl: string;
3534
SeasonPassServiceUrl: string;

src/main/main.ts

Lines changed: 36 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
*/
7983
client
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+
*/
97105
if (!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

130144
async 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() {
201217
async 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) => {

src/main/update/player-update.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,6 @@ export async function performPlayerUpdate(
2525
return;
2626
}
2727

28-
if (get("PlayerUpdateRetryCount", 0) > 3) {
29-
console.error("[ERROR] Player Update Failed 3 Times.");
30-
win.webContents.send("go to error page", "player", {
31-
url: "reinstall",
32-
});
33-
return;
34-
}
35-
3628
try {
3729
lockfile.lockSync(lockfilePath);
3830
console.log(

src/renderer/App.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,17 @@ import { NodeInfo } from "src/config";
1616
function App() {
1717
const { planetary, account, game } = useStore();
1818
const client = useApolloClient();
19+
20+
/** Asynchronous Invoke in useEffect
21+
* As ipcRenderer.invoke() is async we're not guaranteed to receive IPC result on time
22+
*
23+
* Also even if we use .then() to force synchronous flow useEffect() won't wait.
24+
* But we need these to render login page.
25+
* hence we render null until all three initialized;
26+
* Planetary, GQL client, AccountStore
27+
*
28+
* It could be better if we can have react suspense here.
29+
*/
1930
useEffect(() => {
2031
ipcRenderer
2132
.invoke("get-planetary-info")

0 commit comments

Comments
 (0)