Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion electron-builder.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { Configuration } from "electron-builder";
import { chmod, readdir } from "fs/promises";
import path from "path";

/**
* @see https://www.electron.build/configuration
Expand All @@ -13,7 +15,17 @@ const config: Configuration = {
disableDefaultIgnoredFiles: true,
files: ["./.vite/**", "!node_modules", "./node_modules/7zip-bin/**"],
directories: { buildResources: "buildResources" },
asarUnpack: ["resources/**"],
asarUnpack: ["resources/**", "node_modules/7zip-bin/**"],
// electron-builder unpacks the 7za binary from the asar but doesn't preserve its executable bit on Linux, so restore it here.
afterPack: async ({ electronPlatformName, appOutDir }) => {
if (electronPlatformName !== "linux") {
return;
}
const binDir = path.join(appOutDir, "resources", "app.asar.unpacked", "node_modules", "7zip-bin", "linux");
for (const arch of await readdir(binDir)) {
await chmod(path.join(binDir, arch, "7za"), 0o755);
}
},

publish: { provider: "github" },
fileAssociations: [
Expand Down
17 changes: 16 additions & 1 deletion lang/dev/lobby.json
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,22 @@
"playersOnline": "Plyreas Oinnle",
"error": "Eorrr",
"reconnecting": "Recotnnenicg...",
"offline": "Oniflfe"
"offline": "Oniflfe",
"changeStatus": "Canghe Satuts",
"statusOnline": "Oinnle",
"statusBusy": "Busy",
"statusBusyUnavailable": "The sveerr has no way to show tihs yet",
"goOffline": "Go Oniflfe",
"cancel": "Ceancl",
"connect": "Cnnocet",
"connectTitle": "Cnnocet to the sveerr?",
"connectBody": "You are sgneid in but not cntnoeecd. Cniotcneng bnirgs back paiters, leibbos and mnchaaiktmg.",
"disconnect": "Dsnncoicet",
"disconnectTitle": "Dsnncoicet form the sveerr?",
"disconnectBody": "You wlil laeve any party, lobby or mnchaaiktmg qeuue you are in. You stay sgneid in and can ccnneot agian wehenevr you like.",
"stopReconnecting": "Sotp trinyg",
"stopReconnectingTitle": "Sotp rionentcnecg?",
"stopReconnectingBody": "No fuethrr apmtttes wlil be made uitnl you ccnneot agian yreoulsf."
},
"messages": {
"message": "Msaesge",
Expand Down
17 changes: 16 additions & 1 deletion lang/en/lobby.json
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,22 @@
"playersOnline": "Players Online",
"error": "Error",
"reconnecting": "Reconnecting...",
"offline": "Offline"
"offline": "Offline",
"changeStatus": "Change Status",
"statusOnline": "Online",
"statusBusy": "Busy",
"statusBusyUnavailable": "The server has no way to show this yet",
"goOffline": "Go Offline",
"cancel": "Cancel",
"connect": "Connect",
"connectTitle": "Connect to the server?",
"connectBody": "You are signed in but not connected. Connecting brings back parties, lobbies and matchmaking.",
"disconnect": "Disconnect",
"disconnectTitle": "Disconnect from the server?",
"disconnectBody": "You will leave any party, lobby or matchmaking queue you are in. You stay signed in and can connect again whenever you like.",
"stopReconnecting": "Stop trying",
"stopReconnectingTitle": "Stop reconnecting?",
"stopReconnectingBody": "No further attempts will be made until you connect again yourself."
},
"messages": {
"message": "Message",
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "bar-lobby",
"type": "module",
"version": "0.16.0",
"version": "0.16.1",
"private": true,
"description": "Lobby client for the RTS game Beyond All Reason",
"author": "The BAR Lobby Authors",
Expand Down
15 changes: 14 additions & 1 deletion src/main/json/file-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export class FileStore<T extends TObject> {
protected readonly ajv: Ajv;
protected readonly validator: ValidateFunction<Static<T>>;

private writeQueue: Promise<void> = Promise.resolve();

constructor(filePath: string, schema: T, defaultModel?: Static<T>) {
this.filePath = filePath;
this.schema = schema;
Expand Down Expand Up @@ -69,7 +71,18 @@ export class FileStore<T extends TObject> {
}
}

// Writes are chained and go through a temp file, so overlapping saves can't
// interleave and a crash mid-write can't truncate the existing file.
protected async write() {
await fs.promises.writeFile(this.filePath, JSON.stringify(this.model, null, 4));
const write = this.writeQueue.catch(() => {}).then(() => this.writeModel());
this.writeQueue = write.catch(() => {});

return write;
}

private async writeModel() {
const tempPath = `${this.filePath}.tmp`;
await fs.promises.writeFile(tempPath, JSON.stringify(this.model, null, 4));
await fs.promises.rename(tempPath, this.filePath);
}
}
17 changes: 17 additions & 0 deletions src/main/json/model/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,21 @@ import { Type } from "@sinclair/typebox";
export const accountSchema = Type.Object({
token: Type.String({ default: "" }),
refreshToken: Type.String({ default: "" }),
// Access tokens are opaque to us, so their lifetime has to be recorded here
// rather than read back out of the token.
expiresAt: Type.Number({ default: 0 }),
// Deliberately has no default: absent means the file predates this field and
// we have to work out for ourselves whether the values are encrypted.
encrypted: Type.Optional(Type.Boolean()),
// Who the stored credentials belong to. Kept beside them so the two can't
// drift apart, and so the name is available before any socket exists. Only
// the server can tell us this, so it lands here when user/self arrives.
identity: Type.Optional(
Type.Object({
userId: Type.String(),
username: Type.String(),
displayName: Type.String(),
countryCode: Type.String({ default: "" }),
})
),
});
5 changes: 2 additions & 3 deletions src/main/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import netFromNode from "node:net";
import { createWindow } from "@main/main-window";
import { settingsService } from "./services/settings.service";
import { infoService } from "./services/info.service";
import { accountService } from "./services/account.service";
import { logService } from "@main/services/log.service";
import engineService from "./services/engine.service";
import mapsService from "./services/maps.service";
Expand Down Expand Up @@ -130,15 +129,15 @@ app.whenReady().then(async () => {
setAssetsPath(savedAssetsPath);
}
await engineService.init();
await Promise.all([accountService.init(), replaysService.init(), gameService.init(), mapsService.init(), autoUpdaterService.init()]);
await Promise.all([authService.init(), replaysService.init(), gameService.init(), mapsService.init(), autoUpdaterService.init()]);

const mainWindow = createWindow();
const webContents = typedWebContents(mainWindow.webContents);
// Handlers may need the webContents to send events
logService.registerIpcHandlers();
infoService.registerIpcHandlers();
settingsService.registerIpcHandlers();
authService.registerIpcHandlers();
authService.registerIpcHandlers(webContents);
tachyonService.registerIpcHandlers(webContents);
replaysService.registerIpcHandlers(webContents);
engineService.registerIpcHandlers();
Expand Down
11 changes: 11 additions & 0 deletions src/main/model/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@
//
// SPDX-License-Identifier: MIT

// What is kept beside the credentials so the signed in account has a name before
// anything has connected. Deliberately not derived from User: it is a stored
// shape, and following changes to the live model would silently reinterpret what
// is already on disk.
export interface StoredIdentity {
userId: string;
username: string;
displayName: string;
countryCode: string;
}

export type User = {
userId: string;
username: string;
Expand Down
Loading
Loading