omegga 1.12.0 → 1.13.0
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/CHANGELOG.md +6 -0
- package/README.md +2 -0
- package/dist/brickadia/server.js +2 -0
- package/dist/brickadia/server.js.map +1 -1
- package/dist/config/types.js +1 -0
- package/dist/config/types.js.map +1 -1
- package/dist/updater/steam.js +16 -0
- package/dist/updater/steam.js.map +1 -1
- package/package.json +2 -2
- package/templates/safe-ts/omegga.d.ts +497 -125
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## Latest
|
|
4
4
|
|
|
5
|
+
## 1.13.0 - 2026-08-10
|
|
6
|
+
|
|
7
|
+
- Add `server.remoteFiles` config (default `true`); when `false`, the server launches with `-NoRemoteFileAccess`
|
|
8
|
+
- Print the new game version (like the `Launching Brickadia Server` log) when a server update finishes
|
|
9
|
+
- Bump brs-js to 3.7.0 for EA 3.1
|
|
10
|
+
|
|
5
11
|
## 1.12.0 - 2026-08-05
|
|
6
12
|
|
|
7
13
|
Web UI user management (ownership granting, self-rename, new `/user` terminal subcommands) and a typing/lint cleanup across the codebase.
|
package/README.md
CHANGED
|
@@ -1087,6 +1087,8 @@ omegga:
|
|
|
1087
1087
|
server:
|
|
1088
1088
|
port: 7777 # game server port
|
|
1089
1089
|
map: Plate # map name
|
|
1090
|
+
# when false, the server launches with -NoRemoteFileAccess
|
|
1091
|
+
remoteFiles: true
|
|
1090
1092
|
# When branch is present, steamcmd is not used
|
|
1091
1093
|
#branch: release:release-server # branch alias:branch name
|
|
1092
1094
|
steambeta: public # try `unstable`
|
package/dist/brickadia/server.js
CHANGED
|
@@ -216,6 +216,8 @@ class BrickadiaServer extends EventEmitter {
|
|
|
216
216
|
world && `-World="${world.file}"`,
|
|
217
217
|
"-NotInstalled",
|
|
218
218
|
"-log",
|
|
219
|
+
// remote file access is on unless explicitly disabled in the config
|
|
220
|
+
this.config.server.remoteFiles === false ? "-NoRemoteFileAccess" : null,
|
|
219
221
|
wsl.checkWsl() === 1 ? "-OneThread" : null,
|
|
220
222
|
this.path ? `-UserDir="${this.path}"` : null,
|
|
221
223
|
token ? `-Token="${token}"` : null,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sources":["../../src/brickadia/server.ts"],"sourcesContent":["/*\n Brickadia Server Wrapper\n Manages IO with the game server\n*/\n\nimport Logger from '@/logger';\nimport {\n ACTIVE_WORLD_FILE,\n CONFIG_SAVED_DIR,\n GAME_BIN_PATH,\n getOverrideGameBinary,\n getSteamGameDir,\n getSteamInstallDir,\n} from '@/softconfig';\nimport { getGlobalToken } from '@cli/auth';\nimport { type IConfig } from '@config/types';\nimport { checkWsl } from '@util/wsl';\nimport 'colors';\nimport { type ChildProcessWithoutNullStreams, spawn } from 'node:child_process';\nimport EventEmitter from 'node:events';\nimport { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';\nimport path from 'node:path';\nimport { env } from 'node:process';\nimport readline from 'readline';\nimport stripAnsi from 'strip-ansi';\n\n// list of errors that can be solved by yelling at the user\nconst knownErrors: {\n name: string;\n solution?: string;\n match: RegExp;\n message?: string;\n}[] = [\n {\n name: 'MISSING_LIBGL',\n solution: 'apt-get install libgl1-mesa-glx libglib2.0-0',\n match:\n /error while loading shared libraries: libGL\\.so\\.1: cannot open shared object file/,\n },\n {\n name: 'MISSING_GLIB',\n solution: 'apt-get install libgl1-mesa-glx libglib2.0-0',\n match:\n /error while loading shared libraries: libgthread-2\\.0\\.so\\.0: cannot open shared object file/,\n },\n];\n\n/** Start a brickadia server */\nexport default class BrickadiaServer extends EventEmitter {\n #child: ChildProcessWithoutNullStreams | null = null;\n #errInterface: readline.Interface | null = null;\n #outInterface: readline.Interface | null = null;\n\n config: IConfig;\n path: string;\n\n constructor(dataPath: string, config: IConfig) {\n super();\n\n this.config = config;\n // use the data path if it's absolute, otherwise build an absolute path\n this.path =\n path.isAbsolute(dataPath) || dataPath.startsWith('/')\n ? dataPath\n : path.join(process.cwd(), dataPath);\n\n this.lineListener = this.lineListener.bind(this);\n this.errorListener = this.errorListener.bind(this);\n this.exitListener = this.exitListener.bind(this);\n }\n\n getActiveWorldFile(): string {\n return path.join(this.path, ACTIVE_WORLD_FILE);\n }\n\n /** A world specified by the active world file */\n getActiveWorld(): string | null {\n const activeWorldFile = this.getActiveWorldFile();\n if (existsSync(activeWorldFile)) {\n try {\n return readFileSync(activeWorldFile, 'utf8').trim();\n } catch (err) {\n Logger.errorp(\n 'Failed to read active world file',\n activeWorldFile.yellow,\n err,\n );\n return null;\n }\n }\n return null;\n }\n\n /** Set the world to use next startup */\n setActiveWorld(world: string | null): boolean {\n const activeWorldFile = this.getActiveWorldFile();\n if (!world || world === null) {\n if (existsSync(activeWorldFile)) {\n Logger.verbose('Removing active world file', activeWorldFile.yellow);\n unlinkSync(activeWorldFile);\n }\n return true;\n }\n\n if (!this.worldExists(world)) {\n Logger.verbose(\n 'Cannot set active world to',\n world.yellow,\n 'as it does not exist',\n );\n return false;\n }\n\n Logger.verbose('Setting active world to', world.yellow);\n try {\n writeFileSync(activeWorldFile, world, 'utf8');\n return true;\n } catch (err) {\n Logger.errorp(\n 'Failed to write active world file',\n activeWorldFile.yellow,\n err,\n );\n return false;\n }\n }\n\n /** A world specified by the config */\n getConfigWorld(): string | null {\n return this.config?.server?.world ?? null;\n }\n\n /** A world specified by the BRICKADIA_WORLD env variable */\n getEnvWorld(): string | null {\n return env.BRICKADIA_WORLD || null;\n }\n\n /** Check if a world exists */\n worldExists(world: string): boolean {\n const savedDir = this.config?.server?.savedDir ?? CONFIG_SAVED_DIR;\n const worldPath = path.join(this.path, savedDir, 'Worlds', world + '.brdb');\n return existsSync(worldPath);\n }\n\n /** Get the world that will be used next startup */\n getNextWorld(): { source: string; file: string } | null {\n const candidates = [\n { source: 'file', file: this.getActiveWorld() },\n { source: 'config', file: this.getConfigWorld() },\n { source: 'env', file: this.getEnvWorld() },\n ];\n\n return (\n candidates.find(\n (c): c is { source: string; file: string } =>\n !!c.file && this.worldExists(c.file),\n ) ?? null\n );\n }\n\n /**\n * Resolve the path to the game server binary for steam/override installs.\n * Returns null for launcher-managed installs, where the binary lives in a\n * branch directory the launcher owns and isn't known statically here.\n */\n getGameBinaryPath(): string | null {\n const overrideBinary = getOverrideGameBinary();\n if (overrideBinary) return overrideBinary;\n\n // a configured branch means the launcher manages the install, not steam\n const isSteam = !this.config.server.branch;\n if (!isSteam) return null;\n\n const steamBeta = this.config.server.steambeta ?? 'main';\n return path.join(\n getSteamInstallDir(), // steam install directory\n steamBeta, // steam beta branch (or main)\n getSteamGameDir(), // Brickadia\n GAME_BIN_PATH, // path to binary\n );\n }\n\n // start the server child process\n start() {\n const {\n email,\n password,\n token: confToken,\n }: { email?: string; password?: string; token?: string } = this.config\n .credentials || {};\n\n const token = confToken || process.env.BRICKADIA_TOKEN || getGlobalToken();\n\n if (token) {\n Logger.verbose('Starting server with hosting token');\n } else {\n Logger.verbose(\n 'Starting server',\n (!email && !password ? 'without' : 'with').yellow,\n 'credentials',\n );\n }\n\n const isSteam = !this.config.server.branch;\n const steamBeta = this.config.server.steambeta ?? 'main';\n const overrideBinary = getOverrideGameBinary();\n const steamBinary = path.join(\n getSteamInstallDir(), // steam install directory\n steamBeta, // steam beta branch (or main)\n getSteamGameDir(), // Brickadia\n GAME_BIN_PATH, // path to binary\n );\n\n let gameBinary = this.getGameBinaryPath() ?? steamBinary;\n\n if (overrideBinary) {\n if (!existsSync(overrideBinary)) {\n Logger.error(\n 'Override binary',\n overrideBinary.yellow,\n 'does not exist!',\n );\n throw new Error(`Override binary ${overrideBinary} does not exist!`);\n }\n\n Logger.verbose(\n 'Using override binary',\n overrideBinary.yellow,\n 'instead of',\n steamBinary.yellow,\n );\n gameBinary = overrideBinary;\n } else if (isSteam) {\n Logger.verbose('Using steam binary', steamBeta.yellow);\n } else {\n Logger.verbose(\n 'Running',\n (this.config.server.__LOCAL\n ? path.join(__dirname, '../../tools/brickadia.sh')\n : 'brickadia launcher'\n ).yellow,\n );\n if (typeof this.config.server.branch === 'string')\n Logger.verbose('Using branch', this.config.server.branch.yellow);\n }\n\n // handle local launcher support\n const launchArgs = isSteam\n ? [gameBinary]\n : [\n this.config.server.__LOCAL\n ? path.join(__dirname, '../../tools/brickadia.sh')\n : 'brickadia',\n this.config.server.branch && `--branch=${this.config.server.branch}`,\n '--server',\n '--',\n ];\n\n const world = this.getNextWorld();\n if (world) {\n Logger.verbose(\n 'Using world',\n world.file.yellow,\n 'from',\n world.source.yellow,\n );\n } else if (this.config.server.map) {\n Logger.verbose('Using map', this.config.server.map.yellow, 'from config');\n }\n\n const params = [\n '--output=L',\n '--',\n ...launchArgs,\n !world &&\n this.config.server.map &&\n `-Environment=\"${this.config.server.map}\"`,\n world && `-World=\"${world.file}\"`,\n '-NotInstalled',\n '-log',\n checkWsl() === 1 ? '-OneThread' : null,\n this.path ? `-UserDir=\"${this.path}\"` : null,\n token ? `-Token=\"${token}\"` : null, // remove token argument if not provided\n !token && email ? `-User=\"${email}\"` : null, // remove email argument if not provided or token is provided\n !token && password ? `-Password=\"${password}\"` : null, // remove password argument if not provided or token is provided\n `-port=\"${this.config.server.port}\"`,\n this.config.server.launchArgs,\n ]\n .filter(arg => typeof arg === 'string')\n .filter(arg => arg.length > 0); // remove unused/empty arguments\n Logger.verbose(\n 'Params for spawn',\n params\n .join(' ')\n .replace(/-User=\".*?\"/, '-User=\"<hidden>\"')\n .replace(/-Password=\".*?\"/, '-Password=\"<hidden>\"')\n .replace(/-Token=\".*?\"/, '-Token=\"<hidden>\"')\n .replace(/-Cookie=\".*?\"/, '-Cookie=\"<hidden>\"')\n .replace(/-Cookie=\\S+/, '-Cookie=<hidden>'),\n );\n\n // Either unbuffer or stdbuf must be used because brickadia's output is buffered\n // this means that multiple lines can be bundled together if the output buffer is not full\n // unfortunately without stdbuf or unbuffer, the output would not happen immediately\n const child = spawn('stdbuf', params);\n this.#child = child;\n\n Logger.verbose('Spawn process', child.pid ?? 'failed'.red);\n\n child.stdin.setDefaultEncoding('utf8');\n this.#outInterface = readline.createInterface({\n input: child.stdout,\n terminal: false,\n });\n this.#errInterface = readline.createInterface({\n input: child.stderr,\n terminal: false,\n });\n this.attachListeners();\n Logger.verbose('Attached listeners');\n }\n\n // write a string to the child process\n write(line: string) {\n if (line.length >= 512) {\n // show a warning\n Logger.warn(\n 'WARNING'.yellow,\n 'The following line was called and is',\n 'longer than allowed limit'.red,\n );\n Logger.warn(line.replace(/\\n$/, ''));\n // throw a fake error to get the line number\n try {\n throw new Error('Console Line Too Long');\n } catch (err) {\n Logger.warn(err);\n }\n return;\n }\n if (this.#child) {\n Logger.verbose('WRITE'.green, line.replace(/\\n$/, ''));\n this.#child.stdin.write(line);\n }\n }\n\n // write a line to the child process\n writeln(line: string) {\n this.write(line + '\\n');\n }\n\n // forcibly kills the server\n stop() {\n if (!this.#child) {\n Logger.verbose('Cannot stop server as no subprocess exists');\n return;\n }\n\n Logger.verbose('Forcibly stopping server');\n // kill the process\n this.#child.kill('SIGINT');\n\n // ...kill it again just to make sure it's dead\n spawn('kill', ['-9', this.#child.pid + '']);\n }\n\n // detaches listeners\n cleanup() {\n if (!this.#child) return;\n\n Logger.verbose('Cleaning up brickadia server');\n\n // detach listener\n this.detachListeners();\n\n this.#child = null;\n this.#outInterface = null;\n this.#errInterface = null;\n }\n\n // attaches proxy event listeners\n attachListeners() {\n this.#outInterface?.on('line', this.lineListener);\n this.#errInterface?.on('line', this.errorListener);\n this.#child?.on('exit', this.exitListener);\n this.#child?.on('close', () => {});\n }\n\n // removes previously attached proxy event listeners\n detachListeners() {\n this.#outInterface?.off('line', this.lineListener);\n this.#errInterface?.off('line', this.errorListener);\n this.#child?.off('exit', this.exitListener);\n this.#child?.removeAllListeners('close');\n }\n\n // -- listeners for basic events (line, err, exit)\n errorListener(line: string) {\n Logger.verbose('ERROR'.red, line);\n this.emit('err', line);\n for (const { match, solution, name, message } of knownErrors) {\n if (line.match(match)) {\n Logger.error(\n `Encountered ${name.red}. ${\n solution ? 'Known fix:\\n ' + solution : message || 'Unknown error.'\n }`,\n );\n }\n }\n }\n\n exitListener(...args: any[]) {\n Logger.verbose('Exit listener fired');\n this.emit('closed', ...args);\n this.cleanup();\n }\n\n lineListener(line: string) {\n this.emit('line', stripAnsi(line));\n }\n}\n"],"names":["ACTIVE_WORLD_FILE","existsSync","readFileSync","Logger","unlinkSync","writeFileSync","env","CONFIG_SAVED_DIR","getOverrideGameBinary","getSteamInstallDir","getSteamGameDir","GAME_BIN_PATH","getGlobalToken","checkWsl","spawn"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"server.js","sources":["../../src/brickadia/server.ts"],"sourcesContent":["/*\n Brickadia Server Wrapper\n Manages IO with the game server\n*/\n\nimport Logger from '@/logger';\nimport {\n ACTIVE_WORLD_FILE,\n CONFIG_SAVED_DIR,\n GAME_BIN_PATH,\n getOverrideGameBinary,\n getSteamGameDir,\n getSteamInstallDir,\n} from '@/softconfig';\nimport { getGlobalToken } from '@cli/auth';\nimport { type IConfig } from '@config/types';\nimport { checkWsl } from '@util/wsl';\nimport 'colors';\nimport { type ChildProcessWithoutNullStreams, spawn } from 'node:child_process';\nimport EventEmitter from 'node:events';\nimport { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';\nimport path from 'node:path';\nimport { env } from 'node:process';\nimport readline from 'readline';\nimport stripAnsi from 'strip-ansi';\n\n// list of errors that can be solved by yelling at the user\nconst knownErrors: {\n name: string;\n solution?: string;\n match: RegExp;\n message?: string;\n}[] = [\n {\n name: 'MISSING_LIBGL',\n solution: 'apt-get install libgl1-mesa-glx libglib2.0-0',\n match:\n /error while loading shared libraries: libGL\\.so\\.1: cannot open shared object file/,\n },\n {\n name: 'MISSING_GLIB',\n solution: 'apt-get install libgl1-mesa-glx libglib2.0-0',\n match:\n /error while loading shared libraries: libgthread-2\\.0\\.so\\.0: cannot open shared object file/,\n },\n];\n\n/** Start a brickadia server */\nexport default class BrickadiaServer extends EventEmitter {\n #child: ChildProcessWithoutNullStreams | null = null;\n #errInterface: readline.Interface | null = null;\n #outInterface: readline.Interface | null = null;\n\n config: IConfig;\n path: string;\n\n constructor(dataPath: string, config: IConfig) {\n super();\n\n this.config = config;\n // use the data path if it's absolute, otherwise build an absolute path\n this.path =\n path.isAbsolute(dataPath) || dataPath.startsWith('/')\n ? dataPath\n : path.join(process.cwd(), dataPath);\n\n this.lineListener = this.lineListener.bind(this);\n this.errorListener = this.errorListener.bind(this);\n this.exitListener = this.exitListener.bind(this);\n }\n\n getActiveWorldFile(): string {\n return path.join(this.path, ACTIVE_WORLD_FILE);\n }\n\n /** A world specified by the active world file */\n getActiveWorld(): string | null {\n const activeWorldFile = this.getActiveWorldFile();\n if (existsSync(activeWorldFile)) {\n try {\n return readFileSync(activeWorldFile, 'utf8').trim();\n } catch (err) {\n Logger.errorp(\n 'Failed to read active world file',\n activeWorldFile.yellow,\n err,\n );\n return null;\n }\n }\n return null;\n }\n\n /** Set the world to use next startup */\n setActiveWorld(world: string | null): boolean {\n const activeWorldFile = this.getActiveWorldFile();\n if (!world || world === null) {\n if (existsSync(activeWorldFile)) {\n Logger.verbose('Removing active world file', activeWorldFile.yellow);\n unlinkSync(activeWorldFile);\n }\n return true;\n }\n\n if (!this.worldExists(world)) {\n Logger.verbose(\n 'Cannot set active world to',\n world.yellow,\n 'as it does not exist',\n );\n return false;\n }\n\n Logger.verbose('Setting active world to', world.yellow);\n try {\n writeFileSync(activeWorldFile, world, 'utf8');\n return true;\n } catch (err) {\n Logger.errorp(\n 'Failed to write active world file',\n activeWorldFile.yellow,\n err,\n );\n return false;\n }\n }\n\n /** A world specified by the config */\n getConfigWorld(): string | null {\n return this.config?.server?.world ?? null;\n }\n\n /** A world specified by the BRICKADIA_WORLD env variable */\n getEnvWorld(): string | null {\n return env.BRICKADIA_WORLD || null;\n }\n\n /** Check if a world exists */\n worldExists(world: string): boolean {\n const savedDir = this.config?.server?.savedDir ?? CONFIG_SAVED_DIR;\n const worldPath = path.join(this.path, savedDir, 'Worlds', world + '.brdb');\n return existsSync(worldPath);\n }\n\n /** Get the world that will be used next startup */\n getNextWorld(): { source: string; file: string } | null {\n const candidates = [\n { source: 'file', file: this.getActiveWorld() },\n { source: 'config', file: this.getConfigWorld() },\n { source: 'env', file: this.getEnvWorld() },\n ];\n\n return (\n candidates.find(\n (c): c is { source: string; file: string } =>\n !!c.file && this.worldExists(c.file),\n ) ?? null\n );\n }\n\n /**\n * Resolve the path to the game server binary for steam/override installs.\n * Returns null for launcher-managed installs, where the binary lives in a\n * branch directory the launcher owns and isn't known statically here.\n */\n getGameBinaryPath(): string | null {\n const overrideBinary = getOverrideGameBinary();\n if (overrideBinary) return overrideBinary;\n\n // a configured branch means the launcher manages the install, not steam\n const isSteam = !this.config.server.branch;\n if (!isSteam) return null;\n\n const steamBeta = this.config.server.steambeta ?? 'main';\n return path.join(\n getSteamInstallDir(), // steam install directory\n steamBeta, // steam beta branch (or main)\n getSteamGameDir(), // Brickadia\n GAME_BIN_PATH, // path to binary\n );\n }\n\n // start the server child process\n start() {\n const {\n email,\n password,\n token: confToken,\n }: { email?: string; password?: string; token?: string } = this.config\n .credentials || {};\n\n const token = confToken || process.env.BRICKADIA_TOKEN || getGlobalToken();\n\n if (token) {\n Logger.verbose('Starting server with hosting token');\n } else {\n Logger.verbose(\n 'Starting server',\n (!email && !password ? 'without' : 'with').yellow,\n 'credentials',\n );\n }\n\n const isSteam = !this.config.server.branch;\n const steamBeta = this.config.server.steambeta ?? 'main';\n const overrideBinary = getOverrideGameBinary();\n const steamBinary = path.join(\n getSteamInstallDir(), // steam install directory\n steamBeta, // steam beta branch (or main)\n getSteamGameDir(), // Brickadia\n GAME_BIN_PATH, // path to binary\n );\n\n let gameBinary = this.getGameBinaryPath() ?? steamBinary;\n\n if (overrideBinary) {\n if (!existsSync(overrideBinary)) {\n Logger.error(\n 'Override binary',\n overrideBinary.yellow,\n 'does not exist!',\n );\n throw new Error(`Override binary ${overrideBinary} does not exist!`);\n }\n\n Logger.verbose(\n 'Using override binary',\n overrideBinary.yellow,\n 'instead of',\n steamBinary.yellow,\n );\n gameBinary = overrideBinary;\n } else if (isSteam) {\n Logger.verbose('Using steam binary', steamBeta.yellow);\n } else {\n Logger.verbose(\n 'Running',\n (this.config.server.__LOCAL\n ? path.join(__dirname, '../../tools/brickadia.sh')\n : 'brickadia launcher'\n ).yellow,\n );\n if (typeof this.config.server.branch === 'string')\n Logger.verbose('Using branch', this.config.server.branch.yellow);\n }\n\n // handle local launcher support\n const launchArgs = isSteam\n ? [gameBinary]\n : [\n this.config.server.__LOCAL\n ? path.join(__dirname, '../../tools/brickadia.sh')\n : 'brickadia',\n this.config.server.branch && `--branch=${this.config.server.branch}`,\n '--server',\n '--',\n ];\n\n const world = this.getNextWorld();\n if (world) {\n Logger.verbose(\n 'Using world',\n world.file.yellow,\n 'from',\n world.source.yellow,\n );\n } else if (this.config.server.map) {\n Logger.verbose('Using map', this.config.server.map.yellow, 'from config');\n }\n\n const params = [\n '--output=L',\n '--',\n ...launchArgs,\n !world &&\n this.config.server.map &&\n `-Environment=\"${this.config.server.map}\"`,\n world && `-World=\"${world.file}\"`,\n '-NotInstalled',\n '-log',\n // remote file access is on unless explicitly disabled in the config\n this.config.server.remoteFiles === false ? '-NoRemoteFileAccess' : null,\n checkWsl() === 1 ? '-OneThread' : null,\n this.path ? `-UserDir=\"${this.path}\"` : null,\n token ? `-Token=\"${token}\"` : null, // remove token argument if not provided\n !token && email ? `-User=\"${email}\"` : null, // remove email argument if not provided or token is provided\n !token && password ? `-Password=\"${password}\"` : null, // remove password argument if not provided or token is provided\n `-port=\"${this.config.server.port}\"`,\n this.config.server.launchArgs,\n ]\n .filter(arg => typeof arg === 'string')\n .filter(arg => arg.length > 0); // remove unused/empty arguments\n Logger.verbose(\n 'Params for spawn',\n params\n .join(' ')\n .replace(/-User=\".*?\"/, '-User=\"<hidden>\"')\n .replace(/-Password=\".*?\"/, '-Password=\"<hidden>\"')\n .replace(/-Token=\".*?\"/, '-Token=\"<hidden>\"')\n .replace(/-Cookie=\".*?\"/, '-Cookie=\"<hidden>\"')\n .replace(/-Cookie=\\S+/, '-Cookie=<hidden>'),\n );\n\n // Either unbuffer or stdbuf must be used because brickadia's output is buffered\n // this means that multiple lines can be bundled together if the output buffer is not full\n // unfortunately without stdbuf or unbuffer, the output would not happen immediately\n const child = spawn('stdbuf', params);\n this.#child = child;\n\n Logger.verbose('Spawn process', child.pid ?? 'failed'.red);\n\n child.stdin.setDefaultEncoding('utf8');\n this.#outInterface = readline.createInterface({\n input: child.stdout,\n terminal: false,\n });\n this.#errInterface = readline.createInterface({\n input: child.stderr,\n terminal: false,\n });\n this.attachListeners();\n Logger.verbose('Attached listeners');\n }\n\n // write a string to the child process\n write(line: string) {\n if (line.length >= 512) {\n // show a warning\n Logger.warn(\n 'WARNING'.yellow,\n 'The following line was called and is',\n 'longer than allowed limit'.red,\n );\n Logger.warn(line.replace(/\\n$/, ''));\n // throw a fake error to get the line number\n try {\n throw new Error('Console Line Too Long');\n } catch (err) {\n Logger.warn(err);\n }\n return;\n }\n if (this.#child) {\n Logger.verbose('WRITE'.green, line.replace(/\\n$/, ''));\n this.#child.stdin.write(line);\n }\n }\n\n // write a line to the child process\n writeln(line: string) {\n this.write(line + '\\n');\n }\n\n // forcibly kills the server\n stop() {\n if (!this.#child) {\n Logger.verbose('Cannot stop server as no subprocess exists');\n return;\n }\n\n Logger.verbose('Forcibly stopping server');\n // kill the process\n this.#child.kill('SIGINT');\n\n // ...kill it again just to make sure it's dead\n spawn('kill', ['-9', this.#child.pid + '']);\n }\n\n // detaches listeners\n cleanup() {\n if (!this.#child) return;\n\n Logger.verbose('Cleaning up brickadia server');\n\n // detach listener\n this.detachListeners();\n\n this.#child = null;\n this.#outInterface = null;\n this.#errInterface = null;\n }\n\n // attaches proxy event listeners\n attachListeners() {\n this.#outInterface?.on('line', this.lineListener);\n this.#errInterface?.on('line', this.errorListener);\n this.#child?.on('exit', this.exitListener);\n this.#child?.on('close', () => {});\n }\n\n // removes previously attached proxy event listeners\n detachListeners() {\n this.#outInterface?.off('line', this.lineListener);\n this.#errInterface?.off('line', this.errorListener);\n this.#child?.off('exit', this.exitListener);\n this.#child?.removeAllListeners('close');\n }\n\n // -- listeners for basic events (line, err, exit)\n errorListener(line: string) {\n Logger.verbose('ERROR'.red, line);\n this.emit('err', line);\n for (const { match, solution, name, message } of knownErrors) {\n if (line.match(match)) {\n Logger.error(\n `Encountered ${name.red}. ${\n solution ? 'Known fix:\\n ' + solution : message || 'Unknown error.'\n }`,\n );\n }\n }\n }\n\n exitListener(...args: any[]) {\n Logger.verbose('Exit listener fired');\n this.emit('closed', ...args);\n this.cleanup();\n }\n\n lineListener(line: string) {\n this.emit('line', stripAnsi(line));\n }\n}\n"],"names":["ACTIVE_WORLD_FILE","existsSync","readFileSync","Logger","unlinkSync","writeFileSync","env","CONFIG_SAVED_DIR","getOverrideGameBinary","getSteamInstallDir","getSteamGameDir","GAME_BIN_PATH","getGlobalToken","checkWsl","spawn"],"mappings":"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"}
|
package/dist/config/types.js
CHANGED
|
@@ -25,6 +25,7 @@ const BrickadiaConfigSchema = zod.z.object({
|
|
|
25
25
|
authDir: zod.z.string().optional(),
|
|
26
26
|
savedDir: zod.z.string().optional(),
|
|
27
27
|
launchArgs: zod.z.string().optional(),
|
|
28
|
+
remoteFiles: zod.z.boolean().optional(),
|
|
28
29
|
__LOCAL: zod.z.boolean().optional(),
|
|
29
30
|
__LEGACY: zod.z.string().optional()
|
|
30
31
|
});
|
package/dist/config/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sources":["../../src/config/types.ts"],"sourcesContent":["import { z } from 'zod';\n\nexport const ServerConfigSchema = z.object({\n webui: z.boolean().optional(),\n port: z.number().optional(),\n https: z.boolean().optional(),\n plugins: z.boolean().optional(),\n singleUser: z.boolean().optional(),\n debug: z.boolean().optional(),\n});\n\nexport const BrickadiaConfigSchema = z.object({\n port: z.number(),\n map: z.string().optional(),\n world: z.string().optional(),\n branch: z.string().optional(),\n steambeta: z.string().optional(),\n steambetaPassword: z.string().optional(),\n name: z.string().optional(),\n description: z.string().optional(),\n password: z.string().optional(),\n players: z.number().optional(),\n publiclyListed: z.boolean().optional(),\n welcomeMessage: z.string().optional(),\n authDir: z.string().optional(),\n savedDir: z.string().optional(),\n launchArgs: z.string().optional(),\n __LOCAL: z.boolean().optional(),\n __LEGACY: z.string().optional(),\n});\n\nexport const TerminalConfigSchema = z.object({\n timestamp: z.string().optional(),\n});\n\nexport const CredentialsSchema = z.object({\n email: z.string().optional(),\n password: z.string().optional(),\n token: z.string().optional(),\n});\n\nexport const ConfigSchema = z.object({\n omegga: ServerConfigSchema.optional(),\n server: BrickadiaConfigSchema,\n terminal: TerminalConfigSchema.optional(),\n credentials: CredentialsSchema.optional(),\n __STEAM: z.boolean().optional(),\n});\n\nexport type IServerConfig = z.infer<typeof ServerConfigSchema>;\nexport type IBrickadiaConfig = z.infer<typeof BrickadiaConfigSchema>;\nexport type IConfig = z.infer<typeof ConfigSchema>;\n\nexport type IConfigFormat = {\n extension: string;\n writer: (blob: IConfig) => string;\n} & (\n | {\n encoding: 'string';\n reader: (str: string) => IConfig;\n }\n | {\n encoding: 'buffer';\n reader: (str: Buffer) => IConfig;\n }\n);\n"],"names":["z"],"mappings":";;;AAEO,MAAM,qBAAqBA,IAAAA,EAAE,OAAO;AAAA,EACzC,OAAOA,IAAAA,EAAE,QAAA,EAAU,SAAA;AAAA,EACnB,MAAMA,IAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,EACjB,OAAOA,IAAAA,EAAE,QAAA,EAAU,SAAA;AAAA,EACnB,SAASA,IAAAA,EAAE,QAAA,EAAU,SAAA;AAAA,EACrB,YAAYA,IAAAA,EAAE,QAAA,EAAU,SAAA;AAAA,EACxB,OAAOA,IAAAA,EAAE,QAAA,EAAU,SAAA;AACrB,CAAC;AAEM,MAAM,wBAAwBA,IAAAA,EAAE,OAAO;AAAA,EAC5C,MAAMA,IAAAA,EAAE,OAAA;AAAA,EACR,KAAKA,IAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,EAChB,OAAOA,IAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,EAClB,QAAQA,IAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,EACnB,WAAWA,IAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,EACtB,mBAAmBA,IAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,EAC9B,MAAMA,IAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,EACjB,aAAaA,IAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,EACxB,UAAUA,IAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,EACrB,SAASA,IAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,EACpB,gBAAgBA,IAAAA,EAAE,QAAA,EAAU,SAAA;AAAA,EAC5B,gBAAgBA,IAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,EAC3B,SAASA,IAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,EACpB,UAAUA,IAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,EACrB,YAAYA,IAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,EACvB,SAASA,IAAAA,EAAE,QAAA,EAAU,SAAA;AAAA,EACrB,UAAUA,IAAAA,EAAE,OAAA,EAAS,SAAA;AACvB,CAAC;AAEM,MAAM,uBAAuBA,IAAAA,EAAE,OAAO;AAAA,EAC3C,WAAWA,IAAAA,EAAE,OAAA,EAAS,SAAA;AACxB,CAAC;AAEM,MAAM,oBAAoBA,IAAAA,EAAE,OAAO;AAAA,EACxC,OAAOA,IAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,EAClB,UAAUA,IAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,EACrB,OAAOA,IAAAA,EAAE,OAAA,EAAS,SAAA;AACpB,CAAC;AAEM,MAAM,eAAeA,IAAAA,EAAE,OAAO;AAAA,EACnC,QAAQ,mBAAmB,SAAA;AAAA,EAC3B,QAAQ;AAAA,EACR,UAAU,qBAAqB,SAAA;AAAA,EAC/B,aAAa,kBAAkB,SAAA;AAAA,EAC/B,SAASA,IAAAA,EAAE,QAAA,EAAU,SAAA;AACvB,CAAC;;;;;;"}
|
|
1
|
+
{"version":3,"file":"types.js","sources":["../../src/config/types.ts"],"sourcesContent":["import { z } from 'zod';\n\nexport const ServerConfigSchema = z.object({\n webui: z.boolean().optional(),\n port: z.number().optional(),\n https: z.boolean().optional(),\n plugins: z.boolean().optional(),\n singleUser: z.boolean().optional(),\n debug: z.boolean().optional(),\n});\n\nexport const BrickadiaConfigSchema = z.object({\n port: z.number(),\n map: z.string().optional(),\n world: z.string().optional(),\n branch: z.string().optional(),\n steambeta: z.string().optional(),\n steambetaPassword: z.string().optional(),\n name: z.string().optional(),\n description: z.string().optional(),\n password: z.string().optional(),\n players: z.number().optional(),\n publiclyListed: z.boolean().optional(),\n welcomeMessage: z.string().optional(),\n authDir: z.string().optional(),\n savedDir: z.string().optional(),\n launchArgs: z.string().optional(),\n remoteFiles: z.boolean().optional(),\n __LOCAL: z.boolean().optional(),\n __LEGACY: z.string().optional(),\n});\n\nexport const TerminalConfigSchema = z.object({\n timestamp: z.string().optional(),\n});\n\nexport const CredentialsSchema = z.object({\n email: z.string().optional(),\n password: z.string().optional(),\n token: z.string().optional(),\n});\n\nexport const ConfigSchema = z.object({\n omegga: ServerConfigSchema.optional(),\n server: BrickadiaConfigSchema,\n terminal: TerminalConfigSchema.optional(),\n credentials: CredentialsSchema.optional(),\n __STEAM: z.boolean().optional(),\n});\n\nexport type IServerConfig = z.infer<typeof ServerConfigSchema>;\nexport type IBrickadiaConfig = z.infer<typeof BrickadiaConfigSchema>;\nexport type IConfig = z.infer<typeof ConfigSchema>;\n\nexport type IConfigFormat = {\n extension: string;\n writer: (blob: IConfig) => string;\n} & (\n | {\n encoding: 'string';\n reader: (str: string) => IConfig;\n }\n | {\n encoding: 'buffer';\n reader: (str: Buffer) => IConfig;\n }\n);\n"],"names":["z"],"mappings":";;;AAEO,MAAM,qBAAqBA,IAAAA,EAAE,OAAO;AAAA,EACzC,OAAOA,IAAAA,EAAE,QAAA,EAAU,SAAA;AAAA,EACnB,MAAMA,IAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,EACjB,OAAOA,IAAAA,EAAE,QAAA,EAAU,SAAA;AAAA,EACnB,SAASA,IAAAA,EAAE,QAAA,EAAU,SAAA;AAAA,EACrB,YAAYA,IAAAA,EAAE,QAAA,EAAU,SAAA;AAAA,EACxB,OAAOA,IAAAA,EAAE,QAAA,EAAU,SAAA;AACrB,CAAC;AAEM,MAAM,wBAAwBA,IAAAA,EAAE,OAAO;AAAA,EAC5C,MAAMA,IAAAA,EAAE,OAAA;AAAA,EACR,KAAKA,IAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,EAChB,OAAOA,IAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,EAClB,QAAQA,IAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,EACnB,WAAWA,IAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,EACtB,mBAAmBA,IAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,EAC9B,MAAMA,IAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,EACjB,aAAaA,IAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,EACxB,UAAUA,IAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,EACrB,SAASA,IAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,EACpB,gBAAgBA,IAAAA,EAAE,QAAA,EAAU,SAAA;AAAA,EAC5B,gBAAgBA,IAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,EAC3B,SAASA,IAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,EACpB,UAAUA,IAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,EACrB,YAAYA,IAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,EACvB,aAAaA,IAAAA,EAAE,QAAA,EAAU,SAAA;AAAA,EACzB,SAASA,IAAAA,EAAE,QAAA,EAAU,SAAA;AAAA,EACrB,UAAUA,IAAAA,EAAE,OAAA,EAAS,SAAA;AACvB,CAAC;AAEM,MAAM,uBAAuBA,IAAAA,EAAE,OAAO;AAAA,EAC3C,WAAWA,IAAAA,EAAE,OAAA,EAAS,SAAA;AACxB,CAAC;AAEM,MAAM,oBAAoBA,IAAAA,EAAE,OAAO;AAAA,EACxC,OAAOA,IAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,EAClB,UAAUA,IAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,EACrB,OAAOA,IAAAA,EAAE,OAAA,EAAS,SAAA;AACpB,CAAC;AAEM,MAAM,eAAeA,IAAAA,EAAE,OAAO;AAAA,EACnC,QAAQ,mBAAmB,SAAA;AAAA,EAC3B,QAAQ;AAAA,EACR,UAAU,qBAAqB,SAAA;AAAA,EAC/B,aAAa,kBAAkB,SAAA;AAAA,EAC/B,SAASA,IAAAA,EAAE,QAAA,EAAU,SAAA;AACvB,CAAC;;;;;;"}
|
package/dist/updater/steam.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const logger = require("../logger.js");
|
|
4
4
|
const softconfig = require("../softconfig.js");
|
|
5
|
+
const version = require("../omegga/matchers/version.js");
|
|
5
6
|
const node_child_process = require("node:child_process");
|
|
6
7
|
const path = require("node:path");
|
|
7
8
|
const prompts = require("prompts");
|
|
@@ -102,11 +103,25 @@ async function steamcmdDownloadGame({
|
|
|
102
103
|
"+quit"
|
|
103
104
|
].filter(Boolean);
|
|
104
105
|
const cmd = `${softconfig.STEAMCMD_PATH} ${args.join(" ")}`;
|
|
106
|
+
const logUpdatedVersion = () => {
|
|
107
|
+
const version$1 = version.readBinaryVersion(
|
|
108
|
+
path.join(
|
|
109
|
+
installDir,
|
|
110
|
+
steambeta ?? "main",
|
|
111
|
+
softconfig.getSteamGameDir(),
|
|
112
|
+
softconfig.GAME_BIN_PATH
|
|
113
|
+
)
|
|
114
|
+
);
|
|
115
|
+
logger.default.logp(
|
|
116
|
+
`Brickadia Server updated${version$1 ? " to " + ("CL" + version$1).green : ""}`
|
|
117
|
+
);
|
|
118
|
+
};
|
|
105
119
|
const attempts = Math.max(1, retries);
|
|
106
120
|
let triedInteractiveLogin = false;
|
|
107
121
|
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
108
122
|
try {
|
|
109
123
|
node_child_process.execSync(cmd, { stdio: "inherit" });
|
|
124
|
+
logUpdatedVersion();
|
|
110
125
|
return;
|
|
111
126
|
} catch (err) {
|
|
112
127
|
let error = err;
|
|
@@ -120,6 +135,7 @@ async function steamcmdDownloadGame({
|
|
|
120
135
|
logger.default.logp("Login successful. Retrying download...");
|
|
121
136
|
try {
|
|
122
137
|
node_child_process.execSync(cmd, { stdio: "inherit" });
|
|
138
|
+
logUpdatedVersion();
|
|
123
139
|
return;
|
|
124
140
|
} catch (retryErr) {
|
|
125
141
|
handleSteamError(retryErr);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"steam.js","sources":["../../src/updater/steam.ts"],"sourcesContent":["/// <reference path=\"./steam-acf2json.d.ts\" />\nimport Logger from '@/logger';\nimport { getAppId, getSteamInstallDir, STEAMCMD_PATH } from '@/softconfig';\nimport { execSync, spawnSync } from 'node:child_process';\nimport path from 'node:path';\nimport prompts from 'prompts';\nimport acfParser from 'steam-acf2json';\n\n// Steam EAppState flags (bitmask)\nconst STEAM_APP_STATES: Record<number, string> = {\n 0x1: 'Uninstalled',\n 0x2: 'Update Required',\n 0x4: 'Fully Installed',\n 0x8: 'Encrypted',\n 0x10: 'Locked',\n 0x20: 'Files Missing',\n 0x40: 'App Running',\n 0x80: 'Files Corrupt',\n 0x100: 'Update Running',\n 0x200: 'Update Paused',\n 0x400: 'Update Started',\n 0x800: 'Uninstalling',\n 0x1000: 'Backup Running',\n 0x10000: 'Reconfiguring',\n 0x20000: 'Validating',\n 0x40000: 'Adding Files',\n 0x80000: 'Preallocating',\n 0x100000: 'Downloading',\n 0x200000: 'Staging',\n 0x400000: 'Committing',\n 0x800000: 'Update Stopping',\n};\n\nfunction decodeAppState(state: number): string[] {\n const flags: string[] = [];\n for (const [bit, name] of Object.entries(STEAM_APP_STATES)) {\n if (state & Number(bit)) flags.push(name);\n }\n return flags;\n}\n\nexport function steamcmdDownloadSelf() {\n execSync(path.join(__dirname, '../../tools/install_steamcmd.sh'), {\n stdio: 'inherit',\n });\n}\n\nexport async function steamcmdInteractiveLogin(): Promise<boolean> {\n if (!process.env.STEAM_USERNAME) {\n Logger.error('STEAM_USERNAME environment variable is required.');\n return false;\n }\n\n const password =\n process.env.STEAM_PASSWORD ??\n (\n await prompts({\n type: 'password',\n name: 'value',\n message: `Steam password for ${process.env.STEAM_USERNAME}`,\n })\n ).value;\n\n if (!password) {\n Logger.error('No password provided.');\n return false;\n }\n\n const result = spawnSync(\n STEAMCMD_PATH,\n ['+login', process.env.STEAM_USERNAME, password, '+quit'],\n { stdio: 'inherit' },\n );\n\n return result.status === 0;\n}\n\nfunction handleSteamError(err: unknown) {\n if (err instanceof Error && err.message) {\n const stateMatch = err.message.match(\n /state is (0x[0-9A-Fa-f]+) after update job/,\n );\n if (stateMatch) {\n const state = parseInt(stateMatch[1], 16);\n const flags = decodeAppState(state);\n Logger.error(\n `Steam app state ${stateMatch[1]} (${state}):`,\n flags.length > 0 ? flags.join(', ') : 'Unknown',\n );\n }\n\n err.message = err.message\n .replace(/\\+login\\s+\"[^\"]*\"\\s+\"[^\"]*\"/, '+login <REDACTED>')\n .replace(/\\+login\\s+(\\S+)\\s+\\S+/, '+login $1 <REDACTED>')\n .replace(/-betapassword\\s+\\S+/, '-betapassword <REDACTED>');\n }\n}\n\nexport async function steamcmdDownloadGame({\n steambeta,\n steambetaPassword,\n retries = 3,\n retryDelayMs = 5000,\n}: {\n steambeta?: string;\n steambetaPassword?: string;\n /** number of attempts before giving up (steamcmd resumes partial downloads) */\n retries?: number;\n /** delay between retry attempts */\n retryDelayMs?: number;\n} = {}) {\n const hasCredentials = !!process.env.STEAM_USERNAME;\n const steamLogin = hasCredentials\n ? `\"${process.env.STEAM_USERNAME}\"`\n : 'anonymous';\n if (hasCredentials) {\n Logger.verbose('Using cached Steam credentials for download.');\n }\n const installDir = getSteamInstallDir();\n const appId = getAppId();\n\n Logger.verbose('Using with steam install dir:', installDir.yellow);\n Logger.verbose('Using with app ID:', appId.yellow);\n\n const args = [\n `+force_install_dir ${path.join(installDir, steambeta ?? 'main')}`,\n `+login ${steamLogin}`,\n `+app_update ${appId}`,\n steambeta ? `-beta ${steambeta}` : null,\n steambeta && steambetaPassword\n ? `-betapassword ${steambetaPassword}`\n : null,\n '+quit',\n ].filter(Boolean);\n\n const cmd = `${STEAMCMD_PATH} ${args.join(' ')}`;\n\n // steamcmd resumes partial downloads, and late-stage failures (e.g. \"state is\n // 0x406 after update job\") are frequently transient, so retry before giving up.\n const attempts = Math.max(1, retries);\n let triedInteractiveLogin = false;\n\n for (let attempt = 1; attempt <= attempts; attempt++) {\n try {\n execSync(cmd, { stdio: 'inherit' });\n return;\n } catch (err) {\n // the error that is ultimately reported/thrown; replaced when the\n // interactive-login retry fails with a newer error\n let error: unknown = err;\n // Steam Guard interactive re-auth on status 5, attempted at most once.\n if (\n !triedInteractiveLogin &&\n hasCredentials &&\n err instanceof Error &&\n 'status' in err &&\n (err as NodeJS.ErrnoException & { status: number }).status === 5\n ) {\n triedInteractiveLogin = true;\n Logger.warnp(\n 'Steam login failed. This may require Steam Guard authentication.',\n );\n Logger.warnp('Attempting interactive login...');\n\n if (await steamcmdInteractiveLogin()) {\n Logger.logp('Login successful. Retrying download...');\n try {\n execSync(cmd, { stdio: 'inherit' });\n return;\n } catch (retryErr) {\n handleSteamError(retryErr);\n error = retryErr;\n }\n } else {\n Logger.errorp(\n 'Interactive login failed. Run',\n 'omegga steamlogin'.yellow,\n 'to authenticate manually.',\n );\n }\n }\n\n handleSteamError(error);\n\n if (attempt < attempts) {\n Logger.warnp(\n `Steam download attempt ${attempt}/${attempts} failed; retrying in ${Math.round(\n retryDelayMs / 1000,\n )}s...`,\n );\n await new Promise(resolve => setTimeout(resolve, retryDelayMs));\n continue;\n }\n\n throw error;\n }\n }\n}\n\nexport type SteamAppStatus = {\n installState: string;\n buildId: string;\n betaKey: string | null;\n updateState: string;\n};\n\nexport type SteamAppInfo = {\n branches: Record<string, { buildid: string; timeupdated?: string }>;\n};\n\nfunction stripAnsi(s: string): string {\n // eslint-disable-next-line no-control-regex -- the escape byte is the point\n return s.replace(/\\x1b\\[[0-9;]*m/g, '');\n}\n\nfunction parseAppStatus(output: string): SteamAppStatus | null {\n const clean = stripAnsi(output);\n const buildMatch = clean.match(/BuildID\\s+(\\d+)/);\n const stateMatch = clean.match(/install state:\\s*(.+)/i);\n const betaMatch = clean.match(/\"BetaKey\"\\s+\"([^\"]+)\"/);\n const updateMatch = clean.match(/update state:\\s*(.+)/i);\n if (!buildMatch) return null;\n return {\n installState: stateMatch?.[1]?.trim().replace(/,$/, '') ?? 'Unknown',\n buildId: buildMatch[1],\n betaKey: betaMatch?.[1] ?? null,\n updateState: updateMatch?.[1]?.trim() ?? 'Unknown',\n };\n}\n\nfunction parseAppInfo(output: string, appId: string): SteamAppInfo | null {\n const clean = stripAnsi(output);\n const vdfStart = clean.indexOf(`\"${appId}\"`);\n if (vdfStart === -1) return null;\n\n const braceStart = clean.indexOf('{', vdfStart);\n if (braceStart === -1) return null;\n\n let depth = 0;\n let braceEnd = -1;\n for (let i = braceStart; i < clean.length; i++) {\n if (clean[i] === '{') depth++;\n if (clean[i] === '}') depth--;\n if (depth === 0) {\n braceEnd = i;\n break;\n }\n }\n if (braceEnd === -1) return null;\n\n try {\n const vdf = `\"${appId}\"\\n${clean.slice(braceStart, braceEnd + 1)}\\n`;\n const parsed = acfParser.decode(vdf);\n const branches = parsed?.[appId]?.depots?.branches;\n if (!branches || typeof branches !== 'object') return null;\n return { branches };\n } catch {\n return null;\n }\n}\n\nexport type SteamUpdateCheck = {\n local: SteamAppStatus;\n remote: SteamAppInfo | null;\n hasUpdate: boolean | null;\n};\n\nexport function steamcmdCheckUpdate(\n steambeta?: string,\n): SteamUpdateCheck | null {\n const appId = getAppId();\n const installDir = path.join(getSteamInstallDir(), steambeta ?? 'main');\n const steamLogin = process.env.STEAM_USERNAME\n ? `\"${process.env.STEAM_USERNAME}\"`\n : 'anonymous';\n\n try {\n const output = execSync(\n [\n STEAMCMD_PATH,\n `+force_install_dir ${installDir}`,\n `+login ${steamLogin}`,\n '+app_info_update 1',\n `+app_info_print ${appId}`,\n `+app_status ${appId}`,\n '+quit',\n ].join(' '),\n { encoding: 'utf-8', stdio: ['inherit', 'pipe', 'pipe'], timeout: 30000 },\n );\n\n const local = parseAppStatus(output);\n if (!local) return null;\n\n const remote = parseAppInfo(output, appId);\n\n const branch = steambeta ?? (local.betaKey || null);\n const remoteBranch = branch && branch !== 'main' ? branch : 'public';\n const remoteBuildId = remote?.branches?.[remoteBranch]?.buildid;\n\n const hasUpdate =\n remoteBuildId != null ? remoteBuildId !== local.buildId : null;\n\n return { local, remote, hasUpdate };\n } catch (err) {\n Logger.verbose('Failed to check for Steam update via steamcmd:', err);\n return null;\n }\n}\n\nlet lastUpdateCheck = 0;\nlet lastUpdateAvailable = 0;\nlet lastUpdateResult = false;\n\nexport const getLastSteamUpdateCheck = () => ({\n attempt: lastUpdateCheck,\n available: lastUpdateAvailable,\n result: lastUpdateResult,\n});\n\nexport const clearLastSteamUpdateCheck = () => {\n lastUpdateResult = false;\n};\n\nexport async function hasSteamUpdate(steambeta?: string) {\n lastUpdateCheck = Date.now();\n\n const check = steamcmdCheckUpdate(steambeta);\n if (!check) return false;\n\n lastUpdateAvailable = Date.now();\n\n if (check.hasUpdate != null) {\n lastUpdateResult = check.hasUpdate;\n return check.hasUpdate;\n }\n\n return false;\n}\n"],"names":["execSync","Logger","spawnSync","STEAMCMD_PATH","getSteamInstallDir","getAppId"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"steam.js","sources":["../../src/updater/steam.ts"],"sourcesContent":["/// <reference path=\"./steam-acf2json.d.ts\" />\nimport Logger from '@/logger';\nimport {\n GAME_BIN_PATH,\n getAppId,\n getSteamGameDir,\n getSteamInstallDir,\n STEAMCMD_PATH,\n} from '@/softconfig';\nimport { readBinaryVersion } from '@omegga/matchers/version';\nimport { execSync, spawnSync } from 'node:child_process';\nimport path from 'node:path';\nimport prompts from 'prompts';\nimport acfParser from 'steam-acf2json';\n\n// Steam EAppState flags (bitmask)\nconst STEAM_APP_STATES: Record<number, string> = {\n 0x1: 'Uninstalled',\n 0x2: 'Update Required',\n 0x4: 'Fully Installed',\n 0x8: 'Encrypted',\n 0x10: 'Locked',\n 0x20: 'Files Missing',\n 0x40: 'App Running',\n 0x80: 'Files Corrupt',\n 0x100: 'Update Running',\n 0x200: 'Update Paused',\n 0x400: 'Update Started',\n 0x800: 'Uninstalling',\n 0x1000: 'Backup Running',\n 0x10000: 'Reconfiguring',\n 0x20000: 'Validating',\n 0x40000: 'Adding Files',\n 0x80000: 'Preallocating',\n 0x100000: 'Downloading',\n 0x200000: 'Staging',\n 0x400000: 'Committing',\n 0x800000: 'Update Stopping',\n};\n\nfunction decodeAppState(state: number): string[] {\n const flags: string[] = [];\n for (const [bit, name] of Object.entries(STEAM_APP_STATES)) {\n if (state & Number(bit)) flags.push(name);\n }\n return flags;\n}\n\nexport function steamcmdDownloadSelf() {\n execSync(path.join(__dirname, '../../tools/install_steamcmd.sh'), {\n stdio: 'inherit',\n });\n}\n\nexport async function steamcmdInteractiveLogin(): Promise<boolean> {\n if (!process.env.STEAM_USERNAME) {\n Logger.error('STEAM_USERNAME environment variable is required.');\n return false;\n }\n\n const password =\n process.env.STEAM_PASSWORD ??\n (\n await prompts({\n type: 'password',\n name: 'value',\n message: `Steam password for ${process.env.STEAM_USERNAME}`,\n })\n ).value;\n\n if (!password) {\n Logger.error('No password provided.');\n return false;\n }\n\n const result = spawnSync(\n STEAMCMD_PATH,\n ['+login', process.env.STEAM_USERNAME, password, '+quit'],\n { stdio: 'inherit' },\n );\n\n return result.status === 0;\n}\n\nfunction handleSteamError(err: unknown) {\n if (err instanceof Error && err.message) {\n const stateMatch = err.message.match(\n /state is (0x[0-9A-Fa-f]+) after update job/,\n );\n if (stateMatch) {\n const state = parseInt(stateMatch[1], 16);\n const flags = decodeAppState(state);\n Logger.error(\n `Steam app state ${stateMatch[1]} (${state}):`,\n flags.length > 0 ? flags.join(', ') : 'Unknown',\n );\n }\n\n err.message = err.message\n .replace(/\\+login\\s+\"[^\"]*\"\\s+\"[^\"]*\"/, '+login <REDACTED>')\n .replace(/\\+login\\s+(\\S+)\\s+\\S+/, '+login $1 <REDACTED>')\n .replace(/-betapassword\\s+\\S+/, '-betapassword <REDACTED>');\n }\n}\n\nexport async function steamcmdDownloadGame({\n steambeta,\n steambetaPassword,\n retries = 3,\n retryDelayMs = 5000,\n}: {\n steambeta?: string;\n steambetaPassword?: string;\n /** number of attempts before giving up (steamcmd resumes partial downloads) */\n retries?: number;\n /** delay between retry attempts */\n retryDelayMs?: number;\n} = {}) {\n const hasCredentials = !!process.env.STEAM_USERNAME;\n const steamLogin = hasCredentials\n ? `\"${process.env.STEAM_USERNAME}\"`\n : 'anonymous';\n if (hasCredentials) {\n Logger.verbose('Using cached Steam credentials for download.');\n }\n const installDir = getSteamInstallDir();\n const appId = getAppId();\n\n Logger.verbose('Using with steam install dir:', installDir.yellow);\n Logger.verbose('Using with app ID:', appId.yellow);\n\n const args = [\n `+force_install_dir ${path.join(installDir, steambeta ?? 'main')}`,\n `+login ${steamLogin}`,\n `+app_update ${appId}`,\n steambeta ? `-beta ${steambeta}` : null,\n steambeta && steambetaPassword\n ? `-betapassword ${steambetaPassword}`\n : null,\n '+quit',\n ].filter(Boolean);\n\n const cmd = `${STEAMCMD_PATH} ${args.join(' ')}`;\n\n // mirrors the \"Launching Brickadia Server CL####\" startup log\n const logUpdatedVersion = () => {\n const version = readBinaryVersion(\n path.join(\n installDir,\n steambeta ?? 'main',\n getSteamGameDir(),\n GAME_BIN_PATH,\n ),\n );\n Logger.logp(\n `Brickadia Server updated${\n version ? ' to ' + ('CL' + version).green : ''\n }`,\n );\n };\n\n // steamcmd resumes partial downloads, and late-stage failures (e.g. \"state is\n // 0x406 after update job\") are frequently transient, so retry before giving up.\n const attempts = Math.max(1, retries);\n let triedInteractiveLogin = false;\n\n for (let attempt = 1; attempt <= attempts; attempt++) {\n try {\n execSync(cmd, { stdio: 'inherit' });\n logUpdatedVersion();\n return;\n } catch (err) {\n // the error that is ultimately reported/thrown; replaced when the\n // interactive-login retry fails with a newer error\n let error: unknown = err;\n // Steam Guard interactive re-auth on status 5, attempted at most once.\n if (\n !triedInteractiveLogin &&\n hasCredentials &&\n err instanceof Error &&\n 'status' in err &&\n (err as NodeJS.ErrnoException & { status: number }).status === 5\n ) {\n triedInteractiveLogin = true;\n Logger.warnp(\n 'Steam login failed. This may require Steam Guard authentication.',\n );\n Logger.warnp('Attempting interactive login...');\n\n if (await steamcmdInteractiveLogin()) {\n Logger.logp('Login successful. Retrying download...');\n try {\n execSync(cmd, { stdio: 'inherit' });\n logUpdatedVersion();\n return;\n } catch (retryErr) {\n handleSteamError(retryErr);\n error = retryErr;\n }\n } else {\n Logger.errorp(\n 'Interactive login failed. Run',\n 'omegga steamlogin'.yellow,\n 'to authenticate manually.',\n );\n }\n }\n\n handleSteamError(error);\n\n if (attempt < attempts) {\n Logger.warnp(\n `Steam download attempt ${attempt}/${attempts} failed; retrying in ${Math.round(\n retryDelayMs / 1000,\n )}s...`,\n );\n await new Promise(resolve => setTimeout(resolve, retryDelayMs));\n continue;\n }\n\n throw error;\n }\n }\n}\n\nexport type SteamAppStatus = {\n installState: string;\n buildId: string;\n betaKey: string | null;\n updateState: string;\n};\n\nexport type SteamAppInfo = {\n branches: Record<string, { buildid: string; timeupdated?: string }>;\n};\n\nfunction stripAnsi(s: string): string {\n // eslint-disable-next-line no-control-regex -- the escape byte is the point\n return s.replace(/\\x1b\\[[0-9;]*m/g, '');\n}\n\nfunction parseAppStatus(output: string): SteamAppStatus | null {\n const clean = stripAnsi(output);\n const buildMatch = clean.match(/BuildID\\s+(\\d+)/);\n const stateMatch = clean.match(/install state:\\s*(.+)/i);\n const betaMatch = clean.match(/\"BetaKey\"\\s+\"([^\"]+)\"/);\n const updateMatch = clean.match(/update state:\\s*(.+)/i);\n if (!buildMatch) return null;\n return {\n installState: stateMatch?.[1]?.trim().replace(/,$/, '') ?? 'Unknown',\n buildId: buildMatch[1],\n betaKey: betaMatch?.[1] ?? null,\n updateState: updateMatch?.[1]?.trim() ?? 'Unknown',\n };\n}\n\nfunction parseAppInfo(output: string, appId: string): SteamAppInfo | null {\n const clean = stripAnsi(output);\n const vdfStart = clean.indexOf(`\"${appId}\"`);\n if (vdfStart === -1) return null;\n\n const braceStart = clean.indexOf('{', vdfStart);\n if (braceStart === -1) return null;\n\n let depth = 0;\n let braceEnd = -1;\n for (let i = braceStart; i < clean.length; i++) {\n if (clean[i] === '{') depth++;\n if (clean[i] === '}') depth--;\n if (depth === 0) {\n braceEnd = i;\n break;\n }\n }\n if (braceEnd === -1) return null;\n\n try {\n const vdf = `\"${appId}\"\\n${clean.slice(braceStart, braceEnd + 1)}\\n`;\n const parsed = acfParser.decode(vdf);\n const branches = parsed?.[appId]?.depots?.branches;\n if (!branches || typeof branches !== 'object') return null;\n return { branches };\n } catch {\n return null;\n }\n}\n\nexport type SteamUpdateCheck = {\n local: SteamAppStatus;\n remote: SteamAppInfo | null;\n hasUpdate: boolean | null;\n};\n\nexport function steamcmdCheckUpdate(\n steambeta?: string,\n): SteamUpdateCheck | null {\n const appId = getAppId();\n const installDir = path.join(getSteamInstallDir(), steambeta ?? 'main');\n const steamLogin = process.env.STEAM_USERNAME\n ? `\"${process.env.STEAM_USERNAME}\"`\n : 'anonymous';\n\n try {\n const output = execSync(\n [\n STEAMCMD_PATH,\n `+force_install_dir ${installDir}`,\n `+login ${steamLogin}`,\n '+app_info_update 1',\n `+app_info_print ${appId}`,\n `+app_status ${appId}`,\n '+quit',\n ].join(' '),\n { encoding: 'utf-8', stdio: ['inherit', 'pipe', 'pipe'], timeout: 30000 },\n );\n\n const local = parseAppStatus(output);\n if (!local) return null;\n\n const remote = parseAppInfo(output, appId);\n\n const branch = steambeta ?? (local.betaKey || null);\n const remoteBranch = branch && branch !== 'main' ? branch : 'public';\n const remoteBuildId = remote?.branches?.[remoteBranch]?.buildid;\n\n const hasUpdate =\n remoteBuildId != null ? remoteBuildId !== local.buildId : null;\n\n return { local, remote, hasUpdate };\n } catch (err) {\n Logger.verbose('Failed to check for Steam update via steamcmd:', err);\n return null;\n }\n}\n\nlet lastUpdateCheck = 0;\nlet lastUpdateAvailable = 0;\nlet lastUpdateResult = false;\n\nexport const getLastSteamUpdateCheck = () => ({\n attempt: lastUpdateCheck,\n available: lastUpdateAvailable,\n result: lastUpdateResult,\n});\n\nexport const clearLastSteamUpdateCheck = () => {\n lastUpdateResult = false;\n};\n\nexport async function hasSteamUpdate(steambeta?: string) {\n lastUpdateCheck = Date.now();\n\n const check = steamcmdCheckUpdate(steambeta);\n if (!check) return false;\n\n lastUpdateAvailable = Date.now();\n\n if (check.hasUpdate != null) {\n lastUpdateResult = check.hasUpdate;\n return check.hasUpdate;\n }\n\n return false;\n}\n"],"names":["execSync","Logger","spawnSync","STEAMCMD_PATH","getSteamInstallDir","getAppId","version","readBinaryVersion","getSteamGameDir","GAME_BIN_PATH"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omegga",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.0",
|
|
4
4
|
"description": "Brickadia server installer, updater, manager, wrapper, configurator, and automator",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=23.0.0"
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"better-sqlite3": "^12.2.0",
|
|
22
22
|
"better-sqlite3-session-store": "^0.1.0",
|
|
23
23
|
"body-parser": "^1.19.0",
|
|
24
|
-
"brs-js": "^3.
|
|
24
|
+
"brs-js": "^3.7.0",
|
|
25
25
|
"child_process": "^1.0.2",
|
|
26
26
|
"chokidar": "^3.4.2",
|
|
27
27
|
"colors": "^1.4.0",
|