omegga 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/.claude/settings.local.json +39 -0
  2. package/CHANGELOG.md +690 -0
  3. package/README.md +23 -2
  4. package/bin/omegga +1 -1
  5. package/dist/brickadia/server.js +1 -1
  6. package/dist/brickadia/server.js.map +1 -1
  7. package/dist/cli/terminal.js +47 -13
  8. package/dist/cli/terminal.js.map +1 -1
  9. package/dist/config/index.js +6 -0
  10. package/dist/config/index.js.map +1 -1
  11. package/dist/config/types.js +51 -0
  12. package/dist/config/types.js.map +1 -0
  13. package/dist/config/validator.js +8 -20
  14. package/dist/config/validator.js.map +1 -1
  15. package/dist/index.js +12 -6
  16. package/dist/index.js.map +1 -1
  17. package/dist/logger.js +38 -8
  18. package/dist/logger.js.map +1 -1
  19. package/dist/main.js +27 -1
  20. package/dist/main.js.map +1 -1
  21. package/dist/omegga/matchers/command.js +10 -0
  22. package/dist/omegga/matchers/command.js.map +1 -1
  23. package/dist/omegga/plugin/plugin_node_unsafe.js +6 -0
  24. package/dist/omegga/plugin/plugin_node_unsafe.js.map +1 -1
  25. package/dist/omegga/server.js +51 -5
  26. package/dist/omegga/server.js.map +1 -1
  27. package/dist/omegga/wrapper.js +1 -0
  28. package/dist/omegga/wrapper.js.map +1 -1
  29. package/dist/plugin.js +1 -2
  30. package/dist/plugin.js.map +1 -1
  31. package/dist/updater/steam.js +175 -68
  32. package/dist/updater/steam.js.map +1 -1
  33. package/dist/util/brick/checkBounds.js +1 -2
  34. package/dist/util/brick/checkBounds.js.map +1 -1
  35. package/dist/util/brick/displayName.js +1 -2
  36. package/dist/util/brick/displayName.js.map +1 -1
  37. package/dist/util/brick/getBounds.js +1 -2
  38. package/dist/util/brick/getBounds.js.map +1 -1
  39. package/dist/util/brick/getBrickSize.js +1 -2
  40. package/dist/util/brick/getBrickSize.js.map +1 -1
  41. package/dist/util/brick/getScaleAxis.js +1 -2
  42. package/dist/util/brick/getScaleAxis.js.map +1 -1
  43. package/dist/util/brick/setOwnership.js +1 -2
  44. package/dist/util/brick/setOwnership.js.map +1 -1
  45. package/dist/webserver/backend/database.js +5 -3
  46. package/dist/webserver/backend/database.js.map +1 -1
  47. package/dist/webserver/backend/events.js +7 -0
  48. package/dist/webserver/backend/events.js.map +1 -0
  49. package/dist/webserver/backend/index.js +64 -26
  50. package/dist/webserver/backend/index.js.map +1 -1
  51. package/dist/webserver/backend/metrics.js +122 -11
  52. package/dist/webserver/backend/metrics.js.map +1 -1
  53. package/dist/webserver/backend/router/chat.js +58 -0
  54. package/dist/webserver/backend/router/chat.js.map +1 -0
  55. package/dist/webserver/backend/router/index.js +23 -0
  56. package/dist/webserver/backend/router/index.js.map +1 -0
  57. package/dist/webserver/backend/router/player.js +248 -0
  58. package/dist/webserver/backend/router/player.js.map +1 -0
  59. package/dist/webserver/backend/router/plugin.js +150 -0
  60. package/dist/webserver/backend/router/plugin.js.map +1 -0
  61. package/dist/webserver/backend/router/role.js +17 -0
  62. package/dist/webserver/backend/router/role.js.map +1 -0
  63. package/dist/webserver/backend/router/server.js +181 -0
  64. package/dist/webserver/backend/router/server.js.map +1 -0
  65. package/dist/webserver/backend/router/session.js +33 -0
  66. package/dist/webserver/backend/router/session.js.map +1 -0
  67. package/dist/webserver/backend/router/user.js +93 -0
  68. package/dist/webserver/backend/router/user.js.map +1 -0
  69. package/dist/webserver/backend/router/world.js +139 -0
  70. package/dist/webserver/backend/router/world.js.map +1 -0
  71. package/dist/webserver/backend/trpc.js +48 -0
  72. package/dist/webserver/backend/trpc.js.map +1 -0
  73. package/dist/webserver/backend/util.js +18 -0
  74. package/dist/webserver/backend/util.js.map +1 -1
  75. package/package.json +10 -5
  76. package/public/app.html +122 -4
  77. package/public/assets/IconWorldPlus-Dkos72Ws.js +2 -0
  78. package/public/assets/IconWorldPlus-Dkos72Ws.js.map +1 -0
  79. package/public/assets/app-CLZ-5FIi.js +6 -0
  80. package/public/assets/app-CLZ-5FIi.js.map +1 -0
  81. package/public/assets/client-BT1QYH0O.js.map +1 -1
  82. package/public/assets/index-DLZ8-XzO.js +4 -0
  83. package/public/assets/index-DLZ8-XzO.js.map +1 -0
  84. package/public/assets/shared/@components-DPHARroA.js +2 -0
  85. package/public/assets/shared/@components-DPHARroA.js.map +1 -0
  86. package/public/assets/shared/@tabler_icons-react-iQk11eD_.js +3 -0
  87. package/public/assets/shared/{@tabler_icons-react-B4bN8gHc.js.map → @tabler_icons-react-iQk11eD_.js.map} +1 -1
  88. package/public/auth.html +13 -1
  89. package/dist/info/default_commands.json.js +0 -39
  90. package/dist/info/default_commands.json.js.map +0 -1
  91. package/dist/webserver/backend/api.js +0 -797
  92. package/dist/webserver/backend/api.js.map +0 -1
  93. package/public/assets/IconWorldPlus-DpfueG8_.js +0 -2
  94. package/public/assets/IconWorldPlus-DpfueG8_.js.map +0 -1
  95. package/public/assets/app-nJIpyOiP.js +0 -6
  96. package/public/assets/app-nJIpyOiP.js.map +0 -1
  97. package/public/assets/index-3dNmNZ9B.js +0 -10
  98. package/public/assets/index-3dNmNZ9B.js.map +0 -1
  99. package/public/assets/shared/@components-DdtLZOaA.js +0 -2
  100. package/public/assets/shared/@components-DdtLZOaA.js.map +0 -1
  101. package/public/assets/shared/@tabler_icons-react-B4bN8gHc.js +0 -3
@@ -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 type Webserver from './index';\nimport { IStoreAutoRestartConfig, OmeggaSocketIo } from './types';\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, io: OmeggaSocketIo) {\n const { database, omegga } = server;\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 (\n update &&\n omegga.config.__STEAM &&\n !omegga.config.server?.steambetaPassword\n ) {\n Logger.logp('Stopping server for auto-update...');\n await omegga.stop();\n Logger.logp('Downloading update...');\n try {\n 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 // Cannot auto-update on steam betas with passwords\n const lastCheck = getLastSteamUpdateCheck();\n if (\n omegga.config.__STEAM &&\n config.autoUpdateEnabled &&\n !omegga.config.server?.steambetaPassword\n ) {\n const now = Date.now();\n if (\n // If there is no update\n !lastCheck.available &&\n // And the last update was too recent\n now - lastCheck.attempt < config.autoUpdateIntervalMins * 60 * 1000\n ) {\n // Skip this check\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\n server.lastReportedStatus = status;\n io.to('status').emit('server.status', status);\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...');\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 io.to('chat').emit('chat', await database.addChatLog('msg', user, message));\n });\n\n // player leave events\n omegga.on('leave', async ({ id, name, displayName }) => {\n // tell web users a player left\n io.to('chat').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 // tell web users a player joined (and if it's their first time joining)\n io.to('chat').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 io.to('plugins').emit('plugin', shortPath, info);\n },\n );\n\n // server status events\n omegga.on('start', () =>\n io\n .to('server')\n .emit('status', { started: true, starting: false, stopping: false }),\n );\n omegga.on('server:starting', () =>\n io\n .to('server')\n .emit('status', { started: false, starting: true, stopping: false }),\n );\n omegga.on('mapchange', () =>\n io\n .to('server')\n .emit('status', { started: true, starting: false, stopping: false }),\n );\n omegga.on('server:stopped', () =>\n io\n .to('server')\n .emit('status', { started: false, starting: false, stopping: false }),\n );\n omegga.on('server:stopping', () =>\n io\n .to('server')\n .emit('status', { started: true, starting: false, stopping: true }),\n );\n}\n"],"names":["Logger","steamcmdDownloadGame","getLastSteamUpdateCheck","now","hasSteamUpdate","clearLastSteamUpdateCheck","soft"],"mappings":"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"}
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 (\n update &&\n omegga.config.__STEAM &&\n !omegga.config.server?.steambetaPassword\n ) {\n Logger.logp('Stopping server for auto-update...');\n await omegga.stop();\n Logger.logp('Downloading update...');\n try {\n 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 // Cannot auto-update on steam betas with passwords\n const lastCheck = getLastSteamUpdateCheck();\n if (\n omegga.config.__STEAM &&\n config.autoUpdateEnabled &&\n !omegga.config.server?.steambetaPassword\n ) {\n const now = Date.now();\n if (\n // If there is no update\n !lastCheck.available &&\n // And the last update was too recent\n now - lastCheck.attempt < config.autoUpdateIntervalMins * 60 * 1000\n ) {\n // Skip this check\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...');\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 // 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 // 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 );\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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"}
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const v4 = require("zod/v4");
4
+ const EventEmitter = require("events");
5
+ const trpc = require("../trpc.js");
6
+ const events = require("../events.js");
7
+ const logger = require("../../../logger.js");
8
+ const chat = require("../../../util/chat.js");
9
+ const chatRouter = trpc.router({
10
+ chat: trpc.router({
11
+ send: trpc.protectedProcedure().input(v4.z.string()).mutation(async ({ input: message, ctx }) => {
12
+ const { database, omegga } = trpc.getContextDeps();
13
+ if (typeof message !== "string") return;
14
+ if (message.length > 140) message = message.slice(0, 140);
15
+ const user = {
16
+ name: ctx.user.username || "Admin",
17
+ id: ctx.user.playerId,
18
+ color: "ff00ff",
19
+ web: true
20
+ };
21
+ const chatLog = await database.addChatLog("msg", user, message);
22
+ events.serverEvents.emit("chat", chatLog);
23
+ omegga.broadcast(
24
+ `"[<b><color=\\"ff00ff\\">${user.name}</></>]: ${chat.parseLinks(chat.sanitize(message))}"`
25
+ );
26
+ logger.default.log(`[${user.name.brightMagenta.underline}]: ${message}`);
27
+ return "ok";
28
+ }),
29
+ recent: trpc.protectedProcedure().query(() => {
30
+ const { database } = trpc.getContextDeps();
31
+ return database.getChats({ sameServer: true });
32
+ }),
33
+ history: trpc.protectedProcedure().input(
34
+ v4.z.object({
35
+ after: v4.z.number().optional(),
36
+ before: v4.z.number().optional()
37
+ })
38
+ ).query(({ input }) => {
39
+ const { database } = trpc.getContextDeps();
40
+ return database.getChats({ after: input.after, before: input.before });
41
+ }),
42
+ calendar: trpc.protectedProcedure().query(() => {
43
+ const { database } = trpc.getContextDeps();
44
+ return database.calendar.years;
45
+ }),
46
+ onMessage: trpc.protectedProcedure().subscription(
47
+ async function* ({ signal }) {
48
+ for await (const [chatLog] of EventEmitter.on(events.serverEvents, "chat", {
49
+ signal
50
+ })) {
51
+ yield chatLog;
52
+ }
53
+ }
54
+ )
55
+ })
56
+ });
57
+ exports.chatRouter = chatRouter;
58
+ //# sourceMappingURL=chat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat.js","sources":["../../../../src/webserver/backend/router/chat.ts"],"sourcesContent":["import { z } from 'zod/v4';\nimport { on } from 'events';\nimport { router, protectedProcedure, getContextDeps } from '../trpc';\nimport { serverEvents } from '../events';\nimport Logger from '@/logger';\nimport { parseLinks, sanitize } from '@util/chat';\n\nexport const chatRouter = router({\n chat: router({\n send: protectedProcedure('chat.send')\n .input(z.string())\n .mutation(async ({ input: message, ctx }) => {\n const { database, omegga } = getContextDeps();\n\n if (typeof message !== 'string') return;\n if (message.length > 140) message = message.slice(0, 140);\n\n const user = {\n name: ctx.user.username || 'Admin',\n id: ctx.user.playerId,\n color: 'ff00ff',\n web: true,\n };\n\n const chatLog = await database.addChatLog('msg', user, message);\n serverEvents.emit('chat', chatLog);\n\n omegga.broadcast(\n `\"[<b><color=\\\\\"ff00ff\\\\\">${user.name}</></>]: ${parseLinks(sanitize(message))}\"`,\n );\n Logger.log(`[${user.name.brightMagenta.underline}]: ${message}`);\n\n return 'ok';\n }),\n\n recent: protectedProcedure('chat.recent').query(() => {\n const { database } = getContextDeps();\n return database.getChats({ sameServer: true });\n }),\n\n history: protectedProcedure('chat.history')\n .input(\n z.object({\n after: z.number().optional(),\n before: z.number().optional(),\n }),\n )\n .query(({ input }) => {\n const { database } = getContextDeps();\n return database.getChats({ after: input.after, before: input.before });\n }),\n\n calendar: protectedProcedure('chat.calendar').query(() => {\n const { database } = getContextDeps();\n return database.calendar.years;\n }),\n\n onMessage: protectedProcedure('chat.onMessage').subscription(\n async function* ({ signal }) {\n for await (const [chatLog] of on(serverEvents, 'chat', {\n signal,\n })) {\n yield chatLog;\n }\n },\n ),\n }),\n});\n"],"names":["router","protectedProcedure","z","getContextDeps","serverEvents","parseLinks","sanitize","Logger","on"],"mappings":";;;;;;;;AAOO,MAAM,aAAaA,KAAAA,OAAO;AAAA,EAC/B,MAAMA,KAAAA,OAAO;AAAA,IACX,MAAMC,KAAAA,mBAA8B,EACjC,MAAMC,GAAAA,EAAE,OAAA,CAAQ,EAChB,SAAS,OAAO,EAAE,OAAO,SAAS,UAAU;AAC3C,YAAM,EAAE,UAAU,OAAA,IAAWC,oBAAA;AAE7B,UAAI,OAAO,YAAY,SAAU;AACjC,UAAI,QAAQ,SAAS,eAAe,QAAQ,MAAM,GAAG,GAAG;AAExD,YAAM,OAAO;AAAA,QACX,MAAM,IAAI,KAAK,YAAY;AAAA,QAC3B,IAAI,IAAI,KAAK;AAAA,QACb,OAAO;AAAA,QACP,KAAK;AAAA,MAAA;AAGP,YAAM,UAAU,MAAM,SAAS,WAAW,OAAO,MAAM,OAAO;AAC9DC,0BAAa,KAAK,QAAQ,OAAO;AAEjC,aAAO;AAAA,QACL,4BAA4B,KAAK,IAAI,YAAYC,KAAAA,WAAWC,KAAAA,SAAS,OAAO,CAAC,CAAC;AAAA,MAAA;AAEhFC,aAAAA,QAAO,IAAI,IAAI,KAAK,KAAK,cAAc,SAAS,MAAM,OAAO,EAAE;AAE/D,aAAO;AAAA,IACT,CAAC;AAAA,IAEH,QAAQN,KAAAA,mBAAgC,EAAE,MAAM,MAAM;AACpD,YAAM,EAAE,SAAA,IAAaE,oBAAA;AACrB,aAAO,SAAS,SAAS,EAAE,YAAY,MAAM;AAAA,IAC/C,CAAC;AAAA,IAED,SAASF,KAAAA,mBAAiC,EACvC;AAAA,MACCC,GAAAA,EAAE,OAAO;AAAA,QACP,OAAOA,GAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,QAClB,QAAQA,GAAAA,EAAE,OAAA,EAAS,SAAA;AAAA,MAAS,CAC7B;AAAA,IAAA,EAEF,MAAM,CAAC,EAAE,YAAY;AACpB,YAAM,EAAE,SAAA,IAAaC,oBAAA;AACrB,aAAO,SAAS,SAAS,EAAE,OAAO,MAAM,OAAO,QAAQ,MAAM,QAAQ;AAAA,IACvE,CAAC;AAAA,IAEH,UAAUF,KAAAA,mBAAkC,EAAE,MAAM,MAAM;AACxD,YAAM,EAAE,SAAA,IAAaE,oBAAA;AACrB,aAAO,SAAS,SAAS;AAAA,IAC3B,CAAC;AAAA,IAED,WAAWF,KAAAA,mBAAmC,EAAE;AAAA,MAC9C,iBAAiB,EAAE,OAAA,GAAU;AAC3B,yBAAiB,CAAC,OAAO,KAAKO,aAAAA,GAAGJ,OAAAA,cAAc,QAAQ;AAAA,UACrD;AAAA,QAAA,CACD,GAAG;AACF,gBAAM;AAAA,QACR;AAAA,MACF;AAAA,IAAA;AAAA,EACF,CACD;AACH,CAAC;;"}
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const trpc = require("../trpc.js");
4
+ const chat = require("./chat.js");
5
+ const player = require("./player.js");
6
+ const plugin = require("./plugin.js");
7
+ const role = require("./role.js");
8
+ const server = require("./server.js");
9
+ const session = require("./session.js");
10
+ const user = require("./user.js");
11
+ const world = require("./world.js");
12
+ const appRouter = trpc.mergeRouters(
13
+ session.sessionRouter,
14
+ chat.chatRouter,
15
+ player.playerRouter,
16
+ plugin.pluginRouter,
17
+ server.serverRouter,
18
+ user.userRouter,
19
+ world.worldRouter,
20
+ role.roleRouter
21
+ );
22
+ exports.appRouter = appRouter;
23
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../../../../src/webserver/backend/router/index.ts"],"sourcesContent":["import { mergeRouters } from '../trpc';\nimport { chatRouter } from './chat';\nimport { playerRouter } from './player';\nimport { pluginRouter } from './plugin';\nimport { roleRouter } from './role';\nimport { serverRouter } from './server';\nimport { sessionRouter } from './session';\nimport { userRouter } from './user';\nimport { worldRouter } from './world';\n\nexport const appRouter = mergeRouters(\n sessionRouter,\n chatRouter,\n playerRouter,\n pluginRouter,\n serverRouter,\n userRouter,\n worldRouter,\n roleRouter,\n);\n\nexport type AppRouter = typeof appRouter;\n"],"names":["mergeRouters","sessionRouter","chatRouter","playerRouter","pluginRouter","serverRouter","userRouter","worldRouter","roleRouter"],"mappings":";;;;;;;;;;;AAUO,MAAM,YAAYA,KAAAA;AAAAA,EACvBC,QAAAA;AAAAA,EACAC,KAAAA;AAAAA,EACAC,OAAAA;AAAAA,EACAC,OAAAA;AAAAA,EACAC,OAAAA;AAAAA,EACAC,KAAAA;AAAAA,EACAC,MAAAA;AAAAA,EACAC,KAAAA;AACF;;"}
@@ -0,0 +1,248 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const player = require("../../../omegga/player.js");
4
+ const color = require("../../../util/color.js");
5
+ const time = require("../../../util/time.js");
6
+ const uuid = require("../../../util/uuid.js");
7
+ const _ = require("lodash");
8
+ const v4 = require("zod/v4");
9
+ const trpc = require("../trpc.js");
10
+ const util = require("../util.js");
11
+ const playerRouter = trpc.router({
12
+ player: trpc.router({
13
+ list: trpc.protectedProcedure().input(
14
+ v4.z.object({
15
+ page: v4.z.number().optional().default(0),
16
+ search: v4.z.string().optional().default(""),
17
+ sort: v4.z.string().optional().default("name"),
18
+ direction: v4.z.number().optional().default(1),
19
+ filter: v4.z.string().optional().default("")
20
+ })
21
+ ).query(async ({ input }) => {
22
+ const { database, omegga } = trpc.getContextDeps();
23
+ const { page, search, sort, direction, filter } = input;
24
+ const banList = (omegga.getBanList() || { banList: {} }).banList;
25
+ const now = Date.now();
26
+ let limitId;
27
+ if (filter === "banned") {
28
+ limitId = Object.keys(banList).filter(
29
+ (b) => banList[b].expires <= banList[b].created || time.parseBrickadiaTime(banList[b].expires) > Date.now()
30
+ );
31
+ }
32
+ const resp = await database.getPlayers({
33
+ page,
34
+ search,
35
+ sort,
36
+ direction,
37
+ limitId
38
+ });
39
+ const players = resp.players.map((player2) => {
40
+ const foundBan = banList[player2.id];
41
+ let ban = null;
42
+ if (foundBan) {
43
+ const created = time.parseBrickadiaTime(foundBan.created);
44
+ const expires = time.parseBrickadiaTime(foundBan.expires);
45
+ const duration = expires - created;
46
+ if (!(expires < now && duration > 0)) {
47
+ ban = {
48
+ bannerId: foundBan.bannerId,
49
+ reason: foundBan.reason,
50
+ created,
51
+ expires,
52
+ duration,
53
+ remainingTime: expires - now,
54
+ bannerName: _.get(
55
+ omegga.getNameCache(),
56
+ ["savedPlayerNames", foundBan.bannerId],
57
+ ""
58
+ )
59
+ };
60
+ }
61
+ }
62
+ return {
63
+ ...player2,
64
+ seenAgo: now - player2.lastSeen,
65
+ createdAgo: now - player2.created,
66
+ ban
67
+ };
68
+ });
69
+ return { ...resp, players };
70
+ }),
71
+ get: trpc.protectedProcedure().input(v4.z.string()).query(async ({ input: id }) => {
72
+ const { database, omegga } = trpc.getContextDeps();
73
+ const entry = await database.getPlayer(id);
74
+ if (!entry) return null;
75
+ const now = Date.now();
76
+ const nameHistory = entry.nameHistory.map((n) => ({
77
+ ...n,
78
+ ago: now - n.date
79
+ }));
80
+ const banHistory = entry.banHistory.map((b) => ({
81
+ ...b,
82
+ duration: b.expires - b.created,
83
+ bannerName: _.get(
84
+ omegga.getNameCache(),
85
+ ["savedPlayerNames", b.bannerId],
86
+ ""
87
+ )
88
+ }));
89
+ const kickHistory = entry.kickHistory.map((b) => ({
90
+ ...b,
91
+ kickerName: _.get(
92
+ omegga.getNameCache(),
93
+ ["savedPlayerNames", b.kickerId],
94
+ ""
95
+ )
96
+ }));
97
+ const playerRoles = player.default.getRoles(omegga, id) || [];
98
+ const { roles: serverRoles = [] } = omegga.getRoleSetup() ?? {};
99
+ const foundBan = (omegga.getBanList() || { banList: {} }).banList[id];
100
+ let currentBan = null;
101
+ if (foundBan) {
102
+ const created = time.parseBrickadiaTime(foundBan.created);
103
+ const expires = time.parseBrickadiaTime(foundBan.expires);
104
+ const duration = expires - created;
105
+ if (!(expires < now && duration > 0)) {
106
+ currentBan = {
107
+ bannerId: foundBan.bannerId,
108
+ reason: foundBan.reason,
109
+ created,
110
+ expires,
111
+ duration,
112
+ remainingTime: expires - now,
113
+ bannerName: _.get(
114
+ omegga.getNameCache(),
115
+ ["savedPlayerNames", foundBan.bannerId],
116
+ ""
117
+ )
118
+ };
119
+ }
120
+ }
121
+ return {
122
+ ...entry,
123
+ nameHistory,
124
+ banHistory,
125
+ kickHistory,
126
+ seenAgo: now - entry.lastSeen,
127
+ createdAgo: now - entry.created,
128
+ isHost: omegga.getHostId() === id,
129
+ isOnline: omegga.players.some((p) => p.id === id),
130
+ currentBan,
131
+ roles: playerRoles.map((r) => {
132
+ let color$1 = "ffffff";
133
+ const role = serverRoles.find(
134
+ (sr) => sr.name.toLowerCase() === r.toLowerCase()
135
+ );
136
+ if (role && role.bHasColor) color$1 = color.rgbToHex(role.color);
137
+ return { name: r, color: color$1 };
138
+ })
139
+ };
140
+ }),
141
+ ban: trpc.protectedProcedure().input(
142
+ v4.z.object({
143
+ id: v4.z.string(),
144
+ duration: v4.z.number().optional().default(-1),
145
+ reason: v4.z.string().max(128).optional().default("No Reason")
146
+ })
147
+ ).mutation(async ({ input, ctx }) => {
148
+ const { database, omegga } = trpc.getContextDeps();
149
+ const { id, duration } = input;
150
+ let { reason } = input;
151
+ if (typeof duration !== "number") return false;
152
+ if (!uuid.match(id)) return false;
153
+ reason = reason.replace(/\n/g, " ").replace(/"/g, "'");
154
+ ctx.log(
155
+ "Banning player",
156
+ omegga.getNameCache()?.savedPlayerNames?.[id].yellow ?? "with id " + id.yellow
157
+ );
158
+ omegga.writeln(`Chat.Command /Ban "${id}" ${duration} "${reason}"`);
159
+ const ban = await util.waitForEvent(database, "update.bans", () => {
160
+ const banList = (omegga.getBanList() || { banList: {} }).banList;
161
+ if (!banList[id]) return false;
162
+ return banList[id];
163
+ });
164
+ if (ban) {
165
+ const entry = {
166
+ type: "banHistory",
167
+ banned: id,
168
+ bannerId: null,
169
+ created: time.parseBrickadiaTime(ban.created),
170
+ expires: time.parseBrickadiaTime(ban.expires),
171
+ reason: ban.reason
172
+ };
173
+ await database.stores.players.update(
174
+ entry,
175
+ { $set: entry },
176
+ { upsert: true }
177
+ );
178
+ }
179
+ return !!ban;
180
+ }),
181
+ kick: trpc.protectedProcedure().input(
182
+ v4.z.object({
183
+ id: v4.z.string(),
184
+ reason: v4.z.string().max(128).optional().default("No Reason")
185
+ })
186
+ ).mutation(async ({ input, ctx }) => {
187
+ const { database, omegga } = trpc.getContextDeps();
188
+ const { id } = input;
189
+ let { reason } = input;
190
+ if (!uuid.match(id)) return false;
191
+ reason = reason.replace(/\n/g, " ").replace(/"/g, "'");
192
+ const player2 = omegga.players.find((p) => p.id === id);
193
+ if (!player2) return false;
194
+ ctx.log("Kicking player", player2.name.yellow);
195
+ omegga.writeln(`Chat.Command /Kick "${id}" "${reason}"`);
196
+ const ok = await util.waitForEvent(
197
+ omegga,
198
+ "leave",
199
+ (leavingPlayer) => leavingPlayer.id === id
200
+ );
201
+ if (ok) {
202
+ const entry = {
203
+ type: "kickHistory",
204
+ kicked: id,
205
+ kickerId: null,
206
+ created: Date.now(),
207
+ reason
208
+ };
209
+ await database.stores.players.update(
210
+ entry,
211
+ { $set: entry },
212
+ { upsert: true }
213
+ );
214
+ }
215
+ return ok;
216
+ }),
217
+ unban: trpc.protectedProcedure().input(v4.z.object({ id: v4.z.string() })).mutation(async ({ input, ctx }) => {
218
+ const { database, omegga } = trpc.getContextDeps();
219
+ const { id } = input;
220
+ if (!uuid.match(id)) return false;
221
+ const banList = (omegga.getBanList() || { banList: {} }).banList;
222
+ if (!banList[id]) return false;
223
+ ctx.log(
224
+ "Unbanning player",
225
+ omegga.getNameCache()?.savedPlayerNames?.[id].yellow ?? "with id " + id.yellow
226
+ );
227
+ omegga.writeln(`Chat.Command /Unban "${id}"`);
228
+ const ok = await util.waitForEvent(database, "update.bans", () => {
229
+ const banList2 = (omegga.getBanList() || { banList: {} }).banList;
230
+ return !banList2[id] || time.parseBrickadiaTime(banList2[id].expires) < Date.now();
231
+ });
232
+ return ok;
233
+ }),
234
+ clearBricks: trpc.protectedProcedure().input(v4.z.object({ id: v4.z.string() })).mutation(async ({ input, ctx }) => {
235
+ const { omegga } = trpc.getContextDeps();
236
+ const { id } = input;
237
+ if (!uuid.match(id)) return false;
238
+ ctx.log(
239
+ "Clearing bricks for player",
240
+ omegga.getNameCache()?.savedPlayerNames?.[id]?.yellow ?? "with id " + id.yellow
241
+ );
242
+ omegga.writeln(`Bricks.Clear "${id}"`);
243
+ return true;
244
+ })
245
+ })
246
+ });
247
+ exports.playerRouter = playerRouter;
248
+ //# sourceMappingURL=player.js.map
@@ -0,0 +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 { getContextDeps, protectedProcedure, router } from '../trpc';\nimport type { IStoreBanHistory, IStoreKickHistory } from '../types';\nimport { waitForEvent } from '../util';\n\nexport const playerRouter = router({\n player: router({\n list: protectedProcedure('player.list')\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('player.get')\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('player.ban')\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 const entry: IStoreBanHistory = {\n type: 'banHistory',\n banned: id,\n bannerId: null,\n created: parseBrickadiaTime(ban.created),\n expires: parseBrickadiaTime(ban.expires),\n reason: ban.reason,\n };\n await database.stores.players.update(\n entry,\n { $set: entry },\n { upsert: true },\n );\n }\n return !!ban;\n }),\n\n kick: protectedProcedure('player.kick')\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 const entry: IStoreKickHistory = {\n type: 'kickHistory',\n kicked: id,\n kickerId: null,\n created: Date.now(),\n reason,\n };\n await database.stores.players.update(\n entry,\n { $set: entry },\n { upsert: true },\n );\n }\n return ok;\n }),\n\n unban: protectedProcedure('player.unban')\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('player.clearBricks')\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});\n"],"names":["router","protectedProcedure","z","getContextDeps","parseBrickadiaTime","player","Player","color","rgbToHex","uuid.match","waitForEvent","banList"],"mappings":"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"}
@@ -0,0 +1,150 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const v4 = require("zod/v4");
4
+ const _ = require("lodash");
5
+ const trpc = require("../trpc.js");
6
+ const events = require("../events.js");
7
+ const EventEmitter = require("events");
8
+ const pluginRouter = trpc.router({
9
+ plugin: trpc.router({
10
+ list: trpc.protectedProcedure().query(() => {
11
+ const { omegga } = trpc.getContextDeps();
12
+ return _.sortBy(
13
+ omegga.pluginLoader.plugins.map((p) => ({
14
+ name: p.getName(),
15
+ documentation: p.getDocumentation(),
16
+ path: p.shortPath,
17
+ isLoaded: p.isLoaded(),
18
+ isEnabled: p.isEnabled()
19
+ })),
20
+ (p) => p.name.toLowerCase()
21
+ );
22
+ }),
23
+ get: trpc.protectedProcedure().input(v4.z.object({ shortPath: v4.z.string() })).query(async ({ input }) => {
24
+ const { omegga } = trpc.getContextDeps();
25
+ const plugin = omegga.pluginLoader.plugins.find(
26
+ (p) => p.shortPath === input.shortPath
27
+ );
28
+ if (!plugin) return null;
29
+ const [defaultConfig, config, objCount] = await Promise.all([
30
+ plugin.storage.getDefaultConfig(),
31
+ plugin.storage.getConfig(),
32
+ plugin.storage.count()
33
+ ]);
34
+ return {
35
+ name: plugin.getName(),
36
+ format: plugin.constructor.getFormat(),
37
+ info: plugin.getInfo(),
38
+ documentation: plugin.getDocumentation(),
39
+ config,
40
+ defaultConfig,
41
+ objCount,
42
+ path: plugin.shortPath,
43
+ isLoaded: plugin.isLoaded(),
44
+ isEnabled: plugin.isEnabled()
45
+ };
46
+ }),
47
+ config: trpc.protectedProcedure().input(
48
+ v4.z.object({
49
+ shortPath: v4.z.string(),
50
+ config: v4.z.record(v4.z.string(), v4.z.unknown())
51
+ })
52
+ ).mutation(async ({ input }) => {
53
+ const { omegga } = trpc.getContextDeps();
54
+ const plugin = omegga.pluginLoader.plugins.find(
55
+ (p) => p.shortPath === input.shortPath
56
+ );
57
+ if (!plugin) return null;
58
+ await plugin.storage.setConfig(input.config);
59
+ return true;
60
+ }),
61
+ reloadAll: trpc.protectedProcedure().mutation(
62
+ async ({ ctx }) => {
63
+ const { omegga } = trpc.getContextDeps();
64
+ const { log, error } = ctx;
65
+ if (!omegga.pluginLoader) {
66
+ error("Omegga is not using plugins");
67
+ return false;
68
+ }
69
+ log("Unloading current plugins");
70
+ let success = await omegga.pluginLoader.unload();
71
+ if (!success) {
72
+ error("Could not unload all plugins");
73
+ return false;
74
+ }
75
+ log("Scanning for new plugins");
76
+ success = await omegga.pluginLoader.scan();
77
+ if (!success) {
78
+ error("Could not scan for plugins");
79
+ return false;
80
+ }
81
+ log("Starting plugins");
82
+ success = await omegga.pluginLoader.reload();
83
+ if (success) {
84
+ const plugins = omegga.pluginLoader.plugins.filter((p) => p.isLoaded()).map((p) => p.getName());
85
+ log("Loaded", (plugins.length + "").yellow, "plugins:", plugins);
86
+ return true;
87
+ } else {
88
+ error("Could not load all plugins");
89
+ return false;
90
+ }
91
+ }
92
+ ),
93
+ unload: trpc.protectedProcedure().input(v4.z.object({ shortPath: v4.z.string() })).mutation(async ({ input, ctx }) => {
94
+ const { omegga } = trpc.getContextDeps();
95
+ const { log } = ctx;
96
+ const plugin = omegga.pluginLoader.plugins.find(
97
+ (p) => p.shortPath === input.shortPath
98
+ );
99
+ if (!plugin) return false;
100
+ if (!plugin.isLoaded()) return false;
101
+ log("Unloading".red, "plugin", plugin.getName().yellow);
102
+ return await plugin.unload();
103
+ }),
104
+ load: trpc.protectedProcedure().input(v4.z.object({ shortPath: v4.z.string() })).mutation(async ({ input, ctx }) => {
105
+ const { omegga } = trpc.getContextDeps();
106
+ const { log } = ctx;
107
+ const plugin = omegga.pluginLoader.plugins.find(
108
+ (p) => p.shortPath === input.shortPath
109
+ );
110
+ if (!plugin) return false;
111
+ if (plugin.isLoaded() || !plugin.isEnabled()) return false;
112
+ log("Loading".green, "plugin", plugin.getName().yellow);
113
+ return await plugin.load();
114
+ }),
115
+ toggle: trpc.protectedProcedure().input(v4.z.object({ shortPath: v4.z.string(), enabled: v4.z.boolean() })).mutation(({ input, ctx }) => {
116
+ const { omegga } = trpc.getContextDeps();
117
+ const { log, error } = ctx;
118
+ const plugin = omegga.pluginLoader.plugins.find(
119
+ (p) => p.shortPath === input.shortPath
120
+ );
121
+ if (!plugin) return false;
122
+ try {
123
+ plugin.setEnabled(input.enabled);
124
+ log(
125
+ input.enabled ? "Enabled".green : "Disabled".red,
126
+ "plugin",
127
+ plugin.getName().yellow
128
+ );
129
+ return true;
130
+ } catch (e) {
131
+ error(
132
+ "Error",
133
+ input.enabled ? "enabling".green : "disabling".red,
134
+ "plugin",
135
+ plugin.getName().yellow
136
+ );
137
+ return false;
138
+ }
139
+ }),
140
+ onStatus: trpc.protectedProcedure().subscription(
141
+ async function* () {
142
+ for await (const [payload] of EventEmitter.on(events.serverEvents, "plugin")) {
143
+ yield payload;
144
+ }
145
+ }
146
+ )
147
+ })
148
+ });
149
+ exports.pluginRouter = pluginRouter;
150
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sources":["../../../../src/webserver/backend/router/plugin.ts"],"sourcesContent":["import { z } from 'zod/v4';\nimport _ from 'lodash';\nimport { Plugin } from '@omegga/plugin/interface';\nimport { router, protectedProcedure, getContextDeps } from '../trpc';\nimport { serverEvents } from '../events';\nimport { on } from 'events';\n\nexport const pluginRouter = router({\n plugin: router({\n list: protectedProcedure('plugin.list').query(() => {\n const { omegga } = getContextDeps();\n return _.sortBy(\n omegga.pluginLoader.plugins.map(p => ({\n name: p.getName(),\n documentation: p.getDocumentation(),\n path: p.shortPath,\n isLoaded: p.isLoaded(),\n isEnabled: p.isEnabled(),\n })),\n p => p.name.toLowerCase(),\n );\n }),\n\n get: protectedProcedure('plugin.get')\n .input(z.object({ shortPath: z.string() }))\n .query(async ({ input }) => {\n const { omegga } = getContextDeps();\n const plugin = omegga.pluginLoader.plugins.find(\n p => p.shortPath === input.shortPath,\n );\n if (!plugin) return null;\n\n const [defaultConfig, config, objCount] = await Promise.all([\n plugin.storage.getDefaultConfig(),\n plugin.storage.getConfig(),\n plugin.storage.count(),\n ]);\n\n return {\n name: plugin.getName(),\n format: (plugin.constructor as typeof Plugin).getFormat(),\n info: plugin.getInfo(),\n documentation: plugin.getDocumentation(),\n config,\n defaultConfig,\n objCount,\n path: plugin.shortPath,\n isLoaded: plugin.isLoaded(),\n isEnabled: plugin.isEnabled(),\n };\n }),\n\n config: protectedProcedure('plugin.config')\n .input(\n z.object({\n shortPath: z.string(),\n config: z.record(z.string(), z.unknown()),\n }),\n )\n .mutation(async ({ input }) => {\n const { omegga } = getContextDeps();\n const plugin = omegga.pluginLoader.plugins.find(\n p => p.shortPath === input.shortPath,\n );\n if (!plugin) return null;\n\n await plugin.storage.setConfig(input.config);\n return true;\n }),\n\n reloadAll: protectedProcedure('plugin.reloadAll').mutation(\n async ({ ctx }) => {\n const { omegga } = getContextDeps();\n const { log, error } = ctx;\n\n if (!omegga.pluginLoader) {\n error('Omegga is not using plugins');\n return false;\n }\n\n log('Unloading current plugins');\n let success = await omegga.pluginLoader.unload();\n if (!success) {\n error('Could not unload all plugins');\n return false;\n }\n\n log('Scanning for new plugins');\n success = await omegga.pluginLoader.scan();\n if (!success) {\n error('Could not scan for plugins');\n return false;\n }\n\n log('Starting plugins');\n success = await omegga.pluginLoader.reload();\n if (success) {\n const plugins = omegga.pluginLoader.plugins\n .filter(p => p.isLoaded())\n .map(p => p.getName());\n log('Loaded', (plugins.length + '').yellow, 'plugins:', plugins);\n return true;\n } else {\n error('Could not load all plugins');\n return false;\n }\n },\n ),\n\n unload: protectedProcedure('plugin.unload')\n .input(z.object({ shortPath: z.string() }))\n .mutation(async ({ input, ctx }) => {\n const { omegga } = getContextDeps();\n const { log } = ctx;\n\n const plugin = omegga.pluginLoader.plugins.find(\n p => p.shortPath === input.shortPath,\n );\n if (!plugin) return false;\n if (!plugin.isLoaded()) return false;\n\n log('Unloading'.red, 'plugin', plugin.getName().yellow);\n return await plugin.unload();\n }),\n\n load: protectedProcedure('plugin.load')\n .input(z.object({ shortPath: z.string() }))\n .mutation(async ({ input, ctx }) => {\n const { omegga } = getContextDeps();\n const { log } = ctx;\n\n const plugin = omegga.pluginLoader.plugins.find(\n p => p.shortPath === input.shortPath,\n );\n if (!plugin) return false;\n if (plugin.isLoaded() || !plugin.isEnabled()) return false;\n\n log('Loading'.green, 'plugin', plugin.getName().yellow);\n return await plugin.load();\n }),\n\n toggle: protectedProcedure('plugin.toggle')\n .input(z.object({ shortPath: z.string(), enabled: z.boolean() }))\n .mutation(({ input, ctx }) => {\n const { omegga } = getContextDeps();\n const { log, error } = ctx;\n\n const plugin = omegga.pluginLoader.plugins.find(\n p => p.shortPath === input.shortPath,\n );\n if (!plugin) return false;\n\n try {\n plugin.setEnabled(input.enabled);\n log(\n input.enabled ? 'Enabled'.green : 'Disabled'.red,\n 'plugin',\n plugin.getName().yellow,\n );\n return true;\n } catch (e) {\n error(\n 'Error',\n input.enabled ? 'enabling'.green : 'disabling'.red,\n 'plugin',\n plugin.getName().yellow,\n );\n return false;\n }\n }),\n\n onStatus: protectedProcedure('plugin.onStatus').subscription(\n async function* () {\n for await (const [payload] of on(serverEvents, 'plugin')) {\n yield payload as {\n shortPath: string;\n name: string;\n isLoaded: boolean;\n isEnabled: boolean;\n };\n }\n },\n ),\n }),\n});\n"],"names":["router","protectedProcedure","getContextDeps","z","on","serverEvents"],"mappings":"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"}