omegga 1.2.8 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -1
- package/dist/brickadia/config.js +26 -90
- package/dist/brickadia/config.js.map +1 -1
- package/dist/brickadia/server.js +294 -293
- package/dist/brickadia/server.js.map +1 -1
- package/dist/cli/auth.js +149 -177
- package/dist/cli/auth.js.map +1 -1
- package/dist/cli/config.js +64 -66
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/index.js +14 -14
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/installer.js +37 -43
- package/dist/cli/installer.js.map +1 -1
- package/dist/cli/plugin.js +651 -682
- package/dist/cli/plugin.js.map +1 -1
- package/dist/cli/terminal.js +811 -733
- package/dist/cli/terminal.js.map +1 -1
- package/dist/config/formats/format_js.js +6 -6
- package/dist/config/formats/format_js.js.map +1 -1
- package/dist/config/formats/format_yml.js +7 -10
- package/dist/config/formats/format_yml.js.map +1 -1
- package/dist/config/index.js +46 -68
- package/dist/config/index.js.map +1 -1
- package/dist/config/reader.js +16 -27
- package/dist/config/reader.js.map +1 -1
- package/dist/config/validator.js +23 -25
- package/dist/config/validator.js.map +1 -1
- package/dist/config/writer.js +11 -21
- package/dist/config/writer.js.map +1 -1
- package/dist/index.js +18 -64
- package/dist/index.js.map +1 -1
- package/dist/info/default_commands.json.js +39 -0
- package/dist/info/default_commands.json.js.map +1 -0
- package/dist/logger.js +61 -60
- package/dist/logger.js.map +1 -1
- package/dist/main.js +424 -471
- package/dist/main.js.map +1 -1
- package/dist/omegga/auth.js +101 -148
- package/dist/omegga/auth.js.map +1 -1
- package/dist/omegga/commandInjector.js +261 -251
- package/dist/omegga/commandInjector.js.map +1 -1
- package/dist/omegga/index.js +14 -52
- package/dist/omegga/index.js.map +1 -1
- package/dist/omegga/logWrangler.js +224 -275
- package/dist/omegga/logWrangler.js.map +1 -1
- package/dist/omegga/matchers/auth.js +36 -50
- package/dist/omegga/matchers/auth.js.map +1 -1
- package/dist/omegga/matchers/chat.js +49 -71
- package/dist/omegga/matchers/chat.js.map +1 -1
- package/dist/omegga/matchers/command.js +25 -36
- package/dist/omegga/matchers/command.js.map +1 -1
- package/dist/omegga/matchers/exit.js +15 -18
- package/dist/omegga/matchers/exit.js.map +1 -1
- package/dist/omegga/matchers/index.js +26 -40
- package/dist/omegga/matchers/index.js.map +1 -1
- package/dist/omegga/matchers/init.js +20 -23
- package/dist/omegga/matchers/init.js.map +1 -1
- package/dist/omegga/matchers/interact.js +58 -64
- package/dist/omegga/matchers/interact.js.map +1 -1
- package/dist/omegga/matchers/join.js +74 -95
- package/dist/omegga/matchers/join.js.map +1 -1
- package/dist/omegga/matchers/leave.js +43 -56
- package/dist/omegga/matchers/leave.js.map +1 -1
- package/dist/omegga/matchers/mapChange.js +26 -35
- package/dist/omegga/matchers/mapChange.js.map +1 -1
- package/dist/omegga/matchers/minigameJoin.js +24 -31
- package/dist/omegga/matchers/minigameJoin.js.map +1 -1
- package/dist/omegga/matchers/version.js +42 -49
- package/dist/omegga/matchers/version.js.map +1 -1
- package/dist/omegga/player.js +527 -527
- package/dist/omegga/player.js.map +1 -1
- package/dist/omegga/plugin/interface.js +91 -98
- package/dist/omegga/plugin/interface.js.map +1 -1
- package/dist/omegga/plugin/plugin_jsonrpc_stdio.js +524 -429
- package/dist/omegga/plugin/plugin_jsonrpc_stdio.js.map +1 -1
- package/dist/omegga/plugin/plugin_node_safe/proxyOmegga.js +282 -294
- package/dist/omegga/plugin/plugin_node_safe/proxyOmegga.js.map +1 -1
- package/dist/omegga/plugin/plugin_node_safe.js +365 -393
- package/dist/omegga/plugin/plugin_node_safe.js.map +1 -1
- package/dist/omegga/plugin/plugin_node_unsafe.js +119 -179
- package/dist/omegga/plugin/plugin_node_unsafe.js.map +1 -1
- package/dist/omegga/plugin.js +402 -347
- package/dist/omegga/plugin.js.map +1 -1
- package/dist/omegga/server.js +742 -774
- package/dist/omegga/server.js.map +1 -1
- package/dist/omegga/wrapper.js +64 -91
- package/dist/omegga/wrapper.js.map +1 -1
- package/dist/plugin.js +11 -31
- package/dist/plugin.js.map +1 -1
- package/dist/softconfig.js +121 -100
- package/dist/softconfig.js.map +1 -1
- package/dist/updater/steam.js +97 -112
- package/dist/updater/steam.js.map +1 -1
- package/dist/util/brdb.js +496 -528
- package/dist/util/brdb.js.map +1 -1
- package/dist/util/brick/checkBounds.js +11 -15
- package/dist/util/brick/checkBounds.js.map +1 -1
- package/dist/util/brick/constants.js +723 -174
- package/dist/util/brick/constants.js.map +1 -1
- package/dist/util/brick/displayName.js +204 -211
- package/dist/util/brick/displayName.js.map +1 -1
- package/dist/util/brick/getBounds.js +29 -41
- package/dist/util/brick/getBounds.js.map +1 -1
- package/dist/util/brick/getBrickSize.js +6 -7
- package/dist/util/brick/getBrickSize.js.map +1 -1
- package/dist/util/brick/getScaleAxis.js +24 -30
- package/dist/util/brick/getScaleAxis.js.map +1 -1
- package/dist/util/brick/index.js +8 -29
- package/dist/util/brick/index.js.map +1 -1
- package/dist/util/brick/rotate.js +29 -34
- package/dist/util/brick/rotate.js.map +1 -1
- package/dist/util/brick/setOwnership.js +13 -19
- package/dist/util/brick/setOwnership.js.map +1 -1
- package/dist/util/chat.js +53 -44
- package/dist/util/chat.js.map +1 -1
- package/dist/util/color.js +112 -124
- package/dist/util/color.js.map +1 -1
- package/dist/util/file.js +54 -98
- package/dist/util/file.js.map +1 -1
- package/dist/util/index.js +84 -106
- package/dist/util/index.js.map +1 -1
- package/dist/util/map.js +18 -20
- package/dist/util/map.js.map +1 -1
- package/dist/util/pattern.js +6 -9
- package/dist/util/pattern.js.map +1 -1
- package/dist/util/time.js +31 -34
- package/dist/util/time.js.map +1 -1
- package/dist/util/uuid.js +10 -8
- package/dist/util/uuid.js.map +1 -1
- package/dist/util/wsl.js +19 -25
- package/dist/util/wsl.js.map +1 -1
- package/dist/version.js +7 -9
- package/dist/version.js.map +1 -1
- package/dist/webserver/backend/api.js +784 -874
- package/dist/webserver/backend/api.js.map +1 -1
- package/dist/webserver/backend/calendar.js +14 -17
- package/dist/webserver/backend/calendar.js.map +1 -1
- package/dist/webserver/backend/database.js +563 -613
- package/dist/webserver/backend/database.js.map +1 -1
- package/dist/webserver/backend/index.js +153 -191
- package/dist/webserver/backend/index.js.map +1 -1
- package/dist/webserver/backend/metrics.js +219 -248
- package/dist/webserver/backend/metrics.js.map +1 -1
- package/dist/webserver/backend/util.js +47 -68
- package/dist/webserver/backend/util.js.map +1 -1
- package/dist/worker.js +322 -0
- package/dist/worker.js.map +1 -0
- package/package.json +14 -10
- package/templates/safe-ts/omegga.d.ts +2 -2
- package/vite.backend.config.mts +46 -0
- package/vite.frontend.config.mts +58 -0
- package/vitest.backend.config.mts +7 -0
- package/dist/brickadia/config.d.ts +0 -3
- package/dist/brickadia/presets.d.ts +0 -65
- package/dist/brickadia/presets.js +0 -3
- package/dist/brickadia/presets.js.map +0 -1
- package/dist/brickadia/server.d.ts +0 -36
- package/dist/brickadia/types.d.ts +0 -45
- package/dist/brickadia/types.js +0 -3
- package/dist/brickadia/types.js.map +0 -1
- package/dist/cli/auth.d.ts +0 -26
- package/dist/cli/config.d.ts +0 -10
- package/dist/cli/index.d.ts +0 -30
- package/dist/cli/installer.d.ts +0 -1
- package/dist/cli/plugin.d.ts +0 -22
- package/dist/cli/terminal.d.ts +0 -25
- package/dist/config/authorizer.d.ts +0 -2
- package/dist/config/authorizer.js +0 -38
- package/dist/config/authorizer.js.map +0 -1
- package/dist/config/formats/format_js.d.ts +0 -3
- package/dist/config/formats/format_yml.d.ts +0 -3
- package/dist/config/index.d.ts +0 -18
- package/dist/config/reader.d.ts +0 -3
- package/dist/config/types.d.ts +0 -50
- package/dist/config/types.js +0 -3
- package/dist/config/types.js.map +0 -1
- package/dist/config/validator.d.ts +0 -6
- package/dist/config/writer.d.ts +0 -3
- package/dist/index.d.ts +0 -22
- package/dist/info/default_commands.json +0 -29
- package/dist/logger.d.ts +0 -41
- package/dist/main.d.ts +0 -1
- package/dist/omegga/auth.d.ts +0 -9
- package/dist/omegga/commandInjector.d.ts +0 -35
- package/dist/omegga/index.d.ts +0 -7
- package/dist/omegga/logWrangler.d.ts +0 -32
- package/dist/omegga/matchers/auth.d.ts +0 -10
- package/dist/omegga/matchers/chat.d.ts +0 -10
- package/dist/omegga/matchers/command.d.ts +0 -7
- package/dist/omegga/matchers/exit.d.ts +0 -3
- package/dist/omegga/matchers/index.d.ts +0 -3
- package/dist/omegga/matchers/init.d.ts +0 -3
- package/dist/omegga/matchers/interact.d.ts +0 -4
- package/dist/omegga/matchers/join.d.ts +0 -4
- package/dist/omegga/matchers/leave.d.ts +0 -4
- package/dist/omegga/matchers/mapChange.d.ts +0 -3
- package/dist/omegga/matchers/minigameJoin.d.ts +0 -9
- package/dist/omegga/matchers/types.d.ts +0 -5
- package/dist/omegga/matchers/types.js +0 -3
- package/dist/omegga/matchers/types.js.map +0 -1
- package/dist/omegga/matchers/version.d.ts +0 -3
- package/dist/omegga/player.d.ts +0 -81
- package/dist/omegga/plugin/interface.d.ts +0 -30
- package/dist/omegga/plugin/plugin_jsonrpc_stdio.d.ts +0 -23
- package/dist/omegga/plugin/plugin_node_safe/proxyOmegga.d.ts +0 -145
- package/dist/omegga/plugin/plugin_node_safe/worker.d.ts +0 -1
- package/dist/omegga/plugin/plugin_node_safe/worker.js +0 -367
- package/dist/omegga/plugin/plugin_node_safe/worker.js.map +0 -1
- package/dist/omegga/plugin/plugin_node_safe.d.ts +0 -23
- package/dist/omegga/plugin/plugin_node_unsafe.d.ts +0 -13
- package/dist/omegga/plugin.d.ts +0 -63
- package/dist/omegga/server.d.ts +0 -166
- package/dist/omegga/types.d.ts +0 -49
- package/dist/omegga/types.js +0 -3
- package/dist/omegga/types.js.map +0 -1
- package/dist/omegga/wrapper.d.ts +0 -32
- package/dist/plugin.d.ts +0 -916
- package/dist/softconfig.d.ts +0 -67
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/dist/updater/index.d.ts +0 -1
- package/dist/updater/index.js +0 -10
- package/dist/updater/index.js.map +0 -1
- package/dist/updater/steam.d.ts +0 -52
- package/dist/util/brdb.d.ts +0 -31
- package/dist/util/brick/checkBounds.d.ts +0 -8
- package/dist/util/brick/constants.d.ts +0 -18
- package/dist/util/brick/displayName.d.ts +0 -7
- package/dist/util/brick/getBounds.d.ts +0 -5
- package/dist/util/brick/getBrickSize.d.ts +0 -3
- package/dist/util/brick/getScaleAxis.d.ts +0 -3
- package/dist/util/brick/index.d.ts +0 -14
- package/dist/util/brick/rotate.d.ts +0 -16
- package/dist/util/brick/setOwnership.d.ts +0 -12
- package/dist/util/chat.d.ts +0 -23
- package/dist/util/color.d.ts +0 -13
- package/dist/util/file.d.ts +0 -5
- package/dist/util/index.d.ts +0 -183
- package/dist/util/map.d.ts +0 -6
- package/dist/util/pattern.d.ts +0 -1
- package/dist/util/time.d.ts +0 -39
- package/dist/util/uuid.d.ts +0 -3
- package/dist/util/wsl.d.ts +0 -2
- package/dist/version.d.ts +0 -2
- package/dist/webserver/backend/api.d.ts +0 -115
- package/dist/webserver/backend/calendar.d.ts +0 -5
- package/dist/webserver/backend/database.d.ts +0 -150
- package/dist/webserver/backend/index.d.ts +0 -30
- package/dist/webserver/backend/metrics.d.ts +0 -3
- package/dist/webserver/backend/types.d.ts +0 -114
- package/dist/webserver/backend/types.js +0 -3
- package/dist/webserver/backend/types.js.map +0 -1
- package/dist/webserver/backend/util.d.ts +0 -2
- package/dist/webserver/index.d.ts +0 -2
- package/dist/webserver/index.js +0 -9
- package/dist/webserver/index.js.map +0 -1
package/dist/omegga/server.js
CHANGED
|
@@ -1,783 +1,751 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const file_1 = require("../util/file");
|
|
12
|
-
const backend_1 = __importDefault(require("../webserver/backend"));
|
|
13
|
-
const brs_js_1 = require("brs-js");
|
|
2
|
+
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
|
|
3
|
+
const default_commands = require("../info/default_commands.json.js");
|
|
4
|
+
const logger = require("../logger.js");
|
|
5
|
+
const softconfig = require("../softconfig.js");
|
|
6
|
+
const version = require("../version.js");
|
|
7
|
+
const index$2 = require("../util/index.js");
|
|
8
|
+
const file = require("../util/file.js");
|
|
9
|
+
const index = require("../webserver/backend/index.js");
|
|
10
|
+
const brs = require("brs-js");
|
|
14
11
|
require("colors");
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const
|
|
12
|
+
const glob = require("glob");
|
|
13
|
+
const fs = require("node:fs");
|
|
14
|
+
const path = require("path");
|
|
15
|
+
const commandInjector = require("./commandInjector.js");
|
|
16
|
+
const index$1 = require("./matchers/index.js");
|
|
17
|
+
const plugin = require("./plugin.js");
|
|
18
|
+
const wrapper = require("./wrapper.js");
|
|
22
19
|
const MISSING_CMD = '"Command not found. Type <color=\\"ffff00\\">/help</> for a list of commands or <color=\\"ffff00\\">/plugins</> for plugin information."';
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
this.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
this.restoreServer();
|
|
116
|
-
});
|
|
117
|
-
// when brickadia exits, stop omegga
|
|
118
|
-
this.on('exit', () => {
|
|
119
|
-
this.stop();
|
|
120
|
-
});
|
|
121
|
-
// when the process closes, emit the exit signal and stop
|
|
122
|
-
this.on('closed', () => {
|
|
123
|
-
if (this.started)
|
|
124
|
-
this.emit('exit');
|
|
125
|
-
if (!this.stopping)
|
|
126
|
-
this.stop();
|
|
127
|
-
});
|
|
128
|
-
// detect when a missing command is sent
|
|
129
|
-
this.on('cmd', (cmd, name) => {
|
|
130
|
-
// if it's not in the default commands and it's not registered to a plugin,
|
|
131
|
-
// it's okay to send the missing command message
|
|
132
|
-
if (!default_commands_json_1.default.includes(cmd) &&
|
|
133
|
-
(!this.pluginLoader || !this.pluginLoader.isCommand(cmd))) {
|
|
134
|
-
this.whisper(name, MISSING_CMD);
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
/** attempt to save server state */
|
|
139
|
-
async saveServer(config) {
|
|
140
|
-
if (config.players && this.players.length > 0) {
|
|
141
|
-
logger_1.default.logp('Getting player positions...');
|
|
142
|
-
const players = await this.getAllPlayerPositions();
|
|
143
|
-
logger_1.default.logp(`Saving ${players.length} player positions...`);
|
|
144
|
-
const data = players
|
|
145
|
-
.filter(p => !p.isDead && p.pos)
|
|
146
|
-
.map(p => ({ position: p.pos, id: p.player.id }));
|
|
147
|
-
if (players.length > 0)
|
|
148
|
-
(0, fs_1.writeFileSync)((0, path_1.join)(this.path, softconfig_1.DATA_PATH, 'omegga_temp_players.json'), JSON.stringify(data));
|
|
149
|
-
}
|
|
150
|
-
if (config.saveWorld) {
|
|
151
|
-
logger_1.default.logp('Saving world...');
|
|
152
|
-
await this.saveWorld();
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
async restartServer() {
|
|
156
|
-
if (this.starting || this.stopping)
|
|
157
|
-
return;
|
|
158
|
-
if (!this.started)
|
|
159
|
-
return await this.start();
|
|
160
|
-
const nextWorld = this.getNextWorld();
|
|
161
|
-
if (nextWorld) {
|
|
162
|
-
logger_1.default.logp('Loading world', nextWorld.file.yellow);
|
|
163
|
-
logger_1.default.verbose('Next world configured from', nextWorld.source.yellow);
|
|
164
|
-
this.loadWorld(nextWorld.file);
|
|
165
|
-
}
|
|
166
|
-
else {
|
|
167
|
-
this.changeMap(this.currentMap);
|
|
168
|
-
}
|
|
169
|
-
const res = await Promise.race([
|
|
170
|
-
// wait for the map to change
|
|
171
|
-
new Promise(resolve => this.once('mapchange', () => resolve('mapchange'))),
|
|
172
|
-
// Timeout after 10 seconds
|
|
173
|
-
new Promise(resolve => setTimeout(() => resolve('timeout'), 10000)),
|
|
174
|
-
]);
|
|
175
|
-
logger_1.default.verbose('Restart result:', res);
|
|
176
|
-
}
|
|
177
|
-
/** attempt to restore the server's state */
|
|
178
|
-
async restoreServer() {
|
|
179
|
-
const tempPlayersFile = (0, path_1.join)(this.path, softconfig_1.DATA_PATH, 'omegga_temp_players.json');
|
|
180
|
-
if (!(0, fs_1.existsSync)(tempPlayersFile))
|
|
181
|
-
return;
|
|
182
|
-
try {
|
|
183
|
-
logger_1.default.logp('Loading previous player positions...');
|
|
184
|
-
// player positions are an array to address multi-clienting
|
|
185
|
-
const players = JSON.parse((0, fs_1.readFileSync)(tempPlayersFile).toString());
|
|
186
|
-
// restore player position on join
|
|
187
|
-
const callback = (player) => {
|
|
188
|
-
const index = players.findIndex(p => p.id === player.id);
|
|
189
|
-
if (index > -1) {
|
|
190
|
-
const { position } = players[index];
|
|
191
|
-
this.writeln(`Chat.Command /TP "${player.name}" ${position.join(' ')} 0`);
|
|
192
|
-
// remove the entry
|
|
193
|
-
players[index] = players[players.length - 1];
|
|
194
|
-
players.pop();
|
|
195
|
-
}
|
|
196
|
-
};
|
|
197
|
-
this.on('join', callback);
|
|
198
|
-
let timeout = setTimeout(() => {
|
|
199
|
-
try {
|
|
200
|
-
this.off('join', callback);
|
|
201
|
-
if ((0, fs_1.existsSync)(tempPlayersFile))
|
|
202
|
-
(0, fs_1.unlinkSync)(tempPlayersFile);
|
|
203
|
-
}
|
|
204
|
-
catch (err) {
|
|
205
|
-
logger_1.default.error('Error removing omegga_temp_players.json', err);
|
|
206
|
-
}
|
|
207
|
-
}, 10000);
|
|
208
|
-
this.once('changemap', () => {
|
|
209
|
-
clearTimeout(timeout);
|
|
210
|
-
this.off('join', callback);
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
|
-
catch (err) {
|
|
214
|
-
logger_1.default.error('Error restoring previous server state', err);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
/**
|
|
218
|
-
* start webserver, load plugins, start the brickadia server
|
|
219
|
-
* this should not be called by a plugin
|
|
220
|
-
*/
|
|
221
|
-
//
|
|
222
|
-
async start() {
|
|
223
|
-
this.starting = true;
|
|
224
|
-
if (this.webserver)
|
|
225
|
-
await this.webserver.start();
|
|
226
|
-
if (this.pluginLoader) {
|
|
227
|
-
// scan for plugins
|
|
228
|
-
logger_1.default.verbose('Scanning for plugins');
|
|
229
|
-
await this.pluginLoader.scan();
|
|
230
|
-
// load the plugins
|
|
231
|
-
logger_1.default.verbose('Loading plugins');
|
|
232
|
-
await this.pluginLoader.reload();
|
|
233
|
-
}
|
|
234
|
-
logger_1.default.verbose('Starting Brickadia');
|
|
235
|
-
super.start();
|
|
236
|
-
this.emit('server:starting');
|
|
237
|
-
}
|
|
238
|
-
/**
|
|
239
|
-
* unload plugins and stop the server
|
|
240
|
-
* this should not be called by a plugin
|
|
241
|
-
*/
|
|
242
|
-
async stop() {
|
|
243
|
-
if (!this.started && !this.starting) {
|
|
244
|
-
logger_1.default.verbose("Stop called while server wasn't started or was starting");
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
|
-
if (this.stopping) {
|
|
248
|
-
logger_1.default.verbose('Stop called while server was starting');
|
|
249
|
-
return;
|
|
250
|
-
}
|
|
251
|
-
this.stopping = true;
|
|
252
|
-
this.emit('server:stopping');
|
|
253
|
-
if (this.pluginLoader) {
|
|
254
|
-
logger_1.default.verbose('Unloading plugins');
|
|
255
|
-
await this.pluginLoader.unload();
|
|
256
|
-
}
|
|
257
|
-
logger_1.default.verbose('Stopping server');
|
|
258
|
-
super.stop();
|
|
259
|
-
const res = await Promise.race([
|
|
260
|
-
new Promise(resolve => this.once('exit', () => resolve('exit'))),
|
|
261
|
-
// Timeout after 10 seconds
|
|
262
|
-
new Promise(resolve => setTimeout(() => resolve('timeout'), 10000)),
|
|
263
|
-
]);
|
|
264
|
-
logger_1.default.verbose('Stop result:', res);
|
|
265
|
-
if (this.stopping)
|
|
266
|
-
this.emit('server:stopped');
|
|
267
|
-
this.stopping = false;
|
|
268
|
-
this.started = false;
|
|
269
|
-
this.starting = false;
|
|
270
|
-
this.players = [];
|
|
271
|
-
}
|
|
272
|
-
/**
|
|
273
|
-
* Copies auth files from home config dir
|
|
274
|
-
* this should never be called by a plugin
|
|
275
|
-
*/
|
|
276
|
-
copyAuthFiles() {
|
|
277
|
-
var _a, _b;
|
|
278
|
-
const authDir = (_a = this.config.server.authDir) !== null && _a !== void 0 ? _a : softconfig_1.CONFIG_AUTH_DIR;
|
|
279
|
-
const savedDir = (_b = this.config.server.savedDir) !== null && _b !== void 0 ? _b : softconfig_1.CONFIG_SAVED_DIR;
|
|
280
|
-
const authPath = (0, path_1.join)(this.path, softconfig_1.DATA_PATH, savedDir, authDir);
|
|
281
|
-
const homeAuthPath = (0, path_1.join)(softconfig_1.CONFIG_HOME, (savedDir !== softconfig_1.CONFIG_SAVED_DIR ? savedDir : '') + authDir);
|
|
282
|
-
(0, file_1.copyFiles)(homeAuthPath, authPath, softconfig_1.BRICKADIA_AUTH_FILES);
|
|
283
|
-
}
|
|
284
|
-
// TODO: split messages that longer than 512 characters
|
|
285
|
-
// TODO: delete characters that are known to crash the game
|
|
286
|
-
broadcast(...messages) {
|
|
287
|
-
messages
|
|
288
|
-
.flatMap(m => m.toString().split('\n'))
|
|
289
|
-
.filter(m => m.length < 512)
|
|
290
|
-
.forEach(m => this.writeln(`Chat.Broadcast ${m}`));
|
|
291
|
-
}
|
|
292
|
-
whisper(target, ...messages) {
|
|
293
|
-
// find the target player
|
|
294
|
-
if (typeof target !== 'object')
|
|
295
|
-
target = this.getPlayer(target);
|
|
296
|
-
// whisper the messages to that player
|
|
297
|
-
messages
|
|
298
|
-
.flatMap(m => m.toString().split('\n'))
|
|
299
|
-
.filter(m => m.length < 512)
|
|
300
|
-
.forEach(m => this.writeln(`Chat.Whisper "${target.name}" ${m}`));
|
|
301
|
-
}
|
|
302
|
-
middlePrint(target, message) {
|
|
303
|
-
// find the target player
|
|
304
|
-
if (typeof target !== 'object')
|
|
305
|
-
target = this.getPlayer(target);
|
|
306
|
-
// whisper the messages to that player
|
|
307
|
-
if (message.length > 512)
|
|
308
|
-
return;
|
|
309
|
-
this.writeln(`Chat.StatusMessage "${target.name}" ${message}`);
|
|
310
|
-
}
|
|
311
|
-
getPlayers() {
|
|
312
|
-
return this.players.map(p => ({ ...p }));
|
|
313
|
-
}
|
|
314
|
-
getRoleSetup() {
|
|
315
|
-
var _a;
|
|
316
|
-
// Read RoleSetup2, fallback to old RoleSetup if it doesn't exist
|
|
317
|
-
return ((_a = (0, file_1.readWatchedJSON)((0, path_1.join)(this.configPath, 'RoleSetup2.json'))) !== null && _a !== void 0 ? _a : (0, file_1.readWatchedJSON)((0, path_1.join)(this.configPath, 'RoleSetup.json')));
|
|
318
|
-
}
|
|
319
|
-
getRoleAssignments() {
|
|
320
|
-
return (0, file_1.readWatchedJSON)((0, path_1.join)(this.configPath, 'RoleAssignments.json'));
|
|
321
|
-
}
|
|
322
|
-
getBanList() {
|
|
323
|
-
return (0, file_1.readWatchedJSON)((0, path_1.join)(this.configPath, 'BanList.json'));
|
|
324
|
-
}
|
|
325
|
-
getNameCache() {
|
|
326
|
-
return (0, file_1.readWatchedJSON)((0, path_1.join)(this.configPath, 'PlayerNameCache.json'));
|
|
327
|
-
}
|
|
328
|
-
getPlayer(target) {
|
|
329
|
-
return this.players.find(p => p.name === target ||
|
|
330
|
-
p.id === target ||
|
|
331
|
-
p.controller === target ||
|
|
332
|
-
p.state === target);
|
|
333
|
-
}
|
|
334
|
-
findPlayerByName(name) {
|
|
335
|
-
name = name.toLowerCase();
|
|
336
|
-
const exploded = _util_1.pattern.explode(name);
|
|
337
|
-
return (this.players.find(p => p.name === name || p.displayName === name) || // find by exact match
|
|
338
|
-
this.players.find(p => p.name.indexOf(name) > -1 || p.displayName.indexOf(name) > -1) || // find by rough match
|
|
339
|
-
this.players.find(p => p.name.match(exploded) || p.displayName.match(exploded)) // find by exploded regex match (ck finds cake, tbp finds TheBlackParrot)
|
|
20
|
+
class Omegga extends wrapper.default {
|
|
21
|
+
/**
|
|
22
|
+
* Omegga instance
|
|
23
|
+
*/
|
|
24
|
+
constructor(serverPath, cfg, options = {}) {
|
|
25
|
+
super(serverPath, cfg);
|
|
26
|
+
this._tempCounter = { save: 0, environment: 0 };
|
|
27
|
+
this._tempSavePrefix = "omegga_temp_";
|
|
28
|
+
this.pluginLoader = void 0;
|
|
29
|
+
this.started = false;
|
|
30
|
+
this.starting = false;
|
|
31
|
+
this.stopping = false;
|
|
32
|
+
this.verbose = logger.default.VERBOSE;
|
|
33
|
+
logger.default.verbose("Running omegga", `v${version.VERSION}`.green);
|
|
34
|
+
logger.default.verbose("Versions", process.versions);
|
|
35
|
+
logger.default.verbose("Config", cfg);
|
|
36
|
+
logger.default.verbose("Setting up command injector");
|
|
37
|
+
commandInjector.default(this, this.logWrangler);
|
|
38
|
+
this.options = options;
|
|
39
|
+
const savedDir = cfg.server.savedDir ?? softconfig.CONFIG_SAVED_DIR;
|
|
40
|
+
this.savePath = path.join(this.path, softconfig.DATA_PATH, savedDir, "Builds");
|
|
41
|
+
this.worldPath = path.join(this.path, softconfig.DATA_PATH, savedDir, "Worlds");
|
|
42
|
+
this.presetPath = path.join(this.path, softconfig.DATA_PATH, savedDir, "Presets");
|
|
43
|
+
this.configPath = path.join(this.path, softconfig.DATA_PATH, savedDir, "Server");
|
|
44
|
+
logger.default.verbose("Creating directories");
|
|
45
|
+
file.mkdir(this.savePath);
|
|
46
|
+
file.mkdir(this.configPath);
|
|
47
|
+
if (!options.noauth) {
|
|
48
|
+
logger.default.verbose("Copying auth files");
|
|
49
|
+
this.copyAuthFiles();
|
|
50
|
+
}
|
|
51
|
+
if (!options.noweb) {
|
|
52
|
+
logger.default.verbose("Creating webserver");
|
|
53
|
+
this.webserver = new index.default(cfg.omegga, this);
|
|
54
|
+
}
|
|
55
|
+
if (!options.noplugin) {
|
|
56
|
+
logger.default.verbose("Creating plugin loader");
|
|
57
|
+
this.pluginLoader = new plugin.PluginLoader(this.path, this);
|
|
58
|
+
}
|
|
59
|
+
this.players = [];
|
|
60
|
+
this.host = void 0;
|
|
61
|
+
this.version = -1;
|
|
62
|
+
this.started = false;
|
|
63
|
+
this.starting = false;
|
|
64
|
+
this.currentMap = "";
|
|
65
|
+
logger.default.verbose("Adding matchers");
|
|
66
|
+
for (const matcher of index$1.default) {
|
|
67
|
+
const { pattern: pattern2, callback } = matcher(this);
|
|
68
|
+
this.addMatcher(pattern2, callback);
|
|
69
|
+
}
|
|
70
|
+
process.on("uncaughtException", async (err) => {
|
|
71
|
+
logger.default.verbose("Uncaught exception", err);
|
|
72
|
+
this.emit("error", err);
|
|
73
|
+
this.webserver?.database?.addChatLog("server", {}, "Server error");
|
|
74
|
+
try {
|
|
75
|
+
await this.stop();
|
|
76
|
+
} catch (e) {
|
|
77
|
+
logger.default.error(e);
|
|
78
|
+
}
|
|
79
|
+
process.exit();
|
|
80
|
+
});
|
|
81
|
+
this.on("start", ({ map }) => {
|
|
82
|
+
this.started = true;
|
|
83
|
+
this.starting = false;
|
|
84
|
+
this.currentMap = map;
|
|
85
|
+
this.writeln("Chat.MessageForUnknownCommands 0");
|
|
86
|
+
this.restoreServer();
|
|
87
|
+
});
|
|
88
|
+
this.on("exit", () => {
|
|
89
|
+
this.stop();
|
|
90
|
+
});
|
|
91
|
+
this.on("closed", () => {
|
|
92
|
+
if (this.started) this.emit("exit");
|
|
93
|
+
if (!this.stopping) this.stop();
|
|
94
|
+
});
|
|
95
|
+
this.on("cmd", (cmd, name) => {
|
|
96
|
+
if (!default_commands.default.includes(cmd) && (!this.pluginLoader || !this.pluginLoader.isCommand(cmd))) {
|
|
97
|
+
this.whisper(name, MISSING_CMD);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
/** attempt to save server state */
|
|
102
|
+
async saveServer(config) {
|
|
103
|
+
if (config.players && this.players.length > 0) {
|
|
104
|
+
logger.default.logp("Getting player positions...");
|
|
105
|
+
const players = await this.getAllPlayerPositions();
|
|
106
|
+
logger.default.logp(`Saving ${players.length} player positions...`);
|
|
107
|
+
const data = players.filter((p) => !p.isDead && p.pos).map((p) => ({ position: p.pos, id: p.player.id }));
|
|
108
|
+
if (players.length > 0)
|
|
109
|
+
fs.writeFileSync(
|
|
110
|
+
path.join(this.path, softconfig.DATA_PATH, "omegga_temp_players.json"),
|
|
111
|
+
JSON.stringify(data)
|
|
340
112
|
);
|
|
341
113
|
}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
114
|
+
if (config.saveWorld) {
|
|
115
|
+
logger.default.logp("Saving world...");
|
|
116
|
+
await this.saveWorld();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
async restartServer() {
|
|
120
|
+
if (this.starting || this.stopping) return;
|
|
121
|
+
if (!this.started) return await this.start();
|
|
122
|
+
const nextWorld = this.getNextWorld();
|
|
123
|
+
if (nextWorld) {
|
|
124
|
+
logger.default.logp("Loading world", nextWorld.file.yellow);
|
|
125
|
+
logger.default.verbose("Next world configured from", nextWorld.source.yellow);
|
|
126
|
+
this.loadWorld(nextWorld.file);
|
|
127
|
+
} else {
|
|
128
|
+
this.changeMap(this.currentMap);
|
|
129
|
+
}
|
|
130
|
+
const res = await Promise.race([
|
|
131
|
+
// wait for the map to change
|
|
132
|
+
new Promise(
|
|
133
|
+
(resolve) => this.once("mapchange", () => resolve("mapchange"))
|
|
134
|
+
),
|
|
135
|
+
// Timeout after 10 seconds
|
|
136
|
+
new Promise((resolve) => setTimeout(() => resolve("timeout"), 1e4))
|
|
137
|
+
]);
|
|
138
|
+
logger.default.verbose("Restart result:", res);
|
|
139
|
+
}
|
|
140
|
+
/** attempt to restore the server's state */
|
|
141
|
+
async restoreServer() {
|
|
142
|
+
const tempPlayersFile = path.join(
|
|
143
|
+
this.path,
|
|
144
|
+
softconfig.DATA_PATH,
|
|
145
|
+
"omegga_temp_players.json"
|
|
146
|
+
);
|
|
147
|
+
if (!fs.existsSync(tempPlayersFile)) return;
|
|
148
|
+
try {
|
|
149
|
+
logger.default.logp("Loading previous player positions...");
|
|
150
|
+
const players = JSON.parse(
|
|
151
|
+
fs.readFileSync(tempPlayersFile).toString()
|
|
152
|
+
);
|
|
153
|
+
const callback = (player) => {
|
|
154
|
+
const index2 = players.findIndex((p) => p.id === player.id);
|
|
155
|
+
if (index2 > -1) {
|
|
156
|
+
const { position } = players[index2];
|
|
157
|
+
this.writeln(
|
|
158
|
+
`Chat.Command /TP "${player.name}" ${position.join(" ")} 0`
|
|
159
|
+
);
|
|
160
|
+
players[index2] = players[players.length - 1];
|
|
161
|
+
players.pop();
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
this.on("join", callback);
|
|
165
|
+
let timeout = setTimeout(() => {
|
|
390
166
|
try {
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
167
|
+
this.off("join", callback);
|
|
168
|
+
if (fs.existsSync(tempPlayersFile)) fs.unlinkSync(tempPlayersFile);
|
|
169
|
+
} catch (err) {
|
|
170
|
+
logger.default.error("Error removing omegga_temp_players.json", err);
|
|
171
|
+
}
|
|
172
|
+
}, 1e4);
|
|
173
|
+
this.once("changemap", () => {
|
|
174
|
+
clearTimeout(timeout);
|
|
175
|
+
this.off("join", callback);
|
|
176
|
+
});
|
|
177
|
+
} catch (err) {
|
|
178
|
+
logger.default.error("Error restoring previous server state", err);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* start webserver, load plugins, start the brickadia server
|
|
183
|
+
* this should not be called by a plugin
|
|
184
|
+
*/
|
|
185
|
+
//
|
|
186
|
+
async start() {
|
|
187
|
+
this.starting = true;
|
|
188
|
+
if (this.webserver) await this.webserver.start();
|
|
189
|
+
if (this.pluginLoader) {
|
|
190
|
+
logger.default.verbose("Scanning for plugins");
|
|
191
|
+
await this.pluginLoader.scan();
|
|
192
|
+
logger.default.verbose("Loading plugins");
|
|
193
|
+
await this.pluginLoader.reload();
|
|
194
|
+
}
|
|
195
|
+
logger.default.verbose("Starting Brickadia");
|
|
196
|
+
super.start();
|
|
197
|
+
this.emit("server:starting");
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* unload plugins and stop the server
|
|
201
|
+
* this should not be called by a plugin
|
|
202
|
+
*/
|
|
203
|
+
async stop() {
|
|
204
|
+
if (!this.started && !this.starting) {
|
|
205
|
+
logger.default.verbose("Stop called while server wasn't started or was starting");
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
if (this.stopping) {
|
|
209
|
+
logger.default.verbose("Stop called while server was starting");
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
this.stopping = true;
|
|
213
|
+
this.emit("server:stopping");
|
|
214
|
+
if (this.pluginLoader) {
|
|
215
|
+
logger.default.verbose("Unloading plugins");
|
|
216
|
+
await this.pluginLoader.unload();
|
|
217
|
+
}
|
|
218
|
+
logger.default.verbose("Stopping server");
|
|
219
|
+
super.stop();
|
|
220
|
+
const res = await Promise.race([
|
|
221
|
+
new Promise((resolve) => this.once("exit", () => resolve("exit"))),
|
|
222
|
+
// Timeout after 10 seconds
|
|
223
|
+
new Promise((resolve) => setTimeout(() => resolve("timeout"), 1e4))
|
|
224
|
+
]);
|
|
225
|
+
logger.default.verbose("Stop result:", res);
|
|
226
|
+
if (this.stopping) this.emit("server:stopped");
|
|
227
|
+
this.stopping = false;
|
|
228
|
+
this.started = false;
|
|
229
|
+
this.starting = false;
|
|
230
|
+
this.players = [];
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Copies auth files from home config dir
|
|
234
|
+
* this should never be called by a plugin
|
|
235
|
+
*/
|
|
236
|
+
copyAuthFiles() {
|
|
237
|
+
const authDir = this.config.server.authDir ?? softconfig.CONFIG_AUTH_DIR;
|
|
238
|
+
const savedDir = this.config.server.savedDir ?? softconfig.CONFIG_SAVED_DIR;
|
|
239
|
+
const authPath = path.join(this.path, softconfig.DATA_PATH, savedDir, authDir);
|
|
240
|
+
const homeAuthPath = path.join(
|
|
241
|
+
softconfig.CONFIG_HOME,
|
|
242
|
+
(savedDir !== softconfig.CONFIG_SAVED_DIR ? savedDir : "") + authDir
|
|
243
|
+
);
|
|
244
|
+
file.copyFiles(homeAuthPath, authPath, softconfig.BRICKADIA_AUTH_FILES);
|
|
245
|
+
}
|
|
246
|
+
// TODO: split messages that longer than 512 characters
|
|
247
|
+
// TODO: delete characters that are known to crash the game
|
|
248
|
+
broadcast(...messages) {
|
|
249
|
+
messages.flatMap((m) => m.toString().split("\n")).filter((m) => m.length < 512).forEach((m) => this.writeln(`Chat.Broadcast ${m}`));
|
|
250
|
+
}
|
|
251
|
+
whisper(target, ...messages) {
|
|
252
|
+
if (typeof target !== "object") target = this.getPlayer(target);
|
|
253
|
+
messages.flatMap((m) => m.toString().split("\n")).filter((m) => m.length < 512).forEach(
|
|
254
|
+
(m) => this.writeln(
|
|
255
|
+
`Chat.Whisper "${target.name}" ${m}`
|
|
256
|
+
)
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
middlePrint(target, message) {
|
|
260
|
+
if (typeof target !== "object") target = this.getPlayer(target);
|
|
261
|
+
if (message.length > 512) return;
|
|
262
|
+
this.writeln(
|
|
263
|
+
`Chat.StatusMessage "${target.name}" ${message}`
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
getPlayers() {
|
|
267
|
+
return this.players.map((p) => ({ ...p }));
|
|
268
|
+
}
|
|
269
|
+
getRoleSetup() {
|
|
270
|
+
return file.readWatchedJSON(path.join(this.configPath, "RoleSetup2.json")) ?? file.readWatchedJSON(path.join(this.configPath, "RoleSetup.json"));
|
|
271
|
+
}
|
|
272
|
+
getRoleAssignments() {
|
|
273
|
+
return file.readWatchedJSON(
|
|
274
|
+
path.join(this.configPath, "RoleAssignments.json")
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
getBanList() {
|
|
278
|
+
return file.readWatchedJSON(path.join(this.configPath, "BanList.json"));
|
|
279
|
+
}
|
|
280
|
+
getNameCache() {
|
|
281
|
+
return file.readWatchedJSON(
|
|
282
|
+
path.join(this.configPath, "PlayerNameCache.json")
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
getPlayer(target) {
|
|
286
|
+
return this.players.find(
|
|
287
|
+
(p) => p.name === target || p.id === target || p.controller === target || p.state === target
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
findPlayerByName(name) {
|
|
291
|
+
name = name.toLowerCase();
|
|
292
|
+
const exploded = index$2.pattern.explode(name);
|
|
293
|
+
return this.players.find((p) => p.name === name || p.displayName === name) || // find by exact match
|
|
294
|
+
this.players.find(
|
|
295
|
+
(p) => p.name.indexOf(name) > -1 || p.displayName.indexOf(name) > -1
|
|
296
|
+
) || // find by rough match
|
|
297
|
+
this.players.find(
|
|
298
|
+
(p) => p.name.match(exploded) || p.displayName.match(exploded)
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
getHostId() {
|
|
302
|
+
return this.host?.id ?? "";
|
|
303
|
+
}
|
|
304
|
+
saveMinigame(index2, name) {
|
|
305
|
+
this.writeln(`Server.Minigames.SavePreset ${index2} "${name}"`);
|
|
306
|
+
}
|
|
307
|
+
deleteMinigame(index2) {
|
|
308
|
+
this.writeln(`Server.Minigames.Delete ${index2}`);
|
|
309
|
+
}
|
|
310
|
+
resetMinigame(index2) {
|
|
311
|
+
this.writeln(`Server.Minigames.Reset ${index2}`);
|
|
312
|
+
}
|
|
313
|
+
nextRoundMinigame(index2) {
|
|
314
|
+
this.writeln(`Server.Minigames.NextRound ${index2}`);
|
|
315
|
+
}
|
|
316
|
+
loadMinigame(presetName, owner = "") {
|
|
317
|
+
this.writeln(
|
|
318
|
+
`Server.Minigames.LoadPreset "${presetName}" ${owner ? `"${owner}"` : ""}`
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
getMinigamePresets() {
|
|
322
|
+
const presetPath = path.join(this.presetPath, "Minigame");
|
|
323
|
+
return fs.existsSync(presetPath) ? glob.sync(presetPath + "/**/*.bp").map((f) => path.basename(f).replace(/\.bp$/, "")) : [];
|
|
324
|
+
}
|
|
325
|
+
resetEnvironment() {
|
|
326
|
+
this.writeln(`Server.Environment.Reset`);
|
|
327
|
+
}
|
|
328
|
+
async saveEnvironment(presetName) {
|
|
329
|
+
await this.addWatcher(/Environment preset saved.$/, {
|
|
330
|
+
// request the pawn for this player's controller (should only be one)
|
|
331
|
+
exec: () => this.writeln(`Server.Environment.SavePreset "${presetName}"`),
|
|
332
|
+
timeoutDelay: 100
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
async getEnvironmentData() {
|
|
336
|
+
const saveName = this._tempSavePrefix + Date.now() + "_" + this._tempCounter.environment++;
|
|
337
|
+
await this.saveEnvironment(saveName);
|
|
338
|
+
const data = this.readEnvironmentData(saveName);
|
|
339
|
+
const file2 = path.join(this.presetPath, "Environment", saveName + ".bp");
|
|
340
|
+
if (fs.existsSync(file2)) fs.unlinkSync(file2);
|
|
341
|
+
return data;
|
|
342
|
+
}
|
|
343
|
+
readEnvironmentData(saveName) {
|
|
344
|
+
if (typeof saveName !== "string")
|
|
345
|
+
throw "expected name argument for readEnvironmentData";
|
|
346
|
+
const file2 = path.join(this.presetPath, "Environment", saveName + ".bp");
|
|
347
|
+
try {
|
|
348
|
+
if (fs.existsSync(file2)) return JSON.parse(fs.readFileSync(file2).toString());
|
|
349
|
+
} catch (err) {
|
|
350
|
+
logger.default.verbose("Error parsing save data in readEnvironmentData", err);
|
|
351
|
+
}
|
|
352
|
+
return null;
|
|
353
|
+
}
|
|
354
|
+
loadEnvironment(presetName) {
|
|
355
|
+
this.writeln(`Server.Environment.LoadPreset ${presetName}`);
|
|
356
|
+
}
|
|
357
|
+
loadEnvironmentData(preset) {
|
|
358
|
+
if ("data" in preset) preset = preset.data.groups;
|
|
359
|
+
const saveFile = this._tempSavePrefix + Date.now() + "_" + this._tempCounter.environment++;
|
|
360
|
+
const path$1 = path.join(this.presetPath, "Environment", saveFile + ".bp");
|
|
361
|
+
fs.writeFileSync(
|
|
362
|
+
path$1,
|
|
363
|
+
JSON.stringify({
|
|
364
|
+
formatVersion: "1",
|
|
365
|
+
presetVersion: "1",
|
|
366
|
+
type: "Environment",
|
|
367
|
+
data: {
|
|
368
|
+
groups: {
|
|
369
|
+
...preset
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
})
|
|
373
|
+
);
|
|
374
|
+
this.loadEnvironment(saveFile);
|
|
375
|
+
setTimeout(() => fs.unlinkSync(path$1), 5e3);
|
|
376
|
+
}
|
|
377
|
+
getEnvironmentPresets() {
|
|
378
|
+
const presetPath = path.join(this.presetPath, "Environment");
|
|
379
|
+
return fs.existsSync(presetPath) ? glob.sync(presetPath + "/**/*.bp").map((f) => path.basename(f).replace(/\.bp$/, "")) : [];
|
|
380
|
+
}
|
|
381
|
+
clearBricks(target, quiet = false) {
|
|
382
|
+
if (typeof target === "object" && target.id) target = target.id;
|
|
383
|
+
else if (typeof target === "string" && !index$2.uuid.match(target)) {
|
|
384
|
+
const player = this.getPlayer(target);
|
|
385
|
+
target = player && player.id;
|
|
386
|
+
}
|
|
387
|
+
if (!target) return;
|
|
388
|
+
this.writeln(`Bricks.Clear ${target} ${quiet ? 1 : ""}`);
|
|
389
|
+
}
|
|
390
|
+
clearRegion(region, options) {
|
|
391
|
+
let target = "";
|
|
392
|
+
if (options && typeof options.target === "object")
|
|
393
|
+
target = " " + options.target.id;
|
|
394
|
+
if (typeof target === "string" && !index$2.uuid.match(target)) {
|
|
395
|
+
const player = this.getPlayer(target);
|
|
396
|
+
if (player) target = " " + player.id;
|
|
397
|
+
}
|
|
398
|
+
this.writeln(
|
|
399
|
+
`Bricks.ClearRegion ${region.center.join(" ")} ${region.extent.join(
|
|
400
|
+
" "
|
|
401
|
+
)}${target}`
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
clearAllBricks(quiet = false) {
|
|
405
|
+
this.writeln(`Bricks.ClearAll ${quiet ? 1 : ""}`);
|
|
406
|
+
}
|
|
407
|
+
saveBricks(saveName, region) {
|
|
408
|
+
if (!saveName) return;
|
|
409
|
+
if (!(saveName.startsWith('"') && saveName.endsWith('"')))
|
|
410
|
+
saveName = `"${saveName}"`;
|
|
411
|
+
if (region?.center && region?.extent)
|
|
412
|
+
this.writeln(
|
|
413
|
+
`Bricks.SaveRegion ${saveName} ${region.center.join(
|
|
414
|
+
" "
|
|
415
|
+
)} ${region.extent.join(" ")}`
|
|
416
|
+
);
|
|
417
|
+
else this.writeln(`Bricks.Save ${saveName}`);
|
|
418
|
+
}
|
|
419
|
+
async saveBricksAsync(saveName, region) {
|
|
420
|
+
if (!saveName) return;
|
|
421
|
+
let saveNameClean = saveName;
|
|
422
|
+
if (!(saveName.startsWith('"') && saveName.endsWith('"')))
|
|
423
|
+
saveNameClean = `"${saveName}"`;
|
|
424
|
+
const command = region?.center && region?.extent ? `Bricks.SaveRegion ${saveNameClean} ${region.center.join(
|
|
425
|
+
" "
|
|
426
|
+
)} ${region.extent.join(" ")}` : `Bricks.Save ${saveNameClean}`;
|
|
427
|
+
await this.watchLogChunk(command, /^(LogBrickSerializer|LogTemp): (.+)$/, {
|
|
428
|
+
first: (match) => match[0].endsWith(saveName + ".brs..."),
|
|
429
|
+
last: (match) => Boolean(
|
|
430
|
+
match[2].match(
|
|
431
|
+
/Saved .+ bricks and .+ components from .+ owners|Error: No bricks in grid!|Error: No bricks selected to save!/
|
|
432
|
+
)
|
|
433
|
+
),
|
|
434
|
+
afterMatchDelay: 0,
|
|
435
|
+
timeoutDelay: 3e4
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
loadBricks(saveName, {
|
|
439
|
+
offX = 0,
|
|
440
|
+
offY = 0,
|
|
441
|
+
offZ = 0,
|
|
442
|
+
quiet = false,
|
|
443
|
+
correctPalette = false,
|
|
444
|
+
correctCustom = false
|
|
445
|
+
} = {}) {
|
|
446
|
+
if (!(saveName.startsWith('"') && saveName.endsWith('"')))
|
|
447
|
+
saveName = `"${saveName}"`;
|
|
448
|
+
this.writeln(
|
|
449
|
+
`Bricks.Load ${saveName} ${offX} ${offY} ${offZ} ${quiet ? 1 : 0} ${correctPalette ? 1 : 0} ${correctCustom ? 1 : 0}`
|
|
450
|
+
);
|
|
451
|
+
}
|
|
452
|
+
loadBricksOnPlayer(saveName, player, {
|
|
453
|
+
offX = 0,
|
|
454
|
+
offY = 0,
|
|
455
|
+
offZ = 0,
|
|
456
|
+
correctPalette = false,
|
|
457
|
+
correctCustom = false
|
|
458
|
+
} = {}) {
|
|
459
|
+
player = typeof player === "string" ? this.getPlayer(player) : player;
|
|
460
|
+
if (!player) return;
|
|
461
|
+
if (!(saveName.startsWith('"') && saveName.endsWith('"')))
|
|
462
|
+
saveName = `"${saveName}"`;
|
|
463
|
+
this.writeln(
|
|
464
|
+
`Bricks.LoadTemplate ${saveName} ${offX} ${offY} ${offZ} ${correctPalette ? 1 : 0} ${correctCustom ? 1 : 0} "${player.name}"`
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
getSaves() {
|
|
468
|
+
return fs.existsSync(this.savePath) ? glob.sync(this.savePath + "/**/*.brs") : [];
|
|
469
|
+
}
|
|
470
|
+
getWorlds() {
|
|
471
|
+
return fs.existsSync(this.worldPath) ? glob.sync(this.worldPath + "/**/*.brdb") : [];
|
|
472
|
+
}
|
|
473
|
+
getSavePath(saveName) {
|
|
474
|
+
const file2 = path.join(
|
|
475
|
+
this.savePath,
|
|
476
|
+
saveName.endsWith(".brs") ? saveName : saveName + ".brs"
|
|
477
|
+
);
|
|
478
|
+
return fs.existsSync(file2) ? file2 : void 0;
|
|
479
|
+
}
|
|
480
|
+
getWorldPath(worldName) {
|
|
481
|
+
const file2 = path.join(
|
|
482
|
+
this.worldPath,
|
|
483
|
+
worldName.endsWith(".brdb") ? worldName : worldName + ".brdb"
|
|
484
|
+
);
|
|
485
|
+
return fs.existsSync(file2) ? file2 : void 0;
|
|
486
|
+
}
|
|
487
|
+
async getWorldRevisions(worldName) {
|
|
488
|
+
if (!this.started || this.starting || this.stopping) {
|
|
489
|
+
throw new Error("Server is not started");
|
|
490
|
+
}
|
|
491
|
+
worldName = worldName.replace(/\.brdb$/i, "");
|
|
492
|
+
if (!worldName || !this.getWorldPath(worldName)) {
|
|
493
|
+
throw new Error(`World "${worldName}" does not exist`);
|
|
494
|
+
}
|
|
495
|
+
let numRevisions = 0;
|
|
496
|
+
const revisionsRaw = await this.watchLogChunk(
|
|
497
|
+
`BR.World.ListRevisions "${worldName}"`,
|
|
498
|
+
/^LogBRBundleManager: (There are (?<numRevisions>\d+) revisions|Revision (?<revision>\d+) - (?<date>[\d.-]+): (?<note>.+))$/,
|
|
499
|
+
{
|
|
500
|
+
last: (match) => Number(match.groups.reverse) === numRevisions,
|
|
501
|
+
first: (match) => {
|
|
502
|
+
if (match.groups?.numRevisions !== void 0) {
|
|
503
|
+
numRevisions = Number(match.groups.numRevisions);
|
|
504
|
+
return true;
|
|
505
|
+
}
|
|
506
|
+
return false;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
);
|
|
510
|
+
if (!revisionsRaw) return [];
|
|
511
|
+
return revisionsRaw.map((match) => {
|
|
512
|
+
if (match.groups?.numRevisions !== void 0) {
|
|
397
513
|
return null;
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
if (((_a = match === null || match === void 0 ? void 0 : match.groups) === null || _a === void 0 ? void 0 : _a.generator) !== 'LogBRWorldManager')
|
|
634
|
-
return;
|
|
635
|
-
const ok = match.groups.data.match(/^World files saved after /);
|
|
636
|
-
const err = !match.groups.data.startsWith('Error: Failed to capture minigame settings') &&
|
|
637
|
-
match.groups.data.match(/^Error: (World has not been saved\.)?/);
|
|
638
|
-
return ok ? { res: true } : err ? { res: false } : undefined;
|
|
639
|
-
}, {
|
|
640
|
-
exec: () => {
|
|
641
|
-
this.writeln(`BR.World.Save 0`);
|
|
642
|
-
},
|
|
643
|
-
timeoutDelay: 2000,
|
|
644
|
-
});
|
|
645
|
-
return (_b = (_a = match === null || match === void 0 ? void 0 : match[0]) === null || _a === void 0 ? void 0 : _a['res']) !== null && _b !== void 0 ? _b : false;
|
|
646
|
-
}
|
|
647
|
-
catch (err) {
|
|
648
|
-
return false;
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
async createEmptyWorld(worldName, map = 'Plate') {
|
|
652
|
-
var _a, _b;
|
|
653
|
-
if (!worldName)
|
|
654
|
-
return;
|
|
655
|
-
worldName = worldName.replace(/\.brdb$/i, '');
|
|
656
|
-
try {
|
|
657
|
-
const match = await this.addWatcher((_line, match) => {
|
|
658
|
-
var _a;
|
|
659
|
-
if (((_a = match === null || match === void 0 ? void 0 : match.groups) === null || _a === void 0 ? void 0 : _a.generator) !== 'LogBRWorldManager')
|
|
660
|
-
return;
|
|
661
|
-
const ok = match.groups.data.match(/^World files saved after /);
|
|
662
|
-
const err = match.groups.data.match(/^Error: (Invalid preset|World already exists|Failed to create new world)?/);
|
|
663
|
-
return ok ? { res: true } : err ? { res: false } : undefined;
|
|
664
|
-
}, {
|
|
665
|
-
exec: () => {
|
|
666
|
-
this.writeln(`BR.World.CreateEmpty "${worldName}" ${map}`);
|
|
667
|
-
},
|
|
668
|
-
timeoutDelay: 2000,
|
|
669
|
-
});
|
|
670
|
-
return (_b = (_a = match === null || match === void 0 ? void 0 : match[0]) === null || _a === void 0 ? void 0 : _a['res']) !== null && _b !== void 0 ? _b : false;
|
|
671
|
-
}
|
|
672
|
-
catch (err) {
|
|
673
|
-
return false;
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
writeSaveData(saveName, saveData) {
|
|
677
|
-
if (typeof saveName !== 'string')
|
|
678
|
-
throw 'expected name argument for writeSaveData';
|
|
679
|
-
const file = (0, path_1.join)(this.savePath, saveName + '.brs');
|
|
680
|
-
if (!file.startsWith(this.savePath))
|
|
681
|
-
throw 'save file not in Saved/Builds directory';
|
|
682
|
-
(0, fs_1.writeFileSync)(file, new Uint8Array((0, brs_js_1.write)(saveData)));
|
|
683
|
-
}
|
|
684
|
-
readSaveData(saveName, nobricks = false) {
|
|
685
|
-
if (typeof saveName !== 'string')
|
|
686
|
-
throw 'expected name argument for readSaveData';
|
|
687
|
-
const file = this.getSavePath(saveName);
|
|
688
|
-
if (!file || !file.startsWith(this.savePath))
|
|
689
|
-
throw 'save file not in Saved/Builds directory';
|
|
690
|
-
if (file)
|
|
691
|
-
return (0, brs_js_1.read)((0, fs_1.readFileSync)(file), {
|
|
692
|
-
preview: false,
|
|
693
|
-
bricks: !nobricks,
|
|
694
|
-
});
|
|
695
|
-
}
|
|
696
|
-
async loadSaveData(saveData, { offX = 0, offY = 0, offZ = 0, quiet = false, correctPalette = false, correctCustom = false, } = {}) {
|
|
697
|
-
const saveFile = this._tempSavePrefix + Date.now() + '_' + this._tempCounter.save++;
|
|
698
|
-
// write savedata to file
|
|
699
|
-
this.writeSaveData(saveFile, saveData);
|
|
700
|
-
// wait for the server to finish reading the save
|
|
701
|
-
await this.watchLogChunk(`Bricks.Load "${saveFile}" ${offX} ${offY} ${offZ} ${quiet ? 1 : 0} ${correctPalette ? 1 : 0} ${correctCustom ? 1 : 0}`, /^LogBrickSerializer: (.+)$/, {
|
|
702
|
-
first: match => match[0].endsWith(saveFile + '.brs...'),
|
|
703
|
-
last: match => Boolean(match[1].match(/Read .+ bricks/)),
|
|
704
|
-
afterMatchDelay: 0,
|
|
705
|
-
timeoutDelay: 30000,
|
|
706
|
-
});
|
|
707
|
-
// delete the save file after we're done
|
|
708
|
-
const savePath = this.getSavePath(saveFile);
|
|
709
|
-
if (savePath) {
|
|
710
|
-
(0, fs_1.unlinkSync)(savePath);
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
async loadSaveDataOnPlayer(saveData, player, { offX = 0, offY = 0, offZ = 0, correctPalette = false, correctCustom = false, } = {}) {
|
|
714
|
-
player = typeof player === 'string' ? this.getPlayer(player) : player;
|
|
715
|
-
if (!player)
|
|
716
|
-
return;
|
|
717
|
-
const saveFile = this._tempSavePrefix + Date.now() + '_' + this._tempCounter.save++;
|
|
718
|
-
// write savedata to file
|
|
719
|
-
this.writeSaveData(saveFile, saveData);
|
|
720
|
-
// wait for the server to finish reading the save
|
|
721
|
-
await this.watchLogChunk(`Bricks.LoadTemplate "${saveFile}" ${offX} ${offY} ${offZ} ${correctPalette ? 1 : 0} ${correctCustom ? 1 : 0} "${player.name}"`, /^LogBrickSerializer: (.+)$/, {
|
|
722
|
-
first: match => match[0].endsWith(saveFile + '.brs...'),
|
|
723
|
-
last: match => Boolean(match[1].match(/Read .+ bricks/)),
|
|
724
|
-
afterMatchDelay: 0,
|
|
725
|
-
timeoutDelay: 30000,
|
|
726
|
-
});
|
|
727
|
-
// delete the save file after we're done
|
|
728
|
-
const savePath = this.getSavePath(saveFile);
|
|
729
|
-
if (savePath) {
|
|
730
|
-
(0, fs_1.unlinkSync)(savePath);
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
async getSaveData(region) {
|
|
734
|
-
const saveFile = this._tempSavePrefix + Date.now() + '_' + this._tempCounter.save++;
|
|
735
|
-
await this.saveBricksAsync(saveFile, region);
|
|
736
|
-
// read the save file
|
|
737
|
-
const savePath = this.getSavePath(saveFile);
|
|
738
|
-
if (savePath) {
|
|
739
|
-
// read and parse the save file
|
|
740
|
-
const saveData = (0, brs_js_1.read)((0, fs_1.readFileSync)(savePath));
|
|
741
|
-
// delete the save file after we're done reading it
|
|
742
|
-
(0, fs_1.unlinkSync)(savePath);
|
|
743
|
-
// return the parsed save
|
|
744
|
-
return saveData;
|
|
745
|
-
}
|
|
746
|
-
return undefined;
|
|
747
|
-
}
|
|
748
|
-
// TODO: switch this to use worlds...
|
|
749
|
-
async changeMap(map) {
|
|
750
|
-
if (!map)
|
|
751
|
-
return;
|
|
752
|
-
// ServerTravel requires /Game/Maps/Plate/Plate instead of Plate
|
|
753
|
-
const brName = _util_1.map.n2brn(map);
|
|
754
|
-
// wait for the server to change maps
|
|
755
|
-
const match = await this.addWatcher(/^.*(LogLoad: Took .+ seconds to LoadMap\((?<map>.+)\))|(ERROR: The map .+)$/, {
|
|
756
|
-
timeoutDelay: 30000,
|
|
757
|
-
exec: () => this.writeln(`ServerTravel ${brName}`),
|
|
758
|
-
});
|
|
759
|
-
const success = !!(match &&
|
|
760
|
-
match[0] &&
|
|
761
|
-
match[0].groups &&
|
|
762
|
-
match[0].groups.map);
|
|
763
|
-
return success;
|
|
764
|
-
}
|
|
765
|
-
async getPlugin(name) {
|
|
766
|
-
const plugin = this.pluginLoader.plugins.find(p => p.getName() === name);
|
|
767
|
-
if (plugin) {
|
|
768
|
-
return {
|
|
769
|
-
name,
|
|
770
|
-
documentation: plugin.getDocumentation(),
|
|
771
|
-
loaded: plugin.isLoaded(),
|
|
772
|
-
emitPlugin: (event, ...args) => {
|
|
773
|
-
return plugin.emitPlugin(event, 'unsafe', args);
|
|
774
|
-
},
|
|
775
|
-
};
|
|
776
|
-
}
|
|
777
|
-
else {
|
|
778
|
-
return null;
|
|
779
|
-
}
|
|
780
|
-
}
|
|
514
|
+
}
|
|
515
|
+
const revision = match.groups.revision ? Number(match.groups.revision) : 0;
|
|
516
|
+
const dateStr = match.groups.date.replace(
|
|
517
|
+
/(\d{4})\.(\d{2})\.(\d{2})-(\d{2})\.(\d{2})\.(\d{2})/,
|
|
518
|
+
"$1-$2-$3T$4:$5:$6Z"
|
|
519
|
+
);
|
|
520
|
+
const date = new Date(dateStr);
|
|
521
|
+
const note = match.groups.note || "";
|
|
522
|
+
return { index: revision, date, note };
|
|
523
|
+
}).filter(Boolean);
|
|
524
|
+
}
|
|
525
|
+
async loadWorld(worldName) {
|
|
526
|
+
worldName = worldName.replace(/\.brdb$/i, "");
|
|
527
|
+
if (!worldName || !this.getWorldPath(worldName)) return false;
|
|
528
|
+
this.writeln(`BR.World.Load "${worldName}"`);
|
|
529
|
+
const res = await Promise.race([
|
|
530
|
+
// wait for the map to change
|
|
531
|
+
new Promise(
|
|
532
|
+
(resolve) => this.once("mapchange", () => resolve("mapchange"))
|
|
533
|
+
),
|
|
534
|
+
// Timeout after 10 seconds
|
|
535
|
+
new Promise((resolve) => setTimeout(() => resolve("timeout"), 1e4))
|
|
536
|
+
]);
|
|
537
|
+
logger.default.verbose("LoadWorld", worldName, "result:", res);
|
|
538
|
+
return res === "mapchange";
|
|
539
|
+
}
|
|
540
|
+
async loadWorldRevision(worldName, revision) {
|
|
541
|
+
worldName = worldName.replace(/\.brdb$/i, "");
|
|
542
|
+
if (!worldName || !this.getWorldPath(worldName)) return false;
|
|
543
|
+
if (typeof revision !== "number" || revision < 1) {
|
|
544
|
+
throw new Error(`Invalid revision number: ${revision}`);
|
|
545
|
+
}
|
|
546
|
+
this.writeln(`BR.World.LoadRevision "${worldName}" ${revision}`);
|
|
547
|
+
const res = await Promise.race([
|
|
548
|
+
// wait for the map to change
|
|
549
|
+
new Promise(
|
|
550
|
+
(resolve) => this.once("mapchange", () => resolve("mapchange"))
|
|
551
|
+
),
|
|
552
|
+
// Timeout after 10 seconds
|
|
553
|
+
new Promise((resolve) => setTimeout(() => resolve("timeout"), 1e4))
|
|
554
|
+
]);
|
|
555
|
+
logger.default.verbose("LoadWorld", worldName, "result:", res);
|
|
556
|
+
return res === "mapchange";
|
|
557
|
+
}
|
|
558
|
+
async saveWorldAs(worldName) {
|
|
559
|
+
if (!worldName) return false;
|
|
560
|
+
if (this.stopping || this.starting || !this.started) return false;
|
|
561
|
+
if (this.getWorldPath(worldName)) {
|
|
562
|
+
return false;
|
|
563
|
+
}
|
|
564
|
+
worldName = worldName.replace(/\.brdb$/i, "");
|
|
565
|
+
try {
|
|
566
|
+
const match = await this.addWatcher(
|
|
567
|
+
(_line, match2) => {
|
|
568
|
+
if (match2?.groups?.generator !== "LogBRWorldManager") return;
|
|
569
|
+
const ok = match2.groups.data.match(/^World files saved after /);
|
|
570
|
+
const err = !match2.groups.data.startsWith(
|
|
571
|
+
"Error: Failed to capture minigame settings"
|
|
572
|
+
) && match2.groups.data.match(
|
|
573
|
+
/^Error: (World already exists|Failed to create new world)?/
|
|
574
|
+
);
|
|
575
|
+
return ok ? { res: true } : err ? { res: false } : void 0;
|
|
576
|
+
},
|
|
577
|
+
{
|
|
578
|
+
exec: () => {
|
|
579
|
+
this.writeln(`BR.World.SaveAs "${worldName}"`);
|
|
580
|
+
},
|
|
581
|
+
timeoutDelay: 2e3
|
|
582
|
+
}
|
|
583
|
+
);
|
|
584
|
+
return match?.[0]?.["res"] ?? false;
|
|
585
|
+
} catch (err) {
|
|
586
|
+
return false;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
async saveWorld() {
|
|
590
|
+
if (this.stopping || this.starting || !this.started) return false;
|
|
591
|
+
try {
|
|
592
|
+
const match = await this.addWatcher(
|
|
593
|
+
(_line, match2) => {
|
|
594
|
+
if (match2?.groups?.generator !== "LogBRWorldManager") return;
|
|
595
|
+
const ok = match2.groups.data.match(/^World files saved after /);
|
|
596
|
+
const err = !match2.groups.data.startsWith(
|
|
597
|
+
"Error: Failed to capture minigame settings"
|
|
598
|
+
) && match2.groups.data.match(/^Error: (World has not been saved\.)?/);
|
|
599
|
+
return ok ? { res: true } : err ? { res: false } : void 0;
|
|
600
|
+
},
|
|
601
|
+
{
|
|
602
|
+
exec: () => {
|
|
603
|
+
this.writeln(`BR.World.Save 0`);
|
|
604
|
+
},
|
|
605
|
+
timeoutDelay: 2e3
|
|
606
|
+
}
|
|
607
|
+
);
|
|
608
|
+
return match?.[0]?.["res"] ?? false;
|
|
609
|
+
} catch (err) {
|
|
610
|
+
return false;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
async createEmptyWorld(worldName, map = "Plate") {
|
|
614
|
+
if (!worldName) return;
|
|
615
|
+
worldName = worldName.replace(/\.brdb$/i, "");
|
|
616
|
+
try {
|
|
617
|
+
const match = await this.addWatcher(
|
|
618
|
+
(_line, match2) => {
|
|
619
|
+
if (match2?.groups?.generator !== "LogBRWorldManager") return;
|
|
620
|
+
const ok = match2.groups.data.match(/^World files saved after /);
|
|
621
|
+
const err = match2.groups.data.match(
|
|
622
|
+
/^Error: (Invalid preset|World already exists|Failed to create new world)?/
|
|
623
|
+
);
|
|
624
|
+
return ok ? { res: true } : err ? { res: false } : void 0;
|
|
625
|
+
},
|
|
626
|
+
{
|
|
627
|
+
exec: () => {
|
|
628
|
+
this.writeln(`BR.World.CreateEmpty "${worldName}" ${map}`);
|
|
629
|
+
},
|
|
630
|
+
timeoutDelay: 2e3
|
|
631
|
+
}
|
|
632
|
+
);
|
|
633
|
+
return match?.[0]?.["res"] ?? false;
|
|
634
|
+
} catch (err) {
|
|
635
|
+
return false;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
writeSaveData(saveName, saveData) {
|
|
639
|
+
if (typeof saveName !== "string")
|
|
640
|
+
throw "expected name argument for writeSaveData";
|
|
641
|
+
const file2 = path.join(this.savePath, saveName + ".brs");
|
|
642
|
+
if (!file2.startsWith(this.savePath))
|
|
643
|
+
throw "save file not in Saved/Builds directory";
|
|
644
|
+
fs.writeFileSync(file2, new Uint8Array(brs.write(saveData)));
|
|
645
|
+
}
|
|
646
|
+
readSaveData(saveName, nobricks = false) {
|
|
647
|
+
if (typeof saveName !== "string")
|
|
648
|
+
throw "expected name argument for readSaveData";
|
|
649
|
+
const file2 = this.getSavePath(saveName);
|
|
650
|
+
if (!file2 || !file2.startsWith(this.savePath))
|
|
651
|
+
throw "save file not in Saved/Builds directory";
|
|
652
|
+
if (file2)
|
|
653
|
+
return brs.read(fs.readFileSync(file2), {
|
|
654
|
+
preview: false,
|
|
655
|
+
bricks: !nobricks
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
async loadSaveData(saveData, {
|
|
659
|
+
offX = 0,
|
|
660
|
+
offY = 0,
|
|
661
|
+
offZ = 0,
|
|
662
|
+
quiet = false,
|
|
663
|
+
correctPalette = false,
|
|
664
|
+
correctCustom = false
|
|
665
|
+
} = {}) {
|
|
666
|
+
const saveFile = this._tempSavePrefix + Date.now() + "_" + this._tempCounter.save++;
|
|
667
|
+
this.writeSaveData(saveFile, saveData);
|
|
668
|
+
await this.watchLogChunk(
|
|
669
|
+
`Bricks.Load "${saveFile}" ${offX} ${offY} ${offZ} ${quiet ? 1 : 0} ${correctPalette ? 1 : 0} ${correctCustom ? 1 : 0}`,
|
|
670
|
+
/^LogBrickSerializer: (.+)$/,
|
|
671
|
+
{
|
|
672
|
+
first: (match) => match[0].endsWith(saveFile + ".brs..."),
|
|
673
|
+
last: (match) => Boolean(match[1].match(/Read .+ bricks/)),
|
|
674
|
+
afterMatchDelay: 0,
|
|
675
|
+
timeoutDelay: 3e4
|
|
676
|
+
}
|
|
677
|
+
);
|
|
678
|
+
const savePath = this.getSavePath(saveFile);
|
|
679
|
+
if (savePath) {
|
|
680
|
+
fs.unlinkSync(savePath);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
async loadSaveDataOnPlayer(saveData, player, {
|
|
684
|
+
offX = 0,
|
|
685
|
+
offY = 0,
|
|
686
|
+
offZ = 0,
|
|
687
|
+
correctPalette = false,
|
|
688
|
+
correctCustom = false
|
|
689
|
+
} = {}) {
|
|
690
|
+
player = typeof player === "string" ? this.getPlayer(player) : player;
|
|
691
|
+
if (!player) return;
|
|
692
|
+
const saveFile = this._tempSavePrefix + Date.now() + "_" + this._tempCounter.save++;
|
|
693
|
+
this.writeSaveData(saveFile, saveData);
|
|
694
|
+
await this.watchLogChunk(
|
|
695
|
+
`Bricks.LoadTemplate "${saveFile}" ${offX} ${offY} ${offZ} ${correctPalette ? 1 : 0} ${correctCustom ? 1 : 0} "${player.name}"`,
|
|
696
|
+
/^LogBrickSerializer: (.+)$/,
|
|
697
|
+
{
|
|
698
|
+
first: (match) => match[0].endsWith(saveFile + ".brs..."),
|
|
699
|
+
last: (match) => Boolean(match[1].match(/Read .+ bricks/)),
|
|
700
|
+
afterMatchDelay: 0,
|
|
701
|
+
timeoutDelay: 3e4
|
|
702
|
+
}
|
|
703
|
+
);
|
|
704
|
+
const savePath = this.getSavePath(saveFile);
|
|
705
|
+
if (savePath) {
|
|
706
|
+
fs.unlinkSync(savePath);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
async getSaveData(region) {
|
|
710
|
+
const saveFile = this._tempSavePrefix + Date.now() + "_" + this._tempCounter.save++;
|
|
711
|
+
await this.saveBricksAsync(saveFile, region);
|
|
712
|
+
const savePath = this.getSavePath(saveFile);
|
|
713
|
+
if (savePath) {
|
|
714
|
+
const saveData = brs.read(fs.readFileSync(savePath));
|
|
715
|
+
fs.unlinkSync(savePath);
|
|
716
|
+
return saveData;
|
|
717
|
+
}
|
|
718
|
+
return void 0;
|
|
719
|
+
}
|
|
720
|
+
// TODO: switch this to use worlds...
|
|
721
|
+
async changeMap(map) {
|
|
722
|
+
if (!map) return;
|
|
723
|
+
const brName = index$2.map.n2brn(map);
|
|
724
|
+
const match = await this.addWatcher(
|
|
725
|
+
/^.*(LogLoad: Took .+ seconds to LoadMap\((?<map>.+)\))|(ERROR: The map .+)$/,
|
|
726
|
+
{
|
|
727
|
+
timeoutDelay: 3e4,
|
|
728
|
+
exec: () => this.writeln(`ServerTravel ${brName}`)
|
|
729
|
+
}
|
|
730
|
+
);
|
|
731
|
+
const success = !!(match && match[0] && match[0].groups && match[0].groups.map);
|
|
732
|
+
return success;
|
|
733
|
+
}
|
|
734
|
+
async getPlugin(name) {
|
|
735
|
+
const plugin2 = this.pluginLoader.plugins.find((p) => p.getName() === name);
|
|
736
|
+
if (plugin2) {
|
|
737
|
+
return {
|
|
738
|
+
name,
|
|
739
|
+
documentation: plugin2.getDocumentation(),
|
|
740
|
+
loaded: plugin2.isLoaded(),
|
|
741
|
+
emitPlugin: (event, ...args) => {
|
|
742
|
+
return plugin2.emitPlugin(event, "unsafe", args);
|
|
743
|
+
}
|
|
744
|
+
};
|
|
745
|
+
} else {
|
|
746
|
+
return null;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
781
749
|
}
|
|
782
750
|
exports.default = Omegga;
|
|
783
|
-
//# sourceMappingURL=server.js.map
|
|
751
|
+
//# sourceMappingURL=server.js.map
|