omegga 1.4.0 → 1.4.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/.claude/settings.local.json +17 -0
- package/README.md +9 -0
- package/bin/omegga +1 -1
- package/dist/index.js +6 -6
- package/dist/omegga/plugin/plugin_node_unsafe.js +6 -0
- package/dist/omegga/plugin/plugin_node_unsafe.js.map +1 -1
- package/dist/omegga/server.js +35 -1
- package/dist/omegga/server.js.map +1 -1
- package/dist/omegga/wrapper.js +1 -0
- package/dist/omegga/wrapper.js.map +1 -1
- package/dist/plugin.js +1 -2
- package/dist/plugin.js.map +1 -1
- package/dist/updater/steam.js +49 -1
- package/dist/updater/steam.js.map +1 -1
- package/dist/util/brick/checkBounds.js +1 -2
- package/dist/util/brick/checkBounds.js.map +1 -1
- package/dist/util/brick/displayName.js +1 -2
- package/dist/util/brick/displayName.js.map +1 -1
- package/dist/util/brick/getBounds.js +1 -2
- package/dist/util/brick/getBounds.js.map +1 -1
- package/dist/util/brick/getBrickSize.js +1 -2
- package/dist/util/brick/getBrickSize.js.map +1 -1
- package/dist/util/brick/getScaleAxis.js +1 -2
- package/dist/util/brick/getScaleAxis.js.map +1 -1
- package/dist/util/brick/setOwnership.js +1 -2
- package/dist/util/brick/setOwnership.js.map +1 -1
- package/dist/webserver/backend/api.js +2 -1
- package/dist/webserver/backend/api.js.map +1 -1
- package/dist/webserver/backend/database.js +3 -1
- package/dist/webserver/backend/database.js.map +1 -1
- package/package.json +1 -1
- package/public/app.html +3 -3
- package/public/assets/{app-nJIpyOiP.js → app-S3WcjD4u.js} +4 -4
- package/public/assets/{app-nJIpyOiP.js.map → app-S3WcjD4u.js.map} +1 -1
- package/public/assets/client-BT1QYH0O.js.map +1 -1
- package/public/assets/{index-3dNmNZ9B.js → index-Cz1Unv84.js} +5 -5
- package/public/assets/{index-3dNmNZ9B.js.map → index-Cz1Unv84.js.map} +1 -1
- package/public/assets/shared/{@components-DdtLZOaA.js → @components-Bt_lv7Wa.js} +2 -2
- package/public/assets/shared/@components-Bt_lv7Wa.js.map +1 -0
- package/public/auth.html +1 -1
- package/public/assets/shared/@components-DdtLZOaA.js.map +0 -1
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"WebFetch(domain:raw.githubusercontent.com)",
|
|
5
|
+
"WebFetch(domain:steam.readthedocs.io)",
|
|
6
|
+
"WebSearch",
|
|
7
|
+
"WebFetch(domain:docs.linuxgsm.com)",
|
|
8
|
+
"WebFetch(domain:github.com)",
|
|
9
|
+
"mcp__brickadia-luau__execute_lua",
|
|
10
|
+
"mcp__brickadia-luau__search_types",
|
|
11
|
+
"mcp__brickadia-luau__get_module_types",
|
|
12
|
+
"Bash(npx tsc:*)",
|
|
13
|
+
"mcp__brickadia-luau__reload_plugin",
|
|
14
|
+
"Bash(npx prettier:*)"
|
|
15
|
+
]
|
|
16
|
+
}
|
|
17
|
+
}
|
package/README.md
CHANGED
|
@@ -731,6 +731,8 @@ Cleanup is important as code can still be running after the plugin is unloaded r
|
|
|
731
731
|
|
|
732
732
|
Register custom `/commands` by returning `{registeredCommands: ['foo', 'bar']}` (registers command `/foo` and `/bar`) in the `async init()` method.
|
|
733
733
|
|
|
734
|
+
By defining an `async pluginEvent(event, from, ...args)` method in your plugin class, you can respond to events from other plugins, where `from` is the name of the other plugin, `event` is the name of the custom event, and `args` is an array of any passed arguments.
|
|
735
|
+
|
|
734
736
|
### Globals
|
|
735
737
|
|
|
736
738
|
- `OMEGGA_UTIL` - access to the `src/util/index.js` module
|
|
@@ -770,6 +772,13 @@ class PluginName {
|
|
|
770
772
|
.removeAllListeners('chatcmd:ping')
|
|
771
773
|
.removeAllListeners('chatcmd:pos');
|
|
772
774
|
}
|
|
775
|
+
|
|
776
|
+
// optional: respond to events from other plugins
|
|
777
|
+
async pluginEvent(event, from, ...args) {
|
|
778
|
+
if (event === 'greeting') {
|
|
779
|
+
return `Hello from ${from}!`;
|
|
780
|
+
}
|
|
781
|
+
}
|
|
773
782
|
}
|
|
774
783
|
|
|
775
784
|
module.exports = PluginName;
|
package/bin/omegga
CHANGED
package/dist/index.js
CHANGED
|
@@ -4,16 +4,16 @@ const plugin = require("./plugin.js");
|
|
|
4
4
|
const server$1 = require("./brickadia/server.js");
|
|
5
5
|
const config = require("./brickadia/config.js");
|
|
6
6
|
const server = require("./omegga/server.js");
|
|
7
|
-
const index = require("./omegga/index.js");
|
|
8
|
-
const index$
|
|
9
|
-
const index
|
|
7
|
+
const index$1 = require("./omegga/index.js");
|
|
8
|
+
const index$2 = require("./util/index.js");
|
|
9
|
+
const index = require("./config/index.js");
|
|
10
10
|
const brickadia = { Server: server$1, config };
|
|
11
11
|
exports.OmeggaPlugin = plugin.OmeggaPlugin;
|
|
12
12
|
exports._OMEGGA_UTILS_IMPORT = plugin._OMEGGA_UTILS_IMPORT;
|
|
13
13
|
exports.Omegga = server.default;
|
|
14
14
|
exports.default = server.default;
|
|
15
|
-
exports.omegga = index;
|
|
16
|
-
exports.util = index$
|
|
17
|
-
exports.config = index
|
|
15
|
+
exports.omegga = index$1;
|
|
16
|
+
exports.util = index$2;
|
|
17
|
+
exports.config = index;
|
|
18
18
|
exports.brickadia = brickadia;
|
|
19
19
|
//# sourceMappingURL=index.js.map
|
|
@@ -108,6 +108,12 @@ class NodePlugin extends _interface.Plugin {
|
|
|
108
108
|
return false;
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
|
+
// forward plugin events to the loaded plugin instance
|
|
112
|
+
async emitPlugin(ev, from, args) {
|
|
113
|
+
if (typeof this.loadedPlugin?.pluginEvent === "function") {
|
|
114
|
+
return await this.loadedPlugin.pluginEvent(ev, from, ...args);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
111
117
|
// disrequire all that match plugin path in require.cache
|
|
112
118
|
disrequireAll() {
|
|
113
119
|
const requiredFiles = Object.keys(require.cache).filter(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin_node_unsafe.js","sources":["../../../src/omegga/plugin/plugin_node_unsafe.ts"],"sourcesContent":["import Logger from '@/logger';\nimport OmeggaPlugin, { PluginStore } from '@/plugin';\nimport type Omegga from '@omegga/server';\nimport * as util from '@util';\nimport disrequire from 'disrequire';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { Plugin } from './interface';\n\nglobal.OMEGGA_UTIL = util;\n\n// Main plugin file (like index.js)\n// this isn't named 'index.js' or 'plugin.js' because those may be filenames\n// used with other loaders (rpc loader) and are too generic\n// omegga.main.js is rather unique and helps avoid collision\nconst MAIN_FILE = 'omegga.main.js';\n\n// Documentation file (contains name, description, author, command helptext)\nconst DOC_FILE = 'doc.json';\nconst PLUGIN_FILE = 'plugin.json';\n\nexport default class NodePlugin extends Plugin {\n // every node plugin requires the main file and a doc file\n // may evolve this so it checks the contents of the doc file later\n static canLoad(pluginPath: string) {\n return (\n fs.existsSync(path.join(pluginPath, MAIN_FILE)) &&\n fs.existsSync(path.join(pluginPath, DOC_FILE))\n );\n }\n\n // unsafe node plugins (can potentially crash omegga) are powerful\n static getFormat() {\n return 'node_unsafe';\n }\n\n constructor(pluginPath: string, omegga: Omegga) {\n super(pluginPath, omegga);\n // TODO: validate documentation\n this.documentation = Plugin.readJSON(path.join(pluginPath, DOC_FILE));\n this.pluginConfig = Plugin.readJSON(path.join(pluginPath, PLUGIN_FILE));\n this.pluginFile = path.join(pluginPath, MAIN_FILE);\n\n // list of registered comands\n this.commands = [];\n }\n\n // documentation is based on doc.json file\n getDocumentation() {\n return this.documentation;\n }\n\n // loaded state is based on if the loadedPlugin object is created\n isLoaded() {\n return !!this.loadedPlugin;\n }\n\n // determing if a command is registered\n isCommand(cmd: string) {\n return this.commands.includes(cmd);\n }\n\n // require the plugin into the system, run the init func\n async load() {\n const stopPlugin = (reason?: string) => {\n Logger.errorp('error launching node plugin', this.getName(), ':', reason);\n try {\n this.disrequireAll();\n } catch (e) {\n Logger.errorp('error unloading node plugin (2)', this.getName(), e);\n }\n this.emitStatus();\n return false;\n };\n this.commands = [];\n\n try {\n const config = await this.storage.getConfig();\n if (this.pluginConfig?.emitConfig) {\n await fs.promises.writeFile(\n path.join(this.path, this.pluginConfig.emitConfig),\n JSON.stringify(config),\n );\n }\n\n // require the plugin itself\n const Plugin: OmeggaPlugin = require(this.pluginFile);\n\n // node plugins must export a class with a constructor\n if (\n typeof (Plugin as any).prototype !== 'object' ||\n typeof (Plugin as any).prototype.constructor !== 'function'\n )\n return stopPlugin('missing constructor in plugin');\n\n // interface with plugin store\n const store: PluginStore = {\n get: key => this.storage.get(key),\n set: (key, value) => this.storage.set(key, value),\n delete: key => this.storage.delete(key),\n wipe: () => this.storage.wipe(),\n count: () => this.storage.count(),\n keys: () => this.storage.keys(),\n };\n\n // create the loaded plugin\n this.loadedPlugin = new (Plugin as any)(this.omegga, config, store);\n\n // start the loaded plugin\n if (typeof this.loadedPlugin.init === 'function') {\n const result = await this.loadedPlugin.init();\n\n // plugins can return a result object\n if (typeof result === 'object' && result) {\n // if registeredCommands is in the results, register the provided strings as commands\n const cmds = result.registeredCommands;\n if (\n cmds &&\n cmds instanceof Array &&\n cmds.every(i => typeof i === 'string')\n )\n this.commands = cmds;\n }\n }\n\n this.emitStatus();\n return true;\n } catch (e) {\n Logger.errorp('error loading node plugin', this.getName(), e);\n this.emitStatus();\n return false;\n }\n }\n\n // disrequire the plugin into the system, run the stop func\n async unload() {\n // can't unload the plugin if it hasn't been loaded\n if (typeof this.loadedPlugin === 'undefined') {\n this.emitStatus();\n return false;\n }\n\n try {\n // run the stop func on the plugin if applicable\n if (typeof this.loadedPlugin.stop === 'function')\n await this.loadedPlugin.stop();\n\n // unload the plugin\n this.disrequireAll();\n this.loadedPlugin = undefined;\n this.emitStatus();\n this.commands = [];\n return true;\n } catch (e) {\n Logger.errorp('error unloading node plugin', this.getName(), e);\n this.emitStatus();\n return false;\n }\n }\n\n // disrequire all that match plugin path in require.cache\n disrequireAll() {\n // get all files in plugin directory from require cache\n const requiredFiles = Object.keys(require.cache).filter(requiredFile =>\n requiredFile.includes(this.path),\n );\n // disrequire all the files\n requiredFiles.forEach(file => {\n try {\n disrequire(file);\n } catch (e) {\n if (e.code !== 'MODULE_NOT_FOUND')\n // ignore error thrown if a module was deleted\n throw e;\n }\n });\n }\n}\n"],"names":["util","Plugin","Logger"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"plugin_node_unsafe.js","sources":["../../../src/omegga/plugin/plugin_node_unsafe.ts"],"sourcesContent":["import Logger from '@/logger';\nimport OmeggaPlugin, { PluginStore } from '@/plugin';\nimport type Omegga from '@omegga/server';\nimport * as util from '@util';\nimport disrequire from 'disrequire';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { Plugin } from './interface';\n\nglobal.OMEGGA_UTIL = util;\n\n// Main plugin file (like index.js)\n// this isn't named 'index.js' or 'plugin.js' because those may be filenames\n// used with other loaders (rpc loader) and are too generic\n// omegga.main.js is rather unique and helps avoid collision\nconst MAIN_FILE = 'omegga.main.js';\n\n// Documentation file (contains name, description, author, command helptext)\nconst DOC_FILE = 'doc.json';\nconst PLUGIN_FILE = 'plugin.json';\n\nexport default class NodePlugin extends Plugin {\n // every node plugin requires the main file and a doc file\n // may evolve this so it checks the contents of the doc file later\n static canLoad(pluginPath: string) {\n return (\n fs.existsSync(path.join(pluginPath, MAIN_FILE)) &&\n fs.existsSync(path.join(pluginPath, DOC_FILE))\n );\n }\n\n // unsafe node plugins (can potentially crash omegga) are powerful\n static getFormat() {\n return 'node_unsafe';\n }\n\n constructor(pluginPath: string, omegga: Omegga) {\n super(pluginPath, omegga);\n // TODO: validate documentation\n this.documentation = Plugin.readJSON(path.join(pluginPath, DOC_FILE));\n this.pluginConfig = Plugin.readJSON(path.join(pluginPath, PLUGIN_FILE));\n this.pluginFile = path.join(pluginPath, MAIN_FILE);\n\n // list of registered comands\n this.commands = [];\n }\n\n // documentation is based on doc.json file\n getDocumentation() {\n return this.documentation;\n }\n\n // loaded state is based on if the loadedPlugin object is created\n isLoaded() {\n return !!this.loadedPlugin;\n }\n\n // determing if a command is registered\n isCommand(cmd: string) {\n return this.commands.includes(cmd);\n }\n\n // require the plugin into the system, run the init func\n async load() {\n const stopPlugin = (reason?: string) => {\n Logger.errorp('error launching node plugin', this.getName(), ':', reason);\n try {\n this.disrequireAll();\n } catch (e) {\n Logger.errorp('error unloading node plugin (2)', this.getName(), e);\n }\n this.emitStatus();\n return false;\n };\n this.commands = [];\n\n try {\n const config = await this.storage.getConfig();\n if (this.pluginConfig?.emitConfig) {\n await fs.promises.writeFile(\n path.join(this.path, this.pluginConfig.emitConfig),\n JSON.stringify(config),\n );\n }\n\n // require the plugin itself\n const Plugin: OmeggaPlugin = require(this.pluginFile);\n\n // node plugins must export a class with a constructor\n if (\n typeof (Plugin as any).prototype !== 'object' ||\n typeof (Plugin as any).prototype.constructor !== 'function'\n )\n return stopPlugin('missing constructor in plugin');\n\n // interface with plugin store\n const store: PluginStore = {\n get: key => this.storage.get(key),\n set: (key, value) => this.storage.set(key, value),\n delete: key => this.storage.delete(key),\n wipe: () => this.storage.wipe(),\n count: () => this.storage.count(),\n keys: () => this.storage.keys(),\n };\n\n // create the loaded plugin\n this.loadedPlugin = new (Plugin as any)(this.omegga, config, store);\n\n // start the loaded plugin\n if (typeof this.loadedPlugin.init === 'function') {\n const result = await this.loadedPlugin.init();\n\n // plugins can return a result object\n if (typeof result === 'object' && result) {\n // if registeredCommands is in the results, register the provided strings as commands\n const cmds = result.registeredCommands;\n if (\n cmds &&\n cmds instanceof Array &&\n cmds.every(i => typeof i === 'string')\n )\n this.commands = cmds;\n }\n }\n\n this.emitStatus();\n return true;\n } catch (e) {\n Logger.errorp('error loading node plugin', this.getName(), e);\n this.emitStatus();\n return false;\n }\n }\n\n // disrequire the plugin into the system, run the stop func\n async unload() {\n // can't unload the plugin if it hasn't been loaded\n if (typeof this.loadedPlugin === 'undefined') {\n this.emitStatus();\n return false;\n }\n\n try {\n // run the stop func on the plugin if applicable\n if (typeof this.loadedPlugin.stop === 'function')\n await this.loadedPlugin.stop();\n\n // unload the plugin\n this.disrequireAll();\n this.loadedPlugin = undefined;\n this.emitStatus();\n this.commands = [];\n return true;\n } catch (e) {\n Logger.errorp('error unloading node plugin', this.getName(), e);\n this.emitStatus();\n return false;\n }\n }\n\n // forward plugin events to the loaded plugin instance\n async emitPlugin(ev: string, from: string, args: any[]) {\n if (typeof this.loadedPlugin?.pluginEvent === 'function') {\n return await this.loadedPlugin.pluginEvent(ev, from, ...args);\n }\n }\n\n // disrequire all that match plugin path in require.cache\n disrequireAll() {\n // get all files in plugin directory from require cache\n const requiredFiles = Object.keys(require.cache).filter(requiredFile =>\n requiredFile.includes(this.path),\n );\n // disrequire all the files\n requiredFiles.forEach(file => {\n try {\n disrequire(file);\n } catch (e) {\n if (e.code !== 'MODULE_NOT_FOUND')\n // ignore error thrown if a module was deleted\n throw e;\n }\n });\n }\n}\n"],"names":["util","Plugin","Logger"],"mappings":"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"}
|
package/dist/omegga/server.js
CHANGED
|
@@ -29,6 +29,7 @@ class Omegga extends wrapper.default {
|
|
|
29
29
|
this.started = false;
|
|
30
30
|
this.starting = false;
|
|
31
31
|
this.stopping = false;
|
|
32
|
+
this.crashDetected = false;
|
|
32
33
|
this.verbose = logger.default.VERBOSE;
|
|
33
34
|
logger.default.verbose("Running omegga", `v${version.VERSION}`.green);
|
|
34
35
|
logger.default.verbose("Versions", process.versions);
|
|
@@ -85,12 +86,45 @@ class Omegga extends wrapper.default {
|
|
|
85
86
|
this.writeln("Chat.MessageForUnknownCommands 0");
|
|
86
87
|
this.restoreServer();
|
|
87
88
|
});
|
|
89
|
+
const crashHandler = (line) => {
|
|
90
|
+
if (!this.crashDetected && (/Engine crash handling finished; re-raising signal \d+ for the default handler\. Good bye\./.test(
|
|
91
|
+
line
|
|
92
|
+
) || /LogCore: === Critical error: ===/.test(line))) {
|
|
93
|
+
logger.default.error("Engine crash detected!");
|
|
94
|
+
this.crashDetected = true;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
this.on("err", crashHandler);
|
|
98
|
+
this.on("line", crashHandler);
|
|
88
99
|
this.on("exit", () => {
|
|
89
100
|
this.stop();
|
|
90
101
|
});
|
|
91
102
|
this.on("closed", () => {
|
|
103
|
+
const wasCrash = this.crashDetected;
|
|
104
|
+
this.crashDetected = false;
|
|
92
105
|
if (this.started) this.emit("exit");
|
|
93
|
-
|
|
106
|
+
const doRestart = async () => {
|
|
107
|
+
if (!wasCrash) return;
|
|
108
|
+
try {
|
|
109
|
+
const config = await this.webserver?.database?.getAutoRestartConfig();
|
|
110
|
+
if (config?.crashRestartEnabled) {
|
|
111
|
+
logger.default.logp("Restarting server after crash...");
|
|
112
|
+
this.webserver?.database?.addChatLog(
|
|
113
|
+
"server",
|
|
114
|
+
{},
|
|
115
|
+
"Server crashed, restarting..."
|
|
116
|
+
);
|
|
117
|
+
await this.start();
|
|
118
|
+
}
|
|
119
|
+
} catch (err) {
|
|
120
|
+
logger.default.error("Error restarting after crash", err);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
if (!this.stopping) {
|
|
124
|
+
this.stop().then(doRestart);
|
|
125
|
+
} else {
|
|
126
|
+
this.once("server:stopped", () => doRestart());
|
|
127
|
+
}
|
|
94
128
|
});
|
|
95
129
|
this.on("cmd", (cmd, name) => {
|
|
96
130
|
if (!default_commands.default.includes(cmd) && (!this.pluginLoader || !this.pluginLoader.isCommand(cmd))) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sources":["../../src/omegga/server.ts"],"sourcesContent":["import default_commands from '@/info/default_commands.json';\nimport Logger from '@/logger';\nimport { OmeggaLike, OmeggaPlayer, PluginInterop } from '@/plugin';\nimport {\n BRICKADIA_AUTH_FILES,\n CONFIG_AUTH_DIR,\n CONFIG_HOME,\n CONFIG_SAVED_DIR,\n DATA_PATH,\n} from '@/softconfig';\nimport { VERSION } from '@/version';\nimport { EnvironmentPreset } from '@brickadia/presets';\nimport {\n BRBanList,\n BRPlayerNameCache,\n BRRoleAssignments,\n BRRoleSetup,\n} from '@brickadia/types';\nimport { IConfig } from '@config/types';\nimport { map as mapUtils, pattern, uuid } from '@util';\nimport { copyFiles, mkdir, readWatchedJSON } from '@util/file';\nimport Webserver from '@webserver/backend';\nimport brs, { type ReadSaveObject, type WriteSaveObject } from 'brs-js';\nimport 'colors';\nimport glob from 'glob';\nimport { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';\nimport { basename, join } from 'path';\nimport { AutoRestartConfig } from '..';\nimport commandInjector from './commandInjector';\nimport MATCHERS from './matchers';\nimport Player from './player';\nimport { PluginLoader } from './plugin';\nimport {\n ILogMinigame,\n IMinigameList,\n IOmeggaOptions,\n IPlayerPositions,\n IServerStatus,\n} from './types';\nimport OmeggaWrapper from './wrapper';\n\nconst MISSING_CMD =\n '\"Command not found. Type <color=\\\\\"ffff00\\\\\">/help</> for a list of commands or <color=\\\\\"ffff00\\\\\">/plugins</> for plugin information.\"';\n\n// TODO: safe broadcast parsing\n\nexport default class Omegga extends OmeggaWrapper implements OmeggaLike {\n /** The save counter prevents omegga from saving over the same file */\n _tempCounter = { save: 0, environment: 0 };\n /** The save prefix is prepended to all temporary saves */\n _tempSavePrefix = 'omegga_temp_';\n\n // pluginloader is not private so plugins can potentially add more formats\n pluginLoader: PluginLoader = undefined;\n webserver: Webserver;\n\n verbose: boolean;\n savePath: string;\n worldPath: string;\n presetPath: string;\n configPath: string;\n options: IOmeggaOptions;\n\n version: number;\n\n host?: { id: string; name: string };\n players: OmeggaPlayer[];\n\n started = false;\n starting = false;\n stopping = false;\n currentMap: string;\n\n getServerStatus: () => Promise<IServerStatus>;\n listMinigames: () => Promise<IMinigameList>;\n getAllPlayerPositions: () => Promise<IPlayerPositions>;\n getMinigames: () => Promise<ILogMinigame[]>;\n\n /**\n * Omegga instance\n */\n constructor(serverPath: string, cfg: IConfig, options: IOmeggaOptions = {}) {\n super(serverPath, cfg);\n this.verbose = Logger.VERBOSE;\n\n Logger.verbose('Running omegga', `v${VERSION}`.green);\n Logger.verbose('Versions', process.versions);\n Logger.verbose('Config', cfg);\n\n // inject commands\n Logger.verbose('Setting up command injector');\n commandInjector(this, this.logWrangler);\n\n // launch options (disabling webserver)\n this.options = options;\n const savedDir = cfg.server.savedDir ?? CONFIG_SAVED_DIR;\n\n // path to save files\n this.savePath = join(this.path, DATA_PATH, savedDir, 'Builds');\n this.worldPath = join(this.path, DATA_PATH, savedDir, 'Worlds');\n\n this.presetPath = join(this.path, DATA_PATH, savedDir, 'Presets');\n\n // path to config files\n this.configPath = join(this.path, DATA_PATH, savedDir, 'Server');\n\n // create dir folders\n Logger.verbose('Creating directories');\n mkdir(this.savePath);\n mkdir(this.configPath);\n\n // ignore auth file copy\n if (!options.noauth) {\n Logger.verbose('Copying auth files');\n this.copyAuthFiles();\n }\n\n // create the webserver if it's enabled\n // the web interface provides access to server information while the server is running\n // and lets you view chat logs, disable plugins, etc\n if (!options.noweb) {\n Logger.verbose('Creating webserver');\n this.webserver = new Webserver(cfg.omegga, this);\n }\n\n if (!options.noplugin) {\n Logger.verbose('Creating plugin loader');\n // create the pluginloader\n this.pluginLoader = new PluginLoader(this.path, this);\n }\n\n /** @type {Array<Player>}list of online players */\n this.players = [];\n\n /** host player info `{id: uuid, name: player name}` */\n this.host = undefined;\n\n /** @type {String} current game version - may later be turned into CL#### versions */\n this.version = -1;\n\n /** @type {Boolean} whether server has started */\n this.started = false;\n /** @type {Boolean} whether server is starting up */\n this.starting = false;\n\n /** @type {String} current map */\n this.currentMap = '';\n\n // add all the matchers to the server\n Logger.verbose('Adding matchers');\n for (const matcher of MATCHERS) {\n const { pattern, callback } = matcher(this);\n this.addMatcher(pattern, callback);\n }\n\n process.on('uncaughtException', async err => {\n Logger.verbose('Uncaught exception', err);\n this.emit('error', err);\n\n // publish stop to database\n this.webserver?.database?.addChatLog('server', {}, 'Server error');\n\n try {\n await this.stop();\n } catch (e) {\n Logger.error(e);\n }\n process.exit();\n });\n\n // when brickadia starts, mark the server as started\n this.on('start', ({ map }) => {\n this.started = true;\n this.starting = false;\n this.currentMap = map;\n this.writeln('Chat.MessageForUnknownCommands 0');\n\n this.restoreServer();\n });\n\n // when brickadia exits, stop omegga\n this.on('exit', () => {\n this.stop();\n });\n\n // when the process closes, emit the exit signal and stop\n this.on('closed', () => {\n if (this.started) this.emit('exit');\n if (!this.stopping) this.stop();\n });\n\n // detect when a missing command is sent\n this.on('cmd', (cmd, name) => {\n // if it's not in the default commands and it's not registered to a plugin,\n // it's okay to send the missing command message\n if (\n !default_commands.includes(cmd) &&\n (!this.pluginLoader || !this.pluginLoader.isCommand(cmd))\n ) {\n this.whisper(name, MISSING_CMD);\n }\n });\n }\n\n /** attempt to save server state */\n async saveServer(config: AutoRestartConfig) {\n if (config.players && this.players.length > 0) {\n Logger.logp('Getting player positions...');\n const players = await this.getAllPlayerPositions();\n Logger.logp(`Saving ${players.length} player positions...`);\n const data = players\n .filter(p => !p.isDead && p.pos)\n .map(p => ({ position: p.pos, id: p.player.id }));\n if (players.length > 0)\n writeFileSync(\n join(this.path, DATA_PATH, 'omegga_temp_players.json'),\n JSON.stringify(data),\n );\n }\n\n if (config.saveWorld) {\n Logger.logp('Saving world...');\n await this.saveWorld();\n }\n }\n\n async restartServer() {\n if (this.starting || this.stopping) return;\n if (!this.started) return await this.start();\n\n const nextWorld = this.getNextWorld();\n if (nextWorld) {\n Logger.logp('Loading world', nextWorld.file.yellow);\n Logger.verbose('Next world configured from', nextWorld.source.yellow);\n this.loadWorld(nextWorld.file);\n } else {\n this.changeMap(this.currentMap);\n }\n\n const res = await Promise.race([\n // wait for the map to change\n new Promise(resolve =>\n this.once('mapchange', () => resolve('mapchange')),\n ),\n // Timeout after 10 seconds\n new Promise(resolve => setTimeout(() => resolve('timeout'), 10000)),\n ]);\n Logger.verbose('Restart result:', res);\n }\n\n /** attempt to restore the server's state */\n async restoreServer() {\n const tempPlayersFile = join(\n this.path,\n DATA_PATH,\n 'omegga_temp_players.json',\n );\n if (!existsSync(tempPlayersFile)) return;\n\n try {\n Logger.logp('Loading previous player positions...');\n\n // player positions are an array to address multi-clienting\n const players: { position: number[]; id: string }[] = JSON.parse(\n readFileSync(tempPlayersFile).toString(),\n );\n\n // restore player position on join\n const callback = (player: OmeggaPlayer) => {\n const index = players.findIndex(p => p.id === player.id);\n if (index > -1) {\n const { position } = players[index];\n this.writeln(\n `Chat.Command /TP \"${player.name}\" ${position.join(' ')} 0`,\n );\n\n // remove the entry\n players[index] = players[players.length - 1];\n players.pop();\n }\n };\n this.on('join', callback);\n\n let timeout = setTimeout(() => {\n try {\n this.off('join', callback);\n if (existsSync(tempPlayersFile)) unlinkSync(tempPlayersFile);\n } catch (err) {\n Logger.error('Error removing omegga_temp_players.json', err);\n }\n }, 10000);\n this.once('changemap', () => {\n clearTimeout(timeout);\n this.off('join', callback);\n });\n } catch (err) {\n Logger.error('Error restoring previous server state', err);\n }\n }\n\n /**\n * start webserver, load plugins, start the brickadia server\n * this should not be called by a plugin\n */\n //\n async start(): Promise<any> {\n this.starting = true;\n if (this.webserver) await this.webserver.start();\n if (this.pluginLoader) {\n // scan for plugins\n Logger.verbose('Scanning for plugins');\n await this.pluginLoader.scan();\n\n // load the plugins\n Logger.verbose('Loading plugins');\n await this.pluginLoader.reload();\n }\n\n Logger.verbose('Starting Brickadia');\n super.start();\n this.emit('server:starting');\n }\n\n /**\n * unload plugins and stop the server\n * this should not be called by a plugin\n */\n async stop() {\n if (!this.started && !this.starting) {\n Logger.verbose(\"Stop called while server wasn't started or was starting\");\n return;\n }\n\n if (this.stopping) {\n Logger.verbose('Stop called while server was starting');\n return;\n }\n\n this.stopping = true;\n this.emit('server:stopping');\n if (this.pluginLoader) {\n Logger.verbose('Unloading plugins');\n await this.pluginLoader.unload();\n }\n Logger.verbose('Stopping server');\n super.stop();\n\n const res = await Promise.race([\n new Promise(resolve => this.once('exit', () => resolve('exit'))),\n // Timeout after 10 seconds\n new Promise(resolve => setTimeout(() => resolve('timeout'), 10000)),\n ]);\n\n Logger.verbose('Stop result:', res);\n if (this.stopping) this.emit('server:stopped');\n this.stopping = false;\n this.started = false;\n this.starting = false;\n this.players = [];\n }\n\n /**\n * Copies auth files from home config dir\n * this should never be called by a plugin\n */\n copyAuthFiles() {\n const authDir = this.config.server.authDir ?? CONFIG_AUTH_DIR;\n const savedDir = this.config.server.savedDir ?? CONFIG_SAVED_DIR;\n const authPath = join(this.path, DATA_PATH, savedDir, authDir);\n const homeAuthPath = join(\n CONFIG_HOME,\n (savedDir !== CONFIG_SAVED_DIR ? savedDir : '') + authDir,\n );\n\n copyFiles(homeAuthPath, authPath, BRICKADIA_AUTH_FILES);\n }\n\n // TODO: split messages that longer than 512 characters\n // TODO: delete characters that are known to crash the game\n broadcast(...messages: string[]) {\n messages\n .flatMap(m => m.toString().split('\\n'))\n .filter(m => m.length < 512)\n .forEach(m => this.writeln(`Chat.Broadcast ${m}`));\n }\n\n whisper(target: string | OmeggaPlayer, ...messages: string[]) {\n // find the target player\n if (typeof target !== 'object') target = this.getPlayer(target);\n\n // whisper the messages to that player\n messages\n .flatMap(m => m.toString().split('\\n'))\n .filter(m => m.length < 512)\n .forEach(m =>\n this.writeln(\n `Chat.Whisper \"${(target as { name: string }).name}\" ${m}`,\n ),\n );\n }\n\n middlePrint(target: string | OmeggaPlayer, message: string) {\n // find the target player\n if (typeof target !== 'object') target = this.getPlayer(target);\n\n // whisper the messages to that player\n if (message.length > 512) return;\n this.writeln(\n `Chat.StatusMessage \"${(target as { name: string }).name}\" ${message}`,\n );\n }\n\n getPlayers(): {\n id: string;\n name: string;\n displayName: string;\n controller: string;\n state: string;\n }[] {\n return this.players.map(p => ({ ...p }));\n }\n\n getRoleSetup(): BRRoleSetup {\n // Read RoleSetup2, fallback to old RoleSetup if it doesn't exist\n return (readWatchedJSON(join(this.configPath, 'RoleSetup2.json')) ??\n readWatchedJSON(join(this.configPath, 'RoleSetup.json'))) as BRRoleSetup;\n }\n\n getRoleAssignments(): BRRoleAssignments {\n return readWatchedJSON(\n join(this.configPath, 'RoleAssignments.json'),\n ) as BRRoleAssignments;\n }\n\n getBanList(): BRBanList {\n return readWatchedJSON(join(this.configPath, 'BanList.json')) as BRBanList;\n }\n\n getNameCache(): BRPlayerNameCache {\n return readWatchedJSON(\n join(this.configPath, 'PlayerNameCache.json'),\n ) as BRPlayerNameCache;\n }\n\n getPlayer(target: string): OmeggaPlayer {\n return this.players.find(\n p =>\n p.name === target ||\n p.id === target ||\n p.controller === target ||\n p.state === target,\n );\n }\n\n findPlayerByName(name: string): OmeggaPlayer {\n name = name.toLowerCase();\n const exploded = pattern.explode(name);\n return (\n this.players.find(p => p.name === name || p.displayName === name) || // find by exact match\n this.players.find(\n p => p.name.indexOf(name) > -1 || p.displayName.indexOf(name) > -1,\n ) || // find by rough match\n this.players.find(\n p => p.name.match(exploded) || p.displayName.match(exploded),\n ) // find by exploded regex match (ck finds cake, tbp finds TheBlackParrot)\n );\n }\n\n getHostId(): string {\n return this.host?.id ?? '';\n }\n\n saveMinigame(index: number, name: string) {\n this.writeln(`Server.Minigames.SavePreset ${index} \"${name}\"`);\n }\n\n deleteMinigame(index: number) {\n this.writeln(`Server.Minigames.Delete ${index}`);\n }\n\n resetMinigame(index: number) {\n this.writeln(`Server.Minigames.Reset ${index}`);\n }\n\n nextRoundMinigame(index: number) {\n this.writeln(`Server.Minigames.NextRound ${index}`);\n }\n\n loadMinigame(presetName: string, owner = '') {\n this.writeln(\n `Server.Minigames.LoadPreset \"${presetName}\" ${owner ? `\"${owner}\"` : ''}`,\n );\n }\n\n getMinigamePresets(): string[] {\n const presetPath = join(this.presetPath, 'Minigame');\n return existsSync(presetPath)\n ? glob\n .sync(presetPath + '/**/*.bp')\n .map(f => basename(f).replace(/\\.bp$/, ''))\n : [];\n }\n\n resetEnvironment() {\n this.writeln(`Server.Environment.Reset`);\n }\n\n async saveEnvironment(presetName: string): Promise<void> {\n await this.addWatcher(/Environment preset saved.$/, {\n // request the pawn for this player's controller (should only be one)\n exec: () => this.writeln(`Server.Environment.SavePreset \"${presetName}\"`),\n timeoutDelay: 100,\n });\n }\n\n async getEnvironmentData(): Promise<EnvironmentPreset> {\n const saveName =\n this._tempSavePrefix + Date.now() + '_' + this._tempCounter.environment++;\n\n await this.saveEnvironment(saveName);\n const data = this.readEnvironmentData(saveName);\n const file = join(this.presetPath, 'Environment', saveName + '.bp');\n if (existsSync(file)) unlinkSync(file);\n\n return data;\n }\n\n readEnvironmentData(saveName: string): EnvironmentPreset {\n if (typeof saveName !== 'string')\n throw 'expected name argument for readEnvironmentData';\n\n const file = join(this.presetPath, 'Environment', saveName + '.bp');\n try {\n if (existsSync(file)) return JSON.parse(readFileSync(file).toString());\n } catch (err) {\n Logger.verbose('Error parsing save data in readEnvironmentData', err);\n }\n return null;\n }\n\n loadEnvironment(presetName: string) {\n this.writeln(`Server.Environment.LoadPreset ${presetName}`);\n }\n\n loadEnvironmentData(\n preset: EnvironmentPreset | EnvironmentPreset['data']['groups'],\n ) {\n if ('data' in preset) preset = preset.data.groups;\n\n const saveFile =\n this._tempSavePrefix + Date.now() + '_' + this._tempCounter.environment++;\n\n const path = join(this.presetPath, 'Environment', saveFile + '.bp');\n\n writeFileSync(\n path,\n JSON.stringify({\n formatVersion: '1',\n presetVersion: '1',\n type: 'Environment',\n data: {\n groups: {\n ...preset,\n },\n },\n }),\n );\n\n this.loadEnvironment(saveFile);\n\n // this is lazy, but environments should load much faster than builds\n // do, so it's not really worth keeping track of logs for this\n setTimeout(() => unlinkSync(path), 5000);\n }\n\n getEnvironmentPresets(): string[] {\n const presetPath = join(this.presetPath, 'Environment');\n return existsSync(presetPath)\n ? glob\n .sync(presetPath + '/**/*.bp')\n .map(f => basename(f).replace(/\\.bp$/, ''))\n : [];\n }\n\n clearBricks(target: string | { id: string }, quiet = false) {\n // target is a player object, just use that id\n if (typeof target === 'object' && target.id) target = target.id;\n // if the target isn't a uuid already, find the player by name or controller and use that uuid\n else if (typeof target === 'string' && !uuid.match(target)) {\n // only set the target if the player exists\n const player = this.getPlayer(target);\n target = player && player.id;\n }\n\n if (!target) return;\n\n this.writeln(`Bricks.Clear ${target} ${quiet ? 1 : ''}`);\n }\n\n clearRegion(\n region: {\n center: [number, number, number];\n extent: [number, number, number];\n },\n options?: { target: string | OmeggaPlayer },\n ) {\n let target = '';\n\n // target is a player object, just use that id\n if (options && typeof options.target === 'object')\n target = ' ' + options.target.id;\n // if the target isn't a uuid already, find the player by name or controller and use that uuid\n\n if (typeof target === 'string' && !uuid.match(target)) {\n // only set the target if the player exists\n const player = this.getPlayer(target);\n if (player) target = ' ' + player.id;\n }\n\n this.writeln(\n `Bricks.ClearRegion ${region.center.join(' ')} ${region.extent.join(\n ' ',\n )}${target}`,\n );\n }\n\n clearAllBricks(quiet = false) {\n this.writeln(`Bricks.ClearAll ${quiet ? 1 : ''}`);\n }\n\n saveBricks(\n saveName: string,\n region?: {\n center: [number, number, number];\n extent: [number, number, number];\n },\n ) {\n if (!saveName) return;\n\n // add quotes around the filename if it doesn't have them (backwards compat w/ plugins)\n if (!(saveName.startsWith('\"') && saveName.endsWith('\"')))\n saveName = `\"${saveName}\"`;\n\n if (region?.center && region?.extent)\n this.writeln(\n `Bricks.SaveRegion ${saveName} ${region.center.join(\n ' ',\n )} ${region.extent.join(' ')}`,\n );\n else this.writeln(`Bricks.Save ${saveName}`);\n }\n\n async saveBricksAsync(\n saveName: string,\n region?: {\n center: [number, number, number];\n extent: [number, number, number];\n },\n ): Promise<void> {\n if (!saveName) return;\n\n let saveNameClean = saveName;\n // add quotes around the filename if it doesn't have them (backwards compat w/ plugins)\n if (!(saveName.startsWith('\"') && saveName.endsWith('\"')))\n saveNameClean = `\"${saveName}\"`;\n\n const command =\n region?.center && region?.extent\n ? `Bricks.SaveRegion ${saveNameClean} ${region.center.join(\n ' ',\n )} ${region.extent.join(' ')}`\n : `Bricks.Save ${saveNameClean}`;\n\n // wait for the server to save the file\n await this.watchLogChunk(command, /^(LogBrickSerializer|LogTemp): (.+)$/, {\n first: match => match[0].endsWith(saveName + '.brs...'),\n last: match =>\n Boolean(\n match[2].match(\n /Saved .+ bricks and .+ components from .+ owners|Error: No bricks in grid!|Error: No bricks selected to save!/,\n ),\n ),\n afterMatchDelay: 0,\n timeoutDelay: 30000,\n });\n }\n\n loadBricks(\n saveName: string,\n {\n offX = 0,\n offY = 0,\n offZ = 0,\n quiet = false,\n correctPalette = false,\n correctCustom = false,\n } = {},\n ) {\n // add quotes around the filename if it doesn't have them (backwards compat w/ plugins)\n if (!(saveName.startsWith('\"') && saveName.endsWith('\"')))\n saveName = `\"${saveName}\"`;\n\n this.writeln(\n `Bricks.Load ${saveName} ${offX} ${offY} ${offZ} ${quiet ? 1 : 0} ${\n correctPalette ? 1 : 0\n } ${correctCustom ? 1 : 0}`,\n );\n }\n\n loadBricksOnPlayer(\n saveName: string,\n player: string | OmeggaPlayer,\n {\n offX = 0,\n offY = 0,\n offZ = 0,\n correctPalette = false,\n correctCustom = false,\n } = {},\n ) {\n player = typeof player === 'string' ? this.getPlayer(player) : player;\n if (!player) return;\n\n // add quotes around the filename if it doesn't have them (backwards compat w/ plugins)\n if (!(saveName.startsWith('\"') && saveName.endsWith('\"')))\n saveName = `\"${saveName}\"`;\n\n this.writeln(\n `Bricks.LoadTemplate ${saveName} ${offX} ${offY} ${offZ} ${\n correctPalette ? 1 : 0\n } ${correctCustom ? 1 : 0} \"${player.name}\"`,\n );\n }\n\n getSaves(): string[] {\n return existsSync(this.savePath)\n ? glob.sync(this.savePath + '/**/*.brs')\n : [];\n }\n\n getWorlds(): string[] {\n return existsSync(this.worldPath)\n ? glob.sync(this.worldPath + '/**/*.brdb')\n : [];\n }\n\n getSavePath(saveName: string) {\n const file = join(\n this.savePath,\n saveName.endsWith('.brs') ? saveName : saveName + '.brs',\n );\n return existsSync(file) ? file : undefined;\n }\n\n getWorldPath(worldName: string) {\n const file = join(\n this.worldPath,\n worldName.endsWith('.brdb') ? worldName : worldName + '.brdb',\n );\n return existsSync(file) ? file : undefined;\n }\n\n async getWorldRevisions(worldName: string) {\n if (!this.started || this.starting || this.stopping) {\n throw new Error('Server is not started');\n }\n\n worldName = worldName.replace(/\\.brdb$/i, '');\n\n if (!worldName || !this.getWorldPath(worldName)) {\n throw new Error(`World \"${worldName}\" does not exist`);\n }\n\n /*\n LogBRBundleManager: There are 2 revisions\n LogBRBundleManager: Revision 1 - 2001.02.03-04.05.06: Initial Revision\n LogBRBundleManager: Revision 2 - 2001.02.03-04.05.06: Manual Save\n */\n let numRevisions = 0;\n const revisionsRaw = await this.watchLogChunk<RegExpMatchArray>(\n `BR.World.ListRevisions \"${worldName}\"`,\n /^LogBRBundleManager: (There are (?<numRevisions>\\d+) revisions|Revision (?<revision>\\d+) - (?<date>[\\d.-]+): (?<note>.+))$/,\n {\n last: match => Number(match.groups.reverse) === numRevisions,\n first: match => {\n if (match.groups?.numRevisions !== undefined) {\n numRevisions = Number(match.groups.numRevisions);\n return true;\n }\n return false;\n },\n },\n );\n\n if (!revisionsRaw) return [];\n return revisionsRaw\n .map(match => {\n if (match.groups?.numRevisions !== undefined) {\n return null;\n }\n\n const revision = match.groups.revision\n ? Number(match.groups.revision)\n : 0;\n // parse date from YYYY.MM.DD-HH.MM.SS to YYYY-MM-DDTHH:MM:SSZ\n const dateStr = match.groups.date.replace(\n /(\\d{4})\\.(\\d{2})\\.(\\d{2})-(\\d{2})\\.(\\d{2})\\.(\\d{2})/,\n '$1-$2-$3T$4:$5:$6Z',\n );\n const date = new Date(dateStr);\n const note = match.groups.note || '';\n return { index: revision, date, note };\n })\n .filter(Boolean);\n }\n\n async loadWorld(worldName: string): Promise<boolean> {\n worldName = worldName.replace(/\\.brdb$/i, '');\n if (!worldName || !this.getWorldPath(worldName)) return false;\n this.writeln(`BR.World.Load \"${worldName}\"`);\n const res = await Promise.race([\n // wait for the map to change\n new Promise(resolve =>\n this.once('mapchange', () => resolve('mapchange')),\n ),\n // Timeout after 10 seconds\n new Promise(resolve => setTimeout(() => resolve('timeout'), 10000)),\n ]);\n Logger.verbose('LoadWorld', worldName, 'result:', res);\n return res === 'mapchange';\n }\n\n async loadWorldRevision(\n worldName: string,\n revision: number,\n ): Promise<boolean> {\n worldName = worldName.replace(/\\.brdb$/i, '');\n if (!worldName || !this.getWorldPath(worldName)) return false;\n if (typeof revision !== 'number' || revision < 1) {\n throw new Error(`Invalid revision number: ${revision}`);\n }\n this.writeln(`BR.World.LoadRevision \"${worldName}\" ${revision}`);\n const res = await Promise.race([\n // wait for the map to change\n new Promise(resolve =>\n this.once('mapchange', () => resolve('mapchange')),\n ),\n // Timeout after 10 seconds\n new Promise(resolve => setTimeout(() => resolve('timeout'), 10000)),\n ]);\n Logger.verbose('LoadWorld', worldName, 'result:', res);\n return res === 'mapchange';\n }\n\n async saveWorldAs(worldName: string) {\n if (!worldName) return false;\n if (this.stopping || this.starting || !this.started) return false;\n\n if (this.getWorldPath(worldName)) {\n return false;\n }\n worldName = worldName.replace(/\\.brdb$/i, '');\n\n try {\n const match = await this.addWatcher<{ res: boolean }>(\n (_line, match) => {\n if (match?.groups?.generator !== 'LogBRWorldManager') return;\n\n const ok = match.groups.data.match(/^World files saved after /);\n const err =\n !match.groups.data.startsWith(\n 'Error: Failed to capture minigame settings',\n ) &&\n match.groups.data.match(\n /^Error: (World already exists|Failed to create new world)?/,\n );\n return ok ? { res: true } : err ? { res: false } : undefined;\n },\n {\n exec: () => {\n this.writeln(`BR.World.SaveAs \"${worldName}\"`);\n },\n timeoutDelay: 2000,\n },\n );\n return match?.[0]?.['res'] ?? false;\n } catch (err) {\n return false;\n }\n }\n\n async saveWorld(): Promise<boolean> {\n // Don't allow saving while the server is starting or stopping\n if (this.stopping || this.starting || !this.started) return false;\n\n try {\n const match = await this.addWatcher<{ res: boolean }>(\n (_line, match) => {\n if (match?.groups?.generator !== 'LogBRWorldManager') return;\n\n const ok = match.groups.data.match(/^World files saved after /);\n const err =\n !match.groups.data.startsWith(\n 'Error: Failed to capture minigame settings',\n ) &&\n match.groups.data.match(/^Error: (World has not been saved\\.)?/);\n return ok ? { res: true } : err ? { res: false } : undefined;\n },\n {\n exec: () => {\n this.writeln(`BR.World.Save 0`);\n },\n timeoutDelay: 2000,\n },\n );\n return match?.[0]?.['res'] ?? false;\n } catch (err) {\n return false;\n }\n }\n\n async createEmptyWorld(\n worldName: string,\n map: 'Plate' | 'Space' | 'Studio' | 'Peaks' = 'Plate',\n ): Promise<boolean> {\n if (!worldName) return;\n worldName = worldName.replace(/\\.brdb$/i, '');\n\n try {\n const match = await this.addWatcher<{ res: boolean }>(\n (_line, match) => {\n if (match?.groups?.generator !== 'LogBRWorldManager') return;\n\n const ok = match.groups.data.match(/^World files saved after /);\n const err = match.groups.data.match(\n /^Error: (Invalid preset|World already exists|Failed to create new world)?/,\n );\n return ok ? { res: true } : err ? { res: false } : undefined;\n },\n {\n exec: () => {\n this.writeln(`BR.World.CreateEmpty \"${worldName}\" ${map}`);\n },\n timeoutDelay: 2000,\n },\n );\n return match?.[0]?.['res'] ?? false;\n } catch (err) {\n return false;\n }\n }\n\n writeSaveData(saveName: string, saveData: WriteSaveObject) {\n if (typeof saveName !== 'string')\n throw 'expected name argument for writeSaveData';\n\n const file = join(this.savePath, saveName + '.brs');\n if (!file.startsWith(this.savePath))\n throw 'save file not in Saved/Builds directory';\n writeFileSync(file, new Uint8Array(brs.write(saveData)));\n }\n\n readSaveData(saveName: string, nobricks = false): ReadSaveObject {\n if (typeof saveName !== 'string')\n throw 'expected name argument for readSaveData';\n\n const file = this.getSavePath(saveName);\n if (!file || !file.startsWith(this.savePath))\n throw 'save file not in Saved/Builds directory';\n if (file)\n return brs.read(readFileSync(file), {\n preview: false,\n bricks: !nobricks,\n });\n }\n\n async loadSaveData(\n saveData: WriteSaveObject,\n {\n offX = 0,\n offY = 0,\n offZ = 0,\n quiet = false,\n correctPalette = false,\n correctCustom = false,\n } = {},\n ) {\n const saveFile =\n this._tempSavePrefix + Date.now() + '_' + this._tempCounter.save++;\n // write savedata to file\n this.writeSaveData(saveFile, saveData);\n\n // wait for the server to finish reading the save\n await this.watchLogChunk(\n `Bricks.Load \"${saveFile}\" ${offX} ${offY} ${offZ} ${quiet ? 1 : 0} ${\n correctPalette ? 1 : 0\n } ${correctCustom ? 1 : 0}`,\n /^LogBrickSerializer: (.+)$/,\n {\n first: match => match[0].endsWith(saveFile + '.brs...'),\n last: match => Boolean(match[1].match(/Read .+ bricks/)),\n afterMatchDelay: 0,\n timeoutDelay: 30000,\n },\n );\n\n // delete the save file after we're done\n const savePath = this.getSavePath(saveFile);\n if (savePath) {\n unlinkSync(savePath);\n }\n }\n\n async loadSaveDataOnPlayer(\n saveData: WriteSaveObject,\n player: string | OmeggaPlayer,\n {\n offX = 0,\n offY = 0,\n offZ = 0,\n correctPalette = false,\n correctCustom = false,\n } = {},\n ) {\n player = typeof player === 'string' ? this.getPlayer(player) : player;\n if (!player) return;\n\n const saveFile =\n this._tempSavePrefix + Date.now() + '_' + this._tempCounter.save++;\n // write savedata to file\n this.writeSaveData(saveFile, saveData);\n\n // wait for the server to finish reading the save\n await this.watchLogChunk(\n `Bricks.LoadTemplate \"${saveFile}\" ${offX} ${offY} ${offZ} ${\n correctPalette ? 1 : 0\n } ${correctCustom ? 1 : 0} \"${player.name}\"`,\n /^LogBrickSerializer: (.+)$/,\n {\n first: match => match[0].endsWith(saveFile + '.brs...'),\n last: match => Boolean(match[1].match(/Read .+ bricks/)),\n afterMatchDelay: 0,\n timeoutDelay: 30000,\n },\n );\n\n // delete the save file after we're done\n const savePath = this.getSavePath(saveFile);\n if (savePath) {\n unlinkSync(savePath);\n }\n }\n\n async getSaveData(region?: {\n center: [number, number, number];\n extent: [number, number, number];\n }) {\n const saveFile =\n this._tempSavePrefix + Date.now() + '_' + this._tempCounter.save++;\n\n await this.saveBricksAsync(saveFile, region);\n\n // read the save file\n const savePath = this.getSavePath(saveFile);\n if (savePath) {\n // read and parse the save file\n const saveData = brs.read(readFileSync(savePath));\n\n // delete the save file after we're done reading it\n unlinkSync(savePath);\n\n // return the parsed save\n return saveData;\n }\n\n return undefined;\n }\n\n // TODO: switch this to use worlds...\n async changeMap(map: string) {\n if (!map) return;\n\n // ServerTravel requires /Game/Maps/Plate/Plate instead of Plate\n const brName = mapUtils.n2brn(map);\n\n // wait for the server to change maps\n const match = await this.addWatcher(\n /^.*(LogLoad: Took .+ seconds to LoadMap\\((?<map>.+)\\))|(ERROR: The map .+)$/,\n {\n timeoutDelay: 30000,\n exec: () => this.writeln(`ServerTravel ${brName}`),\n },\n );\n const success = !!(\n match &&\n match[0] &&\n match[0].groups &&\n match[0].groups.map\n );\n return success;\n }\n\n async getPlugin(name: string): Promise<PluginInterop> {\n const plugin = this.pluginLoader.plugins.find(p => p.getName() === name);\n\n if (plugin) {\n return {\n name,\n documentation: plugin.getDocumentation(),\n loaded: plugin.isLoaded(),\n emitPlugin: (event: string, ...args: any[]) => {\n return plugin.emitPlugin(event, 'unsafe', args);\n },\n };\n } else {\n return null;\n }\n }\n}\n"],"names":["OmeggaWrapper","Logger","VERSION","commandInjector","CONFIG_SAVED_DIR","join","DATA_PATH","mkdir","Webserver","PluginLoader","MATCHERS","pattern","default_commands","writeFileSync","existsSync","readFileSync","index","unlinkSync","CONFIG_AUTH_DIR","CONFIG_HOME","copyFiles","BRICKADIA_AUTH_FILES","readWatchedJSON","basename","file","path","uuid","match","mapUtils","plugin"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"server.js","sources":["../../src/omegga/server.ts"],"sourcesContent":["import default_commands from '@/info/default_commands.json';\nimport Logger from '@/logger';\nimport { OmeggaLike, OmeggaPlayer, PluginInterop } from '@/plugin';\nimport {\n BRICKADIA_AUTH_FILES,\n CONFIG_AUTH_DIR,\n CONFIG_HOME,\n CONFIG_SAVED_DIR,\n DATA_PATH,\n} from '@/softconfig';\nimport { VERSION } from '@/version';\nimport { EnvironmentPreset } from '@brickadia/presets';\nimport {\n BRBanList,\n BRPlayerNameCache,\n BRRoleAssignments,\n BRRoleSetup,\n} from '@brickadia/types';\nimport { IConfig } from '@config/types';\nimport { map as mapUtils, pattern, uuid } from '@util';\nimport { copyFiles, mkdir, readWatchedJSON } from '@util/file';\nimport Webserver from '@webserver/backend';\nimport brs, { type ReadSaveObject, type WriteSaveObject } from 'brs-js';\nimport 'colors';\nimport glob from 'glob';\nimport { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';\nimport { basename, join } from 'path';\nimport { AutoRestartConfig } from '..';\nimport commandInjector from './commandInjector';\nimport MATCHERS from './matchers';\nimport Player from './player';\nimport { PluginLoader } from './plugin';\nimport {\n ILogMinigame,\n IMinigameList,\n IOmeggaOptions,\n IPlayerPositions,\n IServerStatus,\n} from './types';\nimport OmeggaWrapper from './wrapper';\n\nconst MISSING_CMD =\n '\"Command not found. Type <color=\\\\\"ffff00\\\\\">/help</> for a list of commands or <color=\\\\\"ffff00\\\\\">/plugins</> for plugin information.\"';\n\n// TODO: safe broadcast parsing\n\nexport default class Omegga extends OmeggaWrapper implements OmeggaLike {\n /** The save counter prevents omegga from saving over the same file */\n _tempCounter = { save: 0, environment: 0 };\n /** The save prefix is prepended to all temporary saves */\n _tempSavePrefix = 'omegga_temp_';\n\n // pluginloader is not private so plugins can potentially add more formats\n pluginLoader: PluginLoader = undefined;\n webserver: Webserver;\n\n verbose: boolean;\n savePath: string;\n worldPath: string;\n presetPath: string;\n configPath: string;\n options: IOmeggaOptions;\n\n version: number;\n\n host?: { id: string; name: string };\n players: OmeggaPlayer[];\n\n started = false;\n starting = false;\n stopping = false;\n crashDetected = false;\n currentMap: string;\n\n getServerStatus: () => Promise<IServerStatus>;\n listMinigames: () => Promise<IMinigameList>;\n getAllPlayerPositions: () => Promise<IPlayerPositions>;\n getMinigames: () => Promise<ILogMinigame[]>;\n\n /**\n * Omegga instance\n */\n constructor(serverPath: string, cfg: IConfig, options: IOmeggaOptions = {}) {\n super(serverPath, cfg);\n this.verbose = Logger.VERBOSE;\n\n Logger.verbose('Running omegga', `v${VERSION}`.green);\n Logger.verbose('Versions', process.versions);\n Logger.verbose('Config', cfg);\n\n // inject commands\n Logger.verbose('Setting up command injector');\n commandInjector(this, this.logWrangler);\n\n // launch options (disabling webserver)\n this.options = options;\n const savedDir = cfg.server.savedDir ?? CONFIG_SAVED_DIR;\n\n // path to save files\n this.savePath = join(this.path, DATA_PATH, savedDir, 'Builds');\n this.worldPath = join(this.path, DATA_PATH, savedDir, 'Worlds');\n\n this.presetPath = join(this.path, DATA_PATH, savedDir, 'Presets');\n\n // path to config files\n this.configPath = join(this.path, DATA_PATH, savedDir, 'Server');\n\n // create dir folders\n Logger.verbose('Creating directories');\n mkdir(this.savePath);\n mkdir(this.configPath);\n\n // ignore auth file copy\n if (!options.noauth) {\n Logger.verbose('Copying auth files');\n this.copyAuthFiles();\n }\n\n // create the webserver if it's enabled\n // the web interface provides access to server information while the server is running\n // and lets you view chat logs, disable plugins, etc\n if (!options.noweb) {\n Logger.verbose('Creating webserver');\n this.webserver = new Webserver(cfg.omegga, this);\n }\n\n if (!options.noplugin) {\n Logger.verbose('Creating plugin loader');\n // create the pluginloader\n this.pluginLoader = new PluginLoader(this.path, this);\n }\n\n /** @type {Array<Player>}list of online players */\n this.players = [];\n\n /** host player info `{id: uuid, name: player name}` */\n this.host = undefined;\n\n /** @type {String} current game version - may later be turned into CL#### versions */\n this.version = -1;\n\n /** @type {Boolean} whether server has started */\n this.started = false;\n /** @type {Boolean} whether server is starting up */\n this.starting = false;\n\n /** @type {String} current map */\n this.currentMap = '';\n\n // add all the matchers to the server\n Logger.verbose('Adding matchers');\n for (const matcher of MATCHERS) {\n const { pattern, callback } = matcher(this);\n this.addMatcher(pattern, callback);\n }\n\n process.on('uncaughtException', async err => {\n Logger.verbose('Uncaught exception', err);\n this.emit('error', err);\n\n // publish stop to database\n this.webserver?.database?.addChatLog('server', {}, 'Server error');\n\n try {\n await this.stop();\n } catch (e) {\n Logger.error(e);\n }\n process.exit();\n });\n\n // when brickadia starts, mark the server as started\n this.on('start', ({ map }) => {\n this.started = true;\n this.starting = false;\n this.currentMap = map;\n this.writeln('Chat.MessageForUnknownCommands 0');\n\n this.restoreServer();\n });\n\n // detect engine crash from stderr or stdout\n const crashHandler = (line: string) => {\n if (\n !this.crashDetected &&\n (/Engine crash handling finished; re-raising signal \\d+ for the default handler\\. Good bye\\./.test(\n line,\n ) ||\n /LogCore: === Critical error: ===/.test(line))\n ) {\n Logger.error('Engine crash detected!');\n this.crashDetected = true;\n }\n };\n this.on('err', crashHandler);\n this.on('line', crashHandler);\n\n // when brickadia exits, stop omegga\n this.on('exit', () => {\n this.stop();\n });\n\n // when the process closes, emit the exit signal and stop\n this.on('closed', () => {\n // capture crash state before 'exit' handler triggers stop()\n const wasCrash = this.crashDetected;\n this.crashDetected = false;\n if (this.started) this.emit('exit');\n const doRestart = async () => {\n if (!wasCrash) return;\n try {\n const config = await this.webserver?.database?.getAutoRestartConfig();\n if (config?.crashRestartEnabled) {\n Logger.logp('Restarting server after crash...');\n this.webserver?.database?.addChatLog(\n 'server',\n {},\n 'Server crashed, restarting...',\n );\n await this.start();\n }\n } catch (err) {\n Logger.error('Error restarting after crash', err);\n }\n };\n if (!this.stopping) {\n this.stop().then(doRestart);\n } else {\n // stop() already in progress from 'exit' handler — wait for it to finish\n this.once('server:stopped', () => doRestart());\n }\n });\n\n // detect when a missing command is sent\n this.on('cmd', (cmd, name) => {\n // if it's not in the default commands and it's not registered to a plugin,\n // it's okay to send the missing command message\n if (\n !default_commands.includes(cmd) &&\n (!this.pluginLoader || !this.pluginLoader.isCommand(cmd))\n ) {\n this.whisper(name, MISSING_CMD);\n }\n });\n }\n\n /** attempt to save server state */\n async saveServer(config: AutoRestartConfig) {\n if (config.players && this.players.length > 0) {\n Logger.logp('Getting player positions...');\n const players = await this.getAllPlayerPositions();\n Logger.logp(`Saving ${players.length} player positions...`);\n const data = players\n .filter(p => !p.isDead && p.pos)\n .map(p => ({ position: p.pos, id: p.player.id }));\n if (players.length > 0)\n writeFileSync(\n join(this.path, DATA_PATH, 'omegga_temp_players.json'),\n JSON.stringify(data),\n );\n }\n\n if (config.saveWorld) {\n Logger.logp('Saving world...');\n await this.saveWorld();\n }\n }\n\n async restartServer() {\n if (this.starting || this.stopping) return;\n if (!this.started) return await this.start();\n\n const nextWorld = this.getNextWorld();\n if (nextWorld) {\n Logger.logp('Loading world', nextWorld.file.yellow);\n Logger.verbose('Next world configured from', nextWorld.source.yellow);\n this.loadWorld(nextWorld.file);\n } else {\n this.changeMap(this.currentMap);\n }\n\n const res = await Promise.race([\n // wait for the map to change\n new Promise(resolve =>\n this.once('mapchange', () => resolve('mapchange')),\n ),\n // Timeout after 10 seconds\n new Promise(resolve => setTimeout(() => resolve('timeout'), 10000)),\n ]);\n Logger.verbose('Restart result:', res);\n }\n\n /** attempt to restore the server's state */\n async restoreServer() {\n const tempPlayersFile = join(\n this.path,\n DATA_PATH,\n 'omegga_temp_players.json',\n );\n if (!existsSync(tempPlayersFile)) return;\n\n try {\n Logger.logp('Loading previous player positions...');\n\n // player positions are an array to address multi-clienting\n const players: { position: number[]; id: string }[] = JSON.parse(\n readFileSync(tempPlayersFile).toString(),\n );\n\n // restore player position on join\n const callback = (player: OmeggaPlayer) => {\n const index = players.findIndex(p => p.id === player.id);\n if (index > -1) {\n const { position } = players[index];\n this.writeln(\n `Chat.Command /TP \"${player.name}\" ${position.join(' ')} 0`,\n );\n\n // remove the entry\n players[index] = players[players.length - 1];\n players.pop();\n }\n };\n this.on('join', callback);\n\n let timeout = setTimeout(() => {\n try {\n this.off('join', callback);\n if (existsSync(tempPlayersFile)) unlinkSync(tempPlayersFile);\n } catch (err) {\n Logger.error('Error removing omegga_temp_players.json', err);\n }\n }, 10000);\n this.once('changemap', () => {\n clearTimeout(timeout);\n this.off('join', callback);\n });\n } catch (err) {\n Logger.error('Error restoring previous server state', err);\n }\n }\n\n /**\n * start webserver, load plugins, start the brickadia server\n * this should not be called by a plugin\n */\n //\n async start(): Promise<any> {\n this.starting = true;\n if (this.webserver) await this.webserver.start();\n if (this.pluginLoader) {\n // scan for plugins\n Logger.verbose('Scanning for plugins');\n await this.pluginLoader.scan();\n\n // load the plugins\n Logger.verbose('Loading plugins');\n await this.pluginLoader.reload();\n }\n\n Logger.verbose('Starting Brickadia');\n super.start();\n this.emit('server:starting');\n }\n\n /**\n * unload plugins and stop the server\n * this should not be called by a plugin\n */\n async stop() {\n if (!this.started && !this.starting) {\n Logger.verbose(\"Stop called while server wasn't started or was starting\");\n return;\n }\n\n if (this.stopping) {\n Logger.verbose('Stop called while server was starting');\n return;\n }\n\n this.stopping = true;\n this.emit('server:stopping');\n if (this.pluginLoader) {\n Logger.verbose('Unloading plugins');\n await this.pluginLoader.unload();\n }\n Logger.verbose('Stopping server');\n super.stop();\n\n const res = await Promise.race([\n new Promise(resolve => this.once('exit', () => resolve('exit'))),\n // Timeout after 10 seconds\n new Promise(resolve => setTimeout(() => resolve('timeout'), 10000)),\n ]);\n\n Logger.verbose('Stop result:', res);\n if (this.stopping) this.emit('server:stopped');\n this.stopping = false;\n this.started = false;\n this.starting = false;\n this.players = [];\n }\n\n /**\n * Copies auth files from home config dir\n * this should never be called by a plugin\n */\n copyAuthFiles() {\n const authDir = this.config.server.authDir ?? CONFIG_AUTH_DIR;\n const savedDir = this.config.server.savedDir ?? CONFIG_SAVED_DIR;\n const authPath = join(this.path, DATA_PATH, savedDir, authDir);\n const homeAuthPath = join(\n CONFIG_HOME,\n (savedDir !== CONFIG_SAVED_DIR ? savedDir : '') + authDir,\n );\n\n copyFiles(homeAuthPath, authPath, BRICKADIA_AUTH_FILES);\n }\n\n // TODO: split messages that longer than 512 characters\n // TODO: delete characters that are known to crash the game\n broadcast(...messages: string[]) {\n messages\n .flatMap(m => m.toString().split('\\n'))\n .filter(m => m.length < 512)\n .forEach(m => this.writeln(`Chat.Broadcast ${m}`));\n }\n\n whisper(target: string | OmeggaPlayer, ...messages: string[]) {\n // find the target player\n if (typeof target !== 'object') target = this.getPlayer(target);\n\n // whisper the messages to that player\n messages\n .flatMap(m => m.toString().split('\\n'))\n .filter(m => m.length < 512)\n .forEach(m =>\n this.writeln(\n `Chat.Whisper \"${(target as { name: string }).name}\" ${m}`,\n ),\n );\n }\n\n middlePrint(target: string | OmeggaPlayer, message: string) {\n // find the target player\n if (typeof target !== 'object') target = this.getPlayer(target);\n\n // whisper the messages to that player\n if (message.length > 512) return;\n this.writeln(\n `Chat.StatusMessage \"${(target as { name: string }).name}\" ${message}`,\n );\n }\n\n getPlayers(): {\n id: string;\n name: string;\n displayName: string;\n controller: string;\n state: string;\n }[] {\n return this.players.map(p => ({ ...p }));\n }\n\n getRoleSetup(): BRRoleSetup {\n // Read RoleSetup2, fallback to old RoleSetup if it doesn't exist\n return (readWatchedJSON(join(this.configPath, 'RoleSetup2.json')) ??\n readWatchedJSON(join(this.configPath, 'RoleSetup.json'))) as BRRoleSetup;\n }\n\n getRoleAssignments(): BRRoleAssignments {\n return readWatchedJSON(\n join(this.configPath, 'RoleAssignments.json'),\n ) as BRRoleAssignments;\n }\n\n getBanList(): BRBanList {\n return readWatchedJSON(join(this.configPath, 'BanList.json')) as BRBanList;\n }\n\n getNameCache(): BRPlayerNameCache {\n return readWatchedJSON(\n join(this.configPath, 'PlayerNameCache.json'),\n ) as BRPlayerNameCache;\n }\n\n getPlayer(target: string): OmeggaPlayer {\n return this.players.find(\n p =>\n p.name === target ||\n p.id === target ||\n p.controller === target ||\n p.state === target,\n );\n }\n\n findPlayerByName(name: string): OmeggaPlayer {\n name = name.toLowerCase();\n const exploded = pattern.explode(name);\n return (\n this.players.find(p => p.name === name || p.displayName === name) || // find by exact match\n this.players.find(\n p => p.name.indexOf(name) > -1 || p.displayName.indexOf(name) > -1,\n ) || // find by rough match\n this.players.find(\n p => p.name.match(exploded) || p.displayName.match(exploded),\n ) // find by exploded regex match (ck finds cake, tbp finds TheBlackParrot)\n );\n }\n\n getHostId(): string {\n return this.host?.id ?? '';\n }\n\n saveMinigame(index: number, name: string) {\n this.writeln(`Server.Minigames.SavePreset ${index} \"${name}\"`);\n }\n\n deleteMinigame(index: number) {\n this.writeln(`Server.Minigames.Delete ${index}`);\n }\n\n resetMinigame(index: number) {\n this.writeln(`Server.Minigames.Reset ${index}`);\n }\n\n nextRoundMinigame(index: number) {\n this.writeln(`Server.Minigames.NextRound ${index}`);\n }\n\n loadMinigame(presetName: string, owner = '') {\n this.writeln(\n `Server.Minigames.LoadPreset \"${presetName}\" ${owner ? `\"${owner}\"` : ''}`,\n );\n }\n\n getMinigamePresets(): string[] {\n const presetPath = join(this.presetPath, 'Minigame');\n return existsSync(presetPath)\n ? glob\n .sync(presetPath + '/**/*.bp')\n .map(f => basename(f).replace(/\\.bp$/, ''))\n : [];\n }\n\n resetEnvironment() {\n this.writeln(`Server.Environment.Reset`);\n }\n\n async saveEnvironment(presetName: string): Promise<void> {\n await this.addWatcher(/Environment preset saved.$/, {\n // request the pawn for this player's controller (should only be one)\n exec: () => this.writeln(`Server.Environment.SavePreset \"${presetName}\"`),\n timeoutDelay: 100,\n });\n }\n\n async getEnvironmentData(): Promise<EnvironmentPreset> {\n const saveName =\n this._tempSavePrefix + Date.now() + '_' + this._tempCounter.environment++;\n\n await this.saveEnvironment(saveName);\n const data = this.readEnvironmentData(saveName);\n const file = join(this.presetPath, 'Environment', saveName + '.bp');\n if (existsSync(file)) unlinkSync(file);\n\n return data;\n }\n\n readEnvironmentData(saveName: string): EnvironmentPreset {\n if (typeof saveName !== 'string')\n throw 'expected name argument for readEnvironmentData';\n\n const file = join(this.presetPath, 'Environment', saveName + '.bp');\n try {\n if (existsSync(file)) return JSON.parse(readFileSync(file).toString());\n } catch (err) {\n Logger.verbose('Error parsing save data in readEnvironmentData', err);\n }\n return null;\n }\n\n loadEnvironment(presetName: string) {\n this.writeln(`Server.Environment.LoadPreset ${presetName}`);\n }\n\n loadEnvironmentData(\n preset: EnvironmentPreset | EnvironmentPreset['data']['groups'],\n ) {\n if ('data' in preset) preset = preset.data.groups;\n\n const saveFile =\n this._tempSavePrefix + Date.now() + '_' + this._tempCounter.environment++;\n\n const path = join(this.presetPath, 'Environment', saveFile + '.bp');\n\n writeFileSync(\n path,\n JSON.stringify({\n formatVersion: '1',\n presetVersion: '1',\n type: 'Environment',\n data: {\n groups: {\n ...preset,\n },\n },\n }),\n );\n\n this.loadEnvironment(saveFile);\n\n // this is lazy, but environments should load much faster than builds\n // do, so it's not really worth keeping track of logs for this\n setTimeout(() => unlinkSync(path), 5000);\n }\n\n getEnvironmentPresets(): string[] {\n const presetPath = join(this.presetPath, 'Environment');\n return existsSync(presetPath)\n ? glob\n .sync(presetPath + '/**/*.bp')\n .map(f => basename(f).replace(/\\.bp$/, ''))\n : [];\n }\n\n clearBricks(target: string | { id: string }, quiet = false) {\n // target is a player object, just use that id\n if (typeof target === 'object' && target.id) target = target.id;\n // if the target isn't a uuid already, find the player by name or controller and use that uuid\n else if (typeof target === 'string' && !uuid.match(target)) {\n // only set the target if the player exists\n const player = this.getPlayer(target);\n target = player && player.id;\n }\n\n if (!target) return;\n\n this.writeln(`Bricks.Clear ${target} ${quiet ? 1 : ''}`);\n }\n\n clearRegion(\n region: {\n center: [number, number, number];\n extent: [number, number, number];\n },\n options?: { target: string | OmeggaPlayer },\n ) {\n let target = '';\n\n // target is a player object, just use that id\n if (options && typeof options.target === 'object')\n target = ' ' + options.target.id;\n // if the target isn't a uuid already, find the player by name or controller and use that uuid\n\n if (typeof target === 'string' && !uuid.match(target)) {\n // only set the target if the player exists\n const player = this.getPlayer(target);\n if (player) target = ' ' + player.id;\n }\n\n this.writeln(\n `Bricks.ClearRegion ${region.center.join(' ')} ${region.extent.join(\n ' ',\n )}${target}`,\n );\n }\n\n clearAllBricks(quiet = false) {\n this.writeln(`Bricks.ClearAll ${quiet ? 1 : ''}`);\n }\n\n saveBricks(\n saveName: string,\n region?: {\n center: [number, number, number];\n extent: [number, number, number];\n },\n ) {\n if (!saveName) return;\n\n // add quotes around the filename if it doesn't have them (backwards compat w/ plugins)\n if (!(saveName.startsWith('\"') && saveName.endsWith('\"')))\n saveName = `\"${saveName}\"`;\n\n if (region?.center && region?.extent)\n this.writeln(\n `Bricks.SaveRegion ${saveName} ${region.center.join(\n ' ',\n )} ${region.extent.join(' ')}`,\n );\n else this.writeln(`Bricks.Save ${saveName}`);\n }\n\n async saveBricksAsync(\n saveName: string,\n region?: {\n center: [number, number, number];\n extent: [number, number, number];\n },\n ): Promise<void> {\n if (!saveName) return;\n\n let saveNameClean = saveName;\n // add quotes around the filename if it doesn't have them (backwards compat w/ plugins)\n if (!(saveName.startsWith('\"') && saveName.endsWith('\"')))\n saveNameClean = `\"${saveName}\"`;\n\n const command =\n region?.center && region?.extent\n ? `Bricks.SaveRegion ${saveNameClean} ${region.center.join(\n ' ',\n )} ${region.extent.join(' ')}`\n : `Bricks.Save ${saveNameClean}`;\n\n // wait for the server to save the file\n await this.watchLogChunk(command, /^(LogBrickSerializer|LogTemp): (.+)$/, {\n first: match => match[0].endsWith(saveName + '.brs...'),\n last: match =>\n Boolean(\n match[2].match(\n /Saved .+ bricks and .+ components from .+ owners|Error: No bricks in grid!|Error: No bricks selected to save!/,\n ),\n ),\n afterMatchDelay: 0,\n timeoutDelay: 30000,\n });\n }\n\n loadBricks(\n saveName: string,\n {\n offX = 0,\n offY = 0,\n offZ = 0,\n quiet = false,\n correctPalette = false,\n correctCustom = false,\n } = {},\n ) {\n // add quotes around the filename if it doesn't have them (backwards compat w/ plugins)\n if (!(saveName.startsWith('\"') && saveName.endsWith('\"')))\n saveName = `\"${saveName}\"`;\n\n this.writeln(\n `Bricks.Load ${saveName} ${offX} ${offY} ${offZ} ${quiet ? 1 : 0} ${\n correctPalette ? 1 : 0\n } ${correctCustom ? 1 : 0}`,\n );\n }\n\n loadBricksOnPlayer(\n saveName: string,\n player: string | OmeggaPlayer,\n {\n offX = 0,\n offY = 0,\n offZ = 0,\n correctPalette = false,\n correctCustom = false,\n } = {},\n ) {\n player = typeof player === 'string' ? this.getPlayer(player) : player;\n if (!player) return;\n\n // add quotes around the filename if it doesn't have them (backwards compat w/ plugins)\n if (!(saveName.startsWith('\"') && saveName.endsWith('\"')))\n saveName = `\"${saveName}\"`;\n\n this.writeln(\n `Bricks.LoadTemplate ${saveName} ${offX} ${offY} ${offZ} ${\n correctPalette ? 1 : 0\n } ${correctCustom ? 1 : 0} \"${player.name}\"`,\n );\n }\n\n getSaves(): string[] {\n return existsSync(this.savePath)\n ? glob.sync(this.savePath + '/**/*.brs')\n : [];\n }\n\n getWorlds(): string[] {\n return existsSync(this.worldPath)\n ? glob.sync(this.worldPath + '/**/*.brdb')\n : [];\n }\n\n getSavePath(saveName: string) {\n const file = join(\n this.savePath,\n saveName.endsWith('.brs') ? saveName : saveName + '.brs',\n );\n return existsSync(file) ? file : undefined;\n }\n\n getWorldPath(worldName: string) {\n const file = join(\n this.worldPath,\n worldName.endsWith('.brdb') ? worldName : worldName + '.brdb',\n );\n return existsSync(file) ? file : undefined;\n }\n\n async getWorldRevisions(worldName: string) {\n if (!this.started || this.starting || this.stopping) {\n throw new Error('Server is not started');\n }\n\n worldName = worldName.replace(/\\.brdb$/i, '');\n\n if (!worldName || !this.getWorldPath(worldName)) {\n throw new Error(`World \"${worldName}\" does not exist`);\n }\n\n /*\n LogBRBundleManager: There are 2 revisions\n LogBRBundleManager: Revision 1 - 2001.02.03-04.05.06: Initial Revision\n LogBRBundleManager: Revision 2 - 2001.02.03-04.05.06: Manual Save\n */\n let numRevisions = 0;\n const revisionsRaw = await this.watchLogChunk<RegExpMatchArray>(\n `BR.World.ListRevisions \"${worldName}\"`,\n /^LogBRBundleManager: (There are (?<numRevisions>\\d+) revisions|Revision (?<revision>\\d+) - (?<date>[\\d.-]+): (?<note>.+))$/,\n {\n last: match => Number(match.groups.reverse) === numRevisions,\n first: match => {\n if (match.groups?.numRevisions !== undefined) {\n numRevisions = Number(match.groups.numRevisions);\n return true;\n }\n return false;\n },\n },\n );\n\n if (!revisionsRaw) return [];\n return revisionsRaw\n .map(match => {\n if (match.groups?.numRevisions !== undefined) {\n return null;\n }\n\n const revision = match.groups.revision\n ? Number(match.groups.revision)\n : 0;\n // parse date from YYYY.MM.DD-HH.MM.SS to YYYY-MM-DDTHH:MM:SSZ\n const dateStr = match.groups.date.replace(\n /(\\d{4})\\.(\\d{2})\\.(\\d{2})-(\\d{2})\\.(\\d{2})\\.(\\d{2})/,\n '$1-$2-$3T$4:$5:$6Z',\n );\n const date = new Date(dateStr);\n const note = match.groups.note || '';\n return { index: revision, date, note };\n })\n .filter(Boolean);\n }\n\n async loadWorld(worldName: string): Promise<boolean> {\n worldName = worldName.replace(/\\.brdb$/i, '');\n if (!worldName || !this.getWorldPath(worldName)) return false;\n this.writeln(`BR.World.Load \"${worldName}\"`);\n const res = await Promise.race([\n // wait for the map to change\n new Promise(resolve =>\n this.once('mapchange', () => resolve('mapchange')),\n ),\n // Timeout after 10 seconds\n new Promise(resolve => setTimeout(() => resolve('timeout'), 10000)),\n ]);\n Logger.verbose('LoadWorld', worldName, 'result:', res);\n return res === 'mapchange';\n }\n\n async loadWorldRevision(\n worldName: string,\n revision: number,\n ): Promise<boolean> {\n worldName = worldName.replace(/\\.brdb$/i, '');\n if (!worldName || !this.getWorldPath(worldName)) return false;\n if (typeof revision !== 'number' || revision < 1) {\n throw new Error(`Invalid revision number: ${revision}`);\n }\n this.writeln(`BR.World.LoadRevision \"${worldName}\" ${revision}`);\n const res = await Promise.race([\n // wait for the map to change\n new Promise(resolve =>\n this.once('mapchange', () => resolve('mapchange')),\n ),\n // Timeout after 10 seconds\n new Promise(resolve => setTimeout(() => resolve('timeout'), 10000)),\n ]);\n Logger.verbose('LoadWorld', worldName, 'result:', res);\n return res === 'mapchange';\n }\n\n async saveWorldAs(worldName: string) {\n if (!worldName) return false;\n if (this.stopping || this.starting || !this.started) return false;\n\n if (this.getWorldPath(worldName)) {\n return false;\n }\n worldName = worldName.replace(/\\.brdb$/i, '');\n\n try {\n const match = await this.addWatcher<{ res: boolean }>(\n (_line, match) => {\n if (match?.groups?.generator !== 'LogBRWorldManager') return;\n\n const ok = match.groups.data.match(/^World files saved after /);\n const err =\n !match.groups.data.startsWith(\n 'Error: Failed to capture minigame settings',\n ) &&\n match.groups.data.match(\n /^Error: (World already exists|Failed to create new world)?/,\n );\n return ok ? { res: true } : err ? { res: false } : undefined;\n },\n {\n exec: () => {\n this.writeln(`BR.World.SaveAs \"${worldName}\"`);\n },\n timeoutDelay: 2000,\n },\n );\n return match?.[0]?.['res'] ?? false;\n } catch (err) {\n return false;\n }\n }\n\n async saveWorld(): Promise<boolean> {\n // Don't allow saving while the server is starting or stopping\n if (this.stopping || this.starting || !this.started) return false;\n\n try {\n const match = await this.addWatcher<{ res: boolean }>(\n (_line, match) => {\n if (match?.groups?.generator !== 'LogBRWorldManager') return;\n\n const ok = match.groups.data.match(/^World files saved after /);\n const err =\n !match.groups.data.startsWith(\n 'Error: Failed to capture minigame settings',\n ) &&\n match.groups.data.match(/^Error: (World has not been saved\\.)?/);\n return ok ? { res: true } : err ? { res: false } : undefined;\n },\n {\n exec: () => {\n this.writeln(`BR.World.Save 0`);\n },\n timeoutDelay: 2000,\n },\n );\n return match?.[0]?.['res'] ?? false;\n } catch (err) {\n return false;\n }\n }\n\n async createEmptyWorld(\n worldName: string,\n map: 'Plate' | 'Space' | 'Studio' | 'Peaks' = 'Plate',\n ): Promise<boolean> {\n if (!worldName) return;\n worldName = worldName.replace(/\\.brdb$/i, '');\n\n try {\n const match = await this.addWatcher<{ res: boolean }>(\n (_line, match) => {\n if (match?.groups?.generator !== 'LogBRWorldManager') return;\n\n const ok = match.groups.data.match(/^World files saved after /);\n const err = match.groups.data.match(\n /^Error: (Invalid preset|World already exists|Failed to create new world)?/,\n );\n return ok ? { res: true } : err ? { res: false } : undefined;\n },\n {\n exec: () => {\n this.writeln(`BR.World.CreateEmpty \"${worldName}\" ${map}`);\n },\n timeoutDelay: 2000,\n },\n );\n return match?.[0]?.['res'] ?? false;\n } catch (err) {\n return false;\n }\n }\n\n writeSaveData(saveName: string, saveData: WriteSaveObject) {\n if (typeof saveName !== 'string')\n throw 'expected name argument for writeSaveData';\n\n const file = join(this.savePath, saveName + '.brs');\n if (!file.startsWith(this.savePath))\n throw 'save file not in Saved/Builds directory';\n writeFileSync(file, new Uint8Array(brs.write(saveData)));\n }\n\n readSaveData(saveName: string, nobricks = false): ReadSaveObject {\n if (typeof saveName !== 'string')\n throw 'expected name argument for readSaveData';\n\n const file = this.getSavePath(saveName);\n if (!file || !file.startsWith(this.savePath))\n throw 'save file not in Saved/Builds directory';\n if (file)\n return brs.read(readFileSync(file), {\n preview: false,\n bricks: !nobricks,\n });\n }\n\n async loadSaveData(\n saveData: WriteSaveObject,\n {\n offX = 0,\n offY = 0,\n offZ = 0,\n quiet = false,\n correctPalette = false,\n correctCustom = false,\n } = {},\n ) {\n const saveFile =\n this._tempSavePrefix + Date.now() + '_' + this._tempCounter.save++;\n // write savedata to file\n this.writeSaveData(saveFile, saveData);\n\n // wait for the server to finish reading the save\n await this.watchLogChunk(\n `Bricks.Load \"${saveFile}\" ${offX} ${offY} ${offZ} ${quiet ? 1 : 0} ${\n correctPalette ? 1 : 0\n } ${correctCustom ? 1 : 0}`,\n /^LogBrickSerializer: (.+)$/,\n {\n first: match => match[0].endsWith(saveFile + '.brs...'),\n last: match => Boolean(match[1].match(/Read .+ bricks/)),\n afterMatchDelay: 0,\n timeoutDelay: 30000,\n },\n );\n\n // delete the save file after we're done\n const savePath = this.getSavePath(saveFile);\n if (savePath) {\n unlinkSync(savePath);\n }\n }\n\n async loadSaveDataOnPlayer(\n saveData: WriteSaveObject,\n player: string | OmeggaPlayer,\n {\n offX = 0,\n offY = 0,\n offZ = 0,\n correctPalette = false,\n correctCustom = false,\n } = {},\n ) {\n player = typeof player === 'string' ? this.getPlayer(player) : player;\n if (!player) return;\n\n const saveFile =\n this._tempSavePrefix + Date.now() + '_' + this._tempCounter.save++;\n // write savedata to file\n this.writeSaveData(saveFile, saveData);\n\n // wait for the server to finish reading the save\n await this.watchLogChunk(\n `Bricks.LoadTemplate \"${saveFile}\" ${offX} ${offY} ${offZ} ${\n correctPalette ? 1 : 0\n } ${correctCustom ? 1 : 0} \"${player.name}\"`,\n /^LogBrickSerializer: (.+)$/,\n {\n first: match => match[0].endsWith(saveFile + '.brs...'),\n last: match => Boolean(match[1].match(/Read .+ bricks/)),\n afterMatchDelay: 0,\n timeoutDelay: 30000,\n },\n );\n\n // delete the save file after we're done\n const savePath = this.getSavePath(saveFile);\n if (savePath) {\n unlinkSync(savePath);\n }\n }\n\n async getSaveData(region?: {\n center: [number, number, number];\n extent: [number, number, number];\n }) {\n const saveFile =\n this._tempSavePrefix + Date.now() + '_' + this._tempCounter.save++;\n\n await this.saveBricksAsync(saveFile, region);\n\n // read the save file\n const savePath = this.getSavePath(saveFile);\n if (savePath) {\n // read and parse the save file\n const saveData = brs.read(readFileSync(savePath));\n\n // delete the save file after we're done reading it\n unlinkSync(savePath);\n\n // return the parsed save\n return saveData;\n }\n\n return undefined;\n }\n\n // TODO: switch this to use worlds...\n async changeMap(map: string) {\n if (!map) return;\n\n // ServerTravel requires /Game/Maps/Plate/Plate instead of Plate\n const brName = mapUtils.n2brn(map);\n\n // wait for the server to change maps\n const match = await this.addWatcher(\n /^.*(LogLoad: Took .+ seconds to LoadMap\\((?<map>.+)\\))|(ERROR: The map .+)$/,\n {\n timeoutDelay: 30000,\n exec: () => this.writeln(`ServerTravel ${brName}`),\n },\n );\n const success = !!(\n match &&\n match[0] &&\n match[0].groups &&\n match[0].groups.map\n );\n return success;\n }\n\n async getPlugin(name: string): Promise<PluginInterop> {\n const plugin = this.pluginLoader.plugins.find(p => p.getName() === name);\n\n if (plugin) {\n return {\n name,\n documentation: plugin.getDocumentation(),\n loaded: plugin.isLoaded(),\n emitPlugin: (event: string, ...args: any[]) => {\n return plugin.emitPlugin(event, 'unsafe', args);\n },\n };\n } else {\n return null;\n }\n }\n}\n"],"names":["OmeggaWrapper","Logger","VERSION","commandInjector","CONFIG_SAVED_DIR","join","DATA_PATH","mkdir","Webserver","PluginLoader","MATCHERS","pattern","default_commands","writeFileSync","existsSync","readFileSync","index","unlinkSync","CONFIG_AUTH_DIR","CONFIG_HOME","copyFiles","BRICKADIA_AUTH_FILES","readWatchedJSON","basename","file","path","uuid","match","mapUtils","plugin"],"mappings":"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"}
|
package/dist/omegga/wrapper.js
CHANGED
|
@@ -18,6 +18,7 @@ class OmeggaWrapper extends EventEmitter {
|
|
|
18
18
|
this.logWrangler = new logWrangler.default(this);
|
|
19
19
|
this.#server.on("line", this.logWrangler.callback);
|
|
20
20
|
this.#server.on("line", (line) => this.emit("line", line));
|
|
21
|
+
this.#server.on("err", (line) => this.emit("err", line));
|
|
21
22
|
this.#server.on("closed", () => this.emit("closed"));
|
|
22
23
|
this.addMatcher = this.logWrangler.addMatcher;
|
|
23
24
|
this.addWatcher = this.logWrangler.addWatcher;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wrapper.js","sources":["../../src/omegga/wrapper.ts"],"sourcesContent":["/*\n The wrapper combines the things looking at or waiting for logs with the actual server logs\n*/\n\nimport Logger from '@/logger';\nimport soft from '@/softconfig';\nimport BrickadiaServer from '@brickadia/server';\nimport { IConfig } from '@config/types';\nimport EventEmitter from 'events';\nimport path from 'path';\nimport LogWrangler from './logWrangler';\nimport type Omegga from './server';\n\nclass OmeggaWrapper extends EventEmitter {\n #server: BrickadiaServer;\n dataPath: string;\n path: string;\n\n logWrangler: LogWrangler;\n addMatcher: LogWrangler['addMatcher'];\n addWatcher: LogWrangler['addWatcher'];\n watchLogArray: LogWrangler['watchLogArray'];\n watchLogChunk: LogWrangler['watchLogChunk'];\n\n config: IConfig;\n\n constructor(serverPath: string, cfg: IConfig) {\n super();\n this.setMaxListeners(Infinity);\n\n this.config = cfg;\n this.path =\n path.isAbsolute(serverPath) || serverPath.startsWith('/')\n ? serverPath\n : path.join(process.cwd(), serverPath);\n this.dataPath = path.join(this.path, soft.DATA_PATH);\n this.#server = new BrickadiaServer(this.dataPath, cfg);\n\n // log wrangler wrangles logs... it reads brickadia logs and clumps them together\n // this is cursed but the OmeggaWrapper will never be used without omegga...\n this.logWrangler = new LogWrangler(this as unknown as Omegga);\n this.#server.on('line', this.logWrangler.callback);\n this.#server.on('line', (line: string) => this.emit('line', line));\n this.#server.on('closed', () => this.emit('closed'));\n\n this.addMatcher = this.logWrangler.addMatcher;\n this.addWatcher = this.logWrangler.addWatcher;\n this.watchLogArray = this.logWrangler.watchLogArray;\n this.watchLogChunk = this.logWrangler.watchLogChunk;\n }\n\n // passthrough to server\n write(str: string) {\n this.#server.write(str);\n }\n writeln(str: string) {\n this.#server.writeln(str);\n }\n start() {\n return this.#server.start();\n }\n stop() {\n return this.#server.stop();\n }\n\n // event emitter to catch everything\n emit(type: string, ...args: any) {\n if (type !== 'line') Logger.verbose('Emitting event', type);\n try {\n (super.emit as EventEmitter['emit'])('*', type, ...args);\n } catch (e) {\n Logger.errorp('Error in emitted event', type, e);\n // error emitting\n }\n return (super.emit as EventEmitter['emit'])(type, ...args);\n }\n\n /** Get which world will be loaded on startup (form file, config, or env) */\n getNextWorld() {\n return this.#server.getNextWorld();\n }\n\n /** Get the configured default world */\n getActiveWorld() {\n return this.#server.getActiveWorld();\n }\n\n /** Configure the default world */\n setActiveWorld(world: string | null): boolean {\n return this.#server.setActiveWorld(world);\n }\n\n /** Check if a world exists */\n worldExists(file: string) {\n return this.#server.worldExists(file);\n }\n}\n\nexport default OmeggaWrapper;\n"],"names":["soft","BrickadiaServer","LogWrangler","Logger"],"mappings":";;;;;;;;AAaA,MAAM,sBAAsB,aAAa;AAAA,EACvC;AAAA,EAYA,YAAY,YAAoB,KAAc;AAC5C,UAAA;AACA,SAAK,gBAAgB,QAAQ;AAE7B,SAAK,SAAS;AACd,SAAK,OACH,KAAK,WAAW,UAAU,KAAK,WAAW,WAAW,GAAG,IACpD,aACA,KAAK,KAAK,QAAQ,IAAA,GAAO,UAAU;AACzC,SAAK,WAAW,KAAK,KAAK,KAAK,MAAMA,WAAAA,QAAK,SAAS;AACnD,SAAK,UAAU,IAAIC,OAAAA,QAAgB,KAAK,UAAU,GAAG;AAIrD,SAAK,cAAc,IAAIC,YAAAA,QAAY,IAAyB;AAC5D,SAAK,QAAQ,GAAG,QAAQ,KAAK,YAAY,QAAQ;AACjD,SAAK,QAAQ,GAAG,QAAQ,CAAC,SAAiB,KAAK,KAAK,QAAQ,IAAI,CAAC;AACjE,SAAK,QAAQ,GAAG,UAAU,MAAM,KAAK,KAAK,QAAQ,CAAC;AAEnD,SAAK,aAAa,KAAK,YAAY;AACnC,SAAK,aAAa,KAAK,YAAY;AACnC,SAAK,gBAAgB,KAAK,YAAY;AACtC,SAAK,gBAAgB,KAAK,YAAY;AAAA,EACxC;AAAA;AAAA,EAGA,MAAM,KAAa;AACjB,SAAK,QAAQ,MAAM,GAAG;AAAA,EACxB;AAAA,EACA,QAAQ,KAAa;AACnB,SAAK,QAAQ,QAAQ,GAAG;AAAA,EAC1B;AAAA,EACA,QAAQ;AACN,WAAO,KAAK,QAAQ,MAAA;AAAA,EACtB;AAAA,EACA,OAAO;AACL,WAAO,KAAK,QAAQ,KAAA;AAAA,EACtB;AAAA;AAAA,EAGA,KAAK,SAAiB,MAAW;AAC/B,QAAI,SAAS,OAAQC,QAAAA,QAAO,QAAQ,kBAAkB,IAAI;AAC1D,QAAI;AACD,YAAM,KAA8B,KAAK,MAAM,GAAG,IAAI;AAAA,IACzD,SAAS,GAAG;AACVA,aAAAA,QAAO,OAAO,0BAA0B,MAAM,CAAC;AAAA,IAEjD;AACA,WAAQ,MAAM,KAA8B,MAAM,GAAG,IAAI;AAAA,EAC3D;AAAA;AAAA,EAGA,eAAe;AACb,WAAO,KAAK,QAAQ,aAAA;AAAA,EACtB;AAAA;AAAA,EAGA,iBAAiB;AACf,WAAO,KAAK,QAAQ,eAAA;AAAA,EACtB;AAAA;AAAA,EAGA,eAAe,OAA+B;AAC5C,WAAO,KAAK,QAAQ,eAAe,KAAK;AAAA,EAC1C;AAAA;AAAA,EAGA,YAAY,MAAc;AACxB,WAAO,KAAK,QAAQ,YAAY,IAAI;AAAA,EACtC;AACF;;"}
|
|
1
|
+
{"version":3,"file":"wrapper.js","sources":["../../src/omegga/wrapper.ts"],"sourcesContent":["/*\n The wrapper combines the things looking at or waiting for logs with the actual server logs\n*/\n\nimport Logger from '@/logger';\nimport soft from '@/softconfig';\nimport BrickadiaServer from '@brickadia/server';\nimport { IConfig } from '@config/types';\nimport EventEmitter from 'events';\nimport path from 'path';\nimport LogWrangler from './logWrangler';\nimport type Omegga from './server';\n\nclass OmeggaWrapper extends EventEmitter {\n #server: BrickadiaServer;\n dataPath: string;\n path: string;\n\n logWrangler: LogWrangler;\n addMatcher: LogWrangler['addMatcher'];\n addWatcher: LogWrangler['addWatcher'];\n watchLogArray: LogWrangler['watchLogArray'];\n watchLogChunk: LogWrangler['watchLogChunk'];\n\n config: IConfig;\n\n constructor(serverPath: string, cfg: IConfig) {\n super();\n this.setMaxListeners(Infinity);\n\n this.config = cfg;\n this.path =\n path.isAbsolute(serverPath) || serverPath.startsWith('/')\n ? serverPath\n : path.join(process.cwd(), serverPath);\n this.dataPath = path.join(this.path, soft.DATA_PATH);\n this.#server = new BrickadiaServer(this.dataPath, cfg);\n\n // log wrangler wrangles logs... it reads brickadia logs and clumps them together\n // this is cursed but the OmeggaWrapper will never be used without omegga...\n this.logWrangler = new LogWrangler(this as unknown as Omegga);\n this.#server.on('line', this.logWrangler.callback);\n this.#server.on('line', (line: string) => this.emit('line', line));\n this.#server.on('err', (line: string) => this.emit('err', line));\n this.#server.on('closed', () => this.emit('closed'));\n\n this.addMatcher = this.logWrangler.addMatcher;\n this.addWatcher = this.logWrangler.addWatcher;\n this.watchLogArray = this.logWrangler.watchLogArray;\n this.watchLogChunk = this.logWrangler.watchLogChunk;\n }\n\n // passthrough to server\n write(str: string) {\n this.#server.write(str);\n }\n writeln(str: string) {\n this.#server.writeln(str);\n }\n start() {\n return this.#server.start();\n }\n stop() {\n return this.#server.stop();\n }\n\n // event emitter to catch everything\n emit(type: string, ...args: any) {\n if (type !== 'line') Logger.verbose('Emitting event', type);\n try {\n (super.emit as EventEmitter['emit'])('*', type, ...args);\n } catch (e) {\n Logger.errorp('Error in emitted event', type, e);\n // error emitting\n }\n return (super.emit as EventEmitter['emit'])(type, ...args);\n }\n\n /** Get which world will be loaded on startup (form file, config, or env) */\n getNextWorld() {\n return this.#server.getNextWorld();\n }\n\n /** Get the configured default world */\n getActiveWorld() {\n return this.#server.getActiveWorld();\n }\n\n /** Configure the default world */\n setActiveWorld(world: string | null): boolean {\n return this.#server.setActiveWorld(world);\n }\n\n /** Check if a world exists */\n worldExists(file: string) {\n return this.#server.worldExists(file);\n }\n}\n\nexport default OmeggaWrapper;\n"],"names":["soft","BrickadiaServer","LogWrangler","Logger"],"mappings":";;;;;;;;AAaA,MAAM,sBAAsB,aAAa;AAAA,EACvC;AAAA,EAYA,YAAY,YAAoB,KAAc;AAC5C,UAAA;AACA,SAAK,gBAAgB,QAAQ;AAE7B,SAAK,SAAS;AACd,SAAK,OACH,KAAK,WAAW,UAAU,KAAK,WAAW,WAAW,GAAG,IACpD,aACA,KAAK,KAAK,QAAQ,IAAA,GAAO,UAAU;AACzC,SAAK,WAAW,KAAK,KAAK,KAAK,MAAMA,WAAAA,QAAK,SAAS;AACnD,SAAK,UAAU,IAAIC,OAAAA,QAAgB,KAAK,UAAU,GAAG;AAIrD,SAAK,cAAc,IAAIC,YAAAA,QAAY,IAAyB;AAC5D,SAAK,QAAQ,GAAG,QAAQ,KAAK,YAAY,QAAQ;AACjD,SAAK,QAAQ,GAAG,QAAQ,CAAC,SAAiB,KAAK,KAAK,QAAQ,IAAI,CAAC;AACjE,SAAK,QAAQ,GAAG,OAAO,CAAC,SAAiB,KAAK,KAAK,OAAO,IAAI,CAAC;AAC/D,SAAK,QAAQ,GAAG,UAAU,MAAM,KAAK,KAAK,QAAQ,CAAC;AAEnD,SAAK,aAAa,KAAK,YAAY;AACnC,SAAK,aAAa,KAAK,YAAY;AACnC,SAAK,gBAAgB,KAAK,YAAY;AACtC,SAAK,gBAAgB,KAAK,YAAY;AAAA,EACxC;AAAA;AAAA,EAGA,MAAM,KAAa;AACjB,SAAK,QAAQ,MAAM,GAAG;AAAA,EACxB;AAAA,EACA,QAAQ,KAAa;AACnB,SAAK,QAAQ,QAAQ,GAAG;AAAA,EAC1B;AAAA,EACA,QAAQ;AACN,WAAO,KAAK,QAAQ,MAAA;AAAA,EACtB;AAAA,EACA,OAAO;AACL,WAAO,KAAK,QAAQ,KAAA;AAAA,EACtB;AAAA;AAAA,EAGA,KAAK,SAAiB,MAAW;AAC/B,QAAI,SAAS,OAAQC,QAAAA,QAAO,QAAQ,kBAAkB,IAAI;AAC1D,QAAI;AACD,YAAM,KAA8B,KAAK,MAAM,GAAG,IAAI;AAAA,IACzD,SAAS,GAAG;AACVA,aAAAA,QAAO,OAAO,0BAA0B,MAAM,CAAC;AAAA,IAEjD;AACA,WAAQ,MAAM,KAA8B,MAAM,GAAG,IAAI;AAAA,EAC3D;AAAA;AAAA,EAGA,eAAe;AACb,WAAO,KAAK,QAAQ,aAAA;AAAA,EACtB;AAAA;AAAA,EAGA,iBAAiB;AACf,WAAO,KAAK,QAAQ,eAAA;AAAA,EACtB;AAAA;AAAA,EAGA,eAAe,OAA+B;AAC5C,WAAO,KAAK,QAAQ,eAAe,KAAK;AAAA,EAC1C;AAAA;AAAA,EAGA,YAAY,MAAc;AACxB,WAAO,KAAK,QAAQ,YAAY,IAAI;AAAA,EACtC;AACF;;"}
|
package/dist/plugin.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
Object.
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const index = require("./util/index.js");
|
|
4
4
|
const _OMEGGA_UTILS_IMPORT = index.default;
|
|
5
5
|
class OmeggaPlugin {
|
|
@@ -11,5 +11,4 @@ class OmeggaPlugin {
|
|
|
11
11
|
}
|
|
12
12
|
exports.OmeggaPlugin = OmeggaPlugin;
|
|
13
13
|
exports._OMEGGA_UTILS_IMPORT = _OMEGGA_UTILS_IMPORT;
|
|
14
|
-
exports.default = OmeggaPlugin;
|
|
15
14
|
//# sourceMappingURL=plugin.js.map
|