omegga 1.8.1 → 1.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/README.md +7 -4
- package/dist/db/nedbImport.js +4 -2
- package/dist/db/nedbImport.js.map +1 -1
- package/dist/omegga/matchers/index.js +3 -1
- package/dist/omegga/matchers/index.js.map +1 -1
- package/dist/omegga/matchers/wire.js +27 -0
- package/dist/omegga/matchers/wire.js.map +1 -0
- package/dist/omegga/server.js +112 -70
- package/dist/omegga/server.js.map +1 -1
- package/dist/plugin.js.map +1 -1
- package/dist/util/brdb.js +38 -585
- package/dist/util/brdb.js.map +1 -1
- package/dist/util/index.js +50 -3
- package/dist/util/index.js.map +1 -1
- package/dist/util/time.js +2 -0
- package/dist/util/time.js.map +1 -1
- package/dist/util/uuid.js +2 -4
- package/dist/util/uuid.js.map +1 -1
- package/dist/webserver/backend/database.js +4 -2
- package/dist/webserver/backend/database.js.map +1 -1
- package/package.json +3 -3
- package/public/app.html +20 -11
- package/public/assets/{app-CHjgCOH4.js → app-DvPQUQnE.js} +2 -2
- package/public/assets/app-DvPQUQnE.js.map +1 -0
- package/public/auth.html +19 -4
- package/templates/safe-ts/omegga.d.ts +2518 -38
- package/public/assets/app-CHjgCOH4.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,25 @@
|
|
|
4
4
|
|
|
5
5
|
- Future features go here
|
|
6
6
|
|
|
7
|
+
## 1.9.1 - 2026-07-07
|
|
8
|
+
|
|
9
|
+
Ban-history crash fix, web-ui ban/kick modal polish, and a brs-js bump.
|
|
10
|
+
|
|
11
|
+
- Database: Fix server crash when syncing bans
|
|
12
|
+
- Update brs-js to 3.2.4
|
|
13
|
+
|
|
14
|
+
## 1.9.0 - 2026-07-05
|
|
15
|
+
|
|
16
|
+
Wire graph log events and a brs-js 3.0.0 upgrade for native brdb/brz reading.
|
|
17
|
+
|
|
18
|
+
- Plugins: Add `wirelog` event, emitted for each `[Wire Graph]` console log with the raw text
|
|
19
|
+
- Plugins: Add `wirecmd:command` event, emitted when a `[Wire Graph]` log starts with a command word (lowercased) followed by space-separated args
|
|
20
|
+
- Update brs-js to 3.2.1 and expose brdb/brz features on `OMEGGA_UTIL.brdb`
|
|
21
|
+
- World revisions are now read directly from the `.brdb` file instead of the game console, so `getWorldRevisions` no longer requires the server to be running
|
|
22
|
+
- Plugins: Add `OMEGGA_UTIL.Sqlite` for opening sqlite files (or brdb files)
|
|
23
|
+
- Plugins: `loadSaveData`/`loadSaveDataOnPlayer`/`getSaveData` are back using `brs-js` 3
|
|
24
|
+
- Fix `clearRegion`, `clearAllBricks`, `loadSaveData`, `getSaveData`, in safe plugins
|
|
25
|
+
|
|
7
26
|
## 1.8.1 - 2026-07-03
|
|
8
27
|
|
|
9
28
|
Prefab support for EA3, replacing the removed `.brs` bricks commands.
|
package/README.md
CHANGED
|
@@ -12,17 +12,18 @@ Omegga can do things like:
|
|
|
12
12
|
- Read chat history with timestamps
|
|
13
13
|
- See kick and ban history
|
|
14
14
|
- Configure plugins from a web interface
|
|
15
|
+
- Manage permissions and multi-user role based access to the above features on a web ui
|
|
15
16
|
|
|
16
17
|
Omegga plugins can do things like:
|
|
17
18
|
|
|
19
|
+
- Interface with in-game wires and react to in-game wire events
|
|
18
20
|
- Add custom chat !commands and /commands
|
|
19
21
|
- Respond to and send chat messages
|
|
20
|
-
- Load bricks
|
|
21
|
-
- Load/Clear regions of bricks
|
|
22
|
+
- Load bricks onto a player's template
|
|
23
|
+
- Load/Clear regions of bricks, entities
|
|
22
24
|
- Damage/heal players
|
|
23
25
|
- Give/remove weapons to players
|
|
24
26
|
- Change the environment
|
|
25
|
-
- Create/delete minigames, and join/leave players from minigames
|
|
26
27
|
- Teleport players, detect player's positions
|
|
27
28
|
- Grant players roles
|
|
28
29
|
- Detect when a brick with an interact component is clicked
|
|
@@ -891,7 +892,9 @@ Register custom `/commands` by returning `{registeredCommands: ['foo', 'bar']}`
|
|
|
891
892
|
| `event:NAME` | [>player from click<, ...args] | Runs when an interact component has `event:NAME: arg1,arg2,arg\,3, | |
|
|
892
893
|
| `mapchange` | \[{map}\] | Runs when the map changes | |
|
|
893
894
|
| `autorestart` | [autorestart config] | Runs server has an autorestart scheduled | |
|
|
894
|
-
| `minigamejoin` | {player: {name, id}; minigameName: string} | Runs when a player joins a minigame. Note that minigameName is not unique between minigames. minigameName will be null if player leaves all minigames. This will run before `join`
|
|
895
|
+
| `minigamejoin` | {player: {name, id}; minigameName: string} | **Deprecated as of EA3.** Runs when a player joins a minigame. Note that minigameName is not unique between minigames. minigameName will be null if player leaves all minigames. This will run before `join` | |
|
|
896
|
+
| `wirelog` | [raw string] | Runs when a `[Wire Graph]` log line is emitted. `raw` is the text after the `[Wire Graph] ` prefix | |
|
|
897
|
+
| `wirecmd:command` | [...args] | Runs when a `[Wire Graph]` log starts with `command args`. `command` is lowercased; args are the remaining space-separated words | |
|
|
895
898
|
|
|
896
899
|
### Folder Structure
|
|
897
900
|
|
package/dist/db/nedbImport.js
CHANGED
|
@@ -247,8 +247,10 @@ async function importNedb(dataPath, mainSqlite, mainDb) {
|
|
|
247
247
|
mainDb.insert(schema.banHistory).values({
|
|
248
248
|
banned: b.banned ?? "",
|
|
249
249
|
bannerId: b.bannerId ?? "",
|
|
250
|
-
|
|
251
|
-
|
|
250
|
+
// Number.isFinite (not typeof) so a NaN timestamp becomes 0,
|
|
251
|
+
// binding NaN to a NOT NULL integer column stores NULL and throws
|
|
252
|
+
created: Number.isFinite(b.created) ? b.created : 0,
|
|
253
|
+
expires: Number.isFinite(b.expires) ? b.expires : 0,
|
|
252
254
|
reason: b.reason ?? ""
|
|
253
255
|
}).onConflictDoNothing().run();
|
|
254
256
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nedbImport.js","sources":["../../src/db/nedbImport.ts"],"sourcesContent":["import Logger from '@/logger';\nimport soft from '@/softconfig';\nimport {\n decodePermissions,\n type StoredPermissionSet,\n} from '@webserver/backend/permissions';\nimport type BetterSqlite3 from 'better-sqlite3';\nimport { eq } from 'drizzle-orm';\nimport {\n drizzle,\n type BetterSQLite3Database,\n} from 'drizzle-orm/better-sqlite3';\nimport fs from 'fs';\nimport Datastore from 'nedb-promises';\nimport path from 'path';\nimport { openDb } from './connection';\nimport { runMigrations, runPluginMigrations } from './migrate';\nimport * as mainSchema from './schema';\nimport * as pluginSchema from './pluginSchema';\n\nconst MARKER_FILE = 'nedb-imported.marker';\nconst BATCH_SIZE = 500;\n\n// legacy NeDB database filenames; the one-time import is their only consumer\nexport const NEDB_FILES = {\n users: 'users.db',\n chat: 'chat.db',\n players: 'players.db',\n status: 'status.db',\n server: 'store.db',\n plugins: 'plugins.db',\n} as const;\n\n// Apply the NeDB data migrations that the old doMigrations() would have run.\n// Users v1: convert old NeDB permission format to PermissionSet.\n// Server v1: re-encode defaultPermissions from old format.\n// NeDB stored scope keys colon-encoded ('chat:send'); runtime lookups use\n// dotted keys, so every path below must run decodePermissions.\n\n// the v1 format used 'unset' sentinels and 'enabled'/'disabled' scope strings,\n// none of which are valid v2 values. detect by the presence of any sentinel so\n// an empty-scopes v1 doc (which a scopes-only check would miss) is still\n// normalized, while a valid v2 'none' domain is preserved.\nfunction isLegacyPermissions(p: any): boolean {\n if (p.root === 'unset') return true;\n for (const v of Object.values(p.domains ?? {}))\n if (v === 'unset') return true;\n for (const v of Object.values(p.scopes ?? {}))\n if (typeof v === 'string') return true;\n return false;\n}\n\n// normalize a v1 permission doc to a v2 StoredPermissionSet (still colon-keyed)\nfunction normalizeLegacyPermissions(p: any): StoredPermissionSet {\n const root = p.root === 'all' || p.root === 'read' ? p.root : 'off';\n const domains: Record<string, string> = {};\n for (const [k, v] of Object.entries(p.domains ?? {})) {\n if (v !== 'unset' && v !== 'none') domains[k] = v as string;\n }\n const scopes: Record<string, boolean> = {};\n for (const [k, v] of Object.entries(p.scopes ?? {})) {\n if (v === 'enabled' || v === true) scopes[k] = true;\n else if (v === 'disabled' || v === false) scopes[k] = false;\n }\n return { root, domains, scopes } as StoredPermissionSet;\n}\n\nfunction migrateUserPermissions(user: any): any {\n const p = user.permissions;\n if (!p || typeof p !== 'object') return user;\n const stored = isLegacyPermissions(p) ? normalizeLegacyPermissions(p) : p;\n return { ...user, permissions: decodePermissions(stored) };\n}\n\nfunction migrateDefaultPermissions(doc: any): any {\n if (!doc) return doc;\n const stored = isLegacyPermissions(doc)\n ? normalizeLegacyPermissions(doc)\n : doc;\n return { ...doc, ...decodePermissions(stored) };\n}\n\n// legacy nameHistory may predate the {name,displayName,date} object shape (older\n// docs stored bare strings) or be hand-edited; coerce to well-formed objects so\n// the json_each/REGEXP player search can't hit malformed JSON or null keys\nfunction sanitizeNameHistory(raw: any): {\n name: string;\n displayName: string;\n date: number;\n}[] {\n if (!Array.isArray(raw)) return [];\n return raw\n .filter(h => h && typeof h === 'object')\n .map(h => ({\n name: String(h.name ?? ''),\n displayName: String(h.displayName ?? ''),\n date: typeof h.date === 'number' ? h.date : 0,\n }));\n}\n\nasync function loadNedb<T = any>(filepath: string): Promise<T[]> {\n if (!fs.existsSync(filepath)) return [];\n const store = Datastore.create({ filename: filepath, autoload: true });\n return (await store.find({})) as T[];\n}\n\n// invoke fn on BATCH_SIZE-sized slices of docs (keeps multi-row inserts under\n// SQLite's bound-parameter limit). NOT wrapped in its own transaction — callers\n// run all batches inside one outer transaction for atomicity.\nfunction eachBatch(docs: any[], fn: (docs: any[]) => void) {\n for (let i = 0; i < docs.length; i += BATCH_SIZE) {\n fn(docs.slice(i, i + BATCH_SIZE));\n }\n}\n\n// key in server_config marking the main-DB import as committed. checked\n// in-band with the data so a crash between commit and marker-file write can't\n// cause a re-import (which would duplicate the append-only tables).\nconst IMPORT_FLAG_KEY = 'nedbImported';\n\n// coalesce concurrent import calls for the same data dir (the webserver and the\n// plugin loader both bootstrap it). entries are dropped once settled, so this is\n// purely a concurrency guard — repeat suppression is the marker file / DB flag.\nconst importsInFlight = new Map<string, Promise<void>>();\n\nexport function importNedbIfNeeded(\n dataPath: string,\n mainSqlite?: BetterSqlite3.Database,\n mainDb?: BetterSQLite3Database,\n): Promise<void> {\n const key = path.resolve(dataPath);\n let promise = importsInFlight.get(key);\n if (!promise) {\n promise = importNedb(dataPath, mainSqlite, mainDb).finally(() =>\n importsInFlight.delete(key),\n );\n importsInFlight.set(key, promise);\n }\n return promise;\n}\n\nasync function importNedb(\n dataPath: string,\n mainSqlite?: BetterSqlite3.Database,\n mainDb?: BetterSQLite3Database,\n) {\n const markerPath = path.join(dataPath, MARKER_FILE);\n if (fs.existsSync(markerPath)) return;\n\n const filePath = (f: string) => path.join(dataPath, f);\n const mainNedbFiles = [\n NEDB_FILES.users,\n NEDB_FILES.chat,\n NEDB_FILES.players,\n NEDB_FILES.status,\n NEDB_FILES.server,\n ];\n const hasMainNedb = mainNedbFiles.some(f => fs.existsSync(filePath(f)));\n const pluginPath = filePath(NEDB_FILES.plugins);\n const hasPluginNedb = fs.existsSync(pluginPath);\n if (!hasMainNedb && !hasPluginNedb) {\n fs.writeFileSync(markerPath, new Date().toISOString());\n return;\n }\n\n // open a dedicated main-db connection when the caller doesn't have one\n // (the plugin loader bootstraps the import without a webserver)\n let ownConnection = false;\n if (!mainSqlite || !mainDb) {\n ownConnection = true;\n mainSqlite = openDb(filePath(soft.MAIN_DB));\n mainDb = drizzle(mainSqlite);\n runMigrations(mainDb);\n }\n\n try {\n Logger.log('>>'.green, 'Importing NeDB data into SQLite...');\n const counts: Record<string, number> = {};\n\n // ---- Load phase: read every NeDB file up front (async). All writes happen\n // afterwards inside a single synchronous transaction so a partial failure\n // rolls back cleanly and a retry can't double-import append-only tables. ----\n const users = (await loadNedb(filePath(NEDB_FILES.users)))\n .filter((d: any) => d.type === 'user')\n .map(migrateUserPermissions);\n const chatMessages = (await loadNedb(filePath(NEDB_FILES.chat)))\n .filter((d: any) => d.type === 'chat')\n .sort((a: any, b: any) => (a.created ?? 0) - (b.created ?? 0));\n const playerDocs = await loadNedb(filePath(NEDB_FILES.players));\n const histories = playerDocs.filter((d: any) => d.type === 'userHistory');\n const bans = playerDocs.filter((d: any) => d.type === 'banHistory');\n const kicks = playerDocs.filter((d: any) => d.type === 'kickHistory');\n const notes = playerDocs.filter((d: any) => d.type === 'note');\n const statusDocs = await loadNedb(filePath(NEDB_FILES.status));\n const hbDocs = statusDocs\n .filter((d: any) => d.type === 'heartbeat')\n .sort((a: any, b: any) => (a.created ?? 0) - (b.created ?? 0));\n const pcDocs = statusDocs.filter((d: any) => d.type === 'punchcard');\n const serverDocs = await loadNedb(filePath(NEDB_FILES.server));\n const instances = serverDocs.filter((d: any) => d.type === 'app:start');\n const roles = serverDocs.filter((d: any) => d.type === 'webRole');\n const rawDefaultPerms = serverDocs.find(\n (d: any) => d.type === 'defaultPermissions',\n );\n const autoRestart = serverDocs.find(\n (d: any) => d.type === 'autoRestartConfig',\n );\n\n // warn about user rows that will be dropped by the new UNIQUE(username):\n // NeDB had no such constraint, so legacy data can hold duplicates\n const seenUsernames = new Set<string>();\n for (const u of users) {\n const name = u.username ?? '';\n if (seenUsernames.has(name))\n Logger.error(\n '!>'.red,\n `Duplicate username ${JSON.stringify(name)} in legacy data; user ${u._id} will not be imported`,\n );\n else seenUsernames.add(name);\n }\n\n // ---- Plugins: a separate connection/DB, imported first and idempotently\n // (onConflictDoNothing against real unique constraints), so re-running after\n // a main-DB failure is a safe no-op. ----\n if (hasPluginNedb) {\n const pluginSqlite = openDb(filePath(soft.PLUGINS_DB));\n try {\n const pluginDb = drizzle(pluginSqlite);\n runPluginMigrations(pluginDb);\n const pluginDocs = await loadNedb(pluginPath);\n // NeDB allowed store.set(key, null/undefined); the new value column is\n // NOT NULL, so skip those rows\n const storeDocs = pluginDocs.filter(\n (d: any) => d.type === 'store' && d.value != null,\n );\n const skipped =\n pluginDocs.filter((d: any) => d.type === 'store').length -\n storeDocs.length;\n if (skipped > 0)\n Logger.log(\n ':>'.yellow,\n `Skipping ${skipped} plugin store entr${skipped === 1 ? 'y' : 'ies'} with no value`,\n );\n const configDocs = pluginDocs.filter((d: any) => d.type === 'config');\n\n const pluginTx = pluginSqlite.transaction(() => {\n eachBatch(storeDocs, batch =>\n pluginDb\n .insert(pluginSchema.pluginStore)\n .values(\n batch.map((s: any) => ({\n plugin: s.plugin ?? '',\n key: s.key ?? '',\n value: s.value,\n })),\n )\n .onConflictDoNothing()\n .run(),\n );\n eachBatch(configDocs, batch =>\n pluginDb\n .insert(pluginSchema.pluginConfig)\n .values(\n batch.map((c: any) => ({\n plugin: c.plugin ?? '',\n value: c.value ?? {},\n })),\n )\n .onConflictDoNothing()\n .run(),\n );\n });\n pluginTx();\n if (storeDocs.length) counts.pluginStoreItems = storeDocs.length;\n if (configDocs.length) counts.pluginConfigs = configDocs.length;\n } finally {\n pluginSqlite.close();\n }\n }\n\n // ---- Main DB: one atomic transaction. The completion flag is written in\n // the same transaction as the data, so either both commit or neither does —\n // a crash before the marker file is written can't trigger a re-import. ----\n const alreadyImported = mainDb\n .select()\n .from(mainSchema.serverConfig)\n .where(eq(mainSchema.serverConfig.key, IMPORT_FLAG_KEY))\n .get();\n\n if (!alreadyImported && hasMainNedb) {\n const tx = mainSqlite.transaction(() => {\n eachBatch(users, batch =>\n mainDb\n .insert(mainSchema.users)\n .values(\n batch.map((u: any) => ({\n id: u._id,\n created: u.created ?? Date.now(),\n lastOnline: u.lastOnline ?? 0,\n username: u.username ?? '',\n hash: u.hash ?? '',\n isOwner: !!u.isOwner,\n roles: u.roles ?? [],\n playerId: u.playerId ?? '',\n isBanned: !!u.isBanned,\n permissions: u.permissions ?? null,\n totpSecret: u.totpSecret ?? null,\n totpEnabled: !!u.totpEnabled,\n passkeys: u.passkeys ?? [],\n recoveryCodes: u.recoveryCodes ?? [],\n })),\n )\n .onConflictDoNothing()\n .run(),\n );\n if (users.length) counts.users = users.length;\n\n eachBatch(chatMessages, batch =>\n mainDb\n .insert(mainSchema.chatLogs)\n .values(\n batch.map((c: any) => ({\n created: c.created ?? 0,\n instanceId: c.instanceId ?? '',\n action: c.action ?? 'msg',\n user: c.user ?? {},\n message: c.message ?? null,\n })),\n )\n .run(),\n );\n if (chatMessages.length) counts.chatMessages = chatMessages.length;\n\n eachBatch(histories, batch =>\n mainDb\n .insert(mainSchema.playerHistory)\n .values(\n batch.map((p: any) => ({\n id: p.id,\n name: p.name ?? '',\n displayName: p.displayName ?? '',\n nameHistory: sanitizeNameHistory(p.nameHistory),\n ips: Array.isArray(p.ips) ? p.ips : [],\n created: p.created ?? 0,\n lastSeen: p.lastSeen ?? 0,\n lastInstanceId: p.lastInstanceId ?? '',\n heartbeats: p.heartbeats ?? 0,\n sessions: p.sessions ?? 0,\n instances: p.instances ?? 0,\n })),\n )\n .onConflictDoNothing()\n .run(),\n );\n if (histories.length) counts.players = histories.length;\n\n for (const b of bans) {\n mainDb\n .insert(mainSchema.banHistory)\n .values({\n banned: b.banned ?? '',\n bannerId: b.bannerId ?? '',\n created: typeof b.created === 'number' ? b.created : 0,\n expires: typeof b.expires === 'number' ? b.expires : 0,\n reason: b.reason ?? '',\n })\n .onConflictDoNothing()\n .run();\n }\n if (bans.length) counts.bans = bans.length;\n\n for (const k of kicks) {\n mainDb\n .insert(mainSchema.kickHistory)\n .values({\n kicked: k.kicked ?? '',\n kickerId: k.kickerId ?? '',\n created: k.created ?? 0,\n reason: k.reason ?? '',\n })\n .onConflictDoNothing()\n .run();\n }\n if (kicks.length) counts.kicks = kicks.length;\n\n eachBatch(notes, batch =>\n mainDb\n .insert(mainSchema.playerNotes)\n .values(\n batch.map((n: any) => ({\n playerId: n.id ?? '',\n note: n.note ?? '',\n })),\n )\n .run(),\n );\n if (notes.length) counts.notes = notes.length;\n\n eachBatch(hbDocs, batch =>\n mainDb\n .insert(mainSchema.heartbeats)\n .values(\n batch.map((h: any) => ({\n created: h.created ?? 0,\n bricks: h.bricks ?? 0,\n players: Array.isArray(h.players) ? h.players : [],\n })),\n )\n .run(),\n );\n if (hbDocs.length) counts.heartbeats = hbDocs.length;\n\n for (const p of pcDocs) {\n mainDb\n .insert(mainSchema.punchcards)\n .values({\n kind: p.kind ?? 'playerCount',\n created: p.created ?? 0,\n updated: p.updated ?? 0,\n month: p.month ?? 0,\n year: p.year ?? 0,\n punchcard: p.punchcard ?? [],\n })\n .onConflictDoNothing()\n .run();\n }\n if (pcDocs.length) counts.punchcards = pcDocs.length;\n\n eachBatch(instances, batch =>\n mainDb\n .insert(mainSchema.serverInstances)\n .values(batch.map((i: any) => ({ id: i._id, date: i.date ?? 0 })))\n .onConflictDoNothing()\n .run(),\n );\n if (instances.length) counts.serverInstances = instances.length;\n\n eachBatch(roles, batch =>\n mainDb\n .insert(mainSchema.webRoles)\n .values(\n batch.map((r: any) => ({\n id: r._id,\n name: r.name ?? '',\n description: r.description ?? '',\n order: r.order ?? 0,\n permissions: decodePermissions(r.permissions),\n })),\n )\n .onConflictDoNothing()\n .run(),\n );\n if (roles.length) counts.roles = roles.length;\n\n const defaultPerms = migrateDefaultPermissions(rawDefaultPerms);\n if (defaultPerms) {\n mainDb\n .insert(mainSchema.serverConfig)\n .values({\n key: 'defaultPermissions',\n value: {\n root: defaultPerms.root,\n domains: defaultPerms.domains,\n scopes: defaultPerms.scopes,\n },\n })\n .onConflictDoNothing()\n .run();\n }\n\n if (autoRestart) {\n const { _id: _, type: __, ...config } = autoRestart;\n mainDb\n .insert(mainSchema.serverConfig)\n .values({ key: 'autoRestartConfig', value: config })\n .onConflictDoNothing()\n .run();\n }\n\n // commit the completion flag atomically with the data above\n mainDb\n .insert(mainSchema.serverConfig)\n .values({ key: IMPORT_FLAG_KEY, value: new Date().toISOString() })\n .onConflictDoNothing()\n .run();\n });\n tx();\n }\n\n // marker file enables the cheap fast-path skip on subsequent boots\n fs.writeFileSync(markerPath, new Date().toISOString());\n\n const parts = Object.entries(counts)\n .map(([k, v]) => `${v} ${k}`)\n .join(', ');\n Logger.log('>>'.green, `Imported ${parts || 'no records'}`);\n Logger.log('>>'.green, 'NeDB import complete. Original files preserved.');\n } finally {\n if (ownConnection) mainSqlite.close();\n }\n}\n"],"names":["decodePermissions","openDb","soft","drizzle","runMigrations","Logger","runPluginMigrations","pluginSchema.pluginStore","pluginSchema.pluginConfig","mainSchema.serverConfig","eq","mainSchema.users","mainSchema.chatLogs","mainSchema.playerHistory","mainSchema.banHistory","mainSchema.kickHistory","mainSchema.playerNotes","mainSchema.heartbeats","mainSchema.punchcards","mainSchema.serverInstances","mainSchema.webRoles"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"nedbImport.js","sources":["../../src/db/nedbImport.ts"],"sourcesContent":["import Logger from '@/logger';\nimport soft from '@/softconfig';\nimport {\n decodePermissions,\n type StoredPermissionSet,\n} from '@webserver/backend/permissions';\nimport type BetterSqlite3 from 'better-sqlite3';\nimport { eq } from 'drizzle-orm';\nimport {\n drizzle,\n type BetterSQLite3Database,\n} from 'drizzle-orm/better-sqlite3';\nimport fs from 'fs';\nimport Datastore from 'nedb-promises';\nimport path from 'path';\nimport { openDb } from './connection';\nimport { runMigrations, runPluginMigrations } from './migrate';\nimport * as mainSchema from './schema';\nimport * as pluginSchema from './pluginSchema';\n\nconst MARKER_FILE = 'nedb-imported.marker';\nconst BATCH_SIZE = 500;\n\n// legacy NeDB database filenames; the one-time import is their only consumer\nexport const NEDB_FILES = {\n users: 'users.db',\n chat: 'chat.db',\n players: 'players.db',\n status: 'status.db',\n server: 'store.db',\n plugins: 'plugins.db',\n} as const;\n\n// Apply the NeDB data migrations that the old doMigrations() would have run.\n// Users v1: convert old NeDB permission format to PermissionSet.\n// Server v1: re-encode defaultPermissions from old format.\n// NeDB stored scope keys colon-encoded ('chat:send'); runtime lookups use\n// dotted keys, so every path below must run decodePermissions.\n\n// the v1 format used 'unset' sentinels and 'enabled'/'disabled' scope strings,\n// none of which are valid v2 values. detect by the presence of any sentinel so\n// an empty-scopes v1 doc (which a scopes-only check would miss) is still\n// normalized, while a valid v2 'none' domain is preserved.\nfunction isLegacyPermissions(p: any): boolean {\n if (p.root === 'unset') return true;\n for (const v of Object.values(p.domains ?? {}))\n if (v === 'unset') return true;\n for (const v of Object.values(p.scopes ?? {}))\n if (typeof v === 'string') return true;\n return false;\n}\n\n// normalize a v1 permission doc to a v2 StoredPermissionSet (still colon-keyed)\nfunction normalizeLegacyPermissions(p: any): StoredPermissionSet {\n const root = p.root === 'all' || p.root === 'read' ? p.root : 'off';\n const domains: Record<string, string> = {};\n for (const [k, v] of Object.entries(p.domains ?? {})) {\n if (v !== 'unset' && v !== 'none') domains[k] = v as string;\n }\n const scopes: Record<string, boolean> = {};\n for (const [k, v] of Object.entries(p.scopes ?? {})) {\n if (v === 'enabled' || v === true) scopes[k] = true;\n else if (v === 'disabled' || v === false) scopes[k] = false;\n }\n return { root, domains, scopes } as StoredPermissionSet;\n}\n\nfunction migrateUserPermissions(user: any): any {\n const p = user.permissions;\n if (!p || typeof p !== 'object') return user;\n const stored = isLegacyPermissions(p) ? normalizeLegacyPermissions(p) : p;\n return { ...user, permissions: decodePermissions(stored) };\n}\n\nfunction migrateDefaultPermissions(doc: any): any {\n if (!doc) return doc;\n const stored = isLegacyPermissions(doc)\n ? normalizeLegacyPermissions(doc)\n : doc;\n return { ...doc, ...decodePermissions(stored) };\n}\n\n// legacy nameHistory may predate the {name,displayName,date} object shape (older\n// docs stored bare strings) or be hand-edited; coerce to well-formed objects so\n// the json_each/REGEXP player search can't hit malformed JSON or null keys\nfunction sanitizeNameHistory(raw: any): {\n name: string;\n displayName: string;\n date: number;\n}[] {\n if (!Array.isArray(raw)) return [];\n return raw\n .filter(h => h && typeof h === 'object')\n .map(h => ({\n name: String(h.name ?? ''),\n displayName: String(h.displayName ?? ''),\n date: typeof h.date === 'number' ? h.date : 0,\n }));\n}\n\nasync function loadNedb<T = any>(filepath: string): Promise<T[]> {\n if (!fs.existsSync(filepath)) return [];\n const store = Datastore.create({ filename: filepath, autoload: true });\n return (await store.find({})) as T[];\n}\n\n// invoke fn on BATCH_SIZE-sized slices of docs (keeps multi-row inserts under\n// SQLite's bound-parameter limit). NOT wrapped in its own transaction — callers\n// run all batches inside one outer transaction for atomicity.\nfunction eachBatch(docs: any[], fn: (docs: any[]) => void) {\n for (let i = 0; i < docs.length; i += BATCH_SIZE) {\n fn(docs.slice(i, i + BATCH_SIZE));\n }\n}\n\n// key in server_config marking the main-DB import as committed. checked\n// in-band with the data so a crash between commit and marker-file write can't\n// cause a re-import (which would duplicate the append-only tables).\nconst IMPORT_FLAG_KEY = 'nedbImported';\n\n// coalesce concurrent import calls for the same data dir (the webserver and the\n// plugin loader both bootstrap it). entries are dropped once settled, so this is\n// purely a concurrency guard — repeat suppression is the marker file / DB flag.\nconst importsInFlight = new Map<string, Promise<void>>();\n\nexport function importNedbIfNeeded(\n dataPath: string,\n mainSqlite?: BetterSqlite3.Database,\n mainDb?: BetterSQLite3Database,\n): Promise<void> {\n const key = path.resolve(dataPath);\n let promise = importsInFlight.get(key);\n if (!promise) {\n promise = importNedb(dataPath, mainSqlite, mainDb).finally(() =>\n importsInFlight.delete(key),\n );\n importsInFlight.set(key, promise);\n }\n return promise;\n}\n\nasync function importNedb(\n dataPath: string,\n mainSqlite?: BetterSqlite3.Database,\n mainDb?: BetterSQLite3Database,\n) {\n const markerPath = path.join(dataPath, MARKER_FILE);\n if (fs.existsSync(markerPath)) return;\n\n const filePath = (f: string) => path.join(dataPath, f);\n const mainNedbFiles = [\n NEDB_FILES.users,\n NEDB_FILES.chat,\n NEDB_FILES.players,\n NEDB_FILES.status,\n NEDB_FILES.server,\n ];\n const hasMainNedb = mainNedbFiles.some(f => fs.existsSync(filePath(f)));\n const pluginPath = filePath(NEDB_FILES.plugins);\n const hasPluginNedb = fs.existsSync(pluginPath);\n if (!hasMainNedb && !hasPluginNedb) {\n fs.writeFileSync(markerPath, new Date().toISOString());\n return;\n }\n\n // open a dedicated main-db connection when the caller doesn't have one\n // (the plugin loader bootstraps the import without a webserver)\n let ownConnection = false;\n if (!mainSqlite || !mainDb) {\n ownConnection = true;\n mainSqlite = openDb(filePath(soft.MAIN_DB));\n mainDb = drizzle(mainSqlite);\n runMigrations(mainDb);\n }\n\n try {\n Logger.log('>>'.green, 'Importing NeDB data into SQLite...');\n const counts: Record<string, number> = {};\n\n // ---- Load phase: read every NeDB file up front (async). All writes happen\n // afterwards inside a single synchronous transaction so a partial failure\n // rolls back cleanly and a retry can't double-import append-only tables. ----\n const users = (await loadNedb(filePath(NEDB_FILES.users)))\n .filter((d: any) => d.type === 'user')\n .map(migrateUserPermissions);\n const chatMessages = (await loadNedb(filePath(NEDB_FILES.chat)))\n .filter((d: any) => d.type === 'chat')\n .sort((a: any, b: any) => (a.created ?? 0) - (b.created ?? 0));\n const playerDocs = await loadNedb(filePath(NEDB_FILES.players));\n const histories = playerDocs.filter((d: any) => d.type === 'userHistory');\n const bans = playerDocs.filter((d: any) => d.type === 'banHistory');\n const kicks = playerDocs.filter((d: any) => d.type === 'kickHistory');\n const notes = playerDocs.filter((d: any) => d.type === 'note');\n const statusDocs = await loadNedb(filePath(NEDB_FILES.status));\n const hbDocs = statusDocs\n .filter((d: any) => d.type === 'heartbeat')\n .sort((a: any, b: any) => (a.created ?? 0) - (b.created ?? 0));\n const pcDocs = statusDocs.filter((d: any) => d.type === 'punchcard');\n const serverDocs = await loadNedb(filePath(NEDB_FILES.server));\n const instances = serverDocs.filter((d: any) => d.type === 'app:start');\n const roles = serverDocs.filter((d: any) => d.type === 'webRole');\n const rawDefaultPerms = serverDocs.find(\n (d: any) => d.type === 'defaultPermissions',\n );\n const autoRestart = serverDocs.find(\n (d: any) => d.type === 'autoRestartConfig',\n );\n\n // warn about user rows that will be dropped by the new UNIQUE(username):\n // NeDB had no such constraint, so legacy data can hold duplicates\n const seenUsernames = new Set<string>();\n for (const u of users) {\n const name = u.username ?? '';\n if (seenUsernames.has(name))\n Logger.error(\n '!>'.red,\n `Duplicate username ${JSON.stringify(name)} in legacy data; user ${u._id} will not be imported`,\n );\n else seenUsernames.add(name);\n }\n\n // ---- Plugins: a separate connection/DB, imported first and idempotently\n // (onConflictDoNothing against real unique constraints), so re-running after\n // a main-DB failure is a safe no-op. ----\n if (hasPluginNedb) {\n const pluginSqlite = openDb(filePath(soft.PLUGINS_DB));\n try {\n const pluginDb = drizzle(pluginSqlite);\n runPluginMigrations(pluginDb);\n const pluginDocs = await loadNedb(pluginPath);\n // NeDB allowed store.set(key, null/undefined); the new value column is\n // NOT NULL, so skip those rows\n const storeDocs = pluginDocs.filter(\n (d: any) => d.type === 'store' && d.value != null,\n );\n const skipped =\n pluginDocs.filter((d: any) => d.type === 'store').length -\n storeDocs.length;\n if (skipped > 0)\n Logger.log(\n ':>'.yellow,\n `Skipping ${skipped} plugin store entr${skipped === 1 ? 'y' : 'ies'} with no value`,\n );\n const configDocs = pluginDocs.filter((d: any) => d.type === 'config');\n\n const pluginTx = pluginSqlite.transaction(() => {\n eachBatch(storeDocs, batch =>\n pluginDb\n .insert(pluginSchema.pluginStore)\n .values(\n batch.map((s: any) => ({\n plugin: s.plugin ?? '',\n key: s.key ?? '',\n value: s.value,\n })),\n )\n .onConflictDoNothing()\n .run(),\n );\n eachBatch(configDocs, batch =>\n pluginDb\n .insert(pluginSchema.pluginConfig)\n .values(\n batch.map((c: any) => ({\n plugin: c.plugin ?? '',\n value: c.value ?? {},\n })),\n )\n .onConflictDoNothing()\n .run(),\n );\n });\n pluginTx();\n if (storeDocs.length) counts.pluginStoreItems = storeDocs.length;\n if (configDocs.length) counts.pluginConfigs = configDocs.length;\n } finally {\n pluginSqlite.close();\n }\n }\n\n // ---- Main DB: one atomic transaction. The completion flag is written in\n // the same transaction as the data, so either both commit or neither does —\n // a crash before the marker file is written can't trigger a re-import. ----\n const alreadyImported = mainDb\n .select()\n .from(mainSchema.serverConfig)\n .where(eq(mainSchema.serverConfig.key, IMPORT_FLAG_KEY))\n .get();\n\n if (!alreadyImported && hasMainNedb) {\n const tx = mainSqlite.transaction(() => {\n eachBatch(users, batch =>\n mainDb\n .insert(mainSchema.users)\n .values(\n batch.map((u: any) => ({\n id: u._id,\n created: u.created ?? Date.now(),\n lastOnline: u.lastOnline ?? 0,\n username: u.username ?? '',\n hash: u.hash ?? '',\n isOwner: !!u.isOwner,\n roles: u.roles ?? [],\n playerId: u.playerId ?? '',\n isBanned: !!u.isBanned,\n permissions: u.permissions ?? null,\n totpSecret: u.totpSecret ?? null,\n totpEnabled: !!u.totpEnabled,\n passkeys: u.passkeys ?? [],\n recoveryCodes: u.recoveryCodes ?? [],\n })),\n )\n .onConflictDoNothing()\n .run(),\n );\n if (users.length) counts.users = users.length;\n\n eachBatch(chatMessages, batch =>\n mainDb\n .insert(mainSchema.chatLogs)\n .values(\n batch.map((c: any) => ({\n created: c.created ?? 0,\n instanceId: c.instanceId ?? '',\n action: c.action ?? 'msg',\n user: c.user ?? {},\n message: c.message ?? null,\n })),\n )\n .run(),\n );\n if (chatMessages.length) counts.chatMessages = chatMessages.length;\n\n eachBatch(histories, batch =>\n mainDb\n .insert(mainSchema.playerHistory)\n .values(\n batch.map((p: any) => ({\n id: p.id,\n name: p.name ?? '',\n displayName: p.displayName ?? '',\n nameHistory: sanitizeNameHistory(p.nameHistory),\n ips: Array.isArray(p.ips) ? p.ips : [],\n created: p.created ?? 0,\n lastSeen: p.lastSeen ?? 0,\n lastInstanceId: p.lastInstanceId ?? '',\n heartbeats: p.heartbeats ?? 0,\n sessions: p.sessions ?? 0,\n instances: p.instances ?? 0,\n })),\n )\n .onConflictDoNothing()\n .run(),\n );\n if (histories.length) counts.players = histories.length;\n\n for (const b of bans) {\n mainDb\n .insert(mainSchema.banHistory)\n .values({\n banned: b.banned ?? '',\n bannerId: b.bannerId ?? '',\n // Number.isFinite (not typeof) so a NaN timestamp becomes 0,\n // binding NaN to a NOT NULL integer column stores NULL and throws\n created: Number.isFinite(b.created) ? b.created : 0,\n expires: Number.isFinite(b.expires) ? b.expires : 0,\n reason: b.reason ?? '',\n })\n .onConflictDoNothing()\n .run();\n }\n if (bans.length) counts.bans = bans.length;\n\n for (const k of kicks) {\n mainDb\n .insert(mainSchema.kickHistory)\n .values({\n kicked: k.kicked ?? '',\n kickerId: k.kickerId ?? '',\n created: k.created ?? 0,\n reason: k.reason ?? '',\n })\n .onConflictDoNothing()\n .run();\n }\n if (kicks.length) counts.kicks = kicks.length;\n\n eachBatch(notes, batch =>\n mainDb\n .insert(mainSchema.playerNotes)\n .values(\n batch.map((n: any) => ({\n playerId: n.id ?? '',\n note: n.note ?? '',\n })),\n )\n .run(),\n );\n if (notes.length) counts.notes = notes.length;\n\n eachBatch(hbDocs, batch =>\n mainDb\n .insert(mainSchema.heartbeats)\n .values(\n batch.map((h: any) => ({\n created: h.created ?? 0,\n bricks: h.bricks ?? 0,\n players: Array.isArray(h.players) ? h.players : [],\n })),\n )\n .run(),\n );\n if (hbDocs.length) counts.heartbeats = hbDocs.length;\n\n for (const p of pcDocs) {\n mainDb\n .insert(mainSchema.punchcards)\n .values({\n kind: p.kind ?? 'playerCount',\n created: p.created ?? 0,\n updated: p.updated ?? 0,\n month: p.month ?? 0,\n year: p.year ?? 0,\n punchcard: p.punchcard ?? [],\n })\n .onConflictDoNothing()\n .run();\n }\n if (pcDocs.length) counts.punchcards = pcDocs.length;\n\n eachBatch(instances, batch =>\n mainDb\n .insert(mainSchema.serverInstances)\n .values(batch.map((i: any) => ({ id: i._id, date: i.date ?? 0 })))\n .onConflictDoNothing()\n .run(),\n );\n if (instances.length) counts.serverInstances = instances.length;\n\n eachBatch(roles, batch =>\n mainDb\n .insert(mainSchema.webRoles)\n .values(\n batch.map((r: any) => ({\n id: r._id,\n name: r.name ?? '',\n description: r.description ?? '',\n order: r.order ?? 0,\n permissions: decodePermissions(r.permissions),\n })),\n )\n .onConflictDoNothing()\n .run(),\n );\n if (roles.length) counts.roles = roles.length;\n\n const defaultPerms = migrateDefaultPermissions(rawDefaultPerms);\n if (defaultPerms) {\n mainDb\n .insert(mainSchema.serverConfig)\n .values({\n key: 'defaultPermissions',\n value: {\n root: defaultPerms.root,\n domains: defaultPerms.domains,\n scopes: defaultPerms.scopes,\n },\n })\n .onConflictDoNothing()\n .run();\n }\n\n if (autoRestart) {\n const { _id: _, type: __, ...config } = autoRestart;\n mainDb\n .insert(mainSchema.serverConfig)\n .values({ key: 'autoRestartConfig', value: config })\n .onConflictDoNothing()\n .run();\n }\n\n // commit the completion flag atomically with the data above\n mainDb\n .insert(mainSchema.serverConfig)\n .values({ key: IMPORT_FLAG_KEY, value: new Date().toISOString() })\n .onConflictDoNothing()\n .run();\n });\n tx();\n }\n\n // marker file enables the cheap fast-path skip on subsequent boots\n fs.writeFileSync(markerPath, new Date().toISOString());\n\n const parts = Object.entries(counts)\n .map(([k, v]) => `${v} ${k}`)\n .join(', ');\n Logger.log('>>'.green, `Imported ${parts || 'no records'}`);\n Logger.log('>>'.green, 'NeDB import complete. Original files preserved.');\n } finally {\n if (ownConnection) mainSqlite.close();\n }\n}\n"],"names":["decodePermissions","openDb","soft","drizzle","runMigrations","Logger","runPluginMigrations","pluginSchema.pluginStore","pluginSchema.pluginConfig","mainSchema.serverConfig","eq","mainSchema.users","mainSchema.chatLogs","mainSchema.playerHistory","mainSchema.banHistory","mainSchema.kickHistory","mainSchema.playerNotes","mainSchema.heartbeats","mainSchema.punchcards","mainSchema.serverInstances","mainSchema.webRoles"],"mappings":"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"}
|
|
@@ -11,6 +11,7 @@ const init = require("./init.js");
|
|
|
11
11
|
const mapChange = require("./mapChange.js");
|
|
12
12
|
const interact = require("./interact.js");
|
|
13
13
|
const minigameJoin = require("./minigameJoin.js");
|
|
14
|
+
const wire = require("./wire.js");
|
|
14
15
|
const MATCHERS = [
|
|
15
16
|
join.default,
|
|
16
17
|
leave.default,
|
|
@@ -22,7 +23,8 @@ const MATCHERS = [
|
|
|
22
23
|
init.default,
|
|
23
24
|
mapChange.default,
|
|
24
25
|
interact.default,
|
|
25
|
-
minigameJoin.default
|
|
26
|
+
minigameJoin.default,
|
|
27
|
+
wire.default
|
|
26
28
|
];
|
|
27
29
|
exports.default = MATCHERS;
|
|
28
30
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../src/omegga/matchers/index.ts"],"sourcesContent":["import { MatchGenerator } from './types';\n\nimport join from './join';\n// 'join' event => { name, id, state, controller }\n\nimport leave from './leave';\n// 'leave' event => { name, id, state, controller }\n\nimport chat from './chat';\n// 'chat' event => name, message; 'chatcmd:command' event => name, [...args]\n// 'kick' event => name, kicker, reason\n\nimport command from './command';\n// 'cmd:command' event => name, args (string)\n\nimport auth from './auth';\n// assigns host, 'host' event, 'start' event, 'unauthorized' event\n\nimport exit from './exit';\n// 'exit' event\n\nimport version from './version';\n// 'version' event, check game version\n\nimport init from './init';\n// watch loginit for any funny business\n\nimport mapChange from './mapChange';\n// 'mapchange' event\n\n// interaction event\nimport interact from './interact';\n\n// minigame join event\nimport minigameJoin from './minigameJoin';\n\nexport default [\n join,\n leave,\n chat,\n command,\n auth,\n exit,\n version,\n init,\n mapChange,\n interact,\n minigameJoin,\n] as MatchGenerator<any>[];\n"],"names":["join","leave","chat","command","auth","exit","version","init","mapChange","interact","minigameJoin"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../src/omegga/matchers/index.ts"],"sourcesContent":["import { MatchGenerator } from './types';\n\nimport join from './join';\n// 'join' event => { name, id, state, controller }\n\nimport leave from './leave';\n// 'leave' event => { name, id, state, controller }\n\nimport chat from './chat';\n// 'chat' event => name, message; 'chatcmd:command' event => name, [...args]\n// 'kick' event => name, kicker, reason\n\nimport command from './command';\n// 'cmd:command' event => name, args (string)\n\nimport auth from './auth';\n// assigns host, 'host' event, 'start' event, 'unauthorized' event\n\nimport exit from './exit';\n// 'exit' event\n\nimport version from './version';\n// 'version' event, check game version\n\nimport init from './init';\n// watch loginit for any funny business\n\nimport mapChange from './mapChange';\n// 'mapchange' event\n\n// interaction event\nimport interact from './interact';\n\n// minigame join event\nimport minigameJoin from './minigameJoin';\n\nimport wire from './wire';\n// 'wirelog' event => raw; 'wirecmd:command' event => [...args]\n\nexport default [\n join,\n leave,\n chat,\n command,\n auth,\n exit,\n version,\n init,\n mapChange,\n interact,\n minigameJoin,\n wire,\n] as MatchGenerator<any>[];\n"],"names":["join","leave","chat","command","auth","exit","version","init","mapChange","interact","minigameJoin","wire"],"mappings":";;;;;;;;;;;;;;AAuCA,MAAA,WAAe;AAAA,EACbA,KAAAA;AAAAA,EACAC,MAAAA;AAAAA,EACAC,KAAAA;AAAAA,EACAC,QAAAA;AAAAA,EACAC,KAAAA;AAAAA,EACAC,KAAAA;AAAAA,EACAC,QAAAA;AAAAA,EACAC,KAAAA;AAAAA,EACAC,UAAAA;AAAAA,EACAC,SAAAA;AAAAA,EACAC,aAAAA;AAAAA,EACAC,KAAAA;AACF;;"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
|
|
3
|
+
const wire = (omegga) => {
|
|
4
|
+
const wireRegExp = /^\[Wire Graph\] (?<raw>.*)$/;
|
|
5
|
+
return {
|
|
6
|
+
// listen for wire graph messages
|
|
7
|
+
pattern(_line, logMatch) {
|
|
8
|
+
if (!logMatch) return;
|
|
9
|
+
const { generator, data } = logMatch.groups;
|
|
10
|
+
if (generator !== "LogBrickadia") return;
|
|
11
|
+
const match = data.match(wireRegExp);
|
|
12
|
+
if (!match) return;
|
|
13
|
+
return { raw: match.groups.raw };
|
|
14
|
+
},
|
|
15
|
+
// when there's a match, emit the wire log event
|
|
16
|
+
callback({ raw }) {
|
|
17
|
+
omegga.emit("wirelog", raw);
|
|
18
|
+
const [cmd, ...args] = raw.split(" ");
|
|
19
|
+
if (cmd.length > 0) {
|
|
20
|
+
omegga.emit("wirecmd:" + cmd.toLowerCase(), ...args);
|
|
21
|
+
omegga.emit("wirecmd", cmd.toLowerCase(), ...args);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
exports.default = wire;
|
|
27
|
+
//# sourceMappingURL=wire.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wire.js","sources":["../../../src/omegga/matchers/wire.ts"],"sourcesContent":["import { MatchGenerator } from './types';\n\nconst wire: MatchGenerator<{ raw: string }> = omegga => {\n // pattern to match wire graph log messages\n // e.g. `LogBrickadia: [Wire Graph] test`\n const wireRegExp = /^\\[Wire Graph\\] (?<raw>.*)$/;\n\n return {\n // listen for wire graph messages\n pattern(_line, logMatch) {\n // line is not generic console log\n if (!logMatch) return;\n\n const { generator, data } = logMatch.groups;\n // check if log is a brickadia log\n if (generator !== 'LogBrickadia') return;\n\n // match the wire graph log pattern\n const match = data.match(wireRegExp);\n if (!match) return;\n\n return { raw: match.groups.raw };\n },\n // when there's a match, emit the wire log event\n callback({ raw }) {\n omegga.emit('wirelog', raw);\n\n // wire command parsing, emit `wirecmd:test` when `test ...` is logged\n const [cmd, ...args] = raw.split(' ');\n if (cmd.length > 0) {\n omegga.emit('wirecmd:' + cmd.toLowerCase(), ...args);\n omegga.emit('wirecmd', cmd.toLowerCase(), ...args);\n }\n },\n };\n};\n\nexport default wire;\n"],"names":[],"mappings":";;AAEA,MAAM,OAAwC,CAAA,WAAU;AAGtD,QAAM,aAAa;AAEnB,SAAO;AAAA;AAAA,IAEL,QAAQ,OAAO,UAAU;AAEvB,UAAI,CAAC,SAAU;AAEf,YAAM,EAAE,WAAW,KAAA,IAAS,SAAS;AAErC,UAAI,cAAc,eAAgB;AAGlC,YAAM,QAAQ,KAAK,MAAM,UAAU;AACnC,UAAI,CAAC,MAAO;AAEZ,aAAO,EAAE,KAAK,MAAM,OAAO,IAAA;AAAA,IAC7B;AAAA;AAAA,IAEA,SAAS,EAAE,OAAO;AAChB,aAAO,KAAK,WAAW,GAAG;AAG1B,YAAM,CAAC,KAAK,GAAG,IAAI,IAAI,IAAI,MAAM,GAAG;AACpC,UAAI,IAAI,SAAS,GAAG;AAClB,eAAO,KAAK,aAAa,IAAI,YAAA,GAAe,GAAG,IAAI;AACnD,eAAO,KAAK,WAAW,IAAI,YAAA,GAAe,GAAG,IAAI;AAAA,MACnD;AAAA,IACF;AAAA,EAAA;AAEJ;;"}
|
package/dist/omegga/server.js
CHANGED
|
@@ -4,6 +4,7 @@ const logger = require("../logger.js");
|
|
|
4
4
|
const softconfig = require("../softconfig.js");
|
|
5
5
|
const version = require("../version.js");
|
|
6
6
|
const index$2 = require("../util/index.js");
|
|
7
|
+
const brdb = require("../util/brdb.js");
|
|
7
8
|
const file = require("../util/file.js");
|
|
8
9
|
const index = require("../webserver/backend/index.js");
|
|
9
10
|
const brs = require("brs-js");
|
|
@@ -18,6 +19,25 @@ const plugin = require("./plugin.js");
|
|
|
18
19
|
const wrapper = require("./wrapper.js");
|
|
19
20
|
const MISSING_CMD = '"Command not found. Type <color=\\"ffff00\\">/help</> for a list of commands or <color=\\"ffff00\\">/plugins</> for plugin information."';
|
|
20
21
|
const WHOLE_WORLD_EXTENT = 1e9;
|
|
22
|
+
function brsRemoved(version2) {
|
|
23
|
+
return version2 > 0 && version2 >= commands.PREFAB_VERSION;
|
|
24
|
+
}
|
|
25
|
+
function warnRemoved(version2, method, since, release, replacement) {
|
|
26
|
+
if (!(version2 > 0 && version2 >= since)) return false;
|
|
27
|
+
logger.default.warnp(
|
|
28
|
+
`omegga.${method}() uses a console command removed in Brickadia ${release}. This call has no effect - use ${replacement} instead.`
|
|
29
|
+
);
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
function writeTempPrefab(omegga, saveData) {
|
|
33
|
+
const ref = omegga._tempSavePrefix + Date.now() + "_" + omegga._tempCounter.save++;
|
|
34
|
+
const file$1 = path.join(omegga.prefabPath, ref + ".brz");
|
|
35
|
+
if (!file$1.startsWith(omegga.prefabPath))
|
|
36
|
+
throw "prefab file not in Saved/Prefabs directory";
|
|
37
|
+
file.mkdir(omegga.prefabPath);
|
|
38
|
+
fs.writeFileSync(file$1, new Uint8Array(brs.writeBrzLegacy(saveData)));
|
|
39
|
+
return { ref, file: file$1 };
|
|
40
|
+
}
|
|
21
41
|
class Omegga extends wrapper.default {
|
|
22
42
|
/**
|
|
23
43
|
* Omegga instance
|
|
@@ -446,30 +466,6 @@ class Omegga extends wrapper.default {
|
|
|
446
466
|
const presetPath = path.join(this.presetPath, "Environment");
|
|
447
467
|
return fs.existsSync(presetPath) ? glob.sync(presetPath + "/**/*.bp").map((f) => path.basename(f).replace(/\.bp$/, "")) : [];
|
|
448
468
|
}
|
|
449
|
-
/**
|
|
450
|
-
* Whether the running game version has removed the legacy .brs bricks console
|
|
451
|
-
* commands (Bricks.Save/Load/ClearAll/ClearRegion, World.LoadAdditive) in
|
|
452
|
-
* favor of the prefab (`br.Prefab.*`) and world (`br.World.Clear*`) commands.
|
|
453
|
-
* Removed at Brickadia CL{@link PREFAB_VERSION}.
|
|
454
|
-
*/
|
|
455
|
-
#brsRemoved() {
|
|
456
|
-
return this.version > 0 && this.version >= commands.PREFAB_VERSION;
|
|
457
|
-
}
|
|
458
|
-
/**
|
|
459
|
-
* Warn that a method backed by a removed console command is a no-op on the
|
|
460
|
-
* running game version, and return whether the caller should bail.
|
|
461
|
-
* @param method the omegga method being called (for the message)
|
|
462
|
-
* @param since CL version the underlying command was removed at
|
|
463
|
-
* @param release human release name for that version (e.g. `EA2`, `EA3`)
|
|
464
|
-
* @param replacement suggested replacement method/API
|
|
465
|
-
*/
|
|
466
|
-
#warnRemoved(method, since, release, replacement) {
|
|
467
|
-
if (!(this.version > 0 && this.version >= since)) return false;
|
|
468
|
-
logger.default.warnp(
|
|
469
|
-
`omegga.${method}() uses a console command removed in Brickadia ${release}. This call has no effect - use ${replacement} instead.`
|
|
470
|
-
);
|
|
471
|
-
return true;
|
|
472
|
-
}
|
|
473
469
|
clearBricks(target, quiet = false) {
|
|
474
470
|
if (typeof target === "object" && target.id) target = target.id;
|
|
475
471
|
else if (typeof target === "string" && !index$2.uuid.match(target)) {
|
|
@@ -489,7 +485,7 @@ class Omegga extends wrapper.default {
|
|
|
489
485
|
}
|
|
490
486
|
const center = region.center.join(" ");
|
|
491
487
|
const extent = region.extent.join(" ");
|
|
492
|
-
if (
|
|
488
|
+
if (brsRemoved(this.version)) {
|
|
493
489
|
const bricks = options?.bricks ?? true ? 1 : 0;
|
|
494
490
|
const entities = options?.entities ?? false ? 1 : 0;
|
|
495
491
|
this.writeln(
|
|
@@ -507,7 +503,7 @@ class Omegga extends wrapper.default {
|
|
|
507
503
|
bricks = true,
|
|
508
504
|
entities = false
|
|
509
505
|
} = typeof options === "boolean" ? { quiet: options } : options;
|
|
510
|
-
if (
|
|
506
|
+
if (brsRemoved(this.version)) {
|
|
511
507
|
this.writeln(
|
|
512
508
|
`${this.Console.World.ClearAll} ${bricks ? 1 : 0} ${entities ? 1 : 0} ${quiet ? 1 : 0}`
|
|
513
509
|
);
|
|
@@ -516,7 +512,13 @@ class Omegga extends wrapper.default {
|
|
|
516
512
|
this.writeln(`${this.Console.Bricks.ClearAll} ${quiet ? 1 : ""}`);
|
|
517
513
|
}
|
|
518
514
|
saveBricks(saveName, region) {
|
|
519
|
-
if (
|
|
515
|
+
if (warnRemoved(
|
|
516
|
+
this.version,
|
|
517
|
+
"saveBricks",
|
|
518
|
+
commands.PREFAB_VERSION,
|
|
519
|
+
"EA3",
|
|
520
|
+
"savePrefab"
|
|
521
|
+
))
|
|
520
522
|
return;
|
|
521
523
|
if (!saveName) return;
|
|
522
524
|
if (!(saveName.startsWith('"') && saveName.endsWith('"')))
|
|
@@ -530,7 +532,8 @@ class Omegga extends wrapper.default {
|
|
|
530
532
|
else this.writeln(`${this.Console.Bricks.Save} ${saveName}`);
|
|
531
533
|
}
|
|
532
534
|
async saveBricksAsync(saveName, region) {
|
|
533
|
-
if (
|
|
535
|
+
if (warnRemoved(
|
|
536
|
+
this.version,
|
|
534
537
|
"saveBricksAsync",
|
|
535
538
|
commands.PREFAB_VERSION,
|
|
536
539
|
"EA3",
|
|
@@ -563,7 +566,13 @@ class Omegga extends wrapper.default {
|
|
|
563
566
|
correctPalette = false,
|
|
564
567
|
correctCustom = false
|
|
565
568
|
} = {}) {
|
|
566
|
-
if (
|
|
569
|
+
if (warnRemoved(
|
|
570
|
+
this.version,
|
|
571
|
+
"loadBricks",
|
|
572
|
+
commands.PREFAB_VERSION,
|
|
573
|
+
"EA3",
|
|
574
|
+
"loadPrefab"
|
|
575
|
+
))
|
|
567
576
|
return;
|
|
568
577
|
if (!(saveName.startsWith('"') && saveName.endsWith('"')))
|
|
569
578
|
saveName = `"${saveName}"`;
|
|
@@ -578,7 +587,8 @@ class Omegga extends wrapper.default {
|
|
|
578
587
|
correctPalette = false,
|
|
579
588
|
correctCustom = false
|
|
580
589
|
} = {}) {
|
|
581
|
-
if (
|
|
590
|
+
if (warnRemoved(
|
|
591
|
+
this.version,
|
|
582
592
|
"loadBricksOnPlayer",
|
|
583
593
|
commands.EA2_VERSION,
|
|
584
594
|
"EA2",
|
|
@@ -617,42 +627,12 @@ class Omegga extends wrapper.default {
|
|
|
617
627
|
return fs.existsSync(file2) ? file2 : void 0;
|
|
618
628
|
}
|
|
619
629
|
async getWorldRevisions(worldName) {
|
|
620
|
-
if (!this.started || this.starting || this.stopping) {
|
|
621
|
-
throw new Error("Server is not started");
|
|
622
|
-
}
|
|
623
630
|
worldName = worldName.replace(/\.brdb$/i, "");
|
|
624
|
-
|
|
631
|
+
const path2 = this.getWorldPath(worldName);
|
|
632
|
+
if (!worldName || !path2) {
|
|
625
633
|
throw new Error(`World "${worldName}" does not exist`);
|
|
626
634
|
}
|
|
627
|
-
|
|
628
|
-
const revisionsRaw = await this.watchLogChunk(
|
|
629
|
-
`${this.Console.World.ListRevisions} "${worldName}"`,
|
|
630
|
-
/^LogBRBundleManager: (There are (?<numRevisions>\d+) revisions|Revision (?<revision>\d+) - (?<date>[\d.-]+): (?<note>.+))$/,
|
|
631
|
-
{
|
|
632
|
-
last: (match) => Number(match.groups.reverse) === numRevisions,
|
|
633
|
-
first: (match) => {
|
|
634
|
-
if (match.groups?.numRevisions !== void 0) {
|
|
635
|
-
numRevisions = Number(match.groups.numRevisions);
|
|
636
|
-
return true;
|
|
637
|
-
}
|
|
638
|
-
return false;
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
);
|
|
642
|
-
if (!revisionsRaw) return [];
|
|
643
|
-
return revisionsRaw.map((match) => {
|
|
644
|
-
if (match.groups?.numRevisions !== void 0) {
|
|
645
|
-
return null;
|
|
646
|
-
}
|
|
647
|
-
const revision = match.groups.revision ? Number(match.groups.revision) : 0;
|
|
648
|
-
const dateStr = match.groups.date.replace(
|
|
649
|
-
/(\d{4})\.(\d{2})\.(\d{2})-(\d{2})\.(\d{2})\.(\d{2})/,
|
|
650
|
-
"$1-$2-$3T$4:$5:$6Z"
|
|
651
|
-
);
|
|
652
|
-
const date = new Date(dateStr);
|
|
653
|
-
const note = match.groups.note || "";
|
|
654
|
-
return { index: revision, date, note };
|
|
655
|
-
}).filter(Boolean);
|
|
635
|
+
return brdb.readBrdbRevisions(path2) ?? [];
|
|
656
636
|
}
|
|
657
637
|
async loadWorld(worldName) {
|
|
658
638
|
worldName = worldName.replace(/\.brdb$/i, "");
|
|
@@ -799,8 +779,14 @@ class Omegga extends wrapper.default {
|
|
|
799
779
|
correctPalette = false,
|
|
800
780
|
correctCustom = false
|
|
801
781
|
} = {}) {
|
|
802
|
-
if (this
|
|
782
|
+
if (brsRemoved(this.version)) {
|
|
783
|
+
const { ref, file: file2 } = writeTempPrefab(this, saveData);
|
|
784
|
+
this.loadPrefab(ref, { offX, offY, offZ });
|
|
785
|
+
setTimeout(() => {
|
|
786
|
+
if (fs.existsSync(file2)) fs.unlinkSync(file2);
|
|
787
|
+
}, 5e3);
|
|
803
788
|
return;
|
|
789
|
+
}
|
|
804
790
|
const saveFile = this._tempSavePrefix + Date.now() + "_" + this._tempCounter.save++;
|
|
805
791
|
this.writeSaveData(saveFile, saveData);
|
|
806
792
|
await this.watchLogChunk(
|
|
@@ -825,15 +811,24 @@ class Omegga extends wrapper.default {
|
|
|
825
811
|
correctPalette = false,
|
|
826
812
|
correctCustom = false
|
|
827
813
|
} = {}) {
|
|
828
|
-
|
|
814
|
+
player = typeof player === "string" ? this.getPlayer(player) : player;
|
|
815
|
+
if (!player) return;
|
|
816
|
+
if (brsRemoved(this.version)) {
|
|
817
|
+
const { ref, file: file2 } = writeTempPrefab(this, saveData);
|
|
818
|
+
this.givePrefabToPlayer(ref, player);
|
|
819
|
+
setTimeout(() => {
|
|
820
|
+
if (fs.existsSync(file2)) fs.unlinkSync(file2);
|
|
821
|
+
}, 5e3);
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
824
|
+
if (warnRemoved(
|
|
825
|
+
this.version,
|
|
829
826
|
"loadSaveDataOnPlayer",
|
|
830
827
|
commands.EA2_VERSION,
|
|
831
828
|
"EA2",
|
|
832
829
|
"loadPrefabOnPlayer"
|
|
833
830
|
))
|
|
834
831
|
return;
|
|
835
|
-
player = typeof player === "string" ? this.getPlayer(player) : player;
|
|
836
|
-
if (!player) return;
|
|
837
832
|
const saveFile = this._tempSavePrefix + Date.now() + "_" + this._tempCounter.save++;
|
|
838
833
|
this.writeSaveData(saveFile, saveData);
|
|
839
834
|
await this.watchLogChunk(
|
|
@@ -852,8 +847,54 @@ class Omegga extends wrapper.default {
|
|
|
852
847
|
}
|
|
853
848
|
}
|
|
854
849
|
async getSaveData(region) {
|
|
855
|
-
if (this
|
|
856
|
-
|
|
850
|
+
if (brsRemoved(this.version)) {
|
|
851
|
+
const ref = this._tempSavePrefix + Date.now() + "_" + this._tempCounter.save++;
|
|
852
|
+
const file2 = await this.savePrefabAsync(ref, { region });
|
|
853
|
+
if (!file2) return void 0;
|
|
854
|
+
try {
|
|
855
|
+
const MAIN_GRID = 1;
|
|
856
|
+
const reader = brs.WorldReader.from(new Uint8Array(fs.readFileSync(file2)));
|
|
857
|
+
const bricks = [...reader.bricks(MAIN_GRID)].map((b) => ({
|
|
858
|
+
...b,
|
|
859
|
+
physical_index: 0,
|
|
860
|
+
components: {}
|
|
861
|
+
}));
|
|
862
|
+
let brickOffset = 0;
|
|
863
|
+
for (const chunk of reader.brickChunkIndex(MAIN_GRID)) {
|
|
864
|
+
if (chunk.numComponents > 0) {
|
|
865
|
+
const { components } = reader.componentChunk(
|
|
866
|
+
MAIN_GRID,
|
|
867
|
+
chunk.index
|
|
868
|
+
);
|
|
869
|
+
for (const c of components) {
|
|
870
|
+
const brick = bricks[brickOffset + c.brickIndex];
|
|
871
|
+
if (brick) brick.components[c.typeName] = c.data ?? {};
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
brickOffset += chunk.numBricks;
|
|
875
|
+
}
|
|
876
|
+
return {
|
|
877
|
+
version: 10,
|
|
878
|
+
map: this.currentMap ?? "Unknown",
|
|
879
|
+
author: { id: this.host?.id ?? "", name: this.host?.name ?? "" },
|
|
880
|
+
host: { id: this.host?.id ?? "", name: this.host?.name ?? "" },
|
|
881
|
+
description: "",
|
|
882
|
+
brick_count: bricks.length,
|
|
883
|
+
mods: [],
|
|
884
|
+
brick_assets: reader.brickAssets(),
|
|
885
|
+
colors: [],
|
|
886
|
+
materials: reader.materials(),
|
|
887
|
+
physical_materials: [],
|
|
888
|
+
brick_owners: reader.brickOwners(),
|
|
889
|
+
game_version: this.version,
|
|
890
|
+
save_time: new Uint8Array(),
|
|
891
|
+
bricks,
|
|
892
|
+
components: {}
|
|
893
|
+
};
|
|
894
|
+
} finally {
|
|
895
|
+
if (fs.existsSync(file2)) fs.unlinkSync(file2);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
857
898
|
const saveFile = this._tempSavePrefix + Date.now() + "_" + this._tempCounter.save++;
|
|
858
899
|
await this.saveBricksAsync(saveFile, region);
|
|
859
900
|
const savePath = this.getSavePath(saveFile);
|
|
@@ -881,8 +922,9 @@ class Omegga extends wrapper.default {
|
|
|
881
922
|
overrideUserId = ""
|
|
882
923
|
} = {}) {
|
|
883
924
|
if (!path2) return;
|
|
925
|
+
const rootPart = rootEntityPersistentIndex >= 0 ? ` ${rootEntityPersistentIndex} ${mirrorAxes}${overrideUserId ? ` "${overrideUserId}"` : ""}` : "";
|
|
884
926
|
this.writeln(
|
|
885
|
-
`${this.Console.Prefab.Load} "${path2}" ${offX} ${offY} ${offZ} ${atOriginalPosition ? 1 : 0} ${orientation}
|
|
927
|
+
`${this.Console.Prefab.Load} "${path2}" ${offX} ${offY} ${offZ} ${atOriginalPosition ? 1 : 0} ${orientation}${rootPart}`
|
|
886
928
|
);
|
|
887
929
|
}
|
|
888
930
|
/**
|