omegga 1.6.0 → 1.7.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.
Files changed (105) hide show
  1. package/.claude/settings.local.json +9 -2
  2. package/CHANGELOG.md +22 -1
  3. package/dist/cli/terminal.js +5 -1
  4. package/dist/cli/terminal.js.map +1 -1
  5. package/dist/db/connection.js +31 -0
  6. package/dist/db/connection.js.map +1 -0
  7. package/dist/db/migrate.js +20 -0
  8. package/dist/db/migrate.js.map +1 -0
  9. package/dist/db/migrations/0000_long_killer_shrike.sql +111 -0
  10. package/dist/db/migrations/meta/0000_snapshot.json +737 -0
  11. package/dist/db/migrations/meta/_journal.json +13 -0
  12. package/dist/db/nedbImport.js +344 -0
  13. package/dist/db/nedbImport.js.map +1 -0
  14. package/dist/db/plugin-migrations/0000_faulty_tyger_tiger.sql +11 -0
  15. package/dist/db/plugin-migrations/meta/0000_snapshot.json +81 -0
  16. package/dist/db/plugin-migrations/meta/_journal.json +13 -0
  17. package/dist/db/pluginSchema.js +19 -0
  18. package/dist/db/pluginSchema.js.map +1 -0
  19. package/dist/db/schema.js +174 -0
  20. package/dist/db/schema.js.map +1 -0
  21. package/dist/omegga/matchers/version.js +30 -13
  22. package/dist/omegga/matchers/version.js.map +1 -1
  23. package/dist/omegga/plugin.js +47 -49
  24. package/dist/omegga/plugin.js.map +1 -1
  25. package/dist/omegga/server.js.map +1 -1
  26. package/dist/softconfig.js +9 -21
  27. package/dist/softconfig.js.map +1 -1
  28. package/dist/updater/steam.js +1 -1
  29. package/dist/updater/steam.js.map +1 -1
  30. package/dist/util/brdb.js +84 -2
  31. package/dist/util/brdb.js.map +1 -1
  32. package/dist/webserver/backend/database.js +533 -533
  33. package/dist/webserver/backend/database.js.map +1 -1
  34. package/dist/webserver/backend/index.js +19 -8
  35. package/dist/webserver/backend/index.js.map +1 -1
  36. package/dist/webserver/backend/metrics.js +7 -5
  37. package/dist/webserver/backend/metrics.js.map +1 -1
  38. package/dist/webserver/backend/permissions.js +69 -20
  39. package/dist/webserver/backend/permissions.js.map +1 -1
  40. package/dist/webserver/backend/roleHierarchy.js +106 -0
  41. package/dist/webserver/backend/roleHierarchy.js.map +1 -0
  42. package/dist/webserver/backend/router/player.js +13 -16
  43. package/dist/webserver/backend/router/player.js.map +1 -1
  44. package/dist/webserver/backend/router/role.js +195 -7
  45. package/dist/webserver/backend/router/role.js.map +1 -1
  46. package/dist/webserver/backend/router/server.js +32 -2
  47. package/dist/webserver/backend/router/server.js.map +1 -1
  48. package/dist/webserver/backend/router/session.js +3 -5
  49. package/dist/webserver/backend/router/session.js.map +1 -1
  50. package/dist/webserver/backend/router/user.js +89 -42
  51. package/dist/webserver/backend/router/user.js.map +1 -1
  52. package/dist/webserver/backend/scopes.js +73 -44
  53. package/dist/webserver/backend/scopes.js.map +1 -1
  54. package/dist/webserver/backend/trpc.js +3 -6
  55. package/dist/webserver/backend/trpc.js.map +1 -1
  56. package/doc/permissions.md +83 -12
  57. package/drizzle.config.ts +7 -0
  58. package/drizzle.plugin.config.ts +7 -0
  59. package/package.json +7 -2
  60. package/public/app.html +415 -32
  61. package/public/assets/IconWorld-CmdphDgp.js +2 -0
  62. package/public/assets/IconWorld-CmdphDgp.js.map +1 -0
  63. package/public/assets/IconWorldPlus-DKSZjbqh.js +2 -0
  64. package/public/assets/IconWorldPlus-DKSZjbqh.js.map +1 -0
  65. package/public/assets/app-BiBsxujC.js +7 -0
  66. package/public/assets/app-BiBsxujC.js.map +1 -0
  67. package/public/assets/{auth-CEYiizOt.js → auth-nn5rDnJ9.js} +2 -2
  68. package/public/assets/{auth-CEYiizOt.js.map → auth-nn5rDnJ9.js.map} +1 -1
  69. package/public/assets/favicon-DYc6c67x.ico +0 -0
  70. package/public/assets/{index-DEL-PD_8.js → index-2cgXLhrt.js} +2 -2
  71. package/public/assets/{index-DEL-PD_8.js.map → index-2cgXLhrt.js.map} +1 -1
  72. package/public/assets/index-5EkjXNMi.js +12 -0
  73. package/public/assets/index-5EkjXNMi.js.map +1 -0
  74. package/public/assets/index-B2DCW1zi.js +2 -0
  75. package/public/assets/index-B2DCW1zi.js.map +1 -0
  76. package/public/assets/logo-Dl-LlTow.webp +0 -0
  77. package/public/assets/shared/@components-VkA0DIz6.js +2 -0
  78. package/public/assets/shared/@components-VkA0DIz6.js.map +1 -0
  79. package/public/assets/shared/@hooks-CiJaUL6_.js +2 -0
  80. package/public/assets/shared/@hooks-CiJaUL6_.js.map +1 -0
  81. package/public/assets/shared/@tabler_icons-react-B4130bhe.js +3 -0
  82. package/public/assets/shared/{@tabler_icons-react-DrVFX3uL.js.map → @tabler_icons-react-B4130bhe.js.map} +1 -1
  83. package/public/assets/shared/{wouter-CNISJ5i6.js → wouter-CUC1caxu.js} +2 -2
  84. package/public/assets/shared/wouter-CUC1caxu.js.map +1 -0
  85. package/public/assets/user-XF1UyJMA.js +2 -0
  86. package/public/assets/user-XF1UyJMA.js.map +1 -0
  87. package/public/auth.html +163 -16
  88. package/public/assets/IconWorld-XmQNg6JI.js +0 -2
  89. package/public/assets/IconWorld-XmQNg6JI.js.map +0 -1
  90. package/public/assets/IconWorldPlus-CxGGv2eD.js +0 -2
  91. package/public/assets/IconWorldPlus-CxGGv2eD.js.map +0 -1
  92. package/public/assets/app-DMr_-wik.js +0 -7
  93. package/public/assets/app-DMr_-wik.js.map +0 -1
  94. package/public/assets/index-D_STNela.js +0 -2
  95. package/public/assets/index-D_STNela.js.map +0 -1
  96. package/public/assets/index-Dkh3HiBz.js +0 -4
  97. package/public/assets/index-Dkh3HiBz.js.map +0 -1
  98. package/public/assets/shared/@components-CgsSYMe6.js +0 -2
  99. package/public/assets/shared/@components-CgsSYMe6.js.map +0 -1
  100. package/public/assets/shared/@hooks-DT93N7wd.js +0 -2
  101. package/public/assets/shared/@hooks-DT93N7wd.js.map +0 -1
  102. package/public/assets/shared/@tabler_icons-react-DrVFX3uL.js +0 -3
  103. package/public/assets/shared/wouter-CNISJ5i6.js.map +0 -1
  104. package/public/assets/useHasScope-BOdFktko.js +0 -2
  105. package/public/assets/useHasScope-BOdFktko.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sources":["../../src/omegga/server.ts"],"sourcesContent":["import Logger from '@/logger';\nimport { OmeggaLike, OmeggaPlayer, PluginInterop } from '@/plugin';\nimport {\n BRICKADIA_AUTH_FILES,\n CONFIG_AUTH_DIR,\n CONFIG_HOME,\n CONFIG_SAVED_DIR,\n DATA_PATH,\n} from '@/softconfig';\nimport { VERSION } from '@/version';\nimport { EnvironmentPreset } from '@brickadia/presets';\nimport {\n BRBanList,\n BRPlayerNameCache,\n BRRoleAssignments,\n BRRoleSetup,\n} from '@brickadia/types';\nimport { IConfig } from '@config/types';\nimport { map as mapUtils, pattern, uuid } from '@util';\nimport { copyFiles, mkdir, readWatchedJSON } from '@util/file';\nimport Webserver from '@webserver/backend';\nimport brs, { type ReadSaveObject, type WriteSaveObject } from 'brs-js';\nimport 'colors';\nimport glob from 'glob';\nimport { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';\nimport { basename, join } from 'path';\nimport { AutoRestartConfig } from '..';\nimport commandInjector from './commandInjector';\nimport MATCHERS from './matchers';\nimport Player from './player';\nimport { PluginLoader } from './plugin';\nimport {\n ILogMinigame,\n IMinigameList,\n IOmeggaOptions,\n IPlayerPositions,\n IServerStatus,\n} from './types';\nimport OmeggaWrapper from './wrapper';\n\nconst MISSING_CMD =\n '\"Command not found. Type <color=\\\\\"ffff00\\\\\">/help</> for a list of commands or <color=\\\\\"ffff00\\\\\">/plugins</> for plugin information.\"';\n\n// TODO: safe broadcast parsing\n\nexport default class Omegga extends OmeggaWrapper implements OmeggaLike {\n /** The save counter prevents omegga from saving over the same file */\n _tempCounter = { save: 0, environment: 0 };\n /** The save prefix is prepended to all temporary saves */\n _tempSavePrefix = 'omegga_temp_';\n\n // pluginloader is not private so plugins can potentially add more formats\n pluginLoader: PluginLoader = undefined;\n webserver: Webserver;\n\n verbose: boolean;\n savePath: string;\n worldPath: string;\n presetPath: string;\n configPath: string;\n options: IOmeggaOptions;\n\n version: number;\n\n host?: { id: string; name: string };\n players: OmeggaPlayer[];\n\n started = false;\n starting = false;\n stopping = false;\n crashDetected = false;\n currentMap: string;\n\n getServerStatus: () => Promise<IServerStatus>;\n listMinigames: () => Promise<IMinigameList>;\n getAllPlayerPositions: () => Promise<IPlayerPositions>;\n getMinigames: () => Promise<ILogMinigame[]>;\n\n /**\n * Omegga instance\n */\n constructor(serverPath: string, cfg: IConfig, options: IOmeggaOptions = {}) {\n super(serverPath, cfg);\n this.verbose = Logger.VERBOSE;\n\n Logger.verbose('Running omegga', `v${VERSION}`.green);\n Logger.verbose('Versions', process.versions);\n Logger.verbose('Config', {\n ...cfg,\n credentials: cfg.credentials\n ? Object.fromEntries(\n Object.entries(cfg.credentials).map(([k, v]) => [k, v ? '***' : v]),\n )\n : cfg.credentials,\n server: {\n ...cfg.server,\n ...(cfg.server.password && { password: '***' }),\n ...(cfg.server.steambetaPassword && { steambetaPassword: '***' }),\n ...(cfg.server.launchArgs && {\n launchArgs: cfg.server.launchArgs\n .replace(/-Cookie=\".*?\"/g, '-Cookie=\"<hidden>\"')\n .replace(/-Cookie=\\S+/g, '-Cookie=<hidden>'),\n }),\n },\n });\n\n // inject commands\n Logger.verbose('Setting up command injector');\n commandInjector(this, this.logWrangler);\n\n // launch options (disabling webserver)\n this.options = options;\n const savedDir = cfg.server.savedDir ?? CONFIG_SAVED_DIR;\n\n // path to save files\n this.savePath = join(this.path, DATA_PATH, savedDir, 'Builds');\n this.worldPath = join(this.path, DATA_PATH, savedDir, 'Worlds');\n\n this.presetPath = join(this.path, DATA_PATH, savedDir, 'Presets');\n\n // path to config files\n this.configPath = join(this.path, DATA_PATH, savedDir, 'Server');\n\n // create dir folders\n Logger.verbose('Creating directories');\n mkdir(this.savePath);\n mkdir(this.configPath);\n\n // ignore auth file copy\n if (!options.noauth) {\n Logger.verbose('Copying auth files');\n this.copyAuthFiles();\n }\n\n // create the webserver if it's enabled\n // the web interface provides access to server information while the server is running\n // and lets you view chat logs, disable plugins, etc\n if (!options.noweb) {\n Logger.verbose('Creating webserver');\n this.webserver = new Webserver(cfg.omegga, this);\n }\n\n if (!options.noplugin) {\n Logger.verbose('Creating plugin loader');\n // create the pluginloader\n this.pluginLoader = new PluginLoader(this.path, this);\n }\n\n /** @type {Array<Player>}list of online players */\n this.players = [];\n\n /** host player info `{id: uuid, name: player name}` */\n this.host = undefined;\n\n /** @type {String} current game version - may later be turned into CL#### versions */\n this.version = -1;\n\n /** @type {Boolean} whether server has started */\n this.started = false;\n /** @type {Boolean} whether server is starting up */\n this.starting = false;\n\n /** @type {String} current map */\n this.currentMap = '';\n\n // add all the matchers to the server\n Logger.verbose('Adding matchers');\n for (const matcher of MATCHERS) {\n const { pattern, callback } = matcher(this);\n this.addMatcher(pattern, callback);\n }\n\n process.on('uncaughtException', async err => {\n Logger.verbose('Uncaught exception', err);\n this.emit('error', err);\n\n // publish stop to database\n this.webserver?.database?.addChatLog('server', {}, 'Server error');\n\n try {\n await this.stop();\n } catch (e) {\n Logger.error(e);\n }\n process.exit();\n });\n\n // when brickadia starts, mark the server as started\n this.on('start', ({ map }) => {\n this.started = true;\n this.starting = false;\n this.currentMap = map;\n this.writeln('Chat.MessageForUnknownCommands 0');\n\n this.restoreServer();\n });\n\n // detect engine crash from stderr or stdout\n const crashHandler = (line: string) => {\n if (\n !this.crashDetected &&\n (/Engine crash handling finished; re-raising signal \\d+ for the default handler\\. Good bye\\./.test(\n line,\n ) ||\n /LogCore: === Critical error: ===/.test(line))\n ) {\n Logger.error('Engine crash detected!');\n this.crashDetected = true;\n }\n };\n this.on('err', crashHandler);\n this.on('line', crashHandler);\n\n // when brickadia exits, stop omegga\n this.on('exit', () => {\n this.stop();\n });\n\n // when the process closes, emit the exit signal and stop\n this.on('closed', () => {\n // capture crash state before 'exit' handler triggers stop()\n const wasCrash = this.crashDetected;\n this.crashDetected = false;\n if (this.started) this.emit('exit');\n const doRestart = async () => {\n if (!wasCrash) return;\n try {\n const config = await this.webserver?.database?.getAutoRestartConfig();\n if (config?.crashRestartEnabled) {\n Logger.logp('Restarting server after crash...');\n this.webserver?.database?.addChatLog(\n 'server',\n {},\n 'Server crashed, restarting...',\n );\n await this.start();\n }\n } catch (err) {\n Logger.error('Error restarting after crash', err);\n }\n };\n if (!this.stopping) {\n this.stop().then(doRestart);\n } else {\n // stop() already in progress from 'exit' handler — wait for it to finish\n this.once('server:stopped', () => doRestart());\n }\n });\n\n // detect when the game reports a command does not exist\n this.on('unknownCommand', (name: string, cmd: string) => {\n // if it's not registered to a plugin, send the missing command message\n if (!this.pluginLoader || !this.pluginLoader.isCommand(cmd)) {\n this.whisper(name, MISSING_CMD);\n }\n });\n }\n\n /** attempt to save server state */\n async saveServer(config: AutoRestartConfig) {\n if (config.players && this.players.length > 0) {\n Logger.logp('Getting player positions...');\n const players = await this.getAllPlayerPositions();\n Logger.logp(`Saving ${players.length} player positions...`);\n const data = players\n .filter(p => !p.isDead && p.pos)\n .map(p => ({ position: p.pos, id: p.player.id }));\n if (players.length > 0)\n writeFileSync(\n join(this.path, DATA_PATH, 'omegga_temp_players.json'),\n JSON.stringify(data),\n );\n }\n\n if (config.saveWorld) {\n Logger.logp('Saving world...');\n await this.saveWorld();\n }\n }\n\n async restartServer() {\n if (this.starting || this.stopping) return;\n if (!this.started) return await this.start();\n\n const nextWorld = this.getNextWorld();\n if (nextWorld) {\n Logger.logp('Loading world', nextWorld.file.yellow);\n Logger.verbose('Next world configured from', nextWorld.source.yellow);\n this.loadWorld(nextWorld.file);\n } else {\n this.changeMap(this.currentMap);\n }\n\n const res = await Promise.race([\n // wait for the map to change\n new Promise(resolve =>\n this.once('mapchange', () => resolve('mapchange')),\n ),\n // Timeout after 10 seconds\n new Promise(resolve => setTimeout(() => resolve('timeout'), 10000)),\n ]);\n Logger.verbose('Restart result:', res);\n }\n\n /** attempt to restore the server's state */\n async restoreServer() {\n const tempPlayersFile = join(\n this.path,\n DATA_PATH,\n 'omegga_temp_players.json',\n );\n if (!existsSync(tempPlayersFile)) return;\n\n try {\n Logger.logp('Loading previous player positions...');\n\n // player positions are an array to address multi-clienting\n const players: { position: number[]; id: string }[] = JSON.parse(\n readFileSync(tempPlayersFile).toString(),\n );\n\n // restore player position on join\n const callback = (player: OmeggaPlayer) => {\n const index = players.findIndex(p => p.id === player.id);\n if (index > -1) {\n const { position } = players[index];\n this.writeln(\n `Chat.Command /TP \"${player.name}\" ${position.join(' ')} 0`,\n );\n\n // remove the entry\n players[index] = players[players.length - 1];\n players.pop();\n }\n };\n this.on('join', callback);\n\n let timeout = setTimeout(() => {\n try {\n this.off('join', callback);\n if (existsSync(tempPlayersFile)) unlinkSync(tempPlayersFile);\n } catch (err) {\n Logger.error('Error removing omegga_temp_players.json', err);\n }\n }, 10000);\n this.once('changemap', () => {\n clearTimeout(timeout);\n this.off('join', callback);\n });\n } catch (err) {\n Logger.error('Error restoring previous server state', err);\n }\n }\n\n /**\n * start webserver, load plugins, start the brickadia server\n * this should not be called by a plugin\n */\n //\n async start(): Promise<any> {\n this.starting = true;\n if (this.webserver) await this.webserver.start();\n if (this.pluginLoader) {\n // scan for plugins\n Logger.verbose('Scanning for plugins');\n await this.pluginLoader.scan();\n\n // load the plugins\n Logger.verbose('Loading plugins');\n await this.pluginLoader.reload();\n }\n\n Logger.verbose('Starting Brickadia');\n super.start();\n this.emit('server:starting');\n }\n\n /**\n * unload plugins and stop the server\n * this should not be called by a plugin\n */\n async stop() {\n if (!this.started && !this.starting) {\n Logger.verbose(\"Stop called while server wasn't started or was starting\");\n return;\n }\n\n if (this.stopping) {\n Logger.verbose('Stop called while server was starting');\n return;\n }\n\n this.stopping = true;\n this.emit('server:stopping');\n if (this.pluginLoader) {\n Logger.verbose('Unloading plugins');\n await this.pluginLoader.unload();\n }\n Logger.verbose('Stopping server');\n super.stop();\n\n const res = await Promise.race([\n new Promise(resolve => this.once('exit', () => resolve('exit'))),\n // Timeout after 10 seconds\n new Promise(resolve => setTimeout(() => resolve('timeout'), 10000)),\n ]);\n\n Logger.verbose('Stop result:', res);\n if (this.stopping) this.emit('server:stopped');\n this.stopping = false;\n this.started = false;\n this.starting = false;\n this.players = [];\n }\n\n /**\n * Copies auth files from home config dir\n * this should never be called by a plugin\n */\n copyAuthFiles() {\n const authDir = this.config.server.authDir ?? CONFIG_AUTH_DIR;\n const savedDir = this.config.server.savedDir ?? CONFIG_SAVED_DIR;\n const authPath = join(this.path, DATA_PATH, savedDir, authDir);\n const homeAuthPath = join(\n CONFIG_HOME,\n (savedDir !== CONFIG_SAVED_DIR ? savedDir : '') + authDir,\n );\n\n copyFiles(homeAuthPath, authPath, BRICKADIA_AUTH_FILES);\n }\n\n // TODO: split messages that longer than 512 characters\n // TODO: delete characters that are known to crash the game\n broadcast(...messages: string[]) {\n messages\n .flatMap(m => m.toString().split('\\n'))\n .filter(m => m.length < 512)\n .forEach(m => this.writeln(`Chat.Broadcast ${m}`));\n }\n\n whisper(target: string | OmeggaPlayer, ...messages: string[]) {\n // find the target player\n if (typeof target !== 'object') target = this.getPlayer(target);\n\n // whisper the messages to that player\n messages\n .flatMap(m => m.toString().split('\\n'))\n .filter(m => m.length < 512)\n .forEach(m =>\n this.writeln(\n `Chat.Whisper \"${(target as { name: string }).name}\" ${m}`,\n ),\n );\n }\n\n middlePrint(target: string | OmeggaPlayer, message: string) {\n // find the target player\n if (typeof target !== 'object') target = this.getPlayer(target);\n\n // whisper the messages to that player\n if (message.length > 512) return;\n this.writeln(\n `Chat.StatusMessage \"${(target as { name: string }).name}\" ${message}`,\n );\n }\n\n getPlayers(): {\n id: string;\n name: string;\n displayName: string;\n controller: string;\n state: string;\n }[] {\n return this.players.map(p => ({ ...p }));\n }\n\n getRoleSetup(): BRRoleSetup {\n // Read RoleSetup2, fallback to old RoleSetup if it doesn't exist\n return (readWatchedJSON(join(this.configPath, 'RoleSetup2.json')) ??\n readWatchedJSON(join(this.configPath, 'RoleSetup.json'))) as BRRoleSetup;\n }\n\n getRoleAssignments(): BRRoleAssignments {\n return readWatchedJSON(\n join(this.configPath, 'RoleAssignments.json'),\n ) as BRRoleAssignments;\n }\n\n getBanList(): BRBanList {\n return readWatchedJSON(join(this.configPath, 'BanList.json')) as BRBanList;\n }\n\n getNameCache(): BRPlayerNameCache {\n return readWatchedJSON(\n join(this.configPath, 'PlayerNameCache.json'),\n ) as BRPlayerNameCache;\n }\n\n getPlayer(target: string): OmeggaPlayer {\n return this.players.find(\n p =>\n p.name === target ||\n p.id === target ||\n p.controller === target ||\n p.state === target,\n );\n }\n\n findPlayerByName(name: string): OmeggaPlayer {\n name = name.toLowerCase();\n const exploded = pattern.explode(name);\n return (\n this.players.find(p => p.name === name || p.displayName === name) || // find by exact match\n this.players.find(\n p => p.name.indexOf(name) > -1 || p.displayName.indexOf(name) > -1,\n ) || // find by rough match\n this.players.find(\n p => p.name.match(exploded) || p.displayName.match(exploded),\n ) // find by exploded regex match (ck finds cake, tbp finds TheBlackParrot)\n );\n }\n\n getHostId(): string {\n return this.host?.id ?? '';\n }\n\n saveMinigame(index: number, name: string) {\n this.writeln(`Server.Minigames.SavePreset ${index} \"${name}\"`);\n }\n\n deleteMinigame(index: number) {\n this.writeln(`Server.Minigames.Delete ${index}`);\n }\n\n resetMinigame(index: number) {\n this.writeln(`Server.Minigames.Reset ${index}`);\n }\n\n nextRoundMinigame(index: number) {\n this.writeln(`Server.Minigames.NextRound ${index}`);\n }\n\n loadMinigame(presetName: string, owner = '') {\n this.writeln(\n `Server.Minigames.LoadPreset \"${presetName}\" ${owner ? `\"${owner}\"` : ''}`,\n );\n }\n\n getMinigamePresets(): string[] {\n const presetPath = join(this.presetPath, 'Minigame');\n return existsSync(presetPath)\n ? glob\n .sync(presetPath + '/**/*.bp')\n .map(f => basename(f).replace(/\\.bp$/, ''))\n : [];\n }\n\n resetEnvironment() {\n this.writeln(`Server.Environment.Reset`);\n }\n\n async saveEnvironment(presetName: string): Promise<void> {\n await this.addWatcher(/Environment preset saved.$/, {\n // request the pawn for this player's controller (should only be one)\n exec: () => this.writeln(`Server.Environment.SavePreset \"${presetName}\"`),\n timeoutDelay: 100,\n });\n }\n\n async getEnvironmentData(): Promise<EnvironmentPreset> {\n const saveName =\n this._tempSavePrefix + Date.now() + '_' + this._tempCounter.environment++;\n\n await this.saveEnvironment(saveName);\n const data = this.readEnvironmentData(saveName);\n const file = join(this.presetPath, 'Environment', saveName + '.bp');\n if (existsSync(file)) unlinkSync(file);\n\n return data;\n }\n\n readEnvironmentData(saveName: string): EnvironmentPreset {\n if (typeof saveName !== 'string')\n throw 'expected name argument for readEnvironmentData';\n\n const file = join(this.presetPath, 'Environment', saveName + '.bp');\n try {\n if (existsSync(file)) return JSON.parse(readFileSync(file).toString());\n } catch (err) {\n Logger.verbose('Error parsing save data in readEnvironmentData', err);\n }\n return null;\n }\n\n loadEnvironment(presetName: string) {\n this.writeln(`Server.Environment.LoadPreset ${presetName}`);\n }\n\n loadEnvironmentData(\n preset: EnvironmentPreset | EnvironmentPreset['data']['groups'],\n ) {\n if ('data' in preset) preset = preset.data.groups;\n\n const saveFile =\n this._tempSavePrefix + Date.now() + '_' + this._tempCounter.environment++;\n\n const path = join(this.presetPath, 'Environment', saveFile + '.bp');\n\n writeFileSync(\n path,\n JSON.stringify({\n formatVersion: '1',\n presetVersion: '1',\n type: 'Environment',\n data: {\n groups: {\n ...preset,\n },\n },\n }),\n );\n\n this.loadEnvironment(saveFile);\n\n // this is lazy, but environments should load much faster than builds\n // do, so it's not really worth keeping track of logs for this\n setTimeout(() => unlinkSync(path), 5000);\n }\n\n getEnvironmentPresets(): string[] {\n const presetPath = join(this.presetPath, 'Environment');\n return existsSync(presetPath)\n ? glob\n .sync(presetPath + '/**/*.bp')\n .map(f => basename(f).replace(/\\.bp$/, ''))\n : [];\n }\n\n clearBricks(target: string | { id: string }, quiet = false) {\n // target is a player object, just use that id\n if (typeof target === 'object' && target.id) target = target.id;\n // if the target isn't a uuid already, find the player by name or controller and use that uuid\n else if (typeof target === 'string' && !uuid.match(target)) {\n // only set the target if the player exists\n const player = this.getPlayer(target);\n target = player && player.id;\n }\n\n if (!target) return;\n\n this.writeln(`Bricks.Clear ${target} ${quiet ? 1 : ''}`);\n }\n\n clearRegion(\n region: {\n center: [number, number, number];\n extent: [number, number, number];\n },\n options?: { target: string | OmeggaPlayer },\n ) {\n let target = '';\n\n // target is a player object, just use that id\n if (options && typeof options.target === 'object')\n target = ' ' + options.target.id;\n // if the target isn't a uuid already, find the player by name or controller and use that uuid\n\n if (typeof target === 'string' && !uuid.match(target)) {\n // only set the target if the player exists\n const player = this.getPlayer(target);\n if (player) target = ' ' + player.id;\n }\n\n this.writeln(\n `Bricks.ClearRegion ${region.center.join(' ')} ${region.extent.join(\n ' ',\n )}${target}`,\n );\n }\n\n clearAllBricks(quiet = false) {\n this.writeln(`Bricks.ClearAll ${quiet ? 1 : ''}`);\n }\n\n saveBricks(\n saveName: string,\n region?: {\n center: [number, number, number];\n extent: [number, number, number];\n },\n ) {\n if (!saveName) return;\n\n // add quotes around the filename if it doesn't have them (backwards compat w/ plugins)\n if (!(saveName.startsWith('\"') && saveName.endsWith('\"')))\n saveName = `\"${saveName}\"`;\n\n if (region?.center && region?.extent)\n this.writeln(\n `Bricks.SaveRegion ${saveName} ${region.center.join(\n ' ',\n )} ${region.extent.join(' ')}`,\n );\n else this.writeln(`Bricks.Save ${saveName}`);\n }\n\n async saveBricksAsync(\n saveName: string,\n region?: {\n center: [number, number, number];\n extent: [number, number, number];\n },\n ): Promise<void> {\n if (!saveName) return;\n\n let saveNameClean = saveName;\n // add quotes around the filename if it doesn't have them (backwards compat w/ plugins)\n if (!(saveName.startsWith('\"') && saveName.endsWith('\"')))\n saveNameClean = `\"${saveName}\"`;\n\n const command =\n region?.center && region?.extent\n ? `Bricks.SaveRegion ${saveNameClean} ${region.center.join(\n ' ',\n )} ${region.extent.join(' ')}`\n : `Bricks.Save ${saveNameClean}`;\n\n // wait for the server to save the file\n await this.watchLogChunk(command, /^(LogBrickSerializer|LogTemp): (.+)$/, {\n first: match => match[0].endsWith(saveName + '.brs...'),\n last: match =>\n Boolean(\n match[2].match(\n /Saved .+ bricks and .+ components from .+ owners|Error: No bricks in grid!|Error: No bricks selected to save!/,\n ),\n ),\n afterMatchDelay: 0,\n timeoutDelay: 30000,\n });\n }\n\n loadBricks(\n saveName: string,\n {\n offX = 0,\n offY = 0,\n offZ = 0,\n quiet = false,\n correctPalette = false,\n correctCustom = false,\n } = {},\n ) {\n // add quotes around the filename if it doesn't have them (backwards compat w/ plugins)\n if (!(saveName.startsWith('\"') && saveName.endsWith('\"')))\n saveName = `\"${saveName}\"`;\n\n this.writeln(\n `Bricks.Load ${saveName} ${offX} ${offY} ${offZ} ${quiet ? 1 : 0} ${\n correctPalette ? 1 : 0\n } ${correctCustom ? 1 : 0}`,\n );\n }\n\n loadBricksOnPlayer(\n saveName: string,\n player: string | OmeggaPlayer,\n {\n offX = 0,\n offY = 0,\n offZ = 0,\n correctPalette = false,\n correctCustom = false,\n } = {},\n ) {\n player = typeof player === 'string' ? this.getPlayer(player) : player;\n if (!player) return;\n\n // add quotes around the filename if it doesn't have them (backwards compat w/ plugins)\n if (!(saveName.startsWith('\"') && saveName.endsWith('\"')))\n saveName = `\"${saveName}\"`;\n\n this.writeln(\n `Bricks.LoadTemplate ${saveName} ${offX} ${offY} ${offZ} ${\n correctPalette ? 1 : 0\n } ${correctCustom ? 1 : 0} \"${player.name}\"`,\n );\n }\n\n getSaves(): string[] {\n return existsSync(this.savePath)\n ? glob.sync(this.savePath + '/**/*.brs')\n : [];\n }\n\n getWorlds(): string[] {\n return existsSync(this.worldPath)\n ? glob.sync(this.worldPath + '/**/*.brdb')\n : [];\n }\n\n getSavePath(saveName: string) {\n const file = join(\n this.savePath,\n saveName.endsWith('.brs') ? saveName : saveName + '.brs',\n );\n return existsSync(file) ? file : undefined;\n }\n\n getWorldPath(worldName: string) {\n const file = join(\n this.worldPath,\n worldName.endsWith('.brdb') ? worldName : worldName + '.brdb',\n );\n return existsSync(file) ? file : undefined;\n }\n\n async getWorldRevisions(worldName: string) {\n if (!this.started || this.starting || this.stopping) {\n throw new Error('Server is not started');\n }\n\n worldName = worldName.replace(/\\.brdb$/i, '');\n\n if (!worldName || !this.getWorldPath(worldName)) {\n throw new Error(`World \"${worldName}\" does not exist`);\n }\n\n /*\n LogBRBundleManager: There are 2 revisions\n LogBRBundleManager: Revision 1 - 2001.02.03-04.05.06: Initial Revision\n LogBRBundleManager: Revision 2 - 2001.02.03-04.05.06: Manual Save\n */\n let numRevisions = 0;\n const revisionsRaw = await this.watchLogChunk<RegExpMatchArray>(\n `BR.World.ListRevisions \"${worldName}\"`,\n /^LogBRBundleManager: (There are (?<numRevisions>\\d+) revisions|Revision (?<revision>\\d+) - (?<date>[\\d.-]+): (?<note>.+))$/,\n {\n last: match => Number(match.groups.reverse) === numRevisions,\n first: match => {\n if (match.groups?.numRevisions !== undefined) {\n numRevisions = Number(match.groups.numRevisions);\n return true;\n }\n return false;\n },\n },\n );\n\n if (!revisionsRaw) return [];\n return revisionsRaw\n .map(match => {\n if (match.groups?.numRevisions !== undefined) {\n return null;\n }\n\n const revision = match.groups.revision\n ? Number(match.groups.revision)\n : 0;\n // parse date from YYYY.MM.DD-HH.MM.SS to YYYY-MM-DDTHH:MM:SSZ\n const dateStr = match.groups.date.replace(\n /(\\d{4})\\.(\\d{2})\\.(\\d{2})-(\\d{2})\\.(\\d{2})\\.(\\d{2})/,\n '$1-$2-$3T$4:$5:$6Z',\n );\n const date = new Date(dateStr);\n const note = match.groups.note || '';\n return { index: revision, date, note };\n })\n .filter(Boolean);\n }\n\n async loadWorld(worldName: string): Promise<boolean> {\n worldName = worldName.replace(/\\.brdb$/i, '');\n if (!worldName || !this.getWorldPath(worldName)) return false;\n this.writeln(`BR.World.Load \"${worldName}\"`);\n const res = await Promise.race([\n // wait for the map to change\n new Promise(resolve =>\n this.once('mapchange', () => resolve('mapchange')),\n ),\n // Timeout after 10 seconds\n new Promise(resolve => setTimeout(() => resolve('timeout'), 10000)),\n ]);\n Logger.verbose('LoadWorld', worldName, 'result:', res);\n return res === 'mapchange';\n }\n\n async loadWorldRevision(\n worldName: string,\n revision: number,\n ): Promise<boolean> {\n worldName = worldName.replace(/\\.brdb$/i, '');\n if (!worldName || !this.getWorldPath(worldName)) return false;\n if (typeof revision !== 'number' || revision < 1) {\n throw new Error(`Invalid revision number: ${revision}`);\n }\n this.writeln(`BR.World.LoadRevision \"${worldName}\" ${revision}`);\n const res = await Promise.race([\n // wait for the map to change\n new Promise(resolve =>\n this.once('mapchange', () => resolve('mapchange')),\n ),\n // Timeout after 10 seconds\n new Promise(resolve => setTimeout(() => resolve('timeout'), 10000)),\n ]);\n Logger.verbose('LoadWorld', worldName, 'result:', res);\n return res === 'mapchange';\n }\n\n async saveWorldAs(worldName: string) {\n if (!worldName) return false;\n if (this.stopping || this.starting || !this.started) return false;\n\n if (this.getWorldPath(worldName)) {\n return false;\n }\n worldName = worldName.replace(/\\.brdb$/i, '');\n\n try {\n const match = await this.addWatcher<{ res: boolean }>(\n (_line, match) => {\n if (match?.groups?.generator !== 'LogBRWorldManager') return;\n\n const ok = match.groups.data.match(/^World files saved after /);\n const err =\n !match.groups.data.startsWith(\n 'Error: Failed to capture minigame settings',\n ) &&\n match.groups.data.match(\n /^Error: (World already exists|Failed to create new world)?/,\n );\n return ok ? { res: true } : err ? { res: false } : undefined;\n },\n {\n exec: () => {\n this.writeln(`BR.World.SaveAs \"${worldName}\"`);\n },\n timeoutDelay: 2000,\n },\n );\n return match?.[0]?.['res'] ?? false;\n } catch (err) {\n return false;\n }\n }\n\n async saveWorld(): Promise<boolean> {\n // Don't allow saving while the server is starting or stopping\n if (this.stopping || this.starting || !this.started) return false;\n\n try {\n const match = await this.addWatcher<{ res: boolean }>(\n (_line, match) => {\n if (match?.groups?.generator !== 'LogBRWorldManager') return;\n\n const ok = match.groups.data.match(/^World files saved after /);\n const err =\n !match.groups.data.startsWith(\n 'Error: Failed to capture minigame settings',\n ) &&\n match.groups.data.match(/^Error: (World has not been saved\\.)?/);\n return ok ? { res: true } : err ? { res: false } : undefined;\n },\n {\n exec: () => {\n this.writeln(`BR.World.Save 0`);\n },\n timeoutDelay: 2000,\n },\n );\n return match?.[0]?.['res'] ?? false;\n } catch (err) {\n return false;\n }\n }\n\n async createEmptyWorld(\n worldName: string,\n map: 'Plate' | 'Space' | 'Studio' | 'Peaks' = 'Plate',\n ): Promise<boolean> {\n if (!worldName) return;\n worldName = worldName.replace(/\\.brdb$/i, '');\n\n try {\n const match = await this.addWatcher<{ res: boolean }>(\n (_line, match) => {\n if (match?.groups?.generator !== 'LogBRWorldManager') return;\n\n const ok = match.groups.data.match(/^World files saved after /);\n const err = match.groups.data.match(\n /^Error: (Invalid preset|World already exists|Failed to create new world)?/,\n );\n return ok ? { res: true } : err ? { res: false } : undefined;\n },\n {\n exec: () => {\n this.writeln(`BR.World.CreateEmpty \"${worldName}\" ${map}`);\n },\n timeoutDelay: 2000,\n },\n );\n return match?.[0]?.['res'] ?? false;\n } catch (err) {\n return false;\n }\n }\n\n writeSaveData(saveName: string, saveData: WriteSaveObject) {\n if (typeof saveName !== 'string')\n throw 'expected name argument for writeSaveData';\n\n const file = join(this.savePath, saveName + '.brs');\n if (!file.startsWith(this.savePath))\n throw 'save file not in Saved/Builds directory';\n writeFileSync(file, new Uint8Array(brs.write(saveData)));\n }\n\n readSaveData(saveName: string, nobricks = false): ReadSaveObject {\n if (typeof saveName !== 'string')\n throw 'expected name argument for readSaveData';\n\n const file = this.getSavePath(saveName);\n if (!file || !file.startsWith(this.savePath))\n throw 'save file not in Saved/Builds directory';\n if (file)\n return brs.read(readFileSync(file), {\n preview: false,\n bricks: !nobricks,\n });\n }\n\n async loadSaveData(\n saveData: WriteSaveObject,\n {\n offX = 0,\n offY = 0,\n offZ = 0,\n quiet = false,\n correctPalette = false,\n correctCustom = false,\n } = {},\n ) {\n const saveFile =\n this._tempSavePrefix + Date.now() + '_' + this._tempCounter.save++;\n // write savedata to file\n this.writeSaveData(saveFile, saveData);\n\n // wait for the server to finish reading the save\n await this.watchLogChunk(\n `Bricks.Load \"${saveFile}\" ${offX} ${offY} ${offZ} ${quiet ? 1 : 0} ${\n correctPalette ? 1 : 0\n } ${correctCustom ? 1 : 0}`,\n /^LogBrickSerializer: (.+)$/,\n {\n first: match => match[0].endsWith(saveFile + '.brs...'),\n last: match => Boolean(match[1].match(/Read .+ bricks/)),\n afterMatchDelay: 0,\n timeoutDelay: 30000,\n },\n );\n\n // delete the save file after we're done\n const savePath = this.getSavePath(saveFile);\n if (savePath) {\n unlinkSync(savePath);\n }\n }\n\n async loadSaveDataOnPlayer(\n saveData: WriteSaveObject,\n player: string | OmeggaPlayer,\n {\n offX = 0,\n offY = 0,\n offZ = 0,\n correctPalette = false,\n correctCustom = false,\n } = {},\n ) {\n player = typeof player === 'string' ? this.getPlayer(player) : player;\n if (!player) return;\n\n const saveFile =\n this._tempSavePrefix + Date.now() + '_' + this._tempCounter.save++;\n // write savedata to file\n this.writeSaveData(saveFile, saveData);\n\n // wait for the server to finish reading the save\n await this.watchLogChunk(\n `Bricks.LoadTemplate \"${saveFile}\" ${offX} ${offY} ${offZ} ${\n correctPalette ? 1 : 0\n } ${correctCustom ? 1 : 0} \"${player.name}\"`,\n /^LogBrickSerializer: (.+)$/,\n {\n first: match => match[0].endsWith(saveFile + '.brs...'),\n last: match => Boolean(match[1].match(/Read .+ bricks/)),\n afterMatchDelay: 0,\n timeoutDelay: 30000,\n },\n );\n\n // delete the save file after we're done\n const savePath = this.getSavePath(saveFile);\n if (savePath) {\n unlinkSync(savePath);\n }\n }\n\n async getSaveData(region?: {\n center: [number, number, number];\n extent: [number, number, number];\n }) {\n const saveFile =\n this._tempSavePrefix + Date.now() + '_' + this._tempCounter.save++;\n\n await this.saveBricksAsync(saveFile, region);\n\n // read the save file\n const savePath = this.getSavePath(saveFile);\n if (savePath) {\n // read and parse the save file\n const saveData = brs.read(readFileSync(savePath));\n\n // delete the save file after we're done reading it\n unlinkSync(savePath);\n\n // return the parsed save\n return saveData;\n }\n\n return undefined;\n }\n\n // TODO: switch this to use worlds...\n async changeMap(map: string) {\n if (!map) return;\n\n // ServerTravel requires /Game/Maps/Plate/Plate instead of Plate\n const brName = mapUtils.n2brn(map);\n\n // wait for the server to change maps\n const match = await this.addWatcher(\n /^.*(LogLoad: Took .+ seconds to LoadMap\\((?<map>.+)\\))|(ERROR: The map .+)$/,\n {\n timeoutDelay: 30000,\n exec: () => this.writeln(`ServerTravel ${brName}`),\n },\n );\n const success = !!(\n match &&\n match[0] &&\n match[0].groups &&\n match[0].groups.map\n );\n return success;\n }\n\n async getPlugin(name: string): Promise<PluginInterop> {\n const plugin = this.pluginLoader.plugins.find(p => p.getName() === name);\n\n if (plugin) {\n return {\n name,\n documentation: plugin.getDocumentation(),\n loaded: plugin.isLoaded(),\n emitPlugin: (event: string, ...args: any[]) => {\n return plugin.emitPlugin(event, 'unsafe', args);\n },\n };\n } else {\n return null;\n }\n }\n}\n"],"names":["OmeggaWrapper","Logger","VERSION","commandInjector","CONFIG_SAVED_DIR","join","DATA_PATH","mkdir","Webserver","PluginLoader","MATCHERS","pattern","writeFileSync","existsSync","readFileSync","index","unlinkSync","CONFIG_AUTH_DIR","CONFIG_HOME","copyFiles","BRICKADIA_AUTH_FILES","readWatchedJSON","basename","file","path","uuid","match","mapUtils","plugin"],"mappings":"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"}
1
+ {"version":3,"file":"server.js","sources":["../../src/omegga/server.ts"],"sourcesContent":["import Logger from '@/logger';\nimport { OmeggaLike, OmeggaPlayer, PluginInterop } from '@/plugin';\nimport {\n BRICKADIA_AUTH_FILES,\n CONFIG_AUTH_DIR,\n CONFIG_HOME,\n CONFIG_SAVED_DIR,\n DATA_PATH,\n} from '@/softconfig';\nimport { VERSION } from '@/version';\nimport { EnvironmentPreset } from '@brickadia/presets';\nimport {\n BRBanList,\n BRPlayerNameCache,\n BRRoleAssignments,\n BRRoleSetup,\n} from '@brickadia/types';\nimport { IConfig } from '@config/types';\nimport { map as mapUtils, pattern, uuid } from '@util';\nimport { copyFiles, mkdir, readWatchedJSON } from '@util/file';\nimport Webserver from '@webserver/backend';\nimport brs, { type ReadSaveObject, type WriteSaveObject } from 'brs-js';\nimport 'colors';\nimport glob from 'glob';\nimport { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';\nimport { basename, join } from 'path';\nimport { AutoRestartConfig } from '..';\nimport commandInjector from './commandInjector';\nimport MATCHERS from './matchers';\nimport Player from './player';\nimport { PluginLoader } from './plugin';\nimport {\n ILogMinigame,\n IMinigameList,\n IOmeggaOptions,\n IPlayerPositions,\n IServerStatus,\n} from './types';\nimport OmeggaWrapper from './wrapper';\n\nconst MISSING_CMD =\n '\"Command not found. Type <color=\\\\\"ffff00\\\\\">/help</> for a list of commands or <color=\\\\\"ffff00\\\\\">/plugins</> for plugin information.\"';\n\n// TODO: safe broadcast parsing\n\nexport default class Omegga extends OmeggaWrapper implements OmeggaLike {\n /** The save counter prevents omegga from saving over the same file */\n _tempCounter = { save: 0, environment: 0 };\n /** The save prefix is prepended to all temporary saves */\n _tempSavePrefix = 'omegga_temp_';\n\n // pluginloader is not private so plugins can potentially add more formats\n pluginLoader: PluginLoader = undefined;\n webserver: Webserver;\n\n verbose: boolean;\n savePath: string;\n worldPath: string;\n presetPath: string;\n configPath: string;\n options: IOmeggaOptions;\n\n version: number;\n\n host?: { id: string; name: string };\n players: OmeggaPlayer[];\n\n started = false;\n starting = false;\n stopping = false;\n crashDetected = false;\n currentMap: string;\n\n getServerStatus: () => Promise<IServerStatus>;\n listMinigames: () => Promise<IMinigameList>;\n getAllPlayerPositions: () => Promise<IPlayerPositions>;\n getMinigames: () => Promise<ILogMinigame[]>;\n\n /**\n * Omegga instance\n */\n constructor(serverPath: string, cfg: IConfig, options: IOmeggaOptions = {}) {\n super(serverPath, cfg);\n this.verbose = Logger.VERBOSE;\n\n Logger.verbose('Running omegga', `v${VERSION}`.green);\n Logger.verbose('Versions', process.versions);\n Logger.verbose('Config', {\n ...cfg,\n credentials: cfg.credentials\n ? Object.fromEntries(\n Object.entries(cfg.credentials).map(([k, v]) => [k, v ? '***' : v]),\n )\n : cfg.credentials,\n server: {\n ...cfg.server,\n ...(cfg.server.password && { password: '***' }),\n ...(cfg.server.steambetaPassword && { steambetaPassword: '***' }),\n ...(cfg.server.launchArgs && {\n launchArgs: cfg.server.launchArgs\n .replace(/-Cookie=\".*?\"/g, '-Cookie=\"<hidden>\"')\n .replace(/-Cookie=\\S+/g, '-Cookie=<hidden>'),\n }),\n },\n });\n\n // inject commands\n Logger.verbose('Setting up command injector');\n commandInjector(this, this.logWrangler);\n\n // launch options (disabling webserver)\n this.options = options;\n const savedDir = cfg.server.savedDir ?? CONFIG_SAVED_DIR;\n\n // path to save files\n this.savePath = join(this.path, DATA_PATH, savedDir, 'Builds');\n this.worldPath = join(this.path, DATA_PATH, savedDir, 'Worlds');\n\n this.presetPath = join(this.path, DATA_PATH, savedDir, 'Presets');\n\n // path to config files\n this.configPath = join(this.path, DATA_PATH, savedDir, 'Server');\n\n // create dir folders\n Logger.verbose('Creating directories');\n mkdir(this.savePath);\n mkdir(this.configPath);\n\n // ignore auth file copy\n if (!options.noauth) {\n Logger.verbose('Copying auth files');\n this.copyAuthFiles();\n }\n\n // create the webserver if it's enabled\n // the web interface provides access to server information while the server is running\n // and lets you view chat logs, disable plugins, etc\n if (!options.noweb) {\n Logger.verbose('Creating webserver');\n this.webserver = new Webserver(cfg.omegga, this);\n }\n\n if (!options.noplugin) {\n Logger.verbose('Creating plugin loader');\n this.pluginLoader = new PluginLoader(this.path, this);\n }\n\n /** @type {Array<Player>}list of online players */\n this.players = [];\n\n /** host player info `{id: uuid, name: player name}` */\n this.host = undefined;\n\n /** @type {String} current game version - may later be turned into CL#### versions */\n this.version = -1;\n\n /** @type {Boolean} whether server has started */\n this.started = false;\n /** @type {Boolean} whether server is starting up */\n this.starting = false;\n\n /** @type {String} current map */\n this.currentMap = '';\n\n // add all the matchers to the server\n Logger.verbose('Adding matchers');\n for (const matcher of MATCHERS) {\n const { pattern, callback } = matcher(this);\n this.addMatcher(pattern, callback);\n }\n\n process.on('uncaughtException', async err => {\n Logger.verbose('Uncaught exception', err);\n this.emit('error', err);\n\n // publish stop to database\n this.webserver?.database?.addChatLog('server', {}, 'Server error');\n\n try {\n await this.stop();\n } catch (e) {\n Logger.error(e);\n }\n process.exit();\n });\n\n // when brickadia starts, mark the server as started\n this.on('start', ({ map }) => {\n this.started = true;\n this.starting = false;\n this.currentMap = map;\n this.writeln('Chat.MessageForUnknownCommands 0');\n\n this.restoreServer();\n });\n\n // detect engine crash from stderr or stdout\n const crashHandler = (line: string) => {\n if (\n !this.crashDetected &&\n (/Engine crash handling finished; re-raising signal \\d+ for the default handler\\. Good bye\\./.test(\n line,\n ) ||\n /LogCore: === Critical error: ===/.test(line))\n ) {\n Logger.error('Engine crash detected!');\n this.crashDetected = true;\n }\n };\n this.on('err', crashHandler);\n this.on('line', crashHandler);\n\n // when brickadia exits, stop omegga\n this.on('exit', () => {\n this.stop();\n });\n\n // when the process closes, emit the exit signal and stop\n this.on('closed', () => {\n // capture crash state before 'exit' handler triggers stop()\n const wasCrash = this.crashDetected;\n this.crashDetected = false;\n if (this.started) this.emit('exit');\n const doRestart = async () => {\n if (!wasCrash) return;\n try {\n const config = await this.webserver?.database?.getAutoRestartConfig();\n if (config?.crashRestartEnabled) {\n Logger.logp('Restarting server after crash...');\n this.webserver?.database?.addChatLog(\n 'server',\n {},\n 'Server crashed, restarting...',\n );\n await this.start();\n }\n } catch (err) {\n Logger.error('Error restarting after crash', err);\n }\n };\n if (!this.stopping) {\n this.stop().then(doRestart);\n } else {\n // stop() already in progress from 'exit' handler - wait for it to finish\n this.once('server:stopped', () => doRestart());\n }\n });\n\n // detect when the game reports a command does not exist\n this.on('unknownCommand', (name: string, cmd: string) => {\n // if it's not registered to a plugin, send the missing command message\n if (!this.pluginLoader || !this.pluginLoader.isCommand(cmd)) {\n this.whisper(name, MISSING_CMD);\n }\n });\n }\n\n /** attempt to save server state */\n async saveServer(config: AutoRestartConfig) {\n if (config.players && this.players.length > 0) {\n Logger.logp('Getting player positions...');\n const players = await this.getAllPlayerPositions();\n Logger.logp(`Saving ${players.length} player positions...`);\n const data = players\n .filter(p => !p.isDead && p.pos)\n .map(p => ({ position: p.pos, id: p.player.id }));\n if (players.length > 0)\n writeFileSync(\n join(this.path, DATA_PATH, 'omegga_temp_players.json'),\n JSON.stringify(data),\n );\n }\n\n if (config.saveWorld) {\n Logger.logp('Saving world...');\n await this.saveWorld();\n }\n }\n\n async restartServer() {\n if (this.starting || this.stopping) return;\n if (!this.started) return await this.start();\n\n const nextWorld = this.getNextWorld();\n if (nextWorld) {\n Logger.logp('Loading world', nextWorld.file.yellow);\n Logger.verbose('Next world configured from', nextWorld.source.yellow);\n this.loadWorld(nextWorld.file);\n } else {\n this.changeMap(this.currentMap);\n }\n\n const res = await Promise.race([\n // wait for the map to change\n new Promise(resolve =>\n this.once('mapchange', () => resolve('mapchange')),\n ),\n // Timeout after 10 seconds\n new Promise(resolve => setTimeout(() => resolve('timeout'), 10000)),\n ]);\n Logger.verbose('Restart result:', res);\n }\n\n /** attempt to restore the server's state */\n async restoreServer() {\n const tempPlayersFile = join(\n this.path,\n DATA_PATH,\n 'omegga_temp_players.json',\n );\n if (!existsSync(tempPlayersFile)) return;\n\n try {\n Logger.logp('Loading previous player positions...');\n\n // player positions are an array to address multi-clienting\n const players: { position: number[]; id: string }[] = JSON.parse(\n readFileSync(tempPlayersFile).toString(),\n );\n\n // restore player position on join\n const callback = (player: OmeggaPlayer) => {\n const index = players.findIndex(p => p.id === player.id);\n if (index > -1) {\n const { position } = players[index];\n this.writeln(\n `Chat.Command /TP \"${player.name}\" ${position.join(' ')} 0`,\n );\n\n // remove the entry\n players[index] = players[players.length - 1];\n players.pop();\n }\n };\n this.on('join', callback);\n\n let timeout = setTimeout(() => {\n try {\n this.off('join', callback);\n if (existsSync(tempPlayersFile)) unlinkSync(tempPlayersFile);\n } catch (err) {\n Logger.error('Error removing omegga_temp_players.json', err);\n }\n }, 10000);\n this.once('changemap', () => {\n clearTimeout(timeout);\n this.off('join', callback);\n });\n } catch (err) {\n Logger.error('Error restoring previous server state', err);\n }\n }\n\n /**\n * start webserver, load plugins, start the brickadia server\n * this should not be called by a plugin\n */\n //\n async start(): Promise<any> {\n this.starting = true;\n if (this.webserver) await this.webserver.start();\n if (this.pluginLoader) {\n // scan for plugins\n Logger.verbose('Scanning for plugins');\n await this.pluginLoader.scan();\n\n // load the plugins\n Logger.verbose('Loading plugins');\n await this.pluginLoader.reload();\n }\n\n Logger.verbose('Starting Brickadia');\n super.start();\n this.emit('server:starting');\n }\n\n /**\n * unload plugins and stop the server\n * this should not be called by a plugin\n */\n async stop() {\n if (!this.started && !this.starting) {\n Logger.verbose(\"Stop called while server wasn't started or was starting\");\n return;\n }\n\n if (this.stopping) {\n Logger.verbose('Stop called while server was starting');\n return;\n }\n\n this.stopping = true;\n this.emit('server:stopping');\n if (this.pluginLoader) {\n Logger.verbose('Unloading plugins');\n await this.pluginLoader.unload();\n }\n Logger.verbose('Stopping server');\n super.stop();\n\n const res = await Promise.race([\n new Promise(resolve => this.once('exit', () => resolve('exit'))),\n // Timeout after 10 seconds\n new Promise(resolve => setTimeout(() => resolve('timeout'), 10000)),\n ]);\n\n Logger.verbose('Stop result:', res);\n if (this.stopping) this.emit('server:stopped');\n this.stopping = false;\n this.started = false;\n this.starting = false;\n this.players = [];\n }\n\n /**\n * Copies auth files from home config dir\n * this should never be called by a plugin\n */\n copyAuthFiles() {\n const authDir = this.config.server.authDir ?? CONFIG_AUTH_DIR;\n const savedDir = this.config.server.savedDir ?? CONFIG_SAVED_DIR;\n const authPath = join(this.path, DATA_PATH, savedDir, authDir);\n const homeAuthPath = join(\n CONFIG_HOME,\n (savedDir !== CONFIG_SAVED_DIR ? savedDir : '') + authDir,\n );\n\n copyFiles(homeAuthPath, authPath, BRICKADIA_AUTH_FILES);\n }\n\n // TODO: split messages that longer than 512 characters\n // TODO: delete characters that are known to crash the game\n broadcast(...messages: string[]) {\n messages\n .flatMap(m => m.toString().split('\\n'))\n .filter(m => m.length < 512)\n .forEach(m => this.writeln(`Chat.Broadcast ${m}`));\n }\n\n whisper(target: string | OmeggaPlayer, ...messages: string[]) {\n // find the target player\n if (typeof target !== 'object') target = this.getPlayer(target);\n\n // whisper the messages to that player\n messages\n .flatMap(m => m.toString().split('\\n'))\n .filter(m => m.length < 512)\n .forEach(m =>\n this.writeln(\n `Chat.Whisper \"${(target as { name: string }).name}\" ${m}`,\n ),\n );\n }\n\n middlePrint(target: string | OmeggaPlayer, message: string) {\n // find the target player\n if (typeof target !== 'object') target = this.getPlayer(target);\n\n // whisper the messages to that player\n if (message.length > 512) return;\n this.writeln(\n `Chat.StatusMessage \"${(target as { name: string }).name}\" ${message}`,\n );\n }\n\n getPlayers(): {\n id: string;\n name: string;\n displayName: string;\n controller: string;\n state: string;\n }[] {\n return this.players.map(p => ({ ...p }));\n }\n\n getRoleSetup(): BRRoleSetup {\n // Read RoleSetup2, fallback to old RoleSetup if it doesn't exist\n return (readWatchedJSON(join(this.configPath, 'RoleSetup2.json')) ??\n readWatchedJSON(join(this.configPath, 'RoleSetup.json'))) as BRRoleSetup;\n }\n\n getRoleAssignments(): BRRoleAssignments {\n return readWatchedJSON(\n join(this.configPath, 'RoleAssignments.json'),\n ) as BRRoleAssignments;\n }\n\n getBanList(): BRBanList {\n return readWatchedJSON(join(this.configPath, 'BanList.json')) as BRBanList;\n }\n\n getNameCache(): BRPlayerNameCache {\n return readWatchedJSON(\n join(this.configPath, 'PlayerNameCache.json'),\n ) as BRPlayerNameCache;\n }\n\n getPlayer(target: string): OmeggaPlayer {\n return this.players.find(\n p =>\n p.name === target ||\n p.id === target ||\n p.controller === target ||\n p.state === target,\n );\n }\n\n findPlayerByName(name: string): OmeggaPlayer {\n name = name.toLowerCase();\n const exploded = pattern.explode(name);\n return (\n this.players.find(p => p.name === name || p.displayName === name) || // find by exact match\n this.players.find(\n p => p.name.indexOf(name) > -1 || p.displayName.indexOf(name) > -1,\n ) || // find by rough match\n this.players.find(\n p => p.name.match(exploded) || p.displayName.match(exploded),\n ) // find by exploded regex match (ck finds cake, tbp finds TheBlackParrot)\n );\n }\n\n getHostId(): string {\n return this.host?.id ?? '';\n }\n\n saveMinigame(index: number, name: string) {\n this.writeln(`Server.Minigames.SavePreset ${index} \"${name}\"`);\n }\n\n deleteMinigame(index: number) {\n this.writeln(`Server.Minigames.Delete ${index}`);\n }\n\n resetMinigame(index: number) {\n this.writeln(`Server.Minigames.Reset ${index}`);\n }\n\n nextRoundMinigame(index: number) {\n this.writeln(`Server.Minigames.NextRound ${index}`);\n }\n\n loadMinigame(presetName: string, owner = '') {\n this.writeln(\n `Server.Minigames.LoadPreset \"${presetName}\" ${owner ? `\"${owner}\"` : ''}`,\n );\n }\n\n getMinigamePresets(): string[] {\n const presetPath = join(this.presetPath, 'Minigame');\n return existsSync(presetPath)\n ? glob\n .sync(presetPath + '/**/*.bp')\n .map(f => basename(f).replace(/\\.bp$/, ''))\n : [];\n }\n\n resetEnvironment() {\n this.writeln(`Server.Environment.Reset`);\n }\n\n async saveEnvironment(presetName: string): Promise<void> {\n await this.addWatcher(/Environment preset saved.$/, {\n // request the pawn for this player's controller (should only be one)\n exec: () => this.writeln(`Server.Environment.SavePreset \"${presetName}\"`),\n timeoutDelay: 100,\n });\n }\n\n async getEnvironmentData(): Promise<EnvironmentPreset> {\n const saveName =\n this._tempSavePrefix + Date.now() + '_' + this._tempCounter.environment++;\n\n await this.saveEnvironment(saveName);\n const data = this.readEnvironmentData(saveName);\n const file = join(this.presetPath, 'Environment', saveName + '.bp');\n if (existsSync(file)) unlinkSync(file);\n\n return data;\n }\n\n readEnvironmentData(saveName: string): EnvironmentPreset {\n if (typeof saveName !== 'string')\n throw 'expected name argument for readEnvironmentData';\n\n const file = join(this.presetPath, 'Environment', saveName + '.bp');\n try {\n if (existsSync(file)) return JSON.parse(readFileSync(file).toString());\n } catch (err) {\n Logger.verbose('Error parsing save data in readEnvironmentData', err);\n }\n return null;\n }\n\n loadEnvironment(presetName: string) {\n this.writeln(`Server.Environment.LoadPreset ${presetName}`);\n }\n\n loadEnvironmentData(\n preset: EnvironmentPreset | EnvironmentPreset['data']['groups'],\n ) {\n if ('data' in preset) preset = preset.data.groups;\n\n const saveFile =\n this._tempSavePrefix + Date.now() + '_' + this._tempCounter.environment++;\n\n const path = join(this.presetPath, 'Environment', saveFile + '.bp');\n\n writeFileSync(\n path,\n JSON.stringify({\n formatVersion: '1',\n presetVersion: '1',\n type: 'Environment',\n data: {\n groups: {\n ...preset,\n },\n },\n }),\n );\n\n this.loadEnvironment(saveFile);\n\n // this is lazy, but environments should load much faster than builds\n // do, so it's not really worth keeping track of logs for this\n setTimeout(() => unlinkSync(path), 5000);\n }\n\n getEnvironmentPresets(): string[] {\n const presetPath = join(this.presetPath, 'Environment');\n return existsSync(presetPath)\n ? glob\n .sync(presetPath + '/**/*.bp')\n .map(f => basename(f).replace(/\\.bp$/, ''))\n : [];\n }\n\n clearBricks(target: string | { id: string }, quiet = false) {\n // target is a player object, just use that id\n if (typeof target === 'object' && target.id) target = target.id;\n // if the target isn't a uuid already, find the player by name or controller and use that uuid\n else if (typeof target === 'string' && !uuid.match(target)) {\n // only set the target if the player exists\n const player = this.getPlayer(target);\n target = player && player.id;\n }\n\n if (!target) return;\n\n this.writeln(`Bricks.Clear ${target} ${quiet ? 1 : ''}`);\n }\n\n clearRegion(\n region: {\n center: [number, number, number];\n extent: [number, number, number];\n },\n options?: { target: string | OmeggaPlayer },\n ) {\n let target = '';\n\n // target is a player object, just use that id\n if (options && typeof options.target === 'object')\n target = ' ' + options.target.id;\n // if the target isn't a uuid already, find the player by name or controller and use that uuid\n\n if (typeof target === 'string' && !uuid.match(target)) {\n // only set the target if the player exists\n const player = this.getPlayer(target);\n if (player) target = ' ' + player.id;\n }\n\n this.writeln(\n `Bricks.ClearRegion ${region.center.join(' ')} ${region.extent.join(\n ' ',\n )}${target}`,\n );\n }\n\n clearAllBricks(quiet = false) {\n this.writeln(`Bricks.ClearAll ${quiet ? 1 : ''}`);\n }\n\n saveBricks(\n saveName: string,\n region?: {\n center: [number, number, number];\n extent: [number, number, number];\n },\n ) {\n if (!saveName) return;\n\n // add quotes around the filename if it doesn't have them (backwards compat w/ plugins)\n if (!(saveName.startsWith('\"') && saveName.endsWith('\"')))\n saveName = `\"${saveName}\"`;\n\n if (region?.center && region?.extent)\n this.writeln(\n `Bricks.SaveRegion ${saveName} ${region.center.join(\n ' ',\n )} ${region.extent.join(' ')}`,\n );\n else this.writeln(`Bricks.Save ${saveName}`);\n }\n\n async saveBricksAsync(\n saveName: string,\n region?: {\n center: [number, number, number];\n extent: [number, number, number];\n },\n ): Promise<void> {\n if (!saveName) return;\n\n let saveNameClean = saveName;\n // add quotes around the filename if it doesn't have them (backwards compat w/ plugins)\n if (!(saveName.startsWith('\"') && saveName.endsWith('\"')))\n saveNameClean = `\"${saveName}\"`;\n\n const command =\n region?.center && region?.extent\n ? `Bricks.SaveRegion ${saveNameClean} ${region.center.join(\n ' ',\n )} ${region.extent.join(' ')}`\n : `Bricks.Save ${saveNameClean}`;\n\n // wait for the server to save the file\n await this.watchLogChunk(command, /^(LogBrickSerializer|LogTemp): (.+)$/, {\n first: match => match[0].endsWith(saveName + '.brs...'),\n last: match =>\n Boolean(\n match[2].match(\n /Saved .+ bricks and .+ components from .+ owners|Error: No bricks in grid!|Error: No bricks selected to save!/,\n ),\n ),\n afterMatchDelay: 0,\n timeoutDelay: 30000,\n });\n }\n\n loadBricks(\n saveName: string,\n {\n offX = 0,\n offY = 0,\n offZ = 0,\n quiet = false,\n correctPalette = false,\n correctCustom = false,\n } = {},\n ) {\n // add quotes around the filename if it doesn't have them (backwards compat w/ plugins)\n if (!(saveName.startsWith('\"') && saveName.endsWith('\"')))\n saveName = `\"${saveName}\"`;\n\n this.writeln(\n `Bricks.Load ${saveName} ${offX} ${offY} ${offZ} ${quiet ? 1 : 0} ${\n correctPalette ? 1 : 0\n } ${correctCustom ? 1 : 0}`,\n );\n }\n\n loadBricksOnPlayer(\n saveName: string,\n player: string | OmeggaPlayer,\n {\n offX = 0,\n offY = 0,\n offZ = 0,\n correctPalette = false,\n correctCustom = false,\n } = {},\n ) {\n player = typeof player === 'string' ? this.getPlayer(player) : player;\n if (!player) return;\n\n // add quotes around the filename if it doesn't have them (backwards compat w/ plugins)\n if (!(saveName.startsWith('\"') && saveName.endsWith('\"')))\n saveName = `\"${saveName}\"`;\n\n this.writeln(\n `Bricks.LoadTemplate ${saveName} ${offX} ${offY} ${offZ} ${\n correctPalette ? 1 : 0\n } ${correctCustom ? 1 : 0} \"${player.name}\"`,\n );\n }\n\n getSaves(): string[] {\n return existsSync(this.savePath)\n ? glob.sync(this.savePath + '/**/*.brs')\n : [];\n }\n\n getWorlds(): string[] {\n return existsSync(this.worldPath)\n ? glob.sync(this.worldPath + '/**/*.brdb')\n : [];\n }\n\n getSavePath(saveName: string) {\n const file = join(\n this.savePath,\n saveName.endsWith('.brs') ? saveName : saveName + '.brs',\n );\n return existsSync(file) ? file : undefined;\n }\n\n getWorldPath(worldName: string) {\n const file = join(\n this.worldPath,\n worldName.endsWith('.brdb') ? worldName : worldName + '.brdb',\n );\n return existsSync(file) ? file : undefined;\n }\n\n async getWorldRevisions(worldName: string) {\n if (!this.started || this.starting || this.stopping) {\n throw new Error('Server is not started');\n }\n\n worldName = worldName.replace(/\\.brdb$/i, '');\n\n if (!worldName || !this.getWorldPath(worldName)) {\n throw new Error(`World \"${worldName}\" does not exist`);\n }\n\n /*\n LogBRBundleManager: There are 2 revisions\n LogBRBundleManager: Revision 1 - 2001.02.03-04.05.06: Initial Revision\n LogBRBundleManager: Revision 2 - 2001.02.03-04.05.06: Manual Save\n */\n let numRevisions = 0;\n const revisionsRaw = await this.watchLogChunk<RegExpMatchArray>(\n `BR.World.ListRevisions \"${worldName}\"`,\n /^LogBRBundleManager: (There are (?<numRevisions>\\d+) revisions|Revision (?<revision>\\d+) - (?<date>[\\d.-]+): (?<note>.+))$/,\n {\n last: match => Number(match.groups.reverse) === numRevisions,\n first: match => {\n if (match.groups?.numRevisions !== undefined) {\n numRevisions = Number(match.groups.numRevisions);\n return true;\n }\n return false;\n },\n },\n );\n\n if (!revisionsRaw) return [];\n return revisionsRaw\n .map(match => {\n if (match.groups?.numRevisions !== undefined) {\n return null;\n }\n\n const revision = match.groups.revision\n ? Number(match.groups.revision)\n : 0;\n // parse date from YYYY.MM.DD-HH.MM.SS to YYYY-MM-DDTHH:MM:SSZ\n const dateStr = match.groups.date.replace(\n /(\\d{4})\\.(\\d{2})\\.(\\d{2})-(\\d{2})\\.(\\d{2})\\.(\\d{2})/,\n '$1-$2-$3T$4:$5:$6Z',\n );\n const date = new Date(dateStr);\n const note = match.groups.note || '';\n return { index: revision, date, note };\n })\n .filter(Boolean);\n }\n\n async loadWorld(worldName: string): Promise<boolean> {\n worldName = worldName.replace(/\\.brdb$/i, '');\n if (!worldName || !this.getWorldPath(worldName)) return false;\n this.writeln(`BR.World.Load \"${worldName}\"`);\n const res = await Promise.race([\n // wait for the map to change\n new Promise(resolve =>\n this.once('mapchange', () => resolve('mapchange')),\n ),\n // Timeout after 10 seconds\n new Promise(resolve => setTimeout(() => resolve('timeout'), 10000)),\n ]);\n Logger.verbose('LoadWorld', worldName, 'result:', res);\n return res === 'mapchange';\n }\n\n async loadWorldRevision(\n worldName: string,\n revision: number,\n ): Promise<boolean> {\n worldName = worldName.replace(/\\.brdb$/i, '');\n if (!worldName || !this.getWorldPath(worldName)) return false;\n if (typeof revision !== 'number' || revision < 1) {\n throw new Error(`Invalid revision number: ${revision}`);\n }\n this.writeln(`BR.World.LoadRevision \"${worldName}\" ${revision}`);\n const res = await Promise.race([\n // wait for the map to change\n new Promise(resolve =>\n this.once('mapchange', () => resolve('mapchange')),\n ),\n // Timeout after 10 seconds\n new Promise(resolve => setTimeout(() => resolve('timeout'), 10000)),\n ]);\n Logger.verbose('LoadWorld', worldName, 'result:', res);\n return res === 'mapchange';\n }\n\n async saveWorldAs(worldName: string) {\n if (!worldName) return false;\n if (this.stopping || this.starting || !this.started) return false;\n\n if (this.getWorldPath(worldName)) {\n return false;\n }\n worldName = worldName.replace(/\\.brdb$/i, '');\n\n try {\n const match = await this.addWatcher<{ res: boolean }>(\n (_line, match) => {\n if (match?.groups?.generator !== 'LogBRWorldManager') return;\n\n const ok = match.groups.data.match(/^World files saved after /);\n const err =\n !match.groups.data.startsWith(\n 'Error: Failed to capture minigame settings',\n ) &&\n match.groups.data.match(\n /^Error: (World already exists|Failed to create new world)?/,\n );\n return ok ? { res: true } : err ? { res: false } : undefined;\n },\n {\n exec: () => {\n this.writeln(`BR.World.SaveAs \"${worldName}\"`);\n },\n timeoutDelay: 2000,\n },\n );\n return match?.[0]?.['res'] ?? false;\n } catch (err) {\n return false;\n }\n }\n\n async saveWorld(): Promise<boolean> {\n // Don't allow saving while the server is starting or stopping\n if (this.stopping || this.starting || !this.started) return false;\n\n try {\n const match = await this.addWatcher<{ res: boolean }>(\n (_line, match) => {\n if (match?.groups?.generator !== 'LogBRWorldManager') return;\n\n const ok = match.groups.data.match(/^World files saved after /);\n const err =\n !match.groups.data.startsWith(\n 'Error: Failed to capture minigame settings',\n ) &&\n match.groups.data.match(/^Error: (World has not been saved\\.)?/);\n return ok ? { res: true } : err ? { res: false } : undefined;\n },\n {\n exec: () => {\n this.writeln(`BR.World.Save 0`);\n },\n timeoutDelay: 2000,\n },\n );\n return match?.[0]?.['res'] ?? false;\n } catch (err) {\n return false;\n }\n }\n\n async createEmptyWorld(\n worldName: string,\n map: 'Plate' | 'Space' | 'Studio' | 'Peaks' = 'Plate',\n ): Promise<boolean> {\n if (!worldName) return;\n worldName = worldName.replace(/\\.brdb$/i, '');\n\n try {\n const match = await this.addWatcher<{ res: boolean }>(\n (_line, match) => {\n if (match?.groups?.generator !== 'LogBRWorldManager') return;\n\n const ok = match.groups.data.match(/^World files saved after /);\n const err = match.groups.data.match(\n /^Error: (Invalid preset|World already exists|Failed to create new world)?/,\n );\n return ok ? { res: true } : err ? { res: false } : undefined;\n },\n {\n exec: () => {\n this.writeln(`BR.World.CreateEmpty \"${worldName}\" ${map}`);\n },\n timeoutDelay: 2000,\n },\n );\n return match?.[0]?.['res'] ?? false;\n } catch (err) {\n return false;\n }\n }\n\n writeSaveData(saveName: string, saveData: WriteSaveObject) {\n if (typeof saveName !== 'string')\n throw 'expected name argument for writeSaveData';\n\n const file = join(this.savePath, saveName + '.brs');\n if (!file.startsWith(this.savePath))\n throw 'save file not in Saved/Builds directory';\n writeFileSync(file, new Uint8Array(brs.write(saveData)));\n }\n\n readSaveData(saveName: string, nobricks = false): ReadSaveObject {\n if (typeof saveName !== 'string')\n throw 'expected name argument for readSaveData';\n\n const file = this.getSavePath(saveName);\n if (!file || !file.startsWith(this.savePath))\n throw 'save file not in Saved/Builds directory';\n if (file)\n return brs.read(readFileSync(file), {\n preview: false,\n bricks: !nobricks,\n });\n }\n\n async loadSaveData(\n saveData: WriteSaveObject,\n {\n offX = 0,\n offY = 0,\n offZ = 0,\n quiet = false,\n correctPalette = false,\n correctCustom = false,\n } = {},\n ) {\n const saveFile =\n this._tempSavePrefix + Date.now() + '_' + this._tempCounter.save++;\n // write savedata to file\n this.writeSaveData(saveFile, saveData);\n\n // wait for the server to finish reading the save\n await this.watchLogChunk(\n `Bricks.Load \"${saveFile}\" ${offX} ${offY} ${offZ} ${quiet ? 1 : 0} ${\n correctPalette ? 1 : 0\n } ${correctCustom ? 1 : 0}`,\n /^LogBrickSerializer: (.+)$/,\n {\n first: match => match[0].endsWith(saveFile + '.brs...'),\n last: match => Boolean(match[1].match(/Read .+ bricks/)),\n afterMatchDelay: 0,\n timeoutDelay: 30000,\n },\n );\n\n // delete the save file after we're done\n const savePath = this.getSavePath(saveFile);\n if (savePath) {\n unlinkSync(savePath);\n }\n }\n\n async loadSaveDataOnPlayer(\n saveData: WriteSaveObject,\n player: string | OmeggaPlayer,\n {\n offX = 0,\n offY = 0,\n offZ = 0,\n correctPalette = false,\n correctCustom = false,\n } = {},\n ) {\n player = typeof player === 'string' ? this.getPlayer(player) : player;\n if (!player) return;\n\n const saveFile =\n this._tempSavePrefix + Date.now() + '_' + this._tempCounter.save++;\n // write savedata to file\n this.writeSaveData(saveFile, saveData);\n\n // wait for the server to finish reading the save\n await this.watchLogChunk(\n `Bricks.LoadTemplate \"${saveFile}\" ${offX} ${offY} ${offZ} ${\n correctPalette ? 1 : 0\n } ${correctCustom ? 1 : 0} \"${player.name}\"`,\n /^LogBrickSerializer: (.+)$/,\n {\n first: match => match[0].endsWith(saveFile + '.brs...'),\n last: match => Boolean(match[1].match(/Read .+ bricks/)),\n afterMatchDelay: 0,\n timeoutDelay: 30000,\n },\n );\n\n // delete the save file after we're done\n const savePath = this.getSavePath(saveFile);\n if (savePath) {\n unlinkSync(savePath);\n }\n }\n\n async getSaveData(region?: {\n center: [number, number, number];\n extent: [number, number, number];\n }) {\n const saveFile =\n this._tempSavePrefix + Date.now() + '_' + this._tempCounter.save++;\n\n await this.saveBricksAsync(saveFile, region);\n\n // read the save file\n const savePath = this.getSavePath(saveFile);\n if (savePath) {\n // read and parse the save file\n const saveData = brs.read(readFileSync(savePath));\n\n // delete the save file after we're done reading it\n unlinkSync(savePath);\n\n // return the parsed save\n return saveData;\n }\n\n return undefined;\n }\n\n // TODO: switch this to use worlds...\n async changeMap(map: string) {\n if (!map) return;\n\n // ServerTravel requires /Game/Maps/Plate/Plate instead of Plate\n const brName = mapUtils.n2brn(map);\n\n // wait for the server to change maps\n const match = await this.addWatcher(\n /^.*(LogLoad: Took .+ seconds to LoadMap\\((?<map>.+)\\))|(ERROR: The map .+)$/,\n {\n timeoutDelay: 30000,\n exec: () => this.writeln(`ServerTravel ${brName}`),\n },\n );\n const success = !!(\n match &&\n match[0] &&\n match[0].groups &&\n match[0].groups.map\n );\n return success;\n }\n\n async getPlugin(name: string): Promise<PluginInterop> {\n const plugin = this.pluginLoader.plugins.find(p => p.getName() === name);\n\n if (plugin) {\n return {\n name,\n documentation: plugin.getDocumentation(),\n loaded: plugin.isLoaded(),\n emitPlugin: (event: string, ...args: any[]) => {\n return plugin.emitPlugin(event, 'unsafe', args);\n },\n };\n } else {\n return null;\n }\n }\n}\n"],"names":["OmeggaWrapper","Logger","VERSION","commandInjector","CONFIG_SAVED_DIR","join","DATA_PATH","mkdir","Webserver","PluginLoader","MATCHERS","pattern","writeFileSync","existsSync","readFileSync","index","unlinkSync","CONFIG_AUTH_DIR","CONFIG_HOME","copyFiles","BRICKADIA_AUTH_FILES","readWatchedJSON","basename","file","path","uuid","match","mapUtils","plugin"],"mappings":"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"}
@@ -56,13 +56,9 @@ const DATA_PATH = "./data";
56
56
  const PLUGIN_PATH = "./plugins";
57
57
  const PLUGIN_FILE = "./plugin.json";
58
58
  const PLUGIN_POSTINSTALL = "./setup.sh";
59
- const CHAT_STORE = "chat.db";
60
- const PLAYER_STORE = "players.db";
61
- const PLUGIN_STORE = "plugins.db";
62
- const STATUS_STORE = "status.db";
63
- const USER_STORE = "users.db";
64
- const SERVER_STORE = "store.db";
65
- const SESSION_STORE = "session.db";
59
+ const MAIN_DB = "omegga.db";
60
+ const PLUGINS_DB = "omegga-plugins.db";
61
+ const SESSIONS_DB = "omegga-sessions.db";
66
62
  const ACTIVE_WORLD_FILE = "active_world";
67
63
  const WEB_CERTS_DATA = "web_certs.json";
68
64
  const WEB_SESSION_TOKEN = "web_session_token";
@@ -83,13 +79,9 @@ const soft = {
83
79
  PLUGIN_PATH,
84
80
  PLUGIN_FILE,
85
81
  PLUGIN_POSTINSTALL,
86
- CHAT_STORE,
87
- PLAYER_STORE,
88
- PLUGIN_STORE,
89
- STATUS_STORE,
90
- USER_STORE,
91
- SERVER_STORE,
92
- SESSION_STORE,
82
+ MAIN_DB,
83
+ PLUGINS_DB,
84
+ SESSIONS_DB,
93
85
  WEB_CERTS_DATA,
94
86
  WEB_SESSION_TOKEN,
95
87
  METRIC_HEARTBEAT_INTERVAL,
@@ -107,7 +99,6 @@ const soft = {
107
99
  exports.ACTIVE_WORLD_FILE = ACTIVE_WORLD_FILE;
108
100
  exports.BRICKADIA_AUTH_FILES = BRICKADIA_AUTH_FILES;
109
101
  exports.BRICKADIA_INSTALLS = BRICKADIA_INSTALLS;
110
- exports.CHAT_STORE = CHAT_STORE;
111
102
  exports.CONFIG_AUTH_DIR = CONFIG_AUTH_DIR;
112
103
  exports.CONFIG_FILENAMES = CONFIG_FILENAMES;
113
104
  exports.CONFIG_HOME = CONFIG_HOME;
@@ -118,21 +109,18 @@ exports.DEFAULT_STEAM_APP_ID = DEFAULT_STEAM_APP_ID;
118
109
  exports.GAME_BIN_PATH = GAME_BIN_PATH;
119
110
  exports.GLOBAL_TOKEN = GLOBAL_TOKEN;
120
111
  exports.LOCAL_LAUNCHER = LOCAL_LAUNCHER;
112
+ exports.MAIN_DB = MAIN_DB;
121
113
  exports.METRIC_EMPTIES_BEFORE_PAUSE = METRIC_EMPTIES_BEFORE_PAUSE;
122
114
  exports.METRIC_HEARTBEAT_INTERVAL = METRIC_HEARTBEAT_INTERVAL;
123
- exports.PLAYER_STORE = PLAYER_STORE;
115
+ exports.PLUGINS_DB = PLUGINS_DB;
124
116
  exports.PLUGIN_FILE = PLUGIN_FILE;
125
117
  exports.PLUGIN_PATH = PLUGIN_PATH;
126
118
  exports.PLUGIN_POSTINSTALL = PLUGIN_POSTINSTALL;
127
- exports.PLUGIN_STORE = PLUGIN_STORE;
128
119
  exports.PROJECT_NAME = PROJECT_NAME;
129
- exports.SERVER_STORE = SERVER_STORE;
130
- exports.SESSION_STORE = SESSION_STORE;
131
- exports.STATUS_STORE = STATUS_STORE;
120
+ exports.SESSIONS_DB = SESSIONS_DB;
132
121
  exports.STEAMCMD_PATH = STEAMCMD_PATH;
133
122
  exports.STEAM_DIR = STEAM_DIR;
134
123
  exports.TEMP_DIR_NAME = TEMP_DIR_NAME;
135
- exports.USER_STORE = USER_STORE;
136
124
  exports.WEB_CERTS_DATA = WEB_CERTS_DATA;
137
125
  exports.WEB_SESSION_TOKEN = WEB_SESSION_TOKEN;
138
126
  exports.default = soft;
@@ -1 +1 @@
1
- {"version":3,"file":"softconfig.js","sources":["../src/softconfig.ts"],"sourcesContent":["import os from 'os';\nimport path from 'path';\n\n// home directory for omegga config\nexport const PROJECT_NAME = 'omegga';\nexport const CONFIG_HOME = path.join(os.homedir(), '.config/' + PROJECT_NAME);\nexport const GLOBAL_TOKEN = path.join(CONFIG_HOME, 'global_auth_token');\n\nexport const DEFAULT_PORT = 8080;\n\n// filenames that omegga searches for\n// extensions are added based on the available config formats\nexport const CONFIG_FILENAMES = [\n 'omegga-config',\n 'omegga',\n '.omegga-config',\n '.omegga',\n];\n\nexport const BRICKADIA_INSTALLS = path.join(\n os.homedir(),\n '.local/share/brickadia-launcher/brickadia-installs',\n);\n\nexport const STEAM_DIR = path.join(CONFIG_HOME, 'steam');\nexport const STEAMCMD_PATH = path.join(STEAM_DIR, 'steamcmd.sh');\nexport const DEFAULT_STEAM_APP_ID = '3017590'; // Brickadia app ID\nexport function getAppId() {\n return process.env.STEAM_APP_ID ?? DEFAULT_STEAM_APP_ID;\n}\nexport const GAME_BIN_PATH = 'Binaries/Linux/BrickadiaServer-Linux-Shipping';\nexport const LOCAL_LAUNCHER = path.join(\n CONFIG_HOME,\n 'launcher/brickadia-launcher/main-brickadia-launcher',\n);\n\nexport const getOverrideGameDir = () => process.env.BRICKADIA_DIR;\nexport function getOverrideGameBinary() {\n const overrideDir = getOverrideGameDir();\n if (!overrideDir) return null;\n return path.join(overrideDir, GAME_BIN_PATH);\n}\nexport function getSteamGameDir() {\n let GAME_DIR = 'Brickadia';\n if (process.env.STEAM_GAME_DIR) GAME_DIR = process.env.STEAM_GAME_DIR;\n return process.env.STEAM_GAME_DIR ?? GAME_DIR;\n}\nexport function getSteamInstallDir() {\n let INSTALL_DIR = path.join(CONFIG_HOME, 'steam_installs');\n if (process.env.STEAM_INSTALLS_DIR)\n INSTALL_DIR = process.env.STEAM_INSTALLS_DIR;\n return INSTALL_DIR;\n}\n\n// path to auth files\nexport const CONFIG_AUTH_DIR = 'Auth';\nexport const CONFIG_SAVED_DIR = 'Saved';\n// files in Brickadia/Saved/Auth\nexport const BRICKADIA_AUTH_FILES = [\n 'OfflinePayload.bin',\n 'OfflineSignature.bin',\n 'SessionToken.bin',\n];\n\n// temporary install for generating auth files\nexport const TEMP_DIR_NAME = '.omegga-temp-data';\n\n// path to certain info folders\nexport const DATA_PATH = './data';\nexport const PLUGIN_PATH = './plugins';\n\n// plugin data\nexport const PLUGIN_FILE = './plugin.json';\n// post install file\nexport const PLUGIN_POSTINSTALL = './setup.sh';\n\n// databases\nexport const CHAT_STORE = 'chat.db';\nexport const PLAYER_STORE = 'players.db';\nexport const PLUGIN_STORE = 'plugins.db';\nexport const STATUS_STORE = 'status.db';\nexport const USER_STORE = 'users.db';\nexport const SERVER_STORE = 'store.db';\nexport const SESSION_STORE = 'session.db';\nexport const ACTIVE_WORLD_FILE = 'active_world';\n\n// website config\nexport const WEB_CERTS_DATA = 'web_certs.json';\nexport const WEB_SESSION_TOKEN = 'web_session_token';\n\n// how often server status is requested in a heartbeat\nexport const METRIC_HEARTBEAT_INTERVAL = 60 * 1000;\n// the number of empty server statuses before metric logging is paused\nexport const METRIC_EMPTIES_BEFORE_PAUSE = 3;\n\n// rexport as default\nexport default {\n PROJECT_NAME,\n CONFIG_HOME,\n DEFAULT_PORT,\n CONFIG_FILENAMES,\n BRICKADIA_INSTALLS,\n LOCAL_LAUNCHER,\n CONFIG_AUTH_DIR,\n CONFIG_SAVED_DIR,\n BRICKADIA_AUTH_FILES,\n TEMP_DIR_NAME,\n DATA_PATH,\n PLUGIN_PATH,\n PLUGIN_FILE,\n PLUGIN_POSTINSTALL,\n CHAT_STORE,\n PLAYER_STORE,\n PLUGIN_STORE,\n STATUS_STORE,\n USER_STORE,\n SERVER_STORE,\n SESSION_STORE,\n WEB_CERTS_DATA,\n WEB_SESSION_TOKEN,\n METRIC_HEARTBEAT_INTERVAL,\n METRIC_EMPTIES_BEFORE_PAUSE,\n STEAM_DIR,\n STEAMCMD_PATH,\n STEAM_BRICKADIA_PATH: GAME_BIN_PATH,\n GLOBAL_TOKEN,\n getOverrideGameDir,\n getOverrideGameBinary,\n getSteamGameDir,\n getSteamInstallDir,\n getAppId,\n};\n"],"names":[],"mappings":";;;;AAIO,MAAM,eAAe;AACrB,MAAM,cAAc,KAAK,KAAK,GAAG,QAAA,GAAW,aAAa,YAAY;AACrE,MAAM,eAAe,KAAK,KAAK,aAAa,mBAAmB;AAE/D,MAAM,eAAe;AAIrB,MAAM,mBAAmB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,MAAM,qBAAqB,KAAK;AAAA,EACrC,GAAG,QAAA;AAAA,EACH;AACF;AAEO,MAAM,YAAY,KAAK,KAAK,aAAa,OAAO;AAChD,MAAM,gBAAgB,KAAK,KAAK,WAAW,aAAa;AACxD,MAAM,uBAAuB;AAC7B,SAAS,WAAW;AACzB,SAAO,QAAQ,IAAI,gBAAgB;AACrC;AACO,MAAM,gBAAgB;AACtB,MAAM,iBAAiB,KAAK;AAAA,EACjC;AAAA,EACA;AACF;AAEO,MAAM,qBAAqB,MAAM,QAAQ,IAAI;AAC7C,SAAS,wBAAwB;AACtC,QAAM,cAAc,mBAAA;AACpB,MAAI,CAAC,YAAa,QAAO;AACzB,SAAO,KAAK,KAAK,aAAa,aAAa;AAC7C;AACO,SAAS,kBAAkB;AAChC,MAAI,WAAW;AACf,MAAI,QAAQ,IAAI,eAAgB,YAAW,QAAQ,IAAI;AACvD,SAAO,QAAQ,IAAI,kBAAkB;AACvC;AACO,SAAS,qBAAqB;AACnC,MAAI,cAAc,KAAK,KAAK,aAAa,gBAAgB;AACzD,MAAI,QAAQ,IAAI;AACd,kBAAc,QAAQ,IAAI;AAC5B,SAAO;AACT;AAGO,MAAM,kBAAkB;AACxB,MAAM,mBAAmB;AAEzB,MAAM,uBAAuB;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AACF;AAGO,MAAM,gBAAgB;AAGtB,MAAM,YAAY;AAClB,MAAM,cAAc;AAGpB,MAAM,cAAc;AAEpB,MAAM,qBAAqB;AAG3B,MAAM,aAAa;AACnB,MAAM,eAAe;AACrB,MAAM,eAAe;AACrB,MAAM,eAAe;AACrB,MAAM,aAAa;AACnB,MAAM,eAAe;AACrB,MAAM,gBAAgB;AACtB,MAAM,oBAAoB;AAG1B,MAAM,iBAAiB;AACvB,MAAM,oBAAoB;AAG1B,MAAM,4BAA4B,KAAK;AAEvC,MAAM,8BAA8B;AAG3C,MAAA,OAAe;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,sBAAsB;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"softconfig.js","sources":["../src/softconfig.ts"],"sourcesContent":["import os from 'os';\nimport path from 'path';\n\n// home directory for omegga config\nexport const PROJECT_NAME = 'omegga';\nexport const CONFIG_HOME = path.join(os.homedir(), '.config/' + PROJECT_NAME);\nexport const GLOBAL_TOKEN = path.join(CONFIG_HOME, 'global_auth_token');\n\nexport const DEFAULT_PORT = 8080;\n\n// filenames that omegga searches for\n// extensions are added based on the available config formats\nexport const CONFIG_FILENAMES = [\n 'omegga-config',\n 'omegga',\n '.omegga-config',\n '.omegga',\n];\n\nexport const BRICKADIA_INSTALLS = path.join(\n os.homedir(),\n '.local/share/brickadia-launcher/brickadia-installs',\n);\n\nexport const STEAM_DIR = path.join(CONFIG_HOME, 'steam');\nexport const STEAMCMD_PATH = path.join(STEAM_DIR, 'steamcmd.sh');\nexport const DEFAULT_STEAM_APP_ID = '3017590'; // Brickadia app ID\nexport function getAppId() {\n return process.env.STEAM_APP_ID ?? DEFAULT_STEAM_APP_ID;\n}\nexport const GAME_BIN_PATH = 'Binaries/Linux/BrickadiaServer-Linux-Shipping';\nexport const LOCAL_LAUNCHER = path.join(\n CONFIG_HOME,\n 'launcher/brickadia-launcher/main-brickadia-launcher',\n);\n\nexport const getOverrideGameDir = () => process.env.BRICKADIA_DIR;\nexport function getOverrideGameBinary() {\n const overrideDir = getOverrideGameDir();\n if (!overrideDir) return null;\n return path.join(overrideDir, GAME_BIN_PATH);\n}\nexport function getSteamGameDir() {\n let GAME_DIR = 'Brickadia';\n if (process.env.STEAM_GAME_DIR) GAME_DIR = process.env.STEAM_GAME_DIR;\n return process.env.STEAM_GAME_DIR ?? GAME_DIR;\n}\nexport function getSteamInstallDir() {\n let INSTALL_DIR = path.join(CONFIG_HOME, 'steam_installs');\n if (process.env.STEAM_INSTALLS_DIR)\n INSTALL_DIR = process.env.STEAM_INSTALLS_DIR;\n return INSTALL_DIR;\n}\n\n// path to auth files\nexport const CONFIG_AUTH_DIR = 'Auth';\nexport const CONFIG_SAVED_DIR = 'Saved';\n// files in Brickadia/Saved/Auth\nexport const BRICKADIA_AUTH_FILES = [\n 'OfflinePayload.bin',\n 'OfflineSignature.bin',\n 'SessionToken.bin',\n];\n\n// temporary install for generating auth files\nexport const TEMP_DIR_NAME = '.omegga-temp-data';\n\n// path to certain info folders\nexport const DATA_PATH = './data';\nexport const PLUGIN_PATH = './plugins';\n\n// plugin data\nexport const PLUGIN_FILE = './plugin.json';\n// post install file\nexport const PLUGIN_POSTINSTALL = './setup.sh';\n\n// sqlite databases\n// (legacy nedb filenames live in db/nedbImport.ts, their only consumer)\nexport const MAIN_DB = 'omegga.db';\nexport const PLUGINS_DB = 'omegga-plugins.db';\nexport const SESSIONS_DB = 'omegga-sessions.db';\n\nexport const ACTIVE_WORLD_FILE = 'active_world';\n\n// website config\nexport const WEB_CERTS_DATA = 'web_certs.json';\nexport const WEB_SESSION_TOKEN = 'web_session_token';\n\n// how often server status is requested in a heartbeat\nexport const METRIC_HEARTBEAT_INTERVAL = 60 * 1000;\n// the number of empty server statuses before metric logging is paused\nexport const METRIC_EMPTIES_BEFORE_PAUSE = 3;\n\n// rexport as default\nexport default {\n PROJECT_NAME,\n CONFIG_HOME,\n DEFAULT_PORT,\n CONFIG_FILENAMES,\n BRICKADIA_INSTALLS,\n LOCAL_LAUNCHER,\n CONFIG_AUTH_DIR,\n CONFIG_SAVED_DIR,\n BRICKADIA_AUTH_FILES,\n TEMP_DIR_NAME,\n DATA_PATH,\n PLUGIN_PATH,\n PLUGIN_FILE,\n PLUGIN_POSTINSTALL,\n MAIN_DB,\n PLUGINS_DB,\n SESSIONS_DB,\n WEB_CERTS_DATA,\n WEB_SESSION_TOKEN,\n METRIC_HEARTBEAT_INTERVAL,\n METRIC_EMPTIES_BEFORE_PAUSE,\n STEAM_DIR,\n STEAMCMD_PATH,\n STEAM_BRICKADIA_PATH: GAME_BIN_PATH,\n GLOBAL_TOKEN,\n getOverrideGameDir,\n getOverrideGameBinary,\n getSteamGameDir,\n getSteamInstallDir,\n getAppId,\n};\n"],"names":[],"mappings":";;;;AAIO,MAAM,eAAe;AACrB,MAAM,cAAc,KAAK,KAAK,GAAG,QAAA,GAAW,aAAa,YAAY;AACrE,MAAM,eAAe,KAAK,KAAK,aAAa,mBAAmB;AAE/D,MAAM,eAAe;AAIrB,MAAM,mBAAmB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,MAAM,qBAAqB,KAAK;AAAA,EACrC,GAAG,QAAA;AAAA,EACH;AACF;AAEO,MAAM,YAAY,KAAK,KAAK,aAAa,OAAO;AAChD,MAAM,gBAAgB,KAAK,KAAK,WAAW,aAAa;AACxD,MAAM,uBAAuB;AAC7B,SAAS,WAAW;AACzB,SAAO,QAAQ,IAAI,gBAAgB;AACrC;AACO,MAAM,gBAAgB;AACtB,MAAM,iBAAiB,KAAK;AAAA,EACjC;AAAA,EACA;AACF;AAEO,MAAM,qBAAqB,MAAM,QAAQ,IAAI;AAC7C,SAAS,wBAAwB;AACtC,QAAM,cAAc,mBAAA;AACpB,MAAI,CAAC,YAAa,QAAO;AACzB,SAAO,KAAK,KAAK,aAAa,aAAa;AAC7C;AACO,SAAS,kBAAkB;AAChC,MAAI,WAAW;AACf,MAAI,QAAQ,IAAI,eAAgB,YAAW,QAAQ,IAAI;AACvD,SAAO,QAAQ,IAAI,kBAAkB;AACvC;AACO,SAAS,qBAAqB;AACnC,MAAI,cAAc,KAAK,KAAK,aAAa,gBAAgB;AACzD,MAAI,QAAQ,IAAI;AACd,kBAAc,QAAQ,IAAI;AAC5B,SAAO;AACT;AAGO,MAAM,kBAAkB;AACxB,MAAM,mBAAmB;AAEzB,MAAM,uBAAuB;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AACF;AAGO,MAAM,gBAAgB;AAGtB,MAAM,YAAY;AAClB,MAAM,cAAc;AAGpB,MAAM,cAAc;AAEpB,MAAM,qBAAqB;AAI3B,MAAM,UAAU;AAChB,MAAM,aAAa;AACnB,MAAM,cAAc;AAEpB,MAAM,oBAAoB;AAG1B,MAAM,iBAAiB;AACvB,MAAM,oBAAoB;AAG1B,MAAM,4BAA4B,KAAK;AAEvC,MAAM,8BAA8B;AAG3C,MAAA,OAAe;AAAA,EACb;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,sBAAsB;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -105,7 +105,7 @@ async function steamcmdDownloadGame({
105
105
  } catch (err) {
106
106
  if (hasCredentials && err instanceof Error && "status" in err && err.status === 5) {
107
107
  logger.default.warnp(
108
- "Steam login failed — this may require Steam Guard authentication."
108
+ "Steam login failed. This may require Steam Guard authentication."
109
109
  );
110
110
  logger.default.warnp("Attempting interactive login...");
111
111
  if (await steamcmdInteractiveLogin()) {
@@ -1 +1 @@
1
- {"version":3,"file":"steam.js","sources":["../../src/updater/steam.ts"],"sourcesContent":["import 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}: {\n steambeta?: string;\n steambetaPassword?: string;\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 try {\n execSync(cmd, { stdio: 'inherit' });\n } catch (err) {\n if (\n hasCredentials &&\n err instanceof Error &&\n 'status' in err &&\n (err as NodeJS.ErrnoException & { status: number }).status === 5\n ) {\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 throw retryErr;\n }\n }\n\n Logger.errorp(\n 'Interactive login failed. Run',\n 'omegga steamlogin'.yellow,\n 'to authenticate manually.',\n );\n }\n\n handleSteamError(err);\n throw err;\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 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":["import 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}: {\n steambeta?: string;\n steambetaPassword?: string;\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 try {\n execSync(cmd, { stdio: 'inherit' });\n } catch (err) {\n if (\n hasCredentials &&\n err instanceof Error &&\n 'status' in err &&\n (err as NodeJS.ErrnoException & { status: number }).status === 5\n ) {\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 throw retryErr;\n }\n }\n\n Logger.errorp(\n 'Interactive login failed. Run',\n 'omegga steamlogin'.yellow,\n 'to authenticate manually.',\n );\n }\n\n handleSteamError(err);\n throw err;\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 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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"}
package/dist/util/brdb.js CHANGED
@@ -236,6 +236,84 @@ function mpInt(buf, offset) {
236
236
  }
237
237
  throw new Error(`Expected integer format, got ${marker}`);
238
238
  }
239
+ function skipValue(buf, offset) {
240
+ const [kind, val] = mpMarker(buf[offset]);
241
+ offset++;
242
+ switch (kind) {
243
+ case "fixpos":
244
+ case "fixneg":
245
+ case "nil":
246
+ case "true":
247
+ case "false":
248
+ return offset;
249
+ case "uint8":
250
+ case "int8":
251
+ return offset + 1;
252
+ case "uint16":
253
+ case "int16":
254
+ return offset + 2;
255
+ case "uint32":
256
+ case "int32":
257
+ case "float32":
258
+ return offset + 4;
259
+ case "uint64":
260
+ case "int64":
261
+ case "float64":
262
+ return offset + 8;
263
+ case "fixstr":
264
+ return offset + val;
265
+ case "str8":
266
+ case "bin8":
267
+ return offset + 1 + buf.readUInt8(offset);
268
+ case "str16":
269
+ case "bin16":
270
+ return offset + 2 + buf.readUInt16BE(offset);
271
+ case "str32":
272
+ case "bin32":
273
+ return offset + 4 + buf.readUInt32BE(offset);
274
+ case "fixarray":
275
+ for (let i = 0; i < val; i++) offset = skipValue(buf, offset);
276
+ return offset;
277
+ case "array16": {
278
+ const len = buf.readUInt16BE(offset);
279
+ offset += 2;
280
+ for (let i = 0; i < len; i++) offset = skipValue(buf, offset);
281
+ return offset;
282
+ }
283
+ case "array32": {
284
+ const len = buf.readUInt32BE(offset);
285
+ offset += 4;
286
+ for (let i = 0; i < len; i++) offset = skipValue(buf, offset);
287
+ return offset;
288
+ }
289
+ case "fixmap":
290
+ for (let i = 0; i < val; i++) {
291
+ offset = skipValue(buf, offset);
292
+ offset = skipValue(buf, offset);
293
+ }
294
+ return offset;
295
+ case "map16": {
296
+ const len = buf.readUInt16BE(offset);
297
+ offset += 2;
298
+ for (let i = 0; i < len; i++) {
299
+ offset = skipValue(buf, offset);
300
+ offset = skipValue(buf, offset);
301
+ }
302
+ return offset;
303
+ }
304
+ case "map32": {
305
+ const len = buf.readUInt32BE(offset);
306
+ offset += 4;
307
+ for (let i = 0; i < len; i++) {
308
+ offset = skipValue(buf, offset);
309
+ offset = skipValue(buf, offset);
310
+ }
311
+ return offset;
312
+ }
313
+ default:
314
+ throw new Error(`Cannot skip msgpack value of kind ${kind}`);
315
+ }
316
+ }
239
317
  function readBrdbSchema(buf) {
240
318
  const enums = {};
241
319
  const structs = {};
@@ -274,11 +352,12 @@ function readBrdbSchema(buf) {
274
352
  return len;
275
353
  };
276
354
  const header = nextMarker();
277
- if (header[0] !== "fixarray" && header[1] !== 2) {
355
+ if (header[0] !== "fixarray" || header[1] !== 2 && header[1] !== 3) {
278
356
  throw new Error(
279
- `Expected schema header to be fixarray of length 2, got ${header[0]} ${header[1]}`
357
+ `Expected schema header to be a fixarray of length 2 or 3, got ${header[0]} ${header[1]}`
280
358
  );
281
359
  }
360
+ const hasVariants = header[1] === 3;
282
361
  const numEnums = mapLen("enums");
283
362
  for (let i = 0; i < numEnums; i++) {
284
363
  let name;
@@ -294,6 +373,9 @@ function readBrdbSchema(buf) {
294
373
  enums[name][value] = key;
295
374
  }
296
375
  }
376
+ if (hasVariants) {
377
+ offset = skipValue(buf, offset);
378
+ }
297
379
  const numStructs = mapLen("structs");
298
380
  for (let i = 0; i < numStructs; i++) {
299
381
  let name;