omegga 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +3 -1
- package/CHANGELOG.md +10 -0
- package/dist/omegga/commandInjector.js +110 -18
- package/dist/omegga/commandInjector.js.map +1 -1
- package/dist/omegga/commands.js +620 -0
- package/dist/omegga/commands.js.map +1 -0
- package/dist/omegga/player.js +21 -10
- package/dist/omegga/player.js.map +1 -1
- package/dist/omegga/plugin/plugin_node_safe/proxyOmegga.js +15 -0
- package/dist/omegga/plugin/plugin_node_safe/proxyOmegga.js.map +1 -1
- package/dist/omegga/server.js +54 -30
- package/dist/omegga/server.js.map +1 -1
- package/dist/omegga/wrapper.js +4 -1
- package/dist/omegga/wrapper.js.map +1 -1
- package/dist/plugin.js.map +1 -1
- package/dist/updater/steam.js +42 -24
- package/dist/updater/steam.js.map +1 -1
- package/dist/webserver/backend/metrics.js +2 -1
- package/dist/webserver/backend/metrics.js.map +1 -1
- package/dist/webserver/backend/router/player.js +8 -4
- package/dist/webserver/backend/router/player.js.map +1 -1
- package/dist/worker.js +29 -2
- package/dist/worker.js.map +1 -1
- package/package.json +2 -2
- package/public/app.html +12 -3
- package/public/assets/{app-BiBsxujC.js → app-CHjgCOH4.js} +2 -2
- package/public/assets/app-CHjgCOH4.js.map +1 -0
- package/public/auth.html +1 -1
- package/templates/safe-ts/omegga.d.ts +350 -3
- package/public/assets/app-BiBsxujC.js.map +0 -1
package/dist/plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.js","sources":["../src/plugin.ts"],"sourcesContent":["import { EnvironmentPreset } from '@brickadia/presets';\nimport {\n BRBanList,\n BRPlayerNameCache,\n BRRoleAssignments,\n BRRoleSetup,\n} from '@brickadia/types';\nimport {\n ILogMinigame,\n IMinigameList,\n IPlayerPositions,\n IServerStatus,\n} from '@omegga/types';\nimport util from '@util';\nimport { ReadSaveObject, WriteSaveObject } from 'brs-js';\n\nexport const _OMEGGA_UTILS_IMPORT = util;\ndeclare global {\n export var Omegga: OmeggaLike;\n export var Player: StaticPlayer;\n export var OMEGGA_UTIL: typeof _OMEGGA_UTILS_IMPORT;\n}\n\nexport * from '@brickadia/types';\nexport * from '@omegga/types';\nexport { OmeggaPlugin };\n\nexport type OL = OmeggaLike;\nexport type OP = OmeggaPlugin;\nexport type PC<T extends Record<string, unknown> = Record<string, unknown>> =\n PluginConfig<T>;\nexport type PS<T extends Record<string, unknown> = Record<string, unknown>> =\n PluginStore<T>;\n\nexport interface BrickBounds {\n minBound: [number, number, number];\n maxBound: [number, number, number];\n center: [number, number, number];\n}\n\n/** Created when a player clicks on a brick with an interact component */\nexport interface BrickInteraction {\n /** Brick name from catalog (Turkey Body, 4x Cube) */\n brick_name: string;\n /** Brick asset name */\n brick_asset: string;\n /** Brick size */\n brick_size: [number, number, number];\n /** Player information, id, name, controller, and pawn */\n player: { id: string; name: string; controller: string; pawn: string };\n /** Brick center position */\n position: [number, number, number];\n\n /** message sent from a brick click interaction */\n message: string;\n /** data parsed from the line (if it starts with json:) */\n data: null | number | string | boolean | Record<string, unknown>;\n /** True when there was a json payload */\n json: boolean;\n /** True when there was a parse error */\n error: boolean;\n}\n\n/** AutoRestart options */\nexport type AutoRestartConfig = {\n players: boolean;\n announcement: boolean;\n saveWorld: boolean;\n};\n\nexport interface OmeggaPlayer {\n /** player name */\n name: string;\n\n /** player display name */\n displayName: string;\n\n /** player uuid */\n id: string;\n\n /** player controller id */\n controller: string;\n\n /** player state id */\n state: string;\n\n /**\n * Returns omegga\n */\n getOmegga(): OmeggaLike;\n\n /**\n * Clone a player\n */\n clone(): OmeggaPlayer;\n\n /**\n * Get raw player info (to feed into a constructor)\n */\n raw(): [string, string, string, string, string];\n\n /**\n * True if the player is the host\n */\n isHost(): boolean;\n\n /**\n * Clear player's bricks\n * @param quiet clear bricks quietly\n */\n clearBricks(quiet?: boolean): void;\n\n /**\n * Get player's roles, if any\n */\n getRoles(): readonly string[];\n\n /**\n * Get player's permissions in a map like `{\"Bricks.ClearOwn\": true, ...}`\n * @return permissions map\n */\n getPermissions(): Record<string, boolean>;\n\n /**\n * Get player's name color\n * @return 6 character hex string\n */\n getNameColor(): string;\n\n /**\n * Get the player's pawn\n * @return pawn\n */\n getPawn(): Promise<string>;\n\n /**\n * Get player's position\n * @return [x, y, z] coordinates\n */\n getPosition(): Promise<[number, number, number]>;\n\n /**\n * Gets a user's ghost brick info (by uuid, name, controller, or player object)\n * @return ghost brick data\n */\n getGhostBrick(): Promise<{\n targetGrid: string;\n location: number[];\n orientation: string;\n }>;\n\n /**\n * gets a user's paint tool properties\n */\n getPaint(): Promise<{\n materialIndex: string;\n materialAlpha: string;\n material: string;\n color: number[];\n }>;\n\n /**\n * gets whether or not the player is crouching\n */\n isCrouched(pawn?: string): Promise<boolean>;\n\n /**\n * gets whether or not the player is dead\n */\n isDead(pawn?: string): Promise<boolean>;\n\n /**\n * Gets the bounds of the template in the user's clipboard (bounds of original selection box)\n * @return template bounds\n */\n getTemplateBounds(): Promise<BrickBounds>;\n\n /**\n * Get bricks inside template bounds\n * @return BRS JS Save Data\n */\n getTemplateBoundsData(): Promise<ReadSaveObject>;\n\n /**\n * Load bricks at ghost brick location\n * @param saveData save data to load\n */\n loadDataAtGhostBrick(\n saveData: WriteSaveObject,\n options?: {\n rotate?: boolean;\n offX?: number;\n offY?: number;\n offZ?: number;\n quiet?: boolean;\n },\n ): Promise<void>;\n\n /**\n * Load bricks on this player's clipboard\n * @param saveName Save to load\n */\n loadBricks(saveName: string): void;\n\n /**\n * Load bricks on this player's clipboard passing save data\n * @param saveData save data to load\n */\n loadSaveData(\n saveData: WriteSaveObject,\n options?: {\n rotate?: boolean;\n offX?: number;\n offY?: number;\n offZ?: number;\n quiet?: boolean;\n },\n ): Promise<void>;\n\n /**\n * Kills this player\n */\n kill(): void;\n\n /**\n * Damages a player\n * @param amount Amount to damage\n */\n damage(amount: number): void;\n\n /**\n * Heal this player\n * @param amount to heal\n */\n heal(amount: number): void;\n\n /**\n * Gives a player an item\n * @param item Item name (Weapon_Bow)\n */\n giveItem(item: WeaponClass): void;\n\n /**\n * Removes an item from a player's inventory\n * @param item Item name (Weapon_Bow)\n */\n takeItem(item: WeaponClass): void;\n\n /**\n * Changes a player's team\n * @param teamIndex Team index\n */\n setTeam(teamIndex: number): void;\n\n /**\n * Changes a player's minigame\n * @param index Minigame index\n */\n setMinigame(index: number): void;\n\n /**\n * Changes a player's score\n * @param minigameIndex minigame index\n * @param score Score\n */\n setScore(minigameIndex: number, score: number): void;\n\n /**\n * Fetch a player's score\n * @param minigameIndex minigame index\n */\n getScore(minigameIndex: number): Promise<number>;\n\n /**\n * Set leaderboard value\n * @param key leaderboard key (Score, Kills, Deaths, CorrectGuesses)\n * @param value leaderboard value\n */\n setLeaderboard(key: string, value: number): void;\n\n /**\n * Get leaderboard value\n * @param key leaderboard key (Score, Kills, Deaths, CorrectGuesses)\n * @return leaderboard value\n */\n getLeaderboard(key: string): Promise<number | null>;\n}\n\nexport interface StaticPlayer {\n /**\n * get a player's roles, if any\n * @param omegga omegga instance\n * @param id player uuid\n * @return list of roles\n */\n getRoles(omegga: OmeggaLike, id: string): readonly string[];\n\n /**\n * get a player's permissions in a map like `{\"Bricks.ClearOwn\": true, ...}`\n * @param omegga Omegga instance\n * @param id player uuid\n * @return permissions map\n */\n getPermissions(omegga: OmeggaLike, id: string): Record<string, boolean>;\n\n /**\n * Kills a player\n * @param omegga Omegga instance\n * @param target Player or player name/id\n */\n kill(omegga: OmeggaLike, target: string | OmeggaPlayer): void;\n\n /**\n * Damages a player\n * @param omegga Omegga instance\n * @param target Player or player name/id\n * @param amount Damage amount\n */\n damage(\n omegga: OmeggaLike,\n target: string | OmeggaPlayer,\n amount: number,\n ): void;\n\n /**\n * Heal a player\n * @param omegga Omegga instance\n * @param target Player or player name/id\n * @param amount Heal amount\n */\n heal(omegga: OmeggaLike, target: string | OmeggaPlayer, amount: number): void;\n\n /**\n * Gives a player an item\n * @param omegga Omegga instance\n * @param target Player or player name/id\n * @param item Item name (Weapon_Bow)\n */\n giveItem(\n omegga: OmeggaLike,\n target: string | OmeggaPlayer,\n item: WeaponClass,\n ): void;\n\n /**\n * Removes an item from a player's inventory\n * @param omegga Omegga instance\n * @param target Player or player name/id\n * @param item Item name (Weapon_Bow)\n */\n takeItem(\n omegga: OmeggaLike,\n target: string | OmeggaPlayer,\n item: WeaponClass,\n ): void;\n\n /**\n * Changes a player's team\n * @param omegga Omegga instance\n * @param target Player or player name/id\n * @param teamIndex Team name? index?\n */\n setTeam(\n omegga: OmeggaLike,\n target: string | OmeggaPlayer,\n teamIndex: number,\n ): void;\n\n /**\n * Changes a player's minigame\n * @param omegga Omegga instance\n * @param target Player or player name/id\n * @param index Minigame index\n */\n setMinigame(\n omegga: OmeggaLike,\n target: string | OmeggaPlayer,\n index: number,\n ): void;\n\n /**\n * Changes a player's score\n * @param omegga Omegga instance\n * @param target Player or player name/id\n * @param minigameIndex minigame index\n * @param score Score\n */\n setScore(\n omegga: OmeggaLike,\n target: string | OmeggaPlayer,\n minigameIndex: number,\n score: number,\n ): void;\n\n /**\n * Fetches a player's score\n * @param omegga Omegga instance\n * @param target Player or player name/id\n * @param minigameIndex minigame index\n */\n getScore(\n omegga: OmeggaLike,\n target: string | OmeggaPlayer,\n minigameIndex: number,\n ): Promise<number>;\n\n /**\n * Set leaderboard value\n * @param omegga Omegga instance\n * @param target Player or player name/id\n * @param key leaderboard key (Score, Kills, Deaths, CorrectGuesses)\n * @param value leaderboard value\n */\n setLeaderboard(\n omegga: OmeggaLike,\n target: string | OmeggaPlayer,\n key: string,\n value: number,\n ): void;\n\n /**\n * Get leaderboard value\n * @param omegga Omegga instance\n * @param target Player or player name/id\n * @param key leaderboard key (Score, Kills, Deaths, CorrectGuesses)\n * @return leaderboard value\n */\n getLeaderboard(\n omegga: OmeggaLike,\n target: string | OmeggaPlayer,\n key: string,\n ): Promise<number | null>;\n}\n\nexport interface InjectedCommands {\n /** Get server status */\n getServerStatus(this: OmeggaLike): Promise<IServerStatus | null>;\n /** Get a list of minigames and their indices */\n listMinigames(this: OmeggaLike): Promise<IMinigameList>;\n // /** Get all pawn data */\n // getAllPawnData(this: OmeggaLike, fields: PawnDataField[]): void;\n /** Get all player positions and pawns */\n getAllPlayerPositions(this: OmeggaLike): Promise<IPlayerPositions>;\n /** Get minigames and members */\n getMinigames(this: OmeggaLike): Promise<ILogMinigame[]>;\n}\n\nexport interface MockEventEmitter {\n addListener(event: string, listener: Function): this;\n emit(event: string, ...args: any[]): boolean;\n eventNames(): (string | symbol)[];\n getMaxListeners(): number;\n listenerCount(event: string): number;\n listeners(event: string): Function[];\n off(event: string, listener: Function): this;\n on(event: string, listener: Function): this;\n once(event: string, listener: Function): this;\n prependListener(event: string, listener: Function): this;\n prependOnceListener(event: string, listener: Function): this;\n rawListeners(event: string): Function[];\n removeAllListeners(event?: string): this;\n removeListener(event: string, listener: Function): this;\n setMaxListeners(maxListeners: number): this;\n\n on(event: 'close', listener: () => void): this;\n on(event: 'line', listener: (line: string) => void): this;\n on(event: 'start', listener: (info: { map: string }) => void): this;\n on(event: 'version', listener: (version: number) => void): this;\n on(event: 'unauthorized', listener: () => void): this;\n on(event: 'join', listener: (player: OmeggaPlayer) => void): this;\n on(event: 'leave', listener: (player: OmeggaPlayer) => void): this;\n on(event: 'chat', listener: (name: string, message: string) => void): this;\n on(event: 'mapchange', listener: (info: { map: string }) => void): this;\n on(event: 'autorestart', listener: (config: AutoRestartConfig) => void): this;\n on(\n event: 'interact',\n listener: (interaction: BrickInteraction) => void,\n ): this;\n on(\n event: 'minigamejoin',\n listener: (info: {\n player: { name: string; id: string };\n minigameName: string;\n }) => void,\n ): this;\n}\n\nexport interface OmeggaLike\n extends OmeggaCore,\n LogWrangling,\n InjectedCommands,\n MockEventEmitter {\n writeln(line: string): void;\n\n /** game CL version*/\n version: number;\n\n /** verbose logging is enabled*/\n verbose: boolean;\n\n /** list of players */\n players: OmeggaPlayer[];\n\n /** server host */\n host?: { id: string; name: string };\n\n /** server is started */\n started: boolean;\n /** server is starting */\n starting: boolean;\n /** server is stopping */\n stopping: boolean;\n /** current map */\n currentMap: string;\n\n /** path to config files */\n configPath: string;\n /** path to saves */\n savePath: string;\n /** path to worlds */\n worldPath: string;\n /** path to presets */\n presetPath: string;\n /** path to containing dir */\n path: string;\n\n /** get a plugin's name, documentation, and loaded status\n * If run in an unsafe plugin, the emitPlugin method sends events from\n * an \"unsafe\" plugin\n */\n getPlugin(name: string): Promise<PluginInterop>;\n}\n\nexport interface OmeggaCore {\n /**\n * get a list of players\n * @return list of players {id: uuid, name: name} objects\n */\n getPlayers(): {\n id: string;\n name: string;\n displayName: string;\n controller: string;\n state: string;\n }[];\n\n /**\n * find a player by name, id, controller, or state\n * @param target - name, id, controller, or state\n */\n getPlayer(target: string): OmeggaPlayer | null;\n\n /**\n * find a player by rough name, prioritize exact matches and get fuzzier\n * @param name player name, fuzzy\n */\n findPlayerByName(name: string): OmeggaPlayer | null;\n\n /**\n * get the host's ID\n * @return Host Id\n */\n getHostId(): string;\n\n /**\n * broadcast messages to chat\n * messages are broken by new line\n * multiple arguments are additional lines\n * all messages longer than 512 characters are deleted automatically, though omegga wouldn't have sent them anyway\n * @param messages unescaped chat messages to send. may need to wrap messages with quotes\n */\n broadcast(...messages: string[]): void;\n\n /**\n * whisper messages to a player's chat\n * messages are broken by new line\n * multiple arguments are additional lines\n * all messages longer than 512 characters are deleted automatically, though omegga wouldn't have sent them anyway\n * @param target - player identifier or player object\n * @param messages - unescaped chat messages to send. may need to wrap messages with quotes\n */\n whisper(target: string | OmeggaPlayer, ...messages: string[]): void;\n\n /**\n * prints text to the middle of a player's screen\n * all messages longer than 512 characters are deleted automatically\n * @param target - player identifier or player object\n * @param message - unescaped chat messages to send. may need to wrap messages with quotes\n */\n middlePrint(target: string | OmeggaPlayer, message: string): void;\n\n /**\n * Save a minigame preset based on a minigame index\n * @param index minigame index\n * @param name preset name\n */\n saveMinigame(index: number, name: string): void;\n\n /**\n * Delete a minigame\n * @param index minigame index\n */\n deleteMinigame(index: number): void;\n\n /**\n * Reset a minigame\n * @param index minigame index\n */\n resetMinigame(index: number): void;\n\n /**\n * Force the next round in a minigame\n * @param index minigame index\n */\n nextRoundMinigame(index: number): void;\n\n /**\n * Load an Minigame preset\n * @param presetName preset name\n * @param owner owner id/name\n */\n loadMinigame(presetName: string, owner?: string): void;\n\n /**\n * Get all presets in the minigame folder and child folders\n */\n getMinigamePresets(): string[];\n\n /**\n * Reset the environment settings\n */\n resetEnvironment(): void;\n\n /**\n * Save an environment preset\n * @param presetName preset name\n */\n saveEnvironment(presetName: string): Promise<void>;\n\n /** Save a temporary environment preset and return its contents */\n getEnvironmentData(): Promise<EnvironmentPreset>;\n\n /**\n * Read environment data as json\n * @param presetName preset name\n */\n readEnvironmentData(presetName: string): void;\n\n /**\n * Load an environment preset\n * @param presetName preset name\n */\n loadEnvironment(presetName: string): void;\n\n /**\n * Load some environment preset data\n * @param preset preset data\n */\n loadEnvironmentData(\n preset: EnvironmentPreset | EnvironmentPreset['data']['groups'],\n ): void;\n\n /**\n * Get all presets in the environment folder and child folders\n */\n getEnvironmentPresets(): string[];\n\n /**\n * Clear a user's bricks (by uuid, name, controller, or player object)\n * @param target player or player identifier\n * @param quiet quietly clear bricks\n */\n clearBricks(target: string | { id: string }, quiet?: boolean): void;\n\n /**\n * Clear a region of bricks\n * @param region region to clear\n * @param options optional settings\n */\n clearRegion(\n region: {\n center: [number, number, number];\n extent: [number, number, number];\n },\n options?: {\n target?: string | OmeggaPlayer;\n },\n ): void;\n\n /**\n * Clear all bricks on the server\n * @param quiet quietly clear bricks\n */\n clearAllBricks(quiet?: boolean): void;\n\n /**\n * Save bricks under a filename\n * @param saveName save file name\n * @param region region of bricks to save\n */\n saveBricks(\n saveName: string,\n region?: {\n center: [number, number, number];\n extent: [number, number, number];\n },\n ): void;\n\n /**\n * Save bricks under a filename, with a promise\n * @param saveName save file name\n * @param region region of bricks to save\n */\n saveBricksAsync(\n saveName: string,\n region?: {\n center: [number, number, number];\n extent: [number, number, number];\n },\n ): Promise<void>;\n\n /**\n * Load bricks on the server\n */\n loadBricks(\n saveName: string,\n options?: {\n offX?: number;\n offY?: number;\n offZ?: number;\n quiet?: boolean;\n correctPalette?: boolean;\n correctCustom?: boolean;\n },\n ): void;\n\n /**\n * Load bricks on the server into a player's clipbaord\n */\n loadBricksOnPlayer(\n saveName: string,\n player: string | OmeggaPlayer,\n options?: {\n offX?: number;\n offY?: number;\n offZ?: number;\n correctPalette?: boolean;\n correctCustom?: boolean;\n },\n ): void;\n\n /**\n * Get all saves in the save folder and child folders\n */\n getSaves(): string[];\n\n /**\n * Checks if a save exists and returns an absolute path\n * @param saveName Save filename\n * @return Path to string\n */\n getSavePath(saveName: string): string;\n\n /**\n * Get all worlds in the worlds folder and child folders\n */\n getWorlds(): string[];\n\n /**\n * Checks if a world exists and returns an absolute path\n * @param worldName World name\n * @return Path to string\n */\n getWorldPath(worldName: string): string;\n\n /**\n * Get a list of revisions for a world\n * @param worldName World name\n */\n getWorldRevisions(\n worldName: string,\n ): Promise<{ index: number; date: Date; note: string }[]>;\n\n /**\n * Load a world by its name\n * @param worldName World name\n */\n loadWorld(worldName: string): Promise<boolean>;\n\n /**\n * Load a world at a specific revision\n * @param worldName World name\n */\n loadWorldRevision(worldName: string, revision: number): Promise<boolean>;\n\n /**\n * Save a world as a new name\n * @param worldName World name\n */\n saveWorldAs(worldName: string): Promise<boolean>;\n\n /**\n * Save the current world\n */\n saveWorld(): Promise<boolean>;\n\n /**\n * Create an empty world with the given name\n */\n createEmptyWorld(worldName: string): Promise<boolean>;\n\n /**\n * unsafely load save data (wrap in try/catch)\n * @param saveName save file name\n * @param saveData BRS JS Save data\n */\n writeSaveData(saveName: string, saveData: WriteSaveObject): void;\n\n /**\n * unsafely read save data (wrap in try/catch)\n * @param saveName save file name\n * @param nobricks only read save header data\n * @return BRS JS Save Data\n */\n readSaveData(saveName: string, nobricks?: boolean): ReadSaveObject;\n\n /**\n * load bricks from save data and resolve when game finishes loading\n * @param saveData BRS JS Save data\n */\n loadSaveData(\n saveData: WriteSaveObject,\n options?: {\n offX?: number;\n offY?: number;\n offZ?: number;\n quiet?: boolean;\n correctPalette?: boolean;\n correctCustom?: boolean;\n },\n ): Promise<void>;\n\n /**\n * load bricks from save data and resolve when game finishes loading\n * @param saveData BRS JS Save data\n * @param player Player name/id or player object\n */\n loadSaveDataOnPlayer(\n saveData: WriteSaveObject,\n player: string | OmeggaPlayer,\n options?: {\n offX?: number;\n offY?: number;\n offZ?: number;\n correctPalette?: boolean;\n correctCustom?: boolean;\n },\n ): Promise<void>;\n\n /**\n * get current bricks as save data\n */\n getSaveData(region?: {\n center: [number, number, number];\n extent: [number, number, number];\n }): Promise<ReadSaveObject>;\n\n /**\n * Change server map\n * @param map Map name\n */\n changeMap(map: string): Promise<boolean>;\n\n /**\n * Get up-to-date role setup from RoleSetup.json\n */\n getRoleSetup(): BRRoleSetup;\n\n /**\n * Get up-to-date role assignments from RoleAssignment.json\n */\n getRoleAssignments(): BRRoleAssignments;\n\n /**\n * Get up-to-date ban list from BanList.json\n */\n getBanList(): BRBanList;\n\n /**\n * Get up-to-date name cache from PlayerNameCache.json\n */\n getNameCache(): BRPlayerNameCache;\n}\n\n/** A simple document store for plugins */\nexport interface PluginStore<\n Storage extends Record<string, unknown> = Record<string, unknown>,\n> {\n /** Get a value from plugin storage */\n get<T extends keyof Storage>(key: T): Promise<Storage[T]>;\n /** Set a value to plugin storage */\n set<T extends keyof Storage>(key: T, value: Storage[T]): Promise<void>;\n /** Delete a value from plugin storage */\n delete(key: string): Promise<void>;\n /** Wipe all values in plugin storage */\n wipe(): Promise<void>;\n /** Count entries in plugin storage */\n count(): Promise<number>;\n /** Get a list of keys in plugin storage */\n keys(): Promise<(keyof Storage)[]>;\n}\n\n/** A config representative of the config outlined in doc.json */\nexport type PluginConfig<\n T extends Record<string, unknown> = Record<string, unknown>,\n> = T;\n\n/** An omegga plugin */\nexport default abstract class OmeggaPlugin<\n Config extends Record<string, unknown> = Record<string, unknown>,\n Storage extends Record<string, unknown> = Record<string, unknown>,\n> {\n omegga: OmeggaLike;\n config: PluginConfig<Config>;\n store: PluginStore<Storage>;\n\n constructor(\n omegga: OmeggaLike,\n config: PluginConfig<Config>,\n store: PluginStore<Storage>,\n ) {\n this.omegga = omegga;\n this.config = config;\n this.store = store;\n }\n\n /** Run when plugin starts, returns /commands it uses */\n abstract init(): Promise<void | { registeredCommands?: string[] }>;\n\n /** Run when plugin is stopped */\n abstract stop(): Promise<void>;\n\n /** Run when another plugin tries to interact with this plugin\n * @param event Event name\n * @param from Name of origin plugin\n * @return value other plugin expects\n */\n abstract pluginEvent?(\n event: string,\n from: string,\n ...args: any[]\n ): Promise<unknown>;\n}\n\nexport interface LogWrangling {\n /** Add a passive pattern on console output that invokes callback on match */\n addMatcher<T>(\n pattern: IMatcher<T>['pattern'],\n callback: IMatcher<T>['callback'],\n ): void;\n\n /** Run an active pattern on console output that resolves a match */\n addWatcher<T = RegExpMatchArray>(\n pattern: IWatcher<T>['pattern'],\n options?: {\n timeoutDelay?: number;\n bundle?: boolean;\n debounce?: boolean;\n afterMatchDelay?: number;\n last?: IWatcher<T>['last'];\n exec?: () => void;\n },\n ): Promise<IWatcher<T>['matches']>;\n\n /** Run a command and capture bundled output */\n watchLogChunk<T = string>(\n cmd: string,\n pattern: IWatcher<T>['pattern'],\n options?: {\n first?: 'index' | ((match: T) => boolean);\n last?: IWatcher<T>['last'];\n afterMatchDelay?: number;\n timeoutDelay?: number;\n },\n ): Promise<IWatcher<T>['matches']>;\n\n /** Run a command and capture bundled output for array functions */\n watchLogArray<\n Item extends Record<string, string> = Record<string, string>,\n Member extends Record<string, string> = Record<string, string>,\n >(\n cmd: string,\n itemPattern: RegExp,\n memberPattern: RegExp,\n ): Promise<{ item: Item; members: Member[] }[]>;\n}\n\nexport type WatcherPattern<T> = (\n line: string,\n match: RegExpMatchArray,\n) => T | RegExpMatchArray | '[OMEGGA_WATCHER_DONE]';\n\nexport type IMatcher<T> =\n | {\n pattern: RegExp;\n callback: (match: RegExpMatchArray) => boolean;\n }\n | {\n pattern: (line: string, match: RegExpMatchArray) => T;\n callback: (match: RegExpMatchArray) => T;\n };\n\nexport type IWatcher<T> = {\n bundle: boolean;\n debounce: boolean;\n timeoutDelay: number;\n afterMatchDelay: number;\n last: (match: T) => boolean;\n callback: () => void;\n resolve: (...args: any[]) => void;\n remove: () => void;\n done: () => void;\n timeout: ReturnType<typeof setTimeout>;\n} & (\n | {\n pattern: WatcherPattern<T>;\n matches: T[];\n }\n | {\n pattern: RegExp;\n matches: RegExpMatchArray[];\n }\n);\n\nexport declare type IPluginConfigDefinition = {\n description: string;\n} & (\n | {\n type: 'string' | 'password' | 'role';\n default: string;\n }\n | {\n type: 'boolean';\n default: boolean;\n }\n | {\n type: 'number';\n default: number;\n }\n | {\n type: 'enum';\n options: (string | number)[];\n default: string | number;\n }\n | {\n type: 'players';\n default: {\n id: string;\n name: string;\n };\n }\n | ({\n type: 'list';\n } & (\n | {\n itemType: 'string';\n default: string[];\n }\n | {\n itemType: 'number';\n default: number[];\n }\n | {\n itemType: 'enum';\n options: (string | number)[];\n default: string | number;\n }\n ))\n);\nexport interface IPluginCommandArgument {\n name: string;\n description: string;\n required?: boolean;\n}\nexport interface IPluginCommand {\n name: string;\n description: string;\n example?: string;\n args: IPluginCommandArgument[];\n}\nexport interface IPluginDocumentation {\n name: string;\n description: string;\n author: string;\n config: Record<string, IPluginConfigDefinition>;\n commands: IPluginCommand[];\n}\n\nexport interface PluginInterop {\n name: string;\n documentation: IPluginDocumentation;\n loaded: boolean;\n emitPlugin?(event: string, args: any[]): Promise<any>;\n}\n\nexport type WeaponClass =\n | 'Weapon_APCarbine'\n | 'Weapon_AntiMaterielRifle'\n | 'Weapon_ArmingSword'\n | 'Weapon_AssaultRifle'\n | 'Weapon_AutoShotgun'\n | 'Weapon_Battleaxe'\n | 'Weapon_Bazooka'\n | 'Weapon_BoltActionRifle'\n | 'Weapon_Bow'\n | 'Weapon_BoxPistol'\n | 'Weapon_BullpupRifle'\n | 'Weapon_BullpupSMG'\n | 'Weapon_ChargedLongsword'\n | 'Weapon_CrystalKalis'\n | 'Weapon_Derringer'\n | 'Weapon_Dynamite'\n | 'Weapon_FlintlockPistol'\n | 'Weapon_GrenadeLauncher'\n | 'Weapon_GuardPistol'\n | 'Weapon_Handaxe'\n | 'Weapon_HealthPotion'\n | 'Weapon_HeavyAssaultRifle'\n | 'Weapon_HeavySMG'\n | 'Weapon_HeroSword'\n | 'Weapon_HighPowerPistol'\n | 'Weapon_HoloBlade'\n | 'Weapon_HuntingShotgun'\n | 'Weapon_Ikakalaka'\n | 'Weapon_ImpactGrenade'\n | 'Weapon_ImpactGrenadeLauncher'\n | 'Weapon_ImpulseGrenade'\n | 'Weapon_Khopesh'\n | 'Weapon_Knife'\n | 'Weapon_LeverActionRifle'\n | 'Weapon_LightMachineGun'\n | 'Weapon_LongSword'\n | 'Weapon_MagnumPistol'\n | 'Weapon_MicroSMG'\n | 'Weapon_Minigun'\n | 'Weapon_Minigun'\n | 'Weapon_MissileLauncher'\n | 'Weapon_PDW'\n | 'Weapon_Pickaxe'\n | 'Weapon_PipeBomb'\n | 'Weapon_Pistol'\n | 'Weapon_PlasmaPistol'\n | 'Weapon_PlasmaSMG'\n | 'Weapon_PulseCarbine'\n | 'Weapon_PulseRifle'\n | 'Weapon_QuadLauncher'\n | 'Weapon_Revolver'\n | 'Weapon_RocketJumper'\n | 'Weapon_RocketLauncher'\n | 'Weapon_Sabre'\n | 'Weapon_SemiAutoRifle'\n | 'Weapon_ServiceRifle'\n | 'Weapon_Shotgun'\n | 'Weapon_SlugShotgun'\n | 'Weapon_Sniper'\n | 'Weapon_SodaCan'\n | 'Weapon_Spatha'\n | 'Weapon_SportingShotgun'\n | 'Weapon_StampedSMG'\n | 'Weapon_StandardSubmachineGun'\n | 'Weapon_StickGrenade'\n | 'Weapon_SubmachineGun'\n | 'Weapon_SuperShotgun'\n | 'Weapon_SuppressedAssaultRifle'\n | 'Weapon_SuppressedBullpupSMG'\n | 'Weapon_SuppressedGuardPistol'\n | 'Weapon_SuppressedHeavySMG'\n | 'Weapon_SuppressedMicroSMG'\n | 'Weapon_SuppressedPistol'\n | 'Weapon_SuppressedServiceRifle'\n | 'Weapon_TacticalSMG'\n | 'Weapon_TacticalShotgun'\n | 'Weapon_Tomahawk'\n | 'Weapon_TwinCannon'\n | 'Weapon_TypewriterSMG'\n | 'Weapon_Zweihander';\n"],"names":["util"],"mappings":";;;AAgBO,MAAM,uBAAuBA,MAAAA;AAs4BpC,MAA8B,aAG5B;AAAA,EAKA,YACE,QACA,QACA,OACA;AACA,SAAK,SAAS;AACd,SAAK,SAAS;AACd,SAAK,QAAQ;AAAA,EACf;AAkBF;;;"}
|
|
1
|
+
{"version":3,"file":"plugin.js","sources":["../src/plugin.ts"],"sourcesContent":["import { EnvironmentPreset } from '@brickadia/presets';\nimport {\n BRBanList,\n BRPlayerNameCache,\n BRRoleAssignments,\n BRRoleSetup,\n} from '@brickadia/types';\nimport { ConsoleCommands } from '@omegga/commands';\nimport {\n IGamemode,\n ILogMinigame,\n IMinigameList,\n IPlayerPositions,\n IServerStatus,\n} from '@omegga/types';\nimport util from '@util';\nimport { ReadSaveObject, WriteSaveObject } from 'brs-js';\n\nexport const _OMEGGA_UTILS_IMPORT = util;\ndeclare global {\n export var Omegga: OmeggaLike;\n export var Player: StaticPlayer;\n export var OMEGGA_UTIL: typeof _OMEGGA_UTILS_IMPORT;\n}\n\nexport * from '@brickadia/types';\nexport * from '@omegga/types';\nexport type { ConsoleCommands } from '@omegga/commands';\nexport { OmeggaPlugin };\n\nexport type OL = OmeggaLike;\nexport type OP = OmeggaPlugin;\nexport type PC<T extends Record<string, unknown> = Record<string, unknown>> =\n PluginConfig<T>;\nexport type PS<T extends Record<string, unknown> = Record<string, unknown>> =\n PluginStore<T>;\n\nexport interface BrickBounds {\n minBound: [number, number, number];\n maxBound: [number, number, number];\n center: [number, number, number];\n}\n\n/** Created when a player clicks on a brick with an interact component */\nexport interface BrickInteraction {\n /** Brick name from catalog (Turkey Body, 4x Cube) */\n brick_name: string;\n /** Brick asset name */\n brick_asset: string;\n /** Brick size */\n brick_size: [number, number, number];\n /** Player information, id, name, controller, and pawn */\n player: { id: string; name: string; controller: string; pawn: string };\n /** Brick center position */\n position: [number, number, number];\n\n /** message sent from a brick click interaction */\n message: string;\n /** data parsed from the line (if it starts with json:) */\n data: null | number | string | boolean | Record<string, unknown>;\n /** True when there was a json payload */\n json: boolean;\n /** True when there was a parse error */\n error: boolean;\n}\n\n/** AutoRestart options */\nexport type AutoRestartConfig = {\n players: boolean;\n announcement: boolean;\n saveWorld: boolean;\n};\n\nexport interface OmeggaPlayer {\n /** player name */\n name: string;\n\n /** player display name */\n displayName: string;\n\n /** player uuid */\n id: string;\n\n /** player controller id */\n controller: string;\n\n /** player state id */\n state: string;\n\n /**\n * Returns omegga\n */\n getOmegga(): OmeggaLike;\n\n /**\n * Clone a player\n */\n clone(): OmeggaPlayer;\n\n /**\n * Get raw player info (to feed into a constructor)\n */\n raw(): [string, string, string, string, string];\n\n /**\n * True if the player is the host\n */\n isHost(): boolean;\n\n /**\n * Clear player's bricks\n * @param quiet clear bricks quietly\n */\n clearBricks(quiet?: boolean): void;\n\n /**\n * Get player's roles, if any\n */\n getRoles(): readonly string[];\n\n /**\n * Get player's permissions in a map like `{\"Bricks.ClearOwn\": true, ...}`\n * @return permissions map\n */\n getPermissions(): Record<string, boolean>;\n\n /**\n * Get player's name color\n * @return 6 character hex string\n */\n getNameColor(): string;\n\n /**\n * Get the player's pawn\n * @return pawn\n */\n getPawn(): Promise<string>;\n\n /**\n * Get player's position\n * @return [x, y, z] coordinates\n */\n getPosition(): Promise<[number, number, number]>;\n\n /**\n * Gets a user's ghost brick info (by uuid, name, controller, or player object)\n * @return ghost brick data\n */\n getGhostBrick(): Promise<{\n targetGrid: string;\n location: number[];\n orientation: string;\n }>;\n\n /**\n * gets a user's paint tool properties\n */\n getPaint(): Promise<{\n materialIndex: string;\n materialAlpha: string;\n material: string;\n color: number[];\n }>;\n\n /**\n * gets whether or not the player is crouching\n */\n isCrouched(pawn?: string): Promise<boolean>;\n\n /**\n * gets whether or not the player is dead\n */\n isDead(pawn?: string): Promise<boolean>;\n\n /**\n * Gets the bounds of the template in the user's clipboard (bounds of original selection box)\n * @return template bounds\n */\n getTemplateBounds(): Promise<BrickBounds>;\n\n /**\n * Get bricks inside template bounds\n * @return BRS JS Save Data\n */\n getTemplateBoundsData(): Promise<ReadSaveObject>;\n\n /**\n * Load bricks at ghost brick location\n * @param saveData save data to load\n */\n loadDataAtGhostBrick(\n saveData: WriteSaveObject,\n options?: {\n rotate?: boolean;\n offX?: number;\n offY?: number;\n offZ?: number;\n quiet?: boolean;\n },\n ): Promise<void>;\n\n /**\n * Load bricks on this player's clipboard\n * @param saveName Save to load\n */\n loadBricks(saveName: string): void;\n\n /**\n * Load bricks on this player's clipboard passing save data\n * @param saveData save data to load\n */\n loadSaveData(\n saveData: WriteSaveObject,\n options?: {\n rotate?: boolean;\n offX?: number;\n offY?: number;\n offZ?: number;\n quiet?: boolean;\n },\n ): Promise<void>;\n\n /**\n * Kills this player\n */\n kill(): void;\n\n /**\n * Damages a player\n * @param amount Amount to damage\n */\n damage(amount: number): void;\n\n /**\n * Heal this player\n * @param amount to heal\n */\n heal(amount: number): void;\n\n /**\n * Gives a player an item\n * @param item Item name (Weapon_Bow)\n */\n giveItem(item: WeaponClass): void;\n\n /**\n * Removes an item from a player's inventory\n * @param item Item name (Weapon_Bow)\n */\n takeItem(item: WeaponClass): void;\n\n /**\n * Changes a player's team\n * @param teamIndex Team index\n */\n setTeam(teamIndex: number): void;\n\n /**\n * Changes a player's minigame\n * @param index Minigame index\n */\n setMinigame(index: number): void;\n\n /**\n * Changes a player's score\n * @param minigameIndex minigame index\n * @param score Score\n */\n setScore(minigameIndex: number, score: number): void;\n\n /**\n * Fetch a player's score\n * @param minigameIndex minigame index\n */\n getScore(minigameIndex: number): Promise<number>;\n\n /**\n * Set leaderboard value\n * @param key leaderboard key (Score, Kills, Deaths, CorrectGuesses)\n * @param value leaderboard value\n */\n setLeaderboard(key: string, value: number): void;\n\n /**\n * Get leaderboard value\n * @param key leaderboard key (Score, Kills, Deaths, CorrectGuesses)\n * @return leaderboard value\n */\n getLeaderboard(key: string): Promise<number | null>;\n}\n\nexport interface StaticPlayer {\n /**\n * get a player's roles, if any\n * @param omegga omegga instance\n * @param id player uuid\n * @return list of roles\n */\n getRoles(omegga: OmeggaLike, id: string): readonly string[];\n\n /**\n * get a player's permissions in a map like `{\"Bricks.ClearOwn\": true, ...}`\n * @param omegga Omegga instance\n * @param id player uuid\n * @return permissions map\n */\n getPermissions(omegga: OmeggaLike, id: string): Record<string, boolean>;\n\n /**\n * Kills a player\n * @param omegga Omegga instance\n * @param target Player or player name/id\n */\n kill(omegga: OmeggaLike, target: string | OmeggaPlayer): void;\n\n /**\n * Damages a player\n * @param omegga Omegga instance\n * @param target Player or player name/id\n * @param amount Damage amount\n */\n damage(\n omegga: OmeggaLike,\n target: string | OmeggaPlayer,\n amount: number,\n ): void;\n\n /**\n * Heal a player\n * @param omegga Omegga instance\n * @param target Player or player name/id\n * @param amount Heal amount\n */\n heal(omegga: OmeggaLike, target: string | OmeggaPlayer, amount: number): void;\n\n /**\n * Gives a player an item\n * @param omegga Omegga instance\n * @param target Player or player name/id\n * @param item Item name (Weapon_Bow)\n */\n giveItem(\n omegga: OmeggaLike,\n target: string | OmeggaPlayer,\n item: WeaponClass,\n ): void;\n\n /**\n * Removes an item from a player's inventory\n * @param omegga Omegga instance\n * @param target Player or player name/id\n * @param item Item name (Weapon_Bow)\n */\n takeItem(\n omegga: OmeggaLike,\n target: string | OmeggaPlayer,\n item: WeaponClass,\n ): void;\n\n /**\n * Changes a player's team\n * @param omegga Omegga instance\n * @param target Player or player name/id\n * @param teamIndex Team name? index?\n */\n setTeam(\n omegga: OmeggaLike,\n target: string | OmeggaPlayer,\n teamIndex: number,\n ): void;\n\n /**\n * Changes a player's minigame\n * @param omegga Omegga instance\n * @param target Player or player name/id\n * @param index Minigame index\n */\n setMinigame(\n omegga: OmeggaLike,\n target: string | OmeggaPlayer,\n index: number,\n ): void;\n\n /**\n * Changes a player's score\n * @param omegga Omegga instance\n * @param target Player or player name/id\n * @param minigameIndex minigame index\n * @param score Score\n */\n setScore(\n omegga: OmeggaLike,\n target: string | OmeggaPlayer,\n minigameIndex: number,\n score: number,\n ): void;\n\n /**\n * Fetches a player's score\n * @param omegga Omegga instance\n * @param target Player or player name/id\n * @param minigameIndex minigame index\n */\n getScore(\n omegga: OmeggaLike,\n target: string | OmeggaPlayer,\n minigameIndex: number,\n ): Promise<number>;\n\n /**\n * Set leaderboard value\n * @param omegga Omegga instance\n * @param target Player or player name/id\n * @param key leaderboard key (Score, Kills, Deaths, CorrectGuesses)\n * @param value leaderboard value\n */\n setLeaderboard(\n omegga: OmeggaLike,\n target: string | OmeggaPlayer,\n key: string,\n value: number,\n ): void;\n\n /**\n * Get leaderboard value\n * @param omegga Omegga instance\n * @param target Player or player name/id\n * @param key leaderboard key (Score, Kills, Deaths, CorrectGuesses)\n * @return leaderboard value\n */\n getLeaderboard(\n omegga: OmeggaLike,\n target: string | OmeggaPlayer,\n key: string,\n ): Promise<number | null>;\n}\n\nexport interface InjectedCommands {\n /** Get server status */\n getServerStatus(this: OmeggaLike): Promise<IServerStatus | null>;\n /**\n * Get a list of minigames and their indices\n * @deprecated minigames were replaced by a single gamemode (~CL14000); on\n * modern servers this returns at most one entry with an empty `owner`.\n * Prefer {@link InjectedCommands.getGamemode}.\n */\n listMinigames(this: OmeggaLike): Promise<IMinigameList>;\n // /** Get all pawn data */\n // getAllPawnData(this: OmeggaLike, fields: PawnDataField[]): void;\n /** Get all player positions and pawns */\n getAllPlayerPositions(this: OmeggaLike): Promise<IPlayerPositions>;\n /** Get minigames and members (one entry per gamemode on modern servers) */\n getMinigames(this: OmeggaLike): Promise<ILogMinigame[]>;\n /**\n * Get the single gamemode and its teams/players (modern servers, >=CL14000).\n * Returns null on older servers (use {@link InjectedCommands.getMinigames}).\n */\n getGamemode(this: OmeggaLike): Promise<IGamemode | null>;\n}\n\nexport interface MockEventEmitter {\n addListener(event: string, listener: Function): this;\n emit(event: string, ...args: any[]): boolean;\n eventNames(): (string | symbol)[];\n getMaxListeners(): number;\n listenerCount(event: string): number;\n listeners(event: string): Function[];\n off(event: string, listener: Function): this;\n on(event: string, listener: Function): this;\n once(event: string, listener: Function): this;\n prependListener(event: string, listener: Function): this;\n prependOnceListener(event: string, listener: Function): this;\n rawListeners(event: string): Function[];\n removeAllListeners(event?: string): this;\n removeListener(event: string, listener: Function): this;\n setMaxListeners(maxListeners: number): this;\n\n on(event: 'close', listener: () => void): this;\n on(event: 'line', listener: (line: string) => void): this;\n on(event: 'start', listener: (info: { map: string }) => void): this;\n on(event: 'version', listener: (version: number) => void): this;\n on(event: 'unauthorized', listener: () => void): this;\n on(event: 'join', listener: (player: OmeggaPlayer) => void): this;\n on(event: 'leave', listener: (player: OmeggaPlayer) => void): this;\n on(event: 'chat', listener: (name: string, message: string) => void): this;\n on(event: 'mapchange', listener: (info: { map: string }) => void): this;\n on(event: 'autorestart', listener: (config: AutoRestartConfig) => void): this;\n on(\n event: 'interact',\n listener: (interaction: BrickInteraction) => void,\n ): this;\n on(\n event: 'minigamejoin',\n listener: (info: {\n player: { name: string; id: string };\n minigameName: string;\n }) => void,\n ): this;\n}\n\nexport interface OmeggaLike\n extends OmeggaCore,\n LogWrangling,\n InjectedCommands,\n MockEventEmitter {\n writeln(line: string): void;\n\n /** game CL version*/\n version: number;\n\n /**\n * version-resolved Brickadia console command names, nested by namespace.\n * e.g. `Omegga.Console.Bricks.Clear` resolves to the command string for the\n * running game version (\"Bricks.Clear\" or \"br.Bricks.Clear\")\n */\n Console: ConsoleCommands;\n\n /** verbose logging is enabled*/\n verbose: boolean;\n\n /** list of players */\n players: OmeggaPlayer[];\n\n /** server host */\n host?: { id: string; name: string };\n\n /** server is started */\n started: boolean;\n /** server is starting */\n starting: boolean;\n /** server is stopping */\n stopping: boolean;\n /** current map */\n currentMap: string;\n\n /** path to config files */\n configPath: string;\n /** path to saves */\n savePath: string;\n /** path to worlds */\n worldPath: string;\n /** path to presets */\n presetPath: string;\n /** path to containing dir */\n path: string;\n\n /** get a plugin's name, documentation, and loaded status\n * If run in an unsafe plugin, the emitPlugin method sends events from\n * an \"unsafe\" plugin\n */\n getPlugin(name: string): Promise<PluginInterop>;\n}\n\nexport interface OmeggaCore {\n /**\n * get a list of players\n * @return list of players {id: uuid, name: name} objects\n */\n getPlayers(): {\n id: string;\n name: string;\n displayName: string;\n controller: string;\n state: string;\n }[];\n\n /**\n * find a player by name, id, controller, or state\n * @param target - name, id, controller, or state\n */\n getPlayer(target: string): OmeggaPlayer | null;\n\n /**\n * find a player by rough name, prioritize exact matches and get fuzzier\n * @param name player name, fuzzy\n */\n findPlayerByName(name: string): OmeggaPlayer | null;\n\n /**\n * get the host's ID\n * @return Host Id\n */\n getHostId(): string;\n\n /**\n * broadcast messages to chat\n * messages are broken by new line\n * multiple arguments are additional lines\n * all messages longer than 512 characters are deleted automatically, though omegga wouldn't have sent them anyway\n * @param messages unescaped chat messages to send. may need to wrap messages with quotes\n */\n broadcast(...messages: string[]): void;\n\n /**\n * whisper messages to a player's chat\n * messages are broken by new line\n * multiple arguments are additional lines\n * all messages longer than 512 characters are deleted automatically, though omegga wouldn't have sent them anyway\n * @param target - player identifier or player object\n * @param messages - unescaped chat messages to send. may need to wrap messages with quotes\n */\n whisper(target: string | OmeggaPlayer, ...messages: string[]): void;\n\n /**\n * prints text to the middle of a player's screen\n * all messages longer than 512 characters are deleted automatically\n * @param target - player identifier or player object\n * @param message - unescaped chat messages to send. may need to wrap messages with quotes\n */\n middlePrint(target: string | OmeggaPlayer, message: string): void;\n\n /**\n * Save a minigame preset based on a minigame index\n * @param index minigame index\n * @param name preset name\n */\n saveMinigame(index: number, name: string): void;\n\n /**\n * Delete a minigame\n * @param index minigame index\n */\n deleteMinigame(index: number): void;\n\n /**\n * Reset a minigame\n * @param index minigame index\n */\n resetMinigame(index: number): void;\n\n /**\n * Force the next round in a minigame\n * @param index minigame index\n */\n nextRoundMinigame(index: number): void;\n\n /**\n * Load an Minigame preset\n * @param presetName preset name\n * @param owner owner id/name\n */\n loadMinigame(presetName: string, owner?: string): void;\n\n /**\n * Get all presets in the minigame folder and child folders\n */\n getMinigamePresets(): string[];\n\n /**\n * Reset the environment settings\n */\n resetEnvironment(): void;\n\n /**\n * Save an environment preset\n * @param presetName preset name\n */\n saveEnvironment(presetName: string): Promise<void>;\n\n /** Save a temporary environment preset and return its contents */\n getEnvironmentData(): Promise<EnvironmentPreset>;\n\n /**\n * Read environment data as json\n * @param presetName preset name\n */\n readEnvironmentData(presetName: string): void;\n\n /**\n * Load an environment preset\n * @param presetName preset name\n */\n loadEnvironment(presetName: string): void;\n\n /**\n * Load some environment preset data\n * @param preset preset data\n */\n loadEnvironmentData(\n preset: EnvironmentPreset | EnvironmentPreset['data']['groups'],\n ): void;\n\n /**\n * Get all presets in the environment folder and child folders\n */\n getEnvironmentPresets(): string[];\n\n /**\n * Clear a user's bricks (by uuid, name, controller, or player object)\n * @param target player or player identifier\n * @param quiet quietly clear bricks\n */\n clearBricks(target: string | { id: string }, quiet?: boolean): void;\n\n /**\n * Clear a region of bricks\n * @param region region to clear\n * @param options optional settings\n */\n clearRegion(\n region: {\n center: [number, number, number];\n extent: [number, number, number];\n },\n options?: {\n target?: string | OmeggaPlayer;\n },\n ): void;\n\n /**\n * Clear all bricks on the server\n * @param quiet quietly clear bricks\n */\n clearAllBricks(quiet?: boolean): void;\n\n /**\n * Save bricks under a filename\n * @param saveName save file name\n * @param region region of bricks to save\n */\n saveBricks(\n saveName: string,\n region?: {\n center: [number, number, number];\n extent: [number, number, number];\n },\n ): void;\n\n /**\n * Save bricks under a filename, with a promise\n * @param saveName save file name\n * @param region region of bricks to save\n */\n saveBricksAsync(\n saveName: string,\n region?: {\n center: [number, number, number];\n extent: [number, number, number];\n },\n ): Promise<void>;\n\n /**\n * Load bricks on the server\n */\n loadBricks(\n saveName: string,\n options?: {\n offX?: number;\n offY?: number;\n offZ?: number;\n quiet?: boolean;\n correctPalette?: boolean;\n correctCustom?: boolean;\n },\n ): void;\n\n /**\n * Load bricks on the server into a player's clipbaord\n */\n loadBricksOnPlayer(\n saveName: string,\n player: string | OmeggaPlayer,\n options?: {\n offX?: number;\n offY?: number;\n offZ?: number;\n correctPalette?: boolean;\n correctCustom?: boolean;\n },\n ): void;\n\n /**\n * Get all saves in the save folder and child folders\n */\n getSaves(): string[];\n\n /**\n * Checks if a save exists and returns an absolute path\n * @param saveName Save filename\n * @return Path to string\n */\n getSavePath(saveName: string): string;\n\n /**\n * Get all worlds in the worlds folder and child folders\n */\n getWorlds(): string[];\n\n /**\n * Checks if a world exists and returns an absolute path\n * @param worldName World name\n * @return Path to string\n */\n getWorldPath(worldName: string): string;\n\n /**\n * Get a list of revisions for a world\n * @param worldName World name\n */\n getWorldRevisions(\n worldName: string,\n ): Promise<{ index: number; date: Date; note: string }[]>;\n\n /**\n * Load a world by its name\n * @param worldName World name\n */\n loadWorld(worldName: string): Promise<boolean>;\n\n /**\n * Load a world at a specific revision\n * @param worldName World name\n */\n loadWorldRevision(worldName: string, revision: number): Promise<boolean>;\n\n /**\n * Save a world as a new name\n * @param worldName World name\n */\n saveWorldAs(worldName: string): Promise<boolean>;\n\n /**\n * Save the current world\n */\n saveWorld(): Promise<boolean>;\n\n /**\n * Create an empty world with the given name\n */\n createEmptyWorld(worldName: string): Promise<boolean>;\n\n /**\n * unsafely load save data (wrap in try/catch)\n * @param saveName save file name\n * @param saveData BRS JS Save data\n */\n writeSaveData(saveName: string, saveData: WriteSaveObject): void;\n\n /**\n * unsafely read save data (wrap in try/catch)\n * @param saveName save file name\n * @param nobricks only read save header data\n * @return BRS JS Save Data\n */\n readSaveData(saveName: string, nobricks?: boolean): ReadSaveObject;\n\n /**\n * load bricks from save data and resolve when game finishes loading\n * @param saveData BRS JS Save data\n */\n loadSaveData(\n saveData: WriteSaveObject,\n options?: {\n offX?: number;\n offY?: number;\n offZ?: number;\n quiet?: boolean;\n correctPalette?: boolean;\n correctCustom?: boolean;\n },\n ): Promise<void>;\n\n /**\n * load bricks from save data and resolve when game finishes loading\n * @param saveData BRS JS Save data\n * @param player Player name/id or player object\n */\n loadSaveDataOnPlayer(\n saveData: WriteSaveObject,\n player: string | OmeggaPlayer,\n options?: {\n offX?: number;\n offY?: number;\n offZ?: number;\n correctPalette?: boolean;\n correctCustom?: boolean;\n },\n ): Promise<void>;\n\n /**\n * get current bricks as save data\n */\n getSaveData(region?: {\n center: [number, number, number];\n extent: [number, number, number];\n }): Promise<ReadSaveObject>;\n\n /**\n * Change server map\n * @param map Map name\n */\n changeMap(map: string): Promise<boolean>;\n\n /**\n * Get up-to-date role setup from RoleSetup.json\n */\n getRoleSetup(): BRRoleSetup;\n\n /**\n * Get up-to-date role assignments from RoleAssignment.json\n */\n getRoleAssignments(): BRRoleAssignments;\n\n /**\n * Get up-to-date ban list from BanList.json\n */\n getBanList(): BRBanList;\n\n /**\n * Get up-to-date name cache from PlayerNameCache.json\n */\n getNameCache(): BRPlayerNameCache;\n}\n\n/** A simple document store for plugins */\nexport interface PluginStore<\n Storage extends Record<string, unknown> = Record<string, unknown>,\n> {\n /** Get a value from plugin storage */\n get<T extends keyof Storage>(key: T): Promise<Storage[T]>;\n /** Set a value to plugin storage */\n set<T extends keyof Storage>(key: T, value: Storage[T]): Promise<void>;\n /** Delete a value from plugin storage */\n delete(key: string): Promise<void>;\n /** Wipe all values in plugin storage */\n wipe(): Promise<void>;\n /** Count entries in plugin storage */\n count(): Promise<number>;\n /** Get a list of keys in plugin storage */\n keys(): Promise<(keyof Storage)[]>;\n}\n\n/** A config representative of the config outlined in doc.json */\nexport type PluginConfig<\n T extends Record<string, unknown> = Record<string, unknown>,\n> = T;\n\n/** An omegga plugin */\nexport default abstract class OmeggaPlugin<\n Config extends Record<string, unknown> = Record<string, unknown>,\n Storage extends Record<string, unknown> = Record<string, unknown>,\n> {\n omegga: OmeggaLike;\n config: PluginConfig<Config>;\n store: PluginStore<Storage>;\n\n constructor(\n omegga: OmeggaLike,\n config: PluginConfig<Config>,\n store: PluginStore<Storage>,\n ) {\n this.omegga = omegga;\n this.config = config;\n this.store = store;\n }\n\n /** Run when plugin starts, returns /commands it uses */\n abstract init(): Promise<void | { registeredCommands?: string[] }>;\n\n /** Run when plugin is stopped */\n abstract stop(): Promise<void>;\n\n /** Run when another plugin tries to interact with this plugin\n * @param event Event name\n * @param from Name of origin plugin\n * @return value other plugin expects\n */\n abstract pluginEvent?(\n event: string,\n from: string,\n ...args: any[]\n ): Promise<unknown>;\n}\n\nexport interface LogWrangling {\n /** Add a passive pattern on console output that invokes callback on match */\n addMatcher<T>(\n pattern: IMatcher<T>['pattern'],\n callback: IMatcher<T>['callback'],\n ): void;\n\n /** Run an active pattern on console output that resolves a match */\n addWatcher<T = RegExpMatchArray>(\n pattern: IWatcher<T>['pattern'],\n options?: {\n timeoutDelay?: number;\n bundle?: boolean;\n debounce?: boolean;\n afterMatchDelay?: number;\n last?: IWatcher<T>['last'];\n exec?: () => void;\n },\n ): Promise<IWatcher<T>['matches']>;\n\n /** Run a command and capture bundled output */\n watchLogChunk<T = string>(\n cmd: string,\n pattern: IWatcher<T>['pattern'],\n options?: {\n first?: 'index' | ((match: T) => boolean);\n last?: IWatcher<T>['last'];\n afterMatchDelay?: number;\n timeoutDelay?: number;\n },\n ): Promise<IWatcher<T>['matches']>;\n\n /** Run a command and capture bundled output for array functions */\n watchLogArray<\n Item extends Record<string, string> = Record<string, string>,\n Member extends Record<string, string> = Record<string, string>,\n >(\n cmd: string,\n itemPattern: RegExp,\n memberPattern: RegExp,\n ): Promise<{ item: Item; members: Member[] }[]>;\n}\n\nexport type WatcherPattern<T> = (\n line: string,\n match: RegExpMatchArray,\n) => T | RegExpMatchArray | '[OMEGGA_WATCHER_DONE]';\n\nexport type IMatcher<T> =\n | {\n pattern: RegExp;\n callback: (match: RegExpMatchArray) => boolean;\n }\n | {\n pattern: (line: string, match: RegExpMatchArray) => T;\n callback: (match: RegExpMatchArray) => T;\n };\n\nexport type IWatcher<T> = {\n bundle: boolean;\n debounce: boolean;\n timeoutDelay: number;\n afterMatchDelay: number;\n last: (match: T) => boolean;\n callback: () => void;\n resolve: (...args: any[]) => void;\n remove: () => void;\n done: () => void;\n timeout: ReturnType<typeof setTimeout>;\n} & (\n | {\n pattern: WatcherPattern<T>;\n matches: T[];\n }\n | {\n pattern: RegExp;\n matches: RegExpMatchArray[];\n }\n);\n\nexport declare type IPluginConfigDefinition = {\n description: string;\n} & (\n | {\n type: 'string' | 'password' | 'role';\n default: string;\n }\n | {\n type: 'boolean';\n default: boolean;\n }\n | {\n type: 'number';\n default: number;\n }\n | {\n type: 'enum';\n options: (string | number)[];\n default: string | number;\n }\n | {\n type: 'players';\n default: {\n id: string;\n name: string;\n };\n }\n | ({\n type: 'list';\n } & (\n | {\n itemType: 'string';\n default: string[];\n }\n | {\n itemType: 'number';\n default: number[];\n }\n | {\n itemType: 'enum';\n options: (string | number)[];\n default: string | number;\n }\n ))\n);\nexport interface IPluginCommandArgument {\n name: string;\n description: string;\n required?: boolean;\n}\nexport interface IPluginCommand {\n name: string;\n description: string;\n example?: string;\n args: IPluginCommandArgument[];\n}\nexport interface IPluginDocumentation {\n name: string;\n description: string;\n author: string;\n config: Record<string, IPluginConfigDefinition>;\n commands: IPluginCommand[];\n}\n\nexport interface PluginInterop {\n name: string;\n documentation: IPluginDocumentation;\n loaded: boolean;\n emitPlugin?(event: string, args: any[]): Promise<any>;\n}\n\nexport type WeaponClass =\n | 'Weapon_APCarbine'\n | 'Weapon_AntiMaterielRifle'\n | 'Weapon_ArmingSword'\n | 'Weapon_AssaultRifle'\n | 'Weapon_AutoShotgun'\n | 'Weapon_Battleaxe'\n | 'Weapon_Bazooka'\n | 'Weapon_BoltActionRifle'\n | 'Weapon_Bow'\n | 'Weapon_BoxPistol'\n | 'Weapon_BullpupRifle'\n | 'Weapon_BullpupSMG'\n | 'Weapon_ChargedLongsword'\n | 'Weapon_CrystalKalis'\n | 'Weapon_Derringer'\n | 'Weapon_Dynamite'\n | 'Weapon_FlintlockPistol'\n | 'Weapon_GrenadeLauncher'\n | 'Weapon_GuardPistol'\n | 'Weapon_Handaxe'\n | 'Weapon_HealthPotion'\n | 'Weapon_HeavyAssaultRifle'\n | 'Weapon_HeavySMG'\n | 'Weapon_HeroSword'\n | 'Weapon_HighPowerPistol'\n | 'Weapon_HoloBlade'\n | 'Weapon_HuntingShotgun'\n | 'Weapon_Ikakalaka'\n | 'Weapon_ImpactGrenade'\n | 'Weapon_ImpactGrenadeLauncher'\n | 'Weapon_ImpulseGrenade'\n | 'Weapon_Khopesh'\n | 'Weapon_Knife'\n | 'Weapon_LeverActionRifle'\n | 'Weapon_LightMachineGun'\n | 'Weapon_LongSword'\n | 'Weapon_MagnumPistol'\n | 'Weapon_MicroSMG'\n | 'Weapon_Minigun'\n | 'Weapon_Minigun'\n | 'Weapon_MissileLauncher'\n | 'Weapon_PDW'\n | 'Weapon_Pickaxe'\n | 'Weapon_PipeBomb'\n | 'Weapon_Pistol'\n | 'Weapon_PlasmaPistol'\n | 'Weapon_PlasmaSMG'\n | 'Weapon_PulseCarbine'\n | 'Weapon_PulseRifle'\n | 'Weapon_QuadLauncher'\n | 'Weapon_Revolver'\n | 'Weapon_RocketJumper'\n | 'Weapon_RocketLauncher'\n | 'Weapon_Sabre'\n | 'Weapon_SemiAutoRifle'\n | 'Weapon_ServiceRifle'\n | 'Weapon_Shotgun'\n | 'Weapon_SlugShotgun'\n | 'Weapon_Sniper'\n | 'Weapon_SodaCan'\n | 'Weapon_Spatha'\n | 'Weapon_SportingShotgun'\n | 'Weapon_StampedSMG'\n | 'Weapon_StandardSubmachineGun'\n | 'Weapon_StickGrenade'\n | 'Weapon_SubmachineGun'\n | 'Weapon_SuperShotgun'\n | 'Weapon_SuppressedAssaultRifle'\n | 'Weapon_SuppressedBullpupSMG'\n | 'Weapon_SuppressedGuardPistol'\n | 'Weapon_SuppressedHeavySMG'\n | 'Weapon_SuppressedMicroSMG'\n | 'Weapon_SuppressedPistol'\n | 'Weapon_SuppressedServiceRifle'\n | 'Weapon_TacticalSMG'\n | 'Weapon_TacticalShotgun'\n | 'Weapon_Tomahawk'\n | 'Weapon_TwinCannon'\n | 'Weapon_TypewriterSMG'\n | 'Weapon_Zweihander';\n"],"names":["util"],"mappings":";;;AAkBO,MAAM,uBAAuBA,MAAAA;AAw5BpC,MAA8B,aAG5B;AAAA,EAKA,YACE,QACA,QACA,OACA;AACA,SAAK,SAAS;AACd,SAAK,SAAS;AACd,SAAK,QAAQ;AAAA,EACf;AAkBF;;;"}
|
package/dist/updater/steam.js
CHANGED
|
@@ -80,7 +80,9 @@ function handleSteamError(err) {
|
|
|
80
80
|
}
|
|
81
81
|
async function steamcmdDownloadGame({
|
|
82
82
|
steambeta,
|
|
83
|
-
steambetaPassword
|
|
83
|
+
steambetaPassword,
|
|
84
|
+
retries = 3,
|
|
85
|
+
retryDelayMs = 5e3
|
|
84
86
|
} = {}) {
|
|
85
87
|
const hasCredentials = !!process.env.STEAM_USERNAME;
|
|
86
88
|
const steamLogin = hasCredentials ? `"${process.env.STEAM_USERNAME}"` : "anonymous";
|
|
@@ -100,32 +102,48 @@ async function steamcmdDownloadGame({
|
|
|
100
102
|
"+quit"
|
|
101
103
|
].filter(Boolean);
|
|
102
104
|
const cmd = `${softconfig.STEAMCMD_PATH} ${args.join(" ")}`;
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
logger.default.
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
105
|
+
const attempts = Math.max(1, retries);
|
|
106
|
+
let triedInteractiveLogin = false;
|
|
107
|
+
for (let attempt = 1; attempt <= attempts; attempt++) {
|
|
108
|
+
try {
|
|
109
|
+
node_child_process.execSync(cmd, { stdio: "inherit" });
|
|
110
|
+
return;
|
|
111
|
+
} catch (err) {
|
|
112
|
+
if (!triedInteractiveLogin && hasCredentials && err instanceof Error && "status" in err && err.status === 5) {
|
|
113
|
+
triedInteractiveLogin = true;
|
|
114
|
+
logger.default.warnp(
|
|
115
|
+
"Steam login failed. This may require Steam Guard authentication."
|
|
116
|
+
);
|
|
117
|
+
logger.default.warnp("Attempting interactive login...");
|
|
118
|
+
if (await steamcmdInteractiveLogin()) {
|
|
119
|
+
logger.default.logp("Login successful. Retrying download...");
|
|
120
|
+
try {
|
|
121
|
+
node_child_process.execSync(cmd, { stdio: "inherit" });
|
|
122
|
+
return;
|
|
123
|
+
} catch (retryErr) {
|
|
124
|
+
handleSteamError(retryErr);
|
|
125
|
+
err = retryErr;
|
|
126
|
+
}
|
|
127
|
+
} else {
|
|
128
|
+
logger.default.errorp(
|
|
129
|
+
"Interactive login failed. Run",
|
|
130
|
+
"omegga steamlogin".yellow,
|
|
131
|
+
"to authenticate manually."
|
|
132
|
+
);
|
|
119
133
|
}
|
|
120
134
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
135
|
+
handleSteamError(err);
|
|
136
|
+
if (attempt < attempts) {
|
|
137
|
+
logger.default.warnp(
|
|
138
|
+
`Steam download attempt ${attempt}/${attempts} failed; retrying in ${Math.round(
|
|
139
|
+
retryDelayMs / 1e3
|
|
140
|
+
)}s...`
|
|
141
|
+
);
|
|
142
|
+
await new Promise((resolve) => setTimeout(resolve, retryDelayMs));
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
throw err;
|
|
126
146
|
}
|
|
127
|
-
handleSteamError(err);
|
|
128
|
-
throw err;
|
|
129
147
|
}
|
|
130
148
|
}
|
|
131
149
|
function stripAnsi(s) {
|
|
@@ -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 retries = 3,\n retryDelayMs = 5000,\n}: {\n steambeta?: string;\n steambetaPassword?: string;\n /** number of attempts before giving up (steamcmd resumes partial downloads) */\n retries?: number;\n /** delay between retry attempts */\n retryDelayMs?: number;\n} = {}) {\n const hasCredentials = !!process.env.STEAM_USERNAME;\n const steamLogin = hasCredentials\n ? `\"${process.env.STEAM_USERNAME}\"`\n : 'anonymous';\n if (hasCredentials) {\n Logger.verbose('Using cached Steam credentials for download.');\n }\n const installDir = getSteamInstallDir();\n const appId = getAppId();\n\n Logger.verbose('Using with steam install dir:', installDir.yellow);\n Logger.verbose('Using with app ID:', appId.yellow);\n\n const args = [\n `+force_install_dir ${path.join(installDir, steambeta ?? 'main')}`,\n `+login ${steamLogin}`,\n `+app_update ${appId}`,\n steambeta ? `-beta ${steambeta}` : null,\n steambeta && steambetaPassword\n ? `-betapassword ${steambetaPassword}`\n : null,\n '+quit',\n ].filter(Boolean);\n\n const cmd = `${STEAMCMD_PATH} ${args.join(' ')}`;\n\n // steamcmd resumes partial downloads, and late-stage failures (e.g. \"state is\n // 0x406 after update job\") are frequently transient, so retry before giving up.\n const attempts = Math.max(1, retries);\n let triedInteractiveLogin = false;\n\n for (let attempt = 1; attempt <= attempts; attempt++) {\n try {\n execSync(cmd, { stdio: 'inherit' });\n return;\n } catch (err) {\n // Steam Guard interactive re-auth on status 5, attempted at most once.\n if (\n !triedInteractiveLogin &&\n hasCredentials &&\n err instanceof Error &&\n 'status' in err &&\n (err as NodeJS.ErrnoException & { status: number }).status === 5\n ) {\n triedInteractiveLogin = true;\n Logger.warnp(\n 'Steam login failed. This may require Steam Guard authentication.',\n );\n Logger.warnp('Attempting interactive login...');\n\n if (await steamcmdInteractiveLogin()) {\n Logger.logp('Login successful. Retrying download...');\n try {\n execSync(cmd, { stdio: 'inherit' });\n return;\n } catch (retryErr) {\n handleSteamError(retryErr);\n err = retryErr;\n }\n } else {\n Logger.errorp(\n 'Interactive login failed. Run',\n 'omegga steamlogin'.yellow,\n 'to authenticate manually.',\n );\n }\n }\n\n handleSteamError(err);\n\n if (attempt < attempts) {\n Logger.warnp(\n `Steam download attempt ${attempt}/${attempts} failed; retrying in ${Math.round(\n retryDelayMs / 1000,\n )}s...`,\n );\n await new Promise(resolve => setTimeout(resolve, retryDelayMs));\n continue;\n }\n\n throw err;\n }\n }\n}\n\nexport type SteamAppStatus = {\n installState: string;\n buildId: string;\n betaKey: string | null;\n updateState: string;\n};\n\nexport type SteamAppInfo = {\n branches: Record<string, { buildid: string; timeupdated?: string }>;\n};\n\nfunction stripAnsi(s: string): string {\n 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":";;;;;;;;AAQA,MAAM,mBAA2C;AAAA,EAC/C,GAAK;AAAA,EACL,GAAK;AAAA,EACL,GAAK;AAAA,EACL,GAAK;AAAA,EACL,IAAM;AAAA,EACN,IAAM;AAAA,EACN,IAAM;AAAA,EACN,KAAM;AAAA,EACN,KAAO;AAAA,EACP,KAAO;AAAA,EACP,MAAO;AAAA,EACP,MAAO;AAAA,EACP,MAAQ;AAAA,EACR,OAAS;AAAA,EACT,QAAS;AAAA,EACT,QAAS;AAAA,EACT,QAAS;AAAA,EACT,SAAU;AAAA,EACV,SAAU;AAAA,EACV,SAAU;AAAA,EACV,SAAU;AACZ;AAEA,SAAS,eAAe,OAAyB;AAC/C,QAAM,QAAkB,CAAA;AACxB,aAAW,CAAC,KAAK,IAAI,KAAK,OAAO,QAAQ,gBAAgB,GAAG;AAC1D,QAAI,QAAQ,OAAO,GAAG,EAAG,OAAM,KAAK,IAAI;AAAA,EAC1C;AACA,SAAO;AACT;AAEO,SAAS,uBAAuB;AACrCA,qBAAAA,SAAS,KAAK,KAAK,WAAW,iCAAiC,GAAG;AAAA,IAChE,OAAO;AAAA,EAAA,CACR;AACH;AAEA,eAAsB,2BAA6C;AACjE,MAAI,CAAC,QAAQ,IAAI,gBAAgB;AAC/BC,WAAAA,QAAO,MAAM,kDAAkD;AAC/D,WAAO;AAAA,EACT;AAEA,QAAM,WACJ,QAAQ,IAAI,mBAEV,MAAM,QAAQ;AAAA,IACZ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,SAAS,sBAAsB,QAAQ,IAAI,cAAc;AAAA,EAAA,CAC1D,GACD;AAEJ,MAAI,CAAC,UAAU;AACbA,WAAAA,QAAO,MAAM,uBAAuB;AACpC,WAAO;AAAA,EACT;AAEA,QAAM,SAASC,mBAAAA;AAAAA,IACbC,WAAAA;AAAAA,IACA,CAAC,UAAU,QAAQ,IAAI,gBAAgB,UAAU,OAAO;AAAA,IACxD,EAAE,OAAO,UAAA;AAAA,EAAU;AAGrB,SAAO,OAAO,WAAW;AAC3B;AAEA,SAAS,iBAAiB,KAAc;AACtC,MAAI,eAAe,SAAS,IAAI,SAAS;AACvC,UAAM,aAAa,IAAI,QAAQ;AAAA,MAC7B;AAAA,IAAA;AAEF,QAAI,YAAY;AACd,YAAM,QAAQ,SAAS,WAAW,CAAC,GAAG,EAAE;AACxC,YAAM,QAAQ,eAAe,KAAK;AAClCF,aAAAA,QAAO;AAAA,QACL,mBAAmB,WAAW,CAAC,CAAC,KAAK,KAAK;AAAA,QAC1C,MAAM,SAAS,IAAI,MAAM,KAAK,IAAI,IAAI;AAAA,MAAA;AAAA,IAE1C;AAEA,QAAI,UAAU,IAAI,QACf,QAAQ,+BAA+B,mBAAmB,EAC1D,QAAQ,yBAAyB,sBAAsB,EACvD,QAAQ,uBAAuB,0BAA0B;AAAA,EAC9D;AACF;AAEA,eAAsB,qBAAqB;AAAA,EACzC;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,eAAe;AACjB,IAOI,IAAI;AACN,QAAM,iBAAiB,CAAC,CAAC,QAAQ,IAAI;AACrC,QAAM,aAAa,iBACf,IAAI,QAAQ,IAAI,cAAc,MAC9B;AACJ,MAAI,gBAAgB;AAClBA,WAAAA,QAAO,QAAQ,8CAA8C;AAAA,EAC/D;AACA,QAAM,aAAaG,WAAAA,mBAAA;AACnB,QAAM,QAAQC,WAAAA,SAAA;AAEdJ,SAAAA,QAAO,QAAQ,iCAAiC,WAAW,MAAM;AACjEA,SAAAA,QAAO,QAAQ,sBAAsB,MAAM,MAAM;AAEjD,QAAM,OAAO;AAAA,IACX,sBAAsB,KAAK,KAAK,YAAY,aAAa,MAAM,CAAC;AAAA,IAChE,UAAU,UAAU;AAAA,IACpB,eAAe,KAAK;AAAA,IACpB,YAAY,SAAS,SAAS,KAAK;AAAA,IACnC,aAAa,oBACT,iBAAiB,iBAAiB,KAClC;AAAA,IACJ;AAAA,EAAA,EACA,OAAO,OAAO;AAEhB,QAAM,MAAM,GAAGE,wBAAa,IAAI,KAAK,KAAK,GAAG,CAAC;AAI9C,QAAM,WAAW,KAAK,IAAI,GAAG,OAAO;AACpC,MAAI,wBAAwB;AAE5B,WAAS,UAAU,GAAG,WAAW,UAAU,WAAW;AACpD,QAAI;AACFH,yBAAAA,SAAS,KAAK,EAAE,OAAO,UAAA,CAAW;AAClC;AAAA,IACF,SAAS,KAAK;AAEZ,UACE,CAAC,yBACD,kBACA,eAAe,SACf,YAAY,OACX,IAAmD,WAAW,GAC/D;AACA,gCAAwB;AACxBC,eAAAA,QAAO;AAAA,UACL;AAAA,QAAA;AAEFA,eAAAA,QAAO,MAAM,iCAAiC;AAE9C,YAAI,MAAM,4BAA4B;AACpCA,iBAAAA,QAAO,KAAK,wCAAwC;AACpD,cAAI;AACFD,+BAAAA,SAAS,KAAK,EAAE,OAAO,UAAA,CAAW;AAClC;AAAA,UACF,SAAS,UAAU;AACjB,6BAAiB,QAAQ;AACzB,kBAAM;AAAA,UACR;AAAA,QACF,OAAO;AACLC,iBAAAA,QAAO;AAAA,YACL;AAAA,YACA,oBAAoB;AAAA,YACpB;AAAA,UAAA;AAAA,QAEJ;AAAA,MACF;AAEA,uBAAiB,GAAG;AAEpB,UAAI,UAAU,UAAU;AACtBA,eAAAA,QAAO;AAAA,UACL,0BAA0B,OAAO,IAAI,QAAQ,wBAAwB,KAAK;AAAA,YACxE,eAAe;AAAA,UAAA,CAChB;AAAA,QAAA;AAEH,cAAM,IAAI,QAAQ,CAAA,YAAW,WAAW,SAAS,YAAY,CAAC;AAC9D;AAAA,MACF;AAEA,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAaA,SAAS,UAAU,GAAmB;AACpC,SAAO,EAAE,QAAQ,mBAAmB,EAAE;AACxC;AAEA,SAAS,eAAe,QAAuC;AAC7D,QAAM,QAAQ,UAAU,MAAM;AAC9B,QAAM,aAAa,MAAM,MAAM,iBAAiB;AAChD,QAAM,aAAa,MAAM,MAAM,wBAAwB;AACvD,QAAM,YAAY,MAAM,MAAM,uBAAuB;AACrD,QAAM,cAAc,MAAM,MAAM,uBAAuB;AACvD,MAAI,CAAC,WAAY,QAAO;AACxB,SAAO;AAAA,IACL,cAAc,aAAa,CAAC,GAAG,OAAO,QAAQ,MAAM,EAAE,KAAK;AAAA,IAC3D,SAAS,WAAW,CAAC;AAAA,IACrB,SAAS,YAAY,CAAC,KAAK;AAAA,IAC3B,aAAa,cAAc,CAAC,GAAG,UAAU;AAAA,EAAA;AAE7C;AAEA,SAAS,aAAa,QAAgB,OAAoC;AACxE,QAAM,QAAQ,UAAU,MAAM;AAC9B,QAAM,WAAW,MAAM,QAAQ,IAAI,KAAK,GAAG;AAC3C,MAAI,aAAa,GAAI,QAAO;AAE5B,QAAM,aAAa,MAAM,QAAQ,KAAK,QAAQ;AAC9C,MAAI,eAAe,GAAI,QAAO;AAE9B,MAAI,QAAQ;AACZ,MAAI,WAAW;AACf,WAAS,IAAI,YAAY,IAAI,MAAM,QAAQ,KAAK;AAC9C,QAAI,MAAM,CAAC,MAAM,IAAK;AACtB,QAAI,MAAM,CAAC,MAAM,IAAK;AACtB,QAAI,UAAU,GAAG;AACf,iBAAW;AACX;AAAA,IACF;AAAA,EACF;AACA,MAAI,aAAa,GAAI,QAAO;AAE5B,MAAI;AACF,UAAM,MAAM,IAAI,KAAK;AAAA,EAAM,MAAM,MAAM,YAAY,WAAW,CAAC,CAAC;AAAA;AAChE,UAAM,SAAS,UAAU,OAAO,GAAG;AACnC,UAAM,WAAW,SAAS,KAAK,GAAG,QAAQ;AAC1C,QAAI,CAAC,YAAY,OAAO,aAAa,SAAU,QAAO;AACtD,WAAO,EAAE,SAAA;AAAA,EACX,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAQO,SAAS,oBACd,WACyB;AACzB,QAAM,QAAQI,WAAAA,SAAA;AACd,QAAM,aAAa,KAAK,KAAKD,WAAAA,mBAAA,GAAsB,aAAa,MAAM;AACtE,QAAM,aAAa,QAAQ,IAAI,iBAC3B,IAAI,QAAQ,IAAI,cAAc,MAC9B;AAEJ,MAAI;AACF,UAAM,SAASJ,mBAAAA;AAAAA,MACb;AAAA,QACEG,WAAAA;AAAAA,QACA,sBAAsB,UAAU;AAAA,QAChC,UAAU,UAAU;AAAA,QACpB;AAAA,QACA,mBAAmB,KAAK;AAAA,QACxB,eAAe,KAAK;AAAA,QACpB;AAAA,MAAA,EACA,KAAK,GAAG;AAAA,MACV,EAAE,UAAU,SAAS,OAAO,CAAC,WAAW,QAAQ,MAAM,GAAG,SAAS,IAAA;AAAA,IAAM;AAG1E,UAAM,QAAQ,eAAe,MAAM;AACnC,QAAI,CAAC,MAAO,QAAO;AAEnB,UAAM,SAAS,aAAa,QAAQ,KAAK;AAEzC,UAAM,SAAS,cAAc,MAAM,WAAW;AAC9C,UAAM,eAAe,UAAU,WAAW,SAAS,SAAS;AAC5D,UAAM,gBAAgB,QAAQ,WAAW,YAAY,GAAG;AAExD,UAAM,YACJ,iBAAiB,OAAO,kBAAkB,MAAM,UAAU;AAE5D,WAAO,EAAE,OAAO,QAAQ,UAAA;AAAA,EAC1B,SAAS,KAAK;AACZF,mBAAO,QAAQ,kDAAkD,GAAG;AACpE,WAAO;AAAA,EACT;AACF;AAEA,IAAI,kBAAkB;AACtB,IAAI,sBAAsB;AAC1B,IAAI,mBAAmB;AAEhB,MAAM,0BAA0B,OAAO;AAAA,EAC5C,SAAS;AAAA,EACT,WAAW;AAAA,EACX,QAAQ;AACV;AAEO,MAAM,4BAA4B,MAAM;AAC7C,qBAAmB;AACrB;AAEA,eAAsB,eAAe,WAAoB;AACvD,oBAAkB,KAAK,IAAA;AAEvB,QAAM,QAAQ,oBAAoB,SAAS;AAC3C,MAAI,CAAC,MAAO,QAAO;AAEnB,wBAAsB,KAAK,IAAA;AAE3B,MAAI,MAAM,aAAa,MAAM;AAC3B,uBAAmB,MAAM;AACzB,WAAO,MAAM;AAAA,EACf;AAEA,SAAO;AACT;;;;;;;;"}
|
|
@@ -147,7 +147,8 @@ function setupMetrics(server) {
|
|
|
147
147
|
logger.default.logp("Downloading update...");
|
|
148
148
|
try {
|
|
149
149
|
await steam.steamcmdDownloadGame({
|
|
150
|
-
steambeta: omegga.config.server?.steambeta
|
|
150
|
+
steambeta: omegga.config.server?.steambeta,
|
|
151
|
+
steambetaPassword: omegga.config.server?.steambetaPassword
|
|
151
152
|
});
|
|
152
153
|
} catch (e) {
|
|
153
154
|
error("An error occurred while downloading the update:", e);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metrics.js","sources":["../../../src/webserver/backend/metrics.ts"],"sourcesContent":["import Logger from '@/logger';\nimport { AutoRestartConfig, IServerStatus } from '@/plugin';\nimport soft from '@/softconfig';\nimport {\n clearLastSteamUpdateCheck,\n getLastSteamUpdateCheck,\n hasSteamUpdate,\n steamcmdDownloadGame,\n} from '@/updater/steam';\nimport { readFileSync, statfsSync } from 'fs';\nimport os from 'os';\nimport { serverEvents } from './events';\nimport type Webserver from './index';\nimport type { IStoreAutoRestartConfig, SystemUtilization } from './types';\n\nlet prevCpuIdle = 0;\nlet prevCpuTotal = 0;\nlet prevNetRx = 0;\nlet prevNetTx = 0;\nlet prevNetTime = 0;\n\nfunction getCpuUsage(): number {\n const cpus = os.cpus();\n let idle = 0;\n let total = 0;\n for (const cpu of cpus) {\n idle += cpu.times.idle;\n total +=\n cpu.times.user +\n cpu.times.nice +\n cpu.times.sys +\n cpu.times.irq +\n cpu.times.idle;\n }\n const dIdle = idle - prevCpuIdle;\n const dTotal = total - prevCpuTotal;\n prevCpuIdle = idle;\n prevCpuTotal = total;\n if (dTotal === 0) return 0;\n return Math.round((1 - dIdle / dTotal) * 100);\n}\n\nfunction getDiskUsage(path: string): { used: number; total: number } {\n try {\n const stat = statfsSync(path);\n const total = stat.blocks * stat.bsize;\n const free = stat.bfree * stat.bsize;\n return { used: total - free, total };\n } catch {\n return { used: 0, total: 1 };\n }\n}\n\nfunction getNetworkBytes(): { rx: number; tx: number } {\n if (process.platform !== 'linux') return { rx: 0, tx: 0 };\n try {\n const data = readFileSync('/proc/net/dev', 'utf8');\n let rx = 0;\n let tx = 0;\n for (const line of data.split('\\n').slice(2)) {\n const parts = line.trim().split(/\\s+/);\n if (!parts[0] || parts[0] === 'lo:') continue;\n rx += Number(parts[1]) || 0;\n tx += Number(parts[9]) || 0;\n }\n return { rx, tx };\n } catch {\n return { rx: 0, tx: 0 };\n }\n}\n\nconst UTILIZATION_HISTORY_LEN = 30;\nlet lastUtilization: SystemUtilization | null = null;\nconst utilizationHistory: SystemUtilization[] = [];\n\nexport function getLastUtilization(): {\n current: SystemUtilization | null;\n history: SystemUtilization[];\n} {\n return { current: lastUtilization, history: utilizationHistory };\n}\n\nfunction pushHistory(util: SystemUtilization) {\n utilizationHistory.push(util);\n if (utilizationHistory.length > UTILIZATION_HISTORY_LEN) {\n utilizationHistory.shift();\n }\n}\n\nfunction collectUtilization(omeggaPath: string): SystemUtilization {\n const cpu = getCpuUsage();\n const totalMem = os.totalmem();\n const freeMem = os.freemem();\n const disk = getDiskUsage(omeggaPath);\n const net = getNetworkBytes();\n const now = Date.now();\n const dt = prevNetTime ? (now - prevNetTime) / 1000 : 1;\n const rxSec = prevNetTime ? Math.max(0, (net.rx - prevNetRx) / dt) : 0;\n const txSec = prevNetTime ? Math.max(0, (net.tx - prevNetTx) / dt) : 0;\n prevNetRx = net.rx;\n prevNetTx = net.tx;\n prevNetTime = now;\n return {\n cpu,\n mem: { used: totalMem - freeMem, total: totalMem },\n disk,\n net: { rxSec, txSec },\n };\n}\n\nconst error = (...args: any[]) => Logger.error(...args);\nlet lastRestart = 0;\n\nconst sleep = t => new Promise(resolve => setTimeout(resolve, t));\n\nexport default function (server: Webserver) {\n const { database, omegga } = server;\n\n // Prime CPU and network counters so the first real snapshot reflects a\n // short interval rather than computing a delta from zero (host uptime).\n getCpuUsage();\n getNetworkBytes();\n\n // Collect initial utilization snapshot so queries return data immediately\n lastUtilization = collectUtilization(omegga.path);\n pushHistory(lastUtilization);\n\n // server status is checked every minute\n clearInterval(server.serverStatusInterval);\n // heartbeat happens every 60 seconds\n let empties = 0;\n\n // last heartbeat hour\n let lastHour = -1;\n // players that have joined in the last hour\n let hourlyPlayers: string[] = [];\n\n const exitOnStop = () => {\n if (omegga.stopping || !omegga.started)\n throw new Error('Omegga is already closing');\n };\n\n async function restartServer(\n config: IStoreAutoRestartConfig,\n update?: boolean,\n ) {\n lastRestart = Date.now();\n const iconfig: AutoRestartConfig = {\n players: config.playersEnabled,\n announcement: config.announcementEnabled,\n saveWorld: config.saveWorld,\n };\n omegga.emit('autorestart', iconfig);\n await sleep(1000);\n exitOnStop();\n\n const action = update ? 'Updating' : 'Restarting';\n\n if (config.announcementEnabled) {\n database.addChatLog('server', {}, action + ' in 30 seconds...');\n Logger.logp(action + ' in 30 seconds...');\n const announce = (t: number) =>\n omegga.broadcast(\n `<size=\"20\">${action} in <b><color=\"ffffbb\">${t} second${\n t !== 1 ? 's' : ''\n }</></></>`,\n );\n announce(30);\n await sleep(15000);\n announce(15);\n await sleep(10000);\n announce(5);\n await sleep(1000);\n announce(4);\n await sleep(1000);\n announce(3);\n await sleep(1000);\n announce(2);\n await sleep(1000);\n announce(1);\n await sleep(1000);\n }\n\n exitOnStop();\n\n await omegga.saveServer(iconfig);\n\n Logger.logp(action + '...');\n database.addChatLog('server', {}, action + ' server...');\n omegga.once('mapchange', () => {\n omegga.restoreServer();\n });\n\n if (update && omegga.config.__STEAM) {\n Logger.logp('Stopping server for auto-update...');\n await omegga.stop();\n Logger.logp('Downloading update...');\n try {\n await steamcmdDownloadGame({\n steambeta: omegga.config.server?.steambeta,\n });\n } catch (e) {\n error('An error occurred while downloading the update:', e);\n }\n Logger.logp('Starting server after update...');\n await omegga.start();\n } else {\n await omegga.restartServer();\n }\n }\n\n async function checkAutoRestart(status: IServerStatus) {\n const now = new Date();\n const currHour = now.getHours();\n const currMinute = now.getMinutes();\n\n /// skip restart check if we sent restart command within 5 minutes\n if (lastRestart > now.getTime() - 5 * 60 * 1000) {\n Logger.verbose('Skipping autorestart');\n return;\n }\n\n const config = await database.getAutoRestartConfig();\n Logger.verbose('Autorestart check', config);\n\n const uptimeMinutes = Math.floor(status.time / 1000 / 60);\n const uptimeHours = Math.floor(uptimeMinutes / 60);\n\n if (config.maxUptimeEnabled && uptimeHours >= config.maxUptime) {\n Logger.verbose('Restarting due to max uptime');\n return await restartServer(config);\n }\n\n if (\n config.emptyUptimeEnabled &&\n uptimeHours >= config.emptyUptime &&\n status.players.length === 0\n ) {\n Logger.verbose('Restarting due to max empty uptime');\n return await restartServer(config);\n }\n\n if (\n config.dailyHourEnabled &&\n currHour === config.dailyHour &&\n uptimeMinutes > currMinute\n ) {\n Logger.verbose('Restarting due to daily schedule');\n return await restartServer(config);\n }\n\n const lastCheck = getLastSteamUpdateCheck();\n if (omegga.config.__STEAM && config.autoUpdateEnabled) {\n const now = Date.now();\n if (\n !lastCheck.result &&\n now - lastCheck.attempt < config.autoUpdateIntervalMins * 60 * 1000\n ) {\n Logger.verbose(\n `Skipping auto update check, last check was ${Math.floor(\n (now - lastCheck.attempt) / 1000 / 60,\n )} minutes ago`,\n );\n return;\n }\n Logger.verbose('Checking for steam update');\n const hasUpdate = await hasSteamUpdate(omegga.config.server?.steambeta);\n if (hasUpdate) {\n clearLastSteamUpdateCheck();\n return await restartServer(config, true);\n }\n Logger.verbose('No steam update found');\n }\n }\n\n server.lastReportedStatus = null;\n server.serverStatusInterval = setInterval(async () => {\n if (!omegga.started) return;\n try {\n // get the server status\n const status = await omegga.getServerStatus();\n if (!status) return;\n\n try {\n await checkAutoRestart(status);\n } catch (err) {\n error('Error in autorestart check', err);\n }\n\n // get players by id\n const players = status.players.map(p => p.id);\n\n // send the unaltered status to the frontend via serverEvents\n server.lastReportedStatus = status;\n serverEvents.emit('heartbeat', status);\n\n // collect and emit system utilization metrics\n const utilization = collectUtilization(omegga.path);\n lastUtilization = utilization;\n pushHistory(utilization);\n serverEvents.emit('utilization', utilization);\n try {\n omegga.emit('metrics:heartbeat', status);\n } catch (e) {\n // prevent the omegga callback handlers from crashing this\n error('Error in heartbeat emit', e);\n }\n\n // stop recording metrics after 3 empty server statuses\n if (\n players.length === 0 &&\n ++empties > soft.METRIC_EMPTIES_BEFORE_PAUSE\n ) {\n return;\n }\n\n const now = new Date();\n const hour = now.getUTCHours();\n // check if it's a new hour (for punchcard unique player tracking)\n if (hour !== lastHour) {\n lastHour = hour;\n hourlyPlayers = [];\n }\n\n // find all the players unique to this hour\n const newPlayers = players.filter(p => !hourlyPlayers.includes(p));\n if (newPlayers.length > 0) {\n // update the punchcard\n await database.updatePlayerPunchcard(newPlayers.length);\n // mark those players as previously joined players\n hourlyPlayers.push(...newPlayers);\n }\n\n // server is not empty, reset the counter\n empties = 0;\n\n const data = {\n // number of bricks\n bricks: status.bricks,\n // unique players by id\n players: players.filter((p, i) => players.indexOf(p) === i),\n // addresses by player id\n ips: Object.fromEntries(status.players.map(p => [p.id, p.address])),\n };\n\n // hand the server status off to the database\n await database.addHeartbeat(data);\n } catch (e) {\n // probably an issue getting server status\n error('Server Not Responding...', e);\n }\n }, soft.METRIC_HEARTBEAT_INTERVAL);\n\n // chat events\n omegga.on('chat', async (name, message) => {\n const p = omegga.getPlayer(name);\n const user = {\n id: p.id,\n name,\n displayName: p.displayName,\n color: p.getNameColor(),\n };\n\n // tell web users about a chat message\n serverEvents.emit('chat', await database.addChatLog('msg', user, message));\n });\n\n // player leave events\n omegga.on('leave', async ({ id, name, displayName }) => {\n // update realtime player count subscribers (the player has already been\n // removed from omegga.players by the time 'leave' fires)\n serverEvents.emit('playerCount', omegga.players.length);\n\n // tell web users a player left\n serverEvents.emit(\n 'chat',\n await database.addChatLog('leave', { id, name, displayName }),\n );\n });\n\n // player join events\n omegga.on('join', async ({ id, name, displayName }) => {\n // add the visit to the database\n const isFirst = await database.addVisit({ id, name, displayName });\n\n // update realtime player count subscribers (the awaited DB call above means\n // omegga.players has been pushed to by the time we read it here)\n serverEvents.emit('playerCount', omegga.players.length);\n\n // tell web users a player joined (and if it's their first time joining)\n serverEvents.emit(\n 'chat',\n await database.addChatLog('join', {\n id,\n name,\n displayName,\n ...(isFirst ? { isFirst } : {}),\n }),\n );\n });\n\n // tell web users plugin status\n omegga.on(\n 'plugin:status',\n (\n shortPath: string,\n info: { name: string; isLoaded: boolean; isEnabled: boolean },\n ) => {\n serverEvents.emit('plugin', { shortPath, ...info });\n },\n );\n\n // server status events\n omegga.on('start', () =>\n serverEvents.emit('serverStatus', {\n started: true,\n starting: false,\n stopping: false,\n }),\n );\n omegga.on('server:starting', () =>\n serverEvents.emit('serverStatus', {\n started: false,\n starting: true,\n stopping: false,\n }),\n );\n omegga.on('mapchange', () =>\n serverEvents.emit('serverStatus', {\n started: true,\n starting: false,\n stopping: false,\n }),\n );\n omegga.on('server:stopped', () => {\n serverEvents.emit('serverStatus', {\n started: false,\n starting: false,\n stopping: false,\n });\n // a stopped server has no players - reset the realtime count to 0.\n // ('server:stopped' fires before omegga.players is cleared, so emit 0\n // explicitly rather than reading the still-stale length)\n serverEvents.emit('playerCount', 0);\n });\n omegga.on('server:stopping', () =>\n serverEvents.emit('serverStatus', {\n started: true,\n starting: false,\n stopping: true,\n }),\n );\n}\n"],"names":["statfsSync","readFileSync","Logger","steamcmdDownloadGame","getLastSteamUpdateCheck","now","hasSteamUpdate","clearLastSteamUpdateCheck","serverEvents","soft"],"mappings":";;;;;;;;AAeA,IAAI,cAAc;AAClB,IAAI,eAAe;AACnB,IAAI,YAAY;AAChB,IAAI,YAAY;AAChB,IAAI,cAAc;AAElB,SAAS,cAAsB;AAC7B,QAAM,OAAO,GAAG,KAAA;AAChB,MAAI,OAAO;AACX,MAAI,QAAQ;AACZ,aAAW,OAAO,MAAM;AACtB,YAAQ,IAAI,MAAM;AAClB,aACE,IAAI,MAAM,OACV,IAAI,MAAM,OACV,IAAI,MAAM,MACV,IAAI,MAAM,MACV,IAAI,MAAM;AAAA,EACd;AACA,QAAM,QAAQ,OAAO;AACrB,QAAM,SAAS,QAAQ;AACvB,gBAAc;AACd,iBAAe;AACf,MAAI,WAAW,EAAG,QAAO;AACzB,SAAO,KAAK,OAAO,IAAI,QAAQ,UAAU,GAAG;AAC9C;AAEA,SAAS,aAAa,MAA+C;AACnE,MAAI;AACF,UAAM,OAAOA,GAAAA,WAAW,IAAI;AAC5B,UAAM,QAAQ,KAAK,SAAS,KAAK;AACjC,UAAM,OAAO,KAAK,QAAQ,KAAK;AAC/B,WAAO,EAAE,MAAM,QAAQ,MAAM,MAAA;AAAA,EAC/B,QAAQ;AACN,WAAO,EAAE,MAAM,GAAG,OAAO,EAAA;AAAA,EAC3B;AACF;AAEA,SAAS,kBAA8C;AACrD,MAAI,QAAQ,aAAa,QAAS,QAAO,EAAE,IAAI,GAAG,IAAI,EAAA;AACtD,MAAI;AACF,UAAM,OAAOC,GAAAA,aAAa,iBAAiB,MAAM;AACjD,QAAI,KAAK;AACT,QAAI,KAAK;AACT,eAAW,QAAQ,KAAK,MAAM,IAAI,EAAE,MAAM,CAAC,GAAG;AAC5C,YAAM,QAAQ,KAAK,KAAA,EAAO,MAAM,KAAK;AACrC,UAAI,CAAC,MAAM,CAAC,KAAK,MAAM,CAAC,MAAM,MAAO;AACrC,YAAM,OAAO,MAAM,CAAC,CAAC,KAAK;AAC1B,YAAM,OAAO,MAAM,CAAC,CAAC,KAAK;AAAA,IAC5B;AACA,WAAO,EAAE,IAAI,GAAA;AAAA,EACf,QAAQ;AACN,WAAO,EAAE,IAAI,GAAG,IAAI,EAAA;AAAA,EACtB;AACF;AAEA,MAAM,0BAA0B;AAChC,IAAI,kBAA4C;AAChD,MAAM,qBAA0C,CAAA;AAEzC,SAAS,qBAGd;AACA,SAAO,EAAE,SAAS,iBAAiB,SAAS,mBAAA;AAC9C;AAEA,SAAS,YAAY,MAAyB;AAC5C,qBAAmB,KAAK,IAAI;AAC5B,MAAI,mBAAmB,SAAS,yBAAyB;AACvD,uBAAmB,MAAA;AAAA,EACrB;AACF;AAEA,SAAS,mBAAmB,YAAuC;AACjE,QAAM,MAAM,YAAA;AACZ,QAAM,WAAW,GAAG,SAAA;AACpB,QAAM,UAAU,GAAG,QAAA;AACnB,QAAM,OAAO,aAAa,UAAU;AACpC,QAAM,MAAM,gBAAA;AACZ,QAAM,MAAM,KAAK,IAAA;AACjB,QAAM,KAAK,eAAe,MAAM,eAAe,MAAO;AACtD,QAAM,QAAQ,cAAc,KAAK,IAAI,IAAI,IAAI,KAAK,aAAa,EAAE,IAAI;AACrE,QAAM,QAAQ,cAAc,KAAK,IAAI,IAAI,IAAI,KAAK,aAAa,EAAE,IAAI;AACrE,cAAY,IAAI;AAChB,cAAY,IAAI;AAChB,gBAAc;AACd,SAAO;AAAA,IACL;AAAA,IACA,KAAK,EAAE,MAAM,WAAW,SAAS,OAAO,SAAA;AAAA,IACxC;AAAA,IACA,KAAK,EAAE,OAAO,MAAA;AAAA,EAAM;AAExB;AAEA,MAAM,QAAQ,IAAI,SAAgBC,OAAAA,QAAO,MAAM,GAAG,IAAI;AACtD,IAAI,cAAc;AAElB,MAAM,QAAQ,OAAK,IAAI,QAAQ,aAAW,WAAW,SAAS,CAAC,CAAC;AAEhE,SAAA,aAAyB,QAAmB;AAC1C,QAAM,EAAE,UAAU,OAAA,IAAW;AAI7B,cAAA;AACA,kBAAA;AAGA,oBAAkB,mBAAmB,OAAO,IAAI;AAChD,cAAY,eAAe;AAG3B,gBAAc,OAAO,oBAAoB;AAEzC,MAAI,UAAU;AAGd,MAAI,WAAW;AAEf,MAAI,gBAA0B,CAAA;AAE9B,QAAM,aAAa,MAAM;AACvB,QAAI,OAAO,YAAY,CAAC,OAAO;AAC7B,YAAM,IAAI,MAAM,2BAA2B;AAAA,EAC/C;AAEA,iBAAe,cACb,QACA,QACA;AACA,kBAAc,KAAK,IAAA;AACnB,UAAM,UAA6B;AAAA,MACjC,SAAS,OAAO;AAAA,MAChB,cAAc,OAAO;AAAA,MACrB,WAAW,OAAO;AAAA,IAAA;AAEpB,WAAO,KAAK,eAAe,OAAO;AAClC,UAAM,MAAM,GAAI;AAChB,eAAA;AAEA,UAAM,SAAS,SAAS,aAAa;AAErC,QAAI,OAAO,qBAAqB;AAC9B,eAAS,WAAW,UAAU,CAAA,GAAI,SAAS,mBAAmB;AAC9DA,qBAAO,KAAK,SAAS,mBAAmB;AACxC,YAAM,WAAW,CAAC,MAChB,OAAO;AAAA,QACL,cAAc,MAAM,0BAA0B,CAAC,UAC7C,MAAM,IAAI,MAAM,EAClB;AAAA,MAAA;AAEJ,eAAS,EAAE;AACX,YAAM,MAAM,IAAK;AACjB,eAAS,EAAE;AACX,YAAM,MAAM,GAAK;AACjB,eAAS,CAAC;AACV,YAAM,MAAM,GAAI;AAChB,eAAS,CAAC;AACV,YAAM,MAAM,GAAI;AAChB,eAAS,CAAC;AACV,YAAM,MAAM,GAAI;AAChB,eAAS,CAAC;AACV,YAAM,MAAM,GAAI;AAChB,eAAS,CAAC;AACV,YAAM,MAAM,GAAI;AAAA,IAClB;AAEA,eAAA;AAEA,UAAM,OAAO,WAAW,OAAO;AAE/BA,mBAAO,KAAK,SAAS,KAAK;AAC1B,aAAS,WAAW,UAAU,CAAA,GAAI,SAAS,YAAY;AACvD,WAAO,KAAK,aAAa,MAAM;AAC7B,aAAO,cAAA;AAAA,IACT,CAAC;AAED,QAAI,UAAU,OAAO,OAAO,SAAS;AACnCA,aAAAA,QAAO,KAAK,oCAAoC;AAChD,YAAM,OAAO,KAAA;AACbA,aAAAA,QAAO,KAAK,uBAAuB;AACnC,UAAI;AACF,cAAMC,2BAAqB;AAAA,UACzB,WAAW,OAAO,OAAO,QAAQ;AAAA,QAAA,CAClC;AAAA,MACH,SAAS,GAAG;AACV,cAAM,mDAAmD,CAAC;AAAA,MAC5D;AACAD,aAAAA,QAAO,KAAK,iCAAiC;AAC7C,YAAM,OAAO,MAAA;AAAA,IACf,OAAO;AACL,YAAM,OAAO,cAAA;AAAA,IACf;AAAA,EACF;AAEA,iBAAe,iBAAiB,QAAuB;AACrD,UAAM,0BAAU,KAAA;AAChB,UAAM,WAAW,IAAI,SAAA;AACrB,UAAM,aAAa,IAAI,WAAA;AAGvB,QAAI,cAAc,IAAI,QAAA,IAAY,IAAI,KAAK,KAAM;AAC/CA,aAAAA,QAAO,QAAQ,sBAAsB;AACrC;AAAA,IACF;AAEA,UAAM,SAAS,MAAM,SAAS,qBAAA;AAC9BA,mBAAO,QAAQ,qBAAqB,MAAM;AAE1C,UAAM,gBAAgB,KAAK,MAAM,OAAO,OAAO,MAAO,EAAE;AACxD,UAAM,cAAc,KAAK,MAAM,gBAAgB,EAAE;AAEjD,QAAI,OAAO,oBAAoB,eAAe,OAAO,WAAW;AAC9DA,aAAAA,QAAO,QAAQ,8BAA8B;AAC7C,aAAO,MAAM,cAAc,MAAM;AAAA,IACnC;AAEA,QACE,OAAO,sBACP,eAAe,OAAO,eACtB,OAAO,QAAQ,WAAW,GAC1B;AACAA,aAAAA,QAAO,QAAQ,oCAAoC;AACnD,aAAO,MAAM,cAAc,MAAM;AAAA,IACnC;AAEA,QACE,OAAO,oBACP,aAAa,OAAO,aACpB,gBAAgB,YAChB;AACAA,aAAAA,QAAO,QAAQ,kCAAkC;AACjD,aAAO,MAAM,cAAc,MAAM;AAAA,IACnC;AAEA,UAAM,YAAYE,MAAAA,wBAAA;AAClB,QAAI,OAAO,OAAO,WAAW,OAAO,mBAAmB;AACrD,YAAMC,OAAM,KAAK,IAAA;AACjB,UACE,CAAC,UAAU,UACXA,OAAM,UAAU,UAAU,OAAO,yBAAyB,KAAK,KAC/D;AACAH,eAAAA,QAAO;AAAA,UACL,8CAA8C,KAAK;AAAA,aAChDG,OAAM,UAAU,WAAW,MAAO;AAAA,UAAA,CACpC;AAAA,QAAA;AAEH;AAAA,MACF;AACAH,aAAAA,QAAO,QAAQ,2BAA2B;AAC1C,YAAM,YAAY,MAAMI,MAAAA,eAAe,OAAO,OAAO,QAAQ,SAAS;AACtE,UAAI,WAAW;AACbC,wCAAA;AACA,eAAO,MAAM,cAAc,QAAQ,IAAI;AAAA,MACzC;AACAL,aAAAA,QAAO,QAAQ,uBAAuB;AAAA,IACxC;AAAA,EACF;AAEA,SAAO,qBAAqB;AAC5B,SAAO,uBAAuB,YAAY,YAAY;AACpD,QAAI,CAAC,OAAO,QAAS;AACrB,QAAI;AAEF,YAAM,SAAS,MAAM,OAAO,gBAAA;AAC5B,UAAI,CAAC,OAAQ;AAEb,UAAI;AACF,cAAM,iBAAiB,MAAM;AAAA,MAC/B,SAAS,KAAK;AACZ,cAAM,8BAA8B,GAAG;AAAA,MACzC;AAGA,YAAM,UAAU,OAAO,QAAQ,IAAI,CAAA,MAAK,EAAE,EAAE;AAG5C,aAAO,qBAAqB;AAC5BM,0BAAa,KAAK,aAAa,MAAM;AAGrC,YAAM,cAAc,mBAAmB,OAAO,IAAI;AAClD,wBAAkB;AAClB,kBAAY,WAAW;AACvBA,0BAAa,KAAK,eAAe,WAAW;AAC5C,UAAI;AACF,eAAO,KAAK,qBAAqB,MAAM;AAAA,MACzC,SAAS,GAAG;AAEV,cAAM,2BAA2B,CAAC;AAAA,MACpC;AAGA,UACE,QAAQ,WAAW,KACnB,EAAE,UAAUC,WAAAA,QAAK,6BACjB;AACA;AAAA,MACF;AAEA,YAAM,0BAAU,KAAA;AAChB,YAAM,OAAO,IAAI,YAAA;AAEjB,UAAI,SAAS,UAAU;AACrB,mBAAW;AACX,wBAAgB,CAAA;AAAA,MAClB;AAGA,YAAM,aAAa,QAAQ,OAAO,CAAA,MAAK,CAAC,cAAc,SAAS,CAAC,CAAC;AACjE,UAAI,WAAW,SAAS,GAAG;AAEzB,cAAM,SAAS,sBAAsB,WAAW,MAAM;AAEtD,sBAAc,KAAK,GAAG,UAAU;AAAA,MAClC;AAGA,gBAAU;AAEV,YAAM,OAAO;AAAA;AAAA,QAEX,QAAQ,OAAO;AAAA;AAAA,QAEf,SAAS,QAAQ,OAAO,CAAC,GAAG,MAAM,QAAQ,QAAQ,CAAC,MAAM,CAAC;AAAA;AAAA,QAE1D,KAAK,OAAO,YAAY,OAAO,QAAQ,IAAI,CAAA,MAAK,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;AAAA,MAAA;AAIpE,YAAM,SAAS,aAAa,IAAI;AAAA,IAClC,SAAS,GAAG;AAEV,YAAM,4BAA4B,CAAC;AAAA,IACrC;AAAA,EACF,GAAGA,WAAAA,QAAK,yBAAyB;AAGjC,SAAO,GAAG,QAAQ,OAAO,MAAM,YAAY;AACzC,UAAM,IAAI,OAAO,UAAU,IAAI;AAC/B,UAAM,OAAO;AAAA,MACX,IAAI,EAAE;AAAA,MACN;AAAA,MACA,aAAa,EAAE;AAAA,MACf,OAAO,EAAE,aAAA;AAAA,IAAa;AAIxBD,wBAAa,KAAK,QAAQ,MAAM,SAAS,WAAW,OAAO,MAAM,OAAO,CAAC;AAAA,EAC3E,CAAC;AAGD,SAAO,GAAG,SAAS,OAAO,EAAE,IAAI,MAAM,kBAAkB;AAGtDA,WAAAA,aAAa,KAAK,eAAe,OAAO,QAAQ,MAAM;AAGtDA,WAAAA,aAAa;AAAA,MACX;AAAA,MACA,MAAM,SAAS,WAAW,SAAS,EAAE,IAAI,MAAM,aAAa;AAAA,IAAA;AAAA,EAEhE,CAAC;AAGD,SAAO,GAAG,QAAQ,OAAO,EAAE,IAAI,MAAM,kBAAkB;AAErD,UAAM,UAAU,MAAM,SAAS,SAAS,EAAE,IAAI,MAAM,aAAa;AAIjEA,WAAAA,aAAa,KAAK,eAAe,OAAO,QAAQ,MAAM;AAGtDA,WAAAA,aAAa;AAAA,MACX;AAAA,MACA,MAAM,SAAS,WAAW,QAAQ;AAAA,QAChC;AAAA,QACA;AAAA,QACA;AAAA,QACA,GAAI,UAAU,EAAE,YAAY,CAAA;AAAA,MAAC,CAC9B;AAAA,IAAA;AAAA,EAEL,CAAC;AAGD,SAAO;AAAA,IACL;AAAA,IACA,CACE,WACA,SACG;AACHA,aAAAA,aAAa,KAAK,UAAU,EAAE,WAAW,GAAG,MAAM;AAAA,IACpD;AAAA,EAAA;AAIF,SAAO;AAAA,IAAG;AAAA,IAAS,MACjBA,OAAAA,aAAa,KAAK,gBAAgB;AAAA,MAChC,SAAS;AAAA,MACT,UAAU;AAAA,MACV,UAAU;AAAA,IAAA,CACX;AAAA,EAAA;AAEH,SAAO;AAAA,IAAG;AAAA,IAAmB,MAC3BA,OAAAA,aAAa,KAAK,gBAAgB;AAAA,MAChC,SAAS;AAAA,MACT,UAAU;AAAA,MACV,UAAU;AAAA,IAAA,CACX;AAAA,EAAA;AAEH,SAAO;AAAA,IAAG;AAAA,IAAa,MACrBA,OAAAA,aAAa,KAAK,gBAAgB;AAAA,MAChC,SAAS;AAAA,MACT,UAAU;AAAA,MACV,UAAU;AAAA,IAAA,CACX;AAAA,EAAA;AAEH,SAAO,GAAG,kBAAkB,MAAM;AAChCA,WAAAA,aAAa,KAAK,gBAAgB;AAAA,MAChC,SAAS;AAAA,MACT,UAAU;AAAA,MACV,UAAU;AAAA,IAAA,CACX;AAIDA,wBAAa,KAAK,eAAe,CAAC;AAAA,EACpC,CAAC;AACD,SAAO;AAAA,IAAG;AAAA,IAAmB,MAC3BA,OAAAA,aAAa,KAAK,gBAAgB;AAAA,MAChC,SAAS;AAAA,MACT,UAAU;AAAA,MACV,UAAU;AAAA,IAAA,CACX;AAAA,EAAA;AAEL;;;"}
|
|
1
|
+
{"version":3,"file":"metrics.js","sources":["../../../src/webserver/backend/metrics.ts"],"sourcesContent":["import Logger from '@/logger';\nimport { AutoRestartConfig, IServerStatus } from '@/plugin';\nimport soft from '@/softconfig';\nimport {\n clearLastSteamUpdateCheck,\n getLastSteamUpdateCheck,\n hasSteamUpdate,\n steamcmdDownloadGame,\n} from '@/updater/steam';\nimport { readFileSync, statfsSync } from 'fs';\nimport os from 'os';\nimport { serverEvents } from './events';\nimport type Webserver from './index';\nimport type { IStoreAutoRestartConfig, SystemUtilization } from './types';\n\nlet prevCpuIdle = 0;\nlet prevCpuTotal = 0;\nlet prevNetRx = 0;\nlet prevNetTx = 0;\nlet prevNetTime = 0;\n\nfunction getCpuUsage(): number {\n const cpus = os.cpus();\n let idle = 0;\n let total = 0;\n for (const cpu of cpus) {\n idle += cpu.times.idle;\n total +=\n cpu.times.user +\n cpu.times.nice +\n cpu.times.sys +\n cpu.times.irq +\n cpu.times.idle;\n }\n const dIdle = idle - prevCpuIdle;\n const dTotal = total - prevCpuTotal;\n prevCpuIdle = idle;\n prevCpuTotal = total;\n if (dTotal === 0) return 0;\n return Math.round((1 - dIdle / dTotal) * 100);\n}\n\nfunction getDiskUsage(path: string): { used: number; total: number } {\n try {\n const stat = statfsSync(path);\n const total = stat.blocks * stat.bsize;\n const free = stat.bfree * stat.bsize;\n return { used: total - free, total };\n } catch {\n return { used: 0, total: 1 };\n }\n}\n\nfunction getNetworkBytes(): { rx: number; tx: number } {\n if (process.platform !== 'linux') return { rx: 0, tx: 0 };\n try {\n const data = readFileSync('/proc/net/dev', 'utf8');\n let rx = 0;\n let tx = 0;\n for (const line of data.split('\\n').slice(2)) {\n const parts = line.trim().split(/\\s+/);\n if (!parts[0] || parts[0] === 'lo:') continue;\n rx += Number(parts[1]) || 0;\n tx += Number(parts[9]) || 0;\n }\n return { rx, tx };\n } catch {\n return { rx: 0, tx: 0 };\n }\n}\n\nconst UTILIZATION_HISTORY_LEN = 30;\nlet lastUtilization: SystemUtilization | null = null;\nconst utilizationHistory: SystemUtilization[] = [];\n\nexport function getLastUtilization(): {\n current: SystemUtilization | null;\n history: SystemUtilization[];\n} {\n return { current: lastUtilization, history: utilizationHistory };\n}\n\nfunction pushHistory(util: SystemUtilization) {\n utilizationHistory.push(util);\n if (utilizationHistory.length > UTILIZATION_HISTORY_LEN) {\n utilizationHistory.shift();\n }\n}\n\nfunction collectUtilization(omeggaPath: string): SystemUtilization {\n const cpu = getCpuUsage();\n const totalMem = os.totalmem();\n const freeMem = os.freemem();\n const disk = getDiskUsage(omeggaPath);\n const net = getNetworkBytes();\n const now = Date.now();\n const dt = prevNetTime ? (now - prevNetTime) / 1000 : 1;\n const rxSec = prevNetTime ? Math.max(0, (net.rx - prevNetRx) / dt) : 0;\n const txSec = prevNetTime ? Math.max(0, (net.tx - prevNetTx) / dt) : 0;\n prevNetRx = net.rx;\n prevNetTx = net.tx;\n prevNetTime = now;\n return {\n cpu,\n mem: { used: totalMem - freeMem, total: totalMem },\n disk,\n net: { rxSec, txSec },\n };\n}\n\nconst error = (...args: any[]) => Logger.error(...args);\nlet lastRestart = 0;\n\nconst sleep = t => new Promise(resolve => setTimeout(resolve, t));\n\nexport default function (server: Webserver) {\n const { database, omegga } = server;\n\n // Prime CPU and network counters so the first real snapshot reflects a\n // short interval rather than computing a delta from zero (host uptime).\n getCpuUsage();\n getNetworkBytes();\n\n // Collect initial utilization snapshot so queries return data immediately\n lastUtilization = collectUtilization(omegga.path);\n pushHistory(lastUtilization);\n\n // server status is checked every minute\n clearInterval(server.serverStatusInterval);\n // heartbeat happens every 60 seconds\n let empties = 0;\n\n // last heartbeat hour\n let lastHour = -1;\n // players that have joined in the last hour\n let hourlyPlayers: string[] = [];\n\n const exitOnStop = () => {\n if (omegga.stopping || !omegga.started)\n throw new Error('Omegga is already closing');\n };\n\n async function restartServer(\n config: IStoreAutoRestartConfig,\n update?: boolean,\n ) {\n lastRestart = Date.now();\n const iconfig: AutoRestartConfig = {\n players: config.playersEnabled,\n announcement: config.announcementEnabled,\n saveWorld: config.saveWorld,\n };\n omegga.emit('autorestart', iconfig);\n await sleep(1000);\n exitOnStop();\n\n const action = update ? 'Updating' : 'Restarting';\n\n if (config.announcementEnabled) {\n database.addChatLog('server', {}, action + ' in 30 seconds...');\n Logger.logp(action + ' in 30 seconds...');\n const announce = (t: number) =>\n omegga.broadcast(\n `<size=\"20\">${action} in <b><color=\"ffffbb\">${t} second${\n t !== 1 ? 's' : ''\n }</></></>`,\n );\n announce(30);\n await sleep(15000);\n announce(15);\n await sleep(10000);\n announce(5);\n await sleep(1000);\n announce(4);\n await sleep(1000);\n announce(3);\n await sleep(1000);\n announce(2);\n await sleep(1000);\n announce(1);\n await sleep(1000);\n }\n\n exitOnStop();\n\n await omegga.saveServer(iconfig);\n\n Logger.logp(action + '...');\n database.addChatLog('server', {}, action + ' server...');\n omegga.once('mapchange', () => {\n omegga.restoreServer();\n });\n\n if (update && omegga.config.__STEAM) {\n Logger.logp('Stopping server for auto-update...');\n await omegga.stop();\n Logger.logp('Downloading update...');\n try {\n await steamcmdDownloadGame({\n steambeta: omegga.config.server?.steambeta,\n steambetaPassword: omegga.config.server?.steambetaPassword,\n });\n } catch (e) {\n error('An error occurred while downloading the update:', e);\n }\n Logger.logp('Starting server after update...');\n await omegga.start();\n } else {\n await omegga.restartServer();\n }\n }\n\n async function checkAutoRestart(status: IServerStatus) {\n const now = new Date();\n const currHour = now.getHours();\n const currMinute = now.getMinutes();\n\n /// skip restart check if we sent restart command within 5 minutes\n if (lastRestart > now.getTime() - 5 * 60 * 1000) {\n Logger.verbose('Skipping autorestart');\n return;\n }\n\n const config = await database.getAutoRestartConfig();\n Logger.verbose('Autorestart check', config);\n\n const uptimeMinutes = Math.floor(status.time / 1000 / 60);\n const uptimeHours = Math.floor(uptimeMinutes / 60);\n\n if (config.maxUptimeEnabled && uptimeHours >= config.maxUptime) {\n Logger.verbose('Restarting due to max uptime');\n return await restartServer(config);\n }\n\n if (\n config.emptyUptimeEnabled &&\n uptimeHours >= config.emptyUptime &&\n status.players.length === 0\n ) {\n Logger.verbose('Restarting due to max empty uptime');\n return await restartServer(config);\n }\n\n if (\n config.dailyHourEnabled &&\n currHour === config.dailyHour &&\n uptimeMinutes > currMinute\n ) {\n Logger.verbose('Restarting due to daily schedule');\n return await restartServer(config);\n }\n\n const lastCheck = getLastSteamUpdateCheck();\n if (omegga.config.__STEAM && config.autoUpdateEnabled) {\n const now = Date.now();\n if (\n !lastCheck.result &&\n now - lastCheck.attempt < config.autoUpdateIntervalMins * 60 * 1000\n ) {\n Logger.verbose(\n `Skipping auto update check, last check was ${Math.floor(\n (now - lastCheck.attempt) / 1000 / 60,\n )} minutes ago`,\n );\n return;\n }\n Logger.verbose('Checking for steam update');\n const hasUpdate = await hasSteamUpdate(omegga.config.server?.steambeta);\n if (hasUpdate) {\n clearLastSteamUpdateCheck();\n return await restartServer(config, true);\n }\n Logger.verbose('No steam update found');\n }\n }\n\n server.lastReportedStatus = null;\n server.serverStatusInterval = setInterval(async () => {\n if (!omegga.started) return;\n try {\n // get the server status\n const status = await omegga.getServerStatus();\n if (!status) return;\n\n try {\n await checkAutoRestart(status);\n } catch (err) {\n error('Error in autorestart check', err);\n }\n\n // get players by id\n const players = status.players.map(p => p.id);\n\n // send the unaltered status to the frontend via serverEvents\n server.lastReportedStatus = status;\n serverEvents.emit('heartbeat', status);\n\n // collect and emit system utilization metrics\n const utilization = collectUtilization(omegga.path);\n lastUtilization = utilization;\n pushHistory(utilization);\n serverEvents.emit('utilization', utilization);\n try {\n omegga.emit('metrics:heartbeat', status);\n } catch (e) {\n // prevent the omegga callback handlers from crashing this\n error('Error in heartbeat emit', e);\n }\n\n // stop recording metrics after 3 empty server statuses\n if (\n players.length === 0 &&\n ++empties > soft.METRIC_EMPTIES_BEFORE_PAUSE\n ) {\n return;\n }\n\n const now = new Date();\n const hour = now.getUTCHours();\n // check if it's a new hour (for punchcard unique player tracking)\n if (hour !== lastHour) {\n lastHour = hour;\n hourlyPlayers = [];\n }\n\n // find all the players unique to this hour\n const newPlayers = players.filter(p => !hourlyPlayers.includes(p));\n if (newPlayers.length > 0) {\n // update the punchcard\n await database.updatePlayerPunchcard(newPlayers.length);\n // mark those players as previously joined players\n hourlyPlayers.push(...newPlayers);\n }\n\n // server is not empty, reset the counter\n empties = 0;\n\n const data = {\n // number of bricks\n bricks: status.bricks,\n // unique players by id\n players: players.filter((p, i) => players.indexOf(p) === i),\n // addresses by player id\n ips: Object.fromEntries(status.players.map(p => [p.id, p.address])),\n };\n\n // hand the server status off to the database\n await database.addHeartbeat(data);\n } catch (e) {\n // probably an issue getting server status\n error('Server Not Responding...', e);\n }\n }, soft.METRIC_HEARTBEAT_INTERVAL);\n\n // chat events\n omegga.on('chat', async (name, message) => {\n const p = omegga.getPlayer(name);\n const user = {\n id: p.id,\n name,\n displayName: p.displayName,\n color: p.getNameColor(),\n };\n\n // tell web users about a chat message\n serverEvents.emit('chat', await database.addChatLog('msg', user, message));\n });\n\n // player leave events\n omegga.on('leave', async ({ id, name, displayName }) => {\n // update realtime player count subscribers (the player has already been\n // removed from omegga.players by the time 'leave' fires)\n serverEvents.emit('playerCount', omegga.players.length);\n\n // tell web users a player left\n serverEvents.emit(\n 'chat',\n await database.addChatLog('leave', { id, name, displayName }),\n );\n });\n\n // player join events\n omegga.on('join', async ({ id, name, displayName }) => {\n // add the visit to the database\n const isFirst = await database.addVisit({ id, name, displayName });\n\n // update realtime player count subscribers (the awaited DB call above means\n // omegga.players has been pushed to by the time we read it here)\n serverEvents.emit('playerCount', omegga.players.length);\n\n // tell web users a player joined (and if it's their first time joining)\n serverEvents.emit(\n 'chat',\n await database.addChatLog('join', {\n id,\n name,\n displayName,\n ...(isFirst ? { isFirst } : {}),\n }),\n );\n });\n\n // tell web users plugin status\n omegga.on(\n 'plugin:status',\n (\n shortPath: string,\n info: { name: string; isLoaded: boolean; isEnabled: boolean },\n ) => {\n serverEvents.emit('plugin', { shortPath, ...info });\n },\n );\n\n // server status events\n omegga.on('start', () =>\n serverEvents.emit('serverStatus', {\n started: true,\n starting: false,\n stopping: false,\n }),\n );\n omegga.on('server:starting', () =>\n serverEvents.emit('serverStatus', {\n started: false,\n starting: true,\n stopping: false,\n }),\n );\n omegga.on('mapchange', () =>\n serverEvents.emit('serverStatus', {\n started: true,\n starting: false,\n stopping: false,\n }),\n );\n omegga.on('server:stopped', () => {\n serverEvents.emit('serverStatus', {\n started: false,\n starting: false,\n stopping: false,\n });\n // a stopped server has no players - reset the realtime count to 0.\n // ('server:stopped' fires before omegga.players is cleared, so emit 0\n // explicitly rather than reading the still-stale length)\n serverEvents.emit('playerCount', 0);\n });\n omegga.on('server:stopping', () =>\n serverEvents.emit('serverStatus', {\n started: true,\n starting: false,\n stopping: true,\n }),\n );\n}\n"],"names":["statfsSync","readFileSync","Logger","steamcmdDownloadGame","getLastSteamUpdateCheck","now","hasSteamUpdate","clearLastSteamUpdateCheck","serverEvents","soft"],"mappings":"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"}
|
|
@@ -156,7 +156,9 @@ const playerRouter = trpc.router({
|
|
|
156
156
|
"Banning player",
|
|
157
157
|
omegga.getNameCache()?.savedPlayerNames?.[id].yellow ?? "with id " + id.yellow
|
|
158
158
|
);
|
|
159
|
-
omegga.writeln(
|
|
159
|
+
omegga.writeln(
|
|
160
|
+
`${omegga.Console.Chat.Command} /Ban "${id}" ${duration} "${reason}"`
|
|
161
|
+
);
|
|
160
162
|
const ban = await util.waitForEvent(database, "update.bans", () => {
|
|
161
163
|
const banList = (omegga.getBanList() || { banList: {} }).banList;
|
|
162
164
|
if (!banList[id]) return false;
|
|
@@ -187,7 +189,9 @@ const playerRouter = trpc.router({
|
|
|
187
189
|
const player2 = omegga.players.find((p) => p.id === id);
|
|
188
190
|
if (!player2) return false;
|
|
189
191
|
ctx.log("Kicking player", player2.name.yellow);
|
|
190
|
-
omegga.writeln(
|
|
192
|
+
omegga.writeln(
|
|
193
|
+
`${omegga.Console.Chat.Command} /Kick "${id}" "${reason}"`
|
|
194
|
+
);
|
|
191
195
|
const ok = await util.waitForEvent(
|
|
192
196
|
omegga,
|
|
193
197
|
"leave",
|
|
@@ -213,7 +217,7 @@ const playerRouter = trpc.router({
|
|
|
213
217
|
"Unbanning player",
|
|
214
218
|
omegga.getNameCache()?.savedPlayerNames?.[id].yellow ?? "with id " + id.yellow
|
|
215
219
|
);
|
|
216
|
-
omegga.writeln(
|
|
220
|
+
omegga.writeln(`${omegga.Console.Chat.Command} /Unban "${id}"`);
|
|
217
221
|
const ok = await util.waitForEvent(database, "update.bans", () => {
|
|
218
222
|
const banList2 = (omegga.getBanList() || { banList: {} }).banList;
|
|
219
223
|
return !banList2[id] || time.parseBrickadiaTime(banList2[id].expires) < Date.now();
|
|
@@ -228,7 +232,7 @@ const playerRouter = trpc.router({
|
|
|
228
232
|
"Clearing bricks for player",
|
|
229
233
|
omegga.getNameCache()?.savedPlayerNames?.[id]?.yellow ?? "with id " + id.yellow
|
|
230
234
|
);
|
|
231
|
-
omegga.writeln(
|
|
235
|
+
omegga.writeln(`${omegga.Console.Bricks.Clear} "${id}"`);
|
|
232
236
|
return true;
|
|
233
237
|
}),
|
|
234
238
|
roles: trpc.router({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"player.js","sources":["../../../../src/webserver/backend/router/player.ts"],"sourcesContent":["import Player from '@omegga/player';\nimport { rgbToHex } from '@util/color';\nimport { parseBrickadiaTime } from '@util/time';\nimport * as uuid from '@util/uuid';\nimport _ from 'lodash';\nimport { z } from 'zod/v4';\nimport { ScopeName } from '../scopes';\nimport { getContextDeps, protectedProcedure, router } from '../trpc';\nimport { waitForEvent } from '../util';\n\nexport const playerRouter = router({\n player: router({\n list: protectedProcedure(ScopeName.PlayerList)\n .input(\n z.object({\n page: z.number().optional().default(0),\n search: z.string().optional().default(''),\n sort: z.string().optional().default('name'),\n direction: z.number().optional().default(1),\n filter: z.string().optional().default(''),\n }),\n )\n .query(async ({ input }) => {\n const { database, omegga } = getContextDeps();\n const { page, search, sort, direction, filter } = input;\n const banList = (omegga.getBanList() || { banList: {} }).banList;\n const now = Date.now();\n let limitId: string[] | undefined;\n if (filter === 'banned') {\n limitId = Object.keys(banList).filter(\n b =>\n banList[b].expires <= banList[b].created ||\n parseBrickadiaTime(banList[b].expires) > Date.now(),\n );\n }\n const resp = await database.getPlayers({\n page,\n search,\n sort,\n direction,\n limitId,\n });\n const players = resp.players.map(player => {\n const foundBan = banList[player.id];\n let ban: {\n bannerId: string;\n reason: string;\n created: number;\n expires: number;\n duration: number;\n remainingTime: number;\n bannerName: string;\n } | null = null;\n if (foundBan) {\n const created = parseBrickadiaTime(foundBan.created);\n const expires = parseBrickadiaTime(foundBan.expires);\n const duration = expires - created;\n if (!(expires < now && duration > 0)) {\n ban = {\n bannerId: foundBan.bannerId,\n reason: foundBan.reason,\n created,\n expires,\n duration,\n remainingTime: expires - now,\n bannerName: _.get(\n omegga.getNameCache(),\n ['savedPlayerNames', foundBan.bannerId],\n '',\n ),\n };\n }\n }\n return {\n ...player,\n seenAgo: now - player.lastSeen,\n createdAgo: now - player.created,\n ban,\n };\n });\n return { ...resp, players };\n }),\n\n get: protectedProcedure(ScopeName.PlayerGet)\n .input(z.string())\n .query(async ({ input: id }) => {\n const { database, omegga } = getContextDeps();\n const entry = await database.getPlayer(id);\n if (!entry) return null;\n const now = Date.now();\n const nameHistory = entry.nameHistory.map(n => ({\n ...n,\n ago: now - n.date,\n }));\n const banHistory = entry.banHistory.map(b => ({\n ...b,\n duration: (b.expires as number) - (b.created as number),\n bannerName: _.get(\n omegga.getNameCache(),\n ['savedPlayerNames', b.bannerId],\n '',\n ) as string,\n }));\n const kickHistory = entry.kickHistory.map(b => ({\n ...b,\n kickerName: _.get(\n omegga.getNameCache(),\n ['savedPlayerNames', b.kickerId],\n '',\n ) as string,\n }));\n const playerRoles = Player.getRoles(omegga, id) || [];\n const { roles: serverRoles = [] } = omegga.getRoleSetup() ?? {};\n const foundBan = (omegga.getBanList() || { banList: {} }).banList[id];\n let currentBan: {\n bannerId: string;\n reason: string;\n created: number;\n expires: number;\n duration: number;\n remainingTime: number;\n bannerName: string;\n } | null = null;\n if (foundBan) {\n const created = parseBrickadiaTime(foundBan.created);\n const expires = parseBrickadiaTime(foundBan.expires);\n const duration = expires - created;\n if (!(expires < now && duration > 0)) {\n currentBan = {\n bannerId: foundBan.bannerId,\n reason: foundBan.reason,\n created,\n expires,\n duration,\n remainingTime: expires - now,\n bannerName: _.get(\n omegga.getNameCache(),\n ['savedPlayerNames', foundBan.bannerId],\n '',\n ),\n };\n }\n }\n return {\n ...entry,\n nameHistory,\n banHistory,\n kickHistory,\n seenAgo: now - entry.lastSeen,\n createdAgo: now - entry.created,\n isHost: omegga.getHostId() === id,\n isOnline: omegga.players.some(p => p.id === id),\n currentBan,\n roles: playerRoles.map(r => {\n let color = 'ffffff';\n const role = serverRoles.find(\n sr => sr.name.toLowerCase() === r.toLowerCase(),\n );\n if (role && role.bHasColor) color = rgbToHex(role.color);\n return { name: r, color };\n }),\n };\n }),\n\n ban: protectedProcedure(ScopeName.PlayerBan)\n .input(\n z.object({\n id: z.string(),\n duration: z.number().optional().default(-1),\n reason: z.string().max(128).optional().default('No Reason'),\n }),\n )\n .mutation(async ({ input, ctx }) => {\n const { database, omegga } = getContextDeps();\n const { id, duration } = input;\n let { reason } = input;\n if (typeof duration !== 'number') return false;\n if (!uuid.match(id)) return false;\n reason = reason.replace(/\\n/g, ' ').replace(/\"/g, \"'\");\n ctx.log(\n 'Banning player',\n omegga.getNameCache()?.savedPlayerNames?.[id].yellow ??\n 'with id ' + id.yellow,\n );\n omegga.writeln(`Chat.Command /Ban \"${id}\" ${duration} \"${reason}\"`);\n const ban = await waitForEvent(database, 'update.bans', () => {\n const banList = (omegga.getBanList() || { banList: {} }).banList;\n if (!banList[id]) return false;\n return banList[id];\n });\n if (ban) {\n database.upsertBanHistory({\n banned: id,\n bannerId: null,\n created: parseBrickadiaTime(ban.created),\n expires: parseBrickadiaTime(ban.expires),\n reason: ban.reason,\n });\n }\n return !!ban;\n }),\n\n kick: protectedProcedure(ScopeName.PlayerKick)\n .input(\n z.object({\n id: z.string(),\n reason: z.string().max(128).optional().default('No Reason'),\n }),\n )\n .mutation(async ({ input, ctx }) => {\n const { database, omegga } = getContextDeps();\n const { id } = input;\n let { reason } = input;\n if (!uuid.match(id)) return false;\n reason = reason.replace(/\\n/g, ' ').replace(/\"/g, \"'\");\n const player = omegga.players.find(p => p.id === id);\n if (!player) return false;\n ctx.log('Kicking player', player.name.yellow);\n omegga.writeln(`Chat.Command /Kick \"${id}\" \"${reason}\"`);\n const ok = await waitForEvent(\n omegga,\n 'leave',\n (leavingPlayer: { id: string }) => leavingPlayer.id === id,\n );\n if (ok) {\n database.upsertKickHistory({\n kicked: id,\n kickerId: null,\n created: Date.now(),\n reason,\n });\n }\n return ok;\n }),\n\n unban: protectedProcedure(ScopeName.PlayerUnban)\n .input(z.object({ id: z.string() }))\n .mutation(async ({ input, ctx }) => {\n const { database, omegga } = getContextDeps();\n const { id } = input;\n if (!uuid.match(id)) return false;\n const banList = (omegga.getBanList() || { banList: {} }).banList;\n if (!banList[id]) return false;\n ctx.log(\n 'Unbanning player',\n omegga.getNameCache()?.savedPlayerNames?.[id].yellow ??\n 'with id ' + id.yellow,\n );\n omegga.writeln(`Chat.Command /Unban \"${id}\"`);\n const ok = await waitForEvent(database, 'update.bans', () => {\n const banList = (omegga.getBanList() || { banList: {} }).banList;\n return (\n !banList[id] || parseBrickadiaTime(banList[id].expires) < Date.now()\n );\n });\n return ok;\n }),\n\n clearBricks: protectedProcedure(ScopeName.PlayerClearBricks)\n .input(z.object({ id: z.string() }))\n .mutation(async ({ input, ctx }) => {\n const { omegga } = getContextDeps();\n const { id } = input;\n if (!uuid.match(id)) return false;\n ctx.log(\n 'Clearing bricks for player',\n omegga.getNameCache()?.savedPlayerNames?.[id]?.yellow ??\n 'with id ' + id.yellow,\n );\n omegga.writeln(`Bricks.Clear \"${id}\"`);\n return true;\n }),\n\n roles: router({\n list: protectedProcedure(ScopeName.PlayerList).query(() => {\n const { omegga } = getContextDeps();\n return _.sortBy(omegga.getRoleSetup()?.roles ?? [], p =>\n p.name.toLowerCase(),\n );\n }),\n }),\n }),\n});\n"],"names":["router","protectedProcedure","ScopeName","z","getContextDeps","parseBrickadiaTime","player","Player","color","rgbToHex","uuid.match","waitForEvent","banList"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"player.js","sources":["../../../../src/webserver/backend/router/player.ts"],"sourcesContent":["import Player from '@omegga/player';\nimport { rgbToHex } from '@util/color';\nimport { parseBrickadiaTime } from '@util/time';\nimport * as uuid from '@util/uuid';\nimport _ from 'lodash';\nimport { z } from 'zod/v4';\nimport { ScopeName } from '../scopes';\nimport { getContextDeps, protectedProcedure, router } from '../trpc';\nimport { waitForEvent } from '../util';\n\nexport const playerRouter = router({\n player: router({\n list: protectedProcedure(ScopeName.PlayerList)\n .input(\n z.object({\n page: z.number().optional().default(0),\n search: z.string().optional().default(''),\n sort: z.string().optional().default('name'),\n direction: z.number().optional().default(1),\n filter: z.string().optional().default(''),\n }),\n )\n .query(async ({ input }) => {\n const { database, omegga } = getContextDeps();\n const { page, search, sort, direction, filter } = input;\n const banList = (omegga.getBanList() || { banList: {} }).banList;\n const now = Date.now();\n let limitId: string[] | undefined;\n if (filter === 'banned') {\n limitId = Object.keys(banList).filter(\n b =>\n banList[b].expires <= banList[b].created ||\n parseBrickadiaTime(banList[b].expires) > Date.now(),\n );\n }\n const resp = await database.getPlayers({\n page,\n search,\n sort,\n direction,\n limitId,\n });\n const players = resp.players.map(player => {\n const foundBan = banList[player.id];\n let ban: {\n bannerId: string;\n reason: string;\n created: number;\n expires: number;\n duration: number;\n remainingTime: number;\n bannerName: string;\n } | null = null;\n if (foundBan) {\n const created = parseBrickadiaTime(foundBan.created);\n const expires = parseBrickadiaTime(foundBan.expires);\n const duration = expires - created;\n if (!(expires < now && duration > 0)) {\n ban = {\n bannerId: foundBan.bannerId,\n reason: foundBan.reason,\n created,\n expires,\n duration,\n remainingTime: expires - now,\n bannerName: _.get(\n omegga.getNameCache(),\n ['savedPlayerNames', foundBan.bannerId],\n '',\n ),\n };\n }\n }\n return {\n ...player,\n seenAgo: now - player.lastSeen,\n createdAgo: now - player.created,\n ban,\n };\n });\n return { ...resp, players };\n }),\n\n get: protectedProcedure(ScopeName.PlayerGet)\n .input(z.string())\n .query(async ({ input: id }) => {\n const { database, omegga } = getContextDeps();\n const entry = await database.getPlayer(id);\n if (!entry) return null;\n const now = Date.now();\n const nameHistory = entry.nameHistory.map(n => ({\n ...n,\n ago: now - n.date,\n }));\n const banHistory = entry.banHistory.map(b => ({\n ...b,\n duration: (b.expires as number) - (b.created as number),\n bannerName: _.get(\n omegga.getNameCache(),\n ['savedPlayerNames', b.bannerId],\n '',\n ) as string,\n }));\n const kickHistory = entry.kickHistory.map(b => ({\n ...b,\n kickerName: _.get(\n omegga.getNameCache(),\n ['savedPlayerNames', b.kickerId],\n '',\n ) as string,\n }));\n const playerRoles = Player.getRoles(omegga, id) || [];\n const { roles: serverRoles = [] } = omegga.getRoleSetup() ?? {};\n const foundBan = (omegga.getBanList() || { banList: {} }).banList[id];\n let currentBan: {\n bannerId: string;\n reason: string;\n created: number;\n expires: number;\n duration: number;\n remainingTime: number;\n bannerName: string;\n } | null = null;\n if (foundBan) {\n const created = parseBrickadiaTime(foundBan.created);\n const expires = parseBrickadiaTime(foundBan.expires);\n const duration = expires - created;\n if (!(expires < now && duration > 0)) {\n currentBan = {\n bannerId: foundBan.bannerId,\n reason: foundBan.reason,\n created,\n expires,\n duration,\n remainingTime: expires - now,\n bannerName: _.get(\n omegga.getNameCache(),\n ['savedPlayerNames', foundBan.bannerId],\n '',\n ),\n };\n }\n }\n return {\n ...entry,\n nameHistory,\n banHistory,\n kickHistory,\n seenAgo: now - entry.lastSeen,\n createdAgo: now - entry.created,\n isHost: omegga.getHostId() === id,\n isOnline: omegga.players.some(p => p.id === id),\n currentBan,\n roles: playerRoles.map(r => {\n let color = 'ffffff';\n const role = serverRoles.find(\n sr => sr.name.toLowerCase() === r.toLowerCase(),\n );\n if (role && role.bHasColor) color = rgbToHex(role.color);\n return { name: r, color };\n }),\n };\n }),\n\n ban: protectedProcedure(ScopeName.PlayerBan)\n .input(\n z.object({\n id: z.string(),\n duration: z.number().optional().default(-1),\n reason: z.string().max(128).optional().default('No Reason'),\n }),\n )\n .mutation(async ({ input, ctx }) => {\n const { database, omegga } = getContextDeps();\n const { id, duration } = input;\n let { reason } = input;\n if (typeof duration !== 'number') return false;\n if (!uuid.match(id)) return false;\n reason = reason.replace(/\\n/g, ' ').replace(/\"/g, \"'\");\n ctx.log(\n 'Banning player',\n omegga.getNameCache()?.savedPlayerNames?.[id].yellow ??\n 'with id ' + id.yellow,\n );\n omegga.writeln(\n `${omegga.Console.Chat.Command} /Ban \"${id}\" ${duration} \"${reason}\"`,\n );\n const ban = await waitForEvent(database, 'update.bans', () => {\n const banList = (omegga.getBanList() || { banList: {} }).banList;\n if (!banList[id]) return false;\n return banList[id];\n });\n if (ban) {\n database.upsertBanHistory({\n banned: id,\n bannerId: null,\n created: parseBrickadiaTime(ban.created),\n expires: parseBrickadiaTime(ban.expires),\n reason: ban.reason,\n });\n }\n return !!ban;\n }),\n\n kick: protectedProcedure(ScopeName.PlayerKick)\n .input(\n z.object({\n id: z.string(),\n reason: z.string().max(128).optional().default('No Reason'),\n }),\n )\n .mutation(async ({ input, ctx }) => {\n const { database, omegga } = getContextDeps();\n const { id } = input;\n let { reason } = input;\n if (!uuid.match(id)) return false;\n reason = reason.replace(/\\n/g, ' ').replace(/\"/g, \"'\");\n const player = omegga.players.find(p => p.id === id);\n if (!player) return false;\n ctx.log('Kicking player', player.name.yellow);\n omegga.writeln(\n `${omegga.Console.Chat.Command} /Kick \"${id}\" \"${reason}\"`,\n );\n const ok = await waitForEvent(\n omegga,\n 'leave',\n (leavingPlayer: { id: string }) => leavingPlayer.id === id,\n );\n if (ok) {\n database.upsertKickHistory({\n kicked: id,\n kickerId: null,\n created: Date.now(),\n reason,\n });\n }\n return ok;\n }),\n\n unban: protectedProcedure(ScopeName.PlayerUnban)\n .input(z.object({ id: z.string() }))\n .mutation(async ({ input, ctx }) => {\n const { database, omegga } = getContextDeps();\n const { id } = input;\n if (!uuid.match(id)) return false;\n const banList = (omegga.getBanList() || { banList: {} }).banList;\n if (!banList[id]) return false;\n ctx.log(\n 'Unbanning player',\n omegga.getNameCache()?.savedPlayerNames?.[id].yellow ??\n 'with id ' + id.yellow,\n );\n omegga.writeln(`${omegga.Console.Chat.Command} /Unban \"${id}\"`);\n const ok = await waitForEvent(database, 'update.bans', () => {\n const banList = (omegga.getBanList() || { banList: {} }).banList;\n return (\n !banList[id] || parseBrickadiaTime(banList[id].expires) < Date.now()\n );\n });\n return ok;\n }),\n\n clearBricks: protectedProcedure(ScopeName.PlayerClearBricks)\n .input(z.object({ id: z.string() }))\n .mutation(async ({ input, ctx }) => {\n const { omegga } = getContextDeps();\n const { id } = input;\n if (!uuid.match(id)) return false;\n ctx.log(\n 'Clearing bricks for player',\n omegga.getNameCache()?.savedPlayerNames?.[id]?.yellow ??\n 'with id ' + id.yellow,\n );\n omegga.writeln(`${omegga.Console.Bricks.Clear} \"${id}\"`);\n return true;\n }),\n\n roles: router({\n list: protectedProcedure(ScopeName.PlayerList).query(() => {\n const { omegga } = getContextDeps();\n return _.sortBy(omegga.getRoleSetup()?.roles ?? [], p =>\n p.name.toLowerCase(),\n );\n }),\n }),\n }),\n});\n"],"names":["router","protectedProcedure","ScopeName","z","getContextDeps","parseBrickadiaTime","player","Player","color","rgbToHex","uuid.match","waitForEvent","banList"],"mappings":"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"}
|
package/dist/worker.js
CHANGED
|
@@ -108,7 +108,10 @@ async function createVm(pluginPath, { builtin = ["*"], external = true, isTypeSc
|
|
|
108
108
|
"commonjs " + path.resolve(pluginPath, request)
|
|
109
109
|
);
|
|
110
110
|
}
|
|
111
|
-
|
|
111
|
+
if (request.startsWith(".") || request === "src" || request.startsWith("src/") || path.isAbsolute(request)) {
|
|
112
|
+
return callback();
|
|
113
|
+
}
|
|
114
|
+
callback(null, "commonjs " + request);
|
|
112
115
|
}
|
|
113
116
|
],
|
|
114
117
|
resolve: {
|
|
@@ -201,9 +204,33 @@ async function createVm(pluginPath, { builtin = ["*"], external = true, isTypeSc
|
|
|
201
204
|
}
|
|
202
205
|
}
|
|
203
206
|
if (vm !== void 0) return [false, "vm is already created"];
|
|
207
|
+
const sandbox = {};
|
|
208
|
+
for (const name of [
|
|
209
|
+
"fetch",
|
|
210
|
+
"Headers",
|
|
211
|
+
"Request",
|
|
212
|
+
"Response",
|
|
213
|
+
"FormData",
|
|
214
|
+
"Blob",
|
|
215
|
+
"File",
|
|
216
|
+
"AbortController",
|
|
217
|
+
"AbortSignal",
|
|
218
|
+
"URL",
|
|
219
|
+
"URLSearchParams",
|
|
220
|
+
"TextEncoder",
|
|
221
|
+
"TextDecoder",
|
|
222
|
+
"crypto",
|
|
223
|
+
"structuredClone",
|
|
224
|
+
"queueMicrotask",
|
|
225
|
+
"btoa",
|
|
226
|
+
"atob"
|
|
227
|
+
]) {
|
|
228
|
+
const value = globalThis[name];
|
|
229
|
+
if (value !== void 0) sandbox[name] = value;
|
|
230
|
+
}
|
|
204
231
|
vm = new vm2.NodeVM({
|
|
205
232
|
console: "redirect",
|
|
206
|
-
sandbox
|
|
233
|
+
sandbox,
|
|
207
234
|
require: {
|
|
208
235
|
external,
|
|
209
236
|
builtin,
|