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.
Files changed (255) hide show
  1. package/README.md +20 -1
  2. package/dist/brickadia/config.js +26 -90
  3. package/dist/brickadia/config.js.map +1 -1
  4. package/dist/brickadia/server.js +294 -293
  5. package/dist/brickadia/server.js.map +1 -1
  6. package/dist/cli/auth.js +149 -177
  7. package/dist/cli/auth.js.map +1 -1
  8. package/dist/cli/config.js +64 -66
  9. package/dist/cli/config.js.map +1 -1
  10. package/dist/cli/index.js +14 -14
  11. package/dist/cli/index.js.map +1 -1
  12. package/dist/cli/installer.js +37 -43
  13. package/dist/cli/installer.js.map +1 -1
  14. package/dist/cli/plugin.js +651 -682
  15. package/dist/cli/plugin.js.map +1 -1
  16. package/dist/cli/terminal.js +811 -733
  17. package/dist/cli/terminal.js.map +1 -1
  18. package/dist/config/formats/format_js.js +6 -6
  19. package/dist/config/formats/format_js.js.map +1 -1
  20. package/dist/config/formats/format_yml.js +7 -10
  21. package/dist/config/formats/format_yml.js.map +1 -1
  22. package/dist/config/index.js +46 -68
  23. package/dist/config/index.js.map +1 -1
  24. package/dist/config/reader.js +16 -27
  25. package/dist/config/reader.js.map +1 -1
  26. package/dist/config/validator.js +23 -25
  27. package/dist/config/validator.js.map +1 -1
  28. package/dist/config/writer.js +11 -21
  29. package/dist/config/writer.js.map +1 -1
  30. package/dist/index.js +18 -64
  31. package/dist/index.js.map +1 -1
  32. package/dist/info/default_commands.json.js +39 -0
  33. package/dist/info/default_commands.json.js.map +1 -0
  34. package/dist/logger.js +61 -60
  35. package/dist/logger.js.map +1 -1
  36. package/dist/main.js +424 -471
  37. package/dist/main.js.map +1 -1
  38. package/dist/omegga/auth.js +101 -148
  39. package/dist/omegga/auth.js.map +1 -1
  40. package/dist/omegga/commandInjector.js +261 -251
  41. package/dist/omegga/commandInjector.js.map +1 -1
  42. package/dist/omegga/index.js +14 -52
  43. package/dist/omegga/index.js.map +1 -1
  44. package/dist/omegga/logWrangler.js +224 -275
  45. package/dist/omegga/logWrangler.js.map +1 -1
  46. package/dist/omegga/matchers/auth.js +36 -50
  47. package/dist/omegga/matchers/auth.js.map +1 -1
  48. package/dist/omegga/matchers/chat.js +49 -71
  49. package/dist/omegga/matchers/chat.js.map +1 -1
  50. package/dist/omegga/matchers/command.js +25 -36
  51. package/dist/omegga/matchers/command.js.map +1 -1
  52. package/dist/omegga/matchers/exit.js +15 -18
  53. package/dist/omegga/matchers/exit.js.map +1 -1
  54. package/dist/omegga/matchers/index.js +26 -40
  55. package/dist/omegga/matchers/index.js.map +1 -1
  56. package/dist/omegga/matchers/init.js +20 -23
  57. package/dist/omegga/matchers/init.js.map +1 -1
  58. package/dist/omegga/matchers/interact.js +58 -64
  59. package/dist/omegga/matchers/interact.js.map +1 -1
  60. package/dist/omegga/matchers/join.js +74 -95
  61. package/dist/omegga/matchers/join.js.map +1 -1
  62. package/dist/omegga/matchers/leave.js +43 -56
  63. package/dist/omegga/matchers/leave.js.map +1 -1
  64. package/dist/omegga/matchers/mapChange.js +26 -35
  65. package/dist/omegga/matchers/mapChange.js.map +1 -1
  66. package/dist/omegga/matchers/minigameJoin.js +24 -31
  67. package/dist/omegga/matchers/minigameJoin.js.map +1 -1
  68. package/dist/omegga/matchers/version.js +42 -49
  69. package/dist/omegga/matchers/version.js.map +1 -1
  70. package/dist/omegga/player.js +527 -527
  71. package/dist/omegga/player.js.map +1 -1
  72. package/dist/omegga/plugin/interface.js +91 -98
  73. package/dist/omegga/plugin/interface.js.map +1 -1
  74. package/dist/omegga/plugin/plugin_jsonrpc_stdio.js +524 -429
  75. package/dist/omegga/plugin/plugin_jsonrpc_stdio.js.map +1 -1
  76. package/dist/omegga/plugin/plugin_node_safe/proxyOmegga.js +282 -294
  77. package/dist/omegga/plugin/plugin_node_safe/proxyOmegga.js.map +1 -1
  78. package/dist/omegga/plugin/plugin_node_safe.js +365 -393
  79. package/dist/omegga/plugin/plugin_node_safe.js.map +1 -1
  80. package/dist/omegga/plugin/plugin_node_unsafe.js +119 -179
  81. package/dist/omegga/plugin/plugin_node_unsafe.js.map +1 -1
  82. package/dist/omegga/plugin.js +402 -347
  83. package/dist/omegga/plugin.js.map +1 -1
  84. package/dist/omegga/server.js +742 -774
  85. package/dist/omegga/server.js.map +1 -1
  86. package/dist/omegga/wrapper.js +64 -91
  87. package/dist/omegga/wrapper.js.map +1 -1
  88. package/dist/plugin.js +11 -31
  89. package/dist/plugin.js.map +1 -1
  90. package/dist/softconfig.js +121 -100
  91. package/dist/softconfig.js.map +1 -1
  92. package/dist/updater/steam.js +97 -112
  93. package/dist/updater/steam.js.map +1 -1
  94. package/dist/util/brdb.js +496 -528
  95. package/dist/util/brdb.js.map +1 -1
  96. package/dist/util/brick/checkBounds.js +11 -15
  97. package/dist/util/brick/checkBounds.js.map +1 -1
  98. package/dist/util/brick/constants.js +723 -174
  99. package/dist/util/brick/constants.js.map +1 -1
  100. package/dist/util/brick/displayName.js +204 -211
  101. package/dist/util/brick/displayName.js.map +1 -1
  102. package/dist/util/brick/getBounds.js +29 -41
  103. package/dist/util/brick/getBounds.js.map +1 -1
  104. package/dist/util/brick/getBrickSize.js +6 -7
  105. package/dist/util/brick/getBrickSize.js.map +1 -1
  106. package/dist/util/brick/getScaleAxis.js +24 -30
  107. package/dist/util/brick/getScaleAxis.js.map +1 -1
  108. package/dist/util/brick/index.js +8 -29
  109. package/dist/util/brick/index.js.map +1 -1
  110. package/dist/util/brick/rotate.js +29 -34
  111. package/dist/util/brick/rotate.js.map +1 -1
  112. package/dist/util/brick/setOwnership.js +13 -19
  113. package/dist/util/brick/setOwnership.js.map +1 -1
  114. package/dist/util/chat.js +53 -44
  115. package/dist/util/chat.js.map +1 -1
  116. package/dist/util/color.js +112 -124
  117. package/dist/util/color.js.map +1 -1
  118. package/dist/util/file.js +54 -98
  119. package/dist/util/file.js.map +1 -1
  120. package/dist/util/index.js +84 -106
  121. package/dist/util/index.js.map +1 -1
  122. package/dist/util/map.js +18 -20
  123. package/dist/util/map.js.map +1 -1
  124. package/dist/util/pattern.js +6 -9
  125. package/dist/util/pattern.js.map +1 -1
  126. package/dist/util/time.js +31 -34
  127. package/dist/util/time.js.map +1 -1
  128. package/dist/util/uuid.js +10 -8
  129. package/dist/util/uuid.js.map +1 -1
  130. package/dist/util/wsl.js +19 -25
  131. package/dist/util/wsl.js.map +1 -1
  132. package/dist/version.js +7 -9
  133. package/dist/version.js.map +1 -1
  134. package/dist/webserver/backend/api.js +784 -874
  135. package/dist/webserver/backend/api.js.map +1 -1
  136. package/dist/webserver/backend/calendar.js +14 -17
  137. package/dist/webserver/backend/calendar.js.map +1 -1
  138. package/dist/webserver/backend/database.js +563 -613
  139. package/dist/webserver/backend/database.js.map +1 -1
  140. package/dist/webserver/backend/index.js +153 -191
  141. package/dist/webserver/backend/index.js.map +1 -1
  142. package/dist/webserver/backend/metrics.js +219 -248
  143. package/dist/webserver/backend/metrics.js.map +1 -1
  144. package/dist/webserver/backend/util.js +47 -68
  145. package/dist/webserver/backend/util.js.map +1 -1
  146. package/dist/worker.js +322 -0
  147. package/dist/worker.js.map +1 -0
  148. package/package.json +14 -10
  149. package/templates/safe-ts/omegga.d.ts +2 -2
  150. package/vite.backend.config.mts +46 -0
  151. package/vite.frontend.config.mts +58 -0
  152. package/vitest.backend.config.mts +7 -0
  153. package/dist/brickadia/config.d.ts +0 -3
  154. package/dist/brickadia/presets.d.ts +0 -65
  155. package/dist/brickadia/presets.js +0 -3
  156. package/dist/brickadia/presets.js.map +0 -1
  157. package/dist/brickadia/server.d.ts +0 -36
  158. package/dist/brickadia/types.d.ts +0 -45
  159. package/dist/brickadia/types.js +0 -3
  160. package/dist/brickadia/types.js.map +0 -1
  161. package/dist/cli/auth.d.ts +0 -26
  162. package/dist/cli/config.d.ts +0 -10
  163. package/dist/cli/index.d.ts +0 -30
  164. package/dist/cli/installer.d.ts +0 -1
  165. package/dist/cli/plugin.d.ts +0 -22
  166. package/dist/cli/terminal.d.ts +0 -25
  167. package/dist/config/authorizer.d.ts +0 -2
  168. package/dist/config/authorizer.js +0 -38
  169. package/dist/config/authorizer.js.map +0 -1
  170. package/dist/config/formats/format_js.d.ts +0 -3
  171. package/dist/config/formats/format_yml.d.ts +0 -3
  172. package/dist/config/index.d.ts +0 -18
  173. package/dist/config/reader.d.ts +0 -3
  174. package/dist/config/types.d.ts +0 -50
  175. package/dist/config/types.js +0 -3
  176. package/dist/config/types.js.map +0 -1
  177. package/dist/config/validator.d.ts +0 -6
  178. package/dist/config/writer.d.ts +0 -3
  179. package/dist/index.d.ts +0 -22
  180. package/dist/info/default_commands.json +0 -29
  181. package/dist/logger.d.ts +0 -41
  182. package/dist/main.d.ts +0 -1
  183. package/dist/omegga/auth.d.ts +0 -9
  184. package/dist/omegga/commandInjector.d.ts +0 -35
  185. package/dist/omegga/index.d.ts +0 -7
  186. package/dist/omegga/logWrangler.d.ts +0 -32
  187. package/dist/omegga/matchers/auth.d.ts +0 -10
  188. package/dist/omegga/matchers/chat.d.ts +0 -10
  189. package/dist/omegga/matchers/command.d.ts +0 -7
  190. package/dist/omegga/matchers/exit.d.ts +0 -3
  191. package/dist/omegga/matchers/index.d.ts +0 -3
  192. package/dist/omegga/matchers/init.d.ts +0 -3
  193. package/dist/omegga/matchers/interact.d.ts +0 -4
  194. package/dist/omegga/matchers/join.d.ts +0 -4
  195. package/dist/omegga/matchers/leave.d.ts +0 -4
  196. package/dist/omegga/matchers/mapChange.d.ts +0 -3
  197. package/dist/omegga/matchers/minigameJoin.d.ts +0 -9
  198. package/dist/omegga/matchers/types.d.ts +0 -5
  199. package/dist/omegga/matchers/types.js +0 -3
  200. package/dist/omegga/matchers/types.js.map +0 -1
  201. package/dist/omegga/matchers/version.d.ts +0 -3
  202. package/dist/omegga/player.d.ts +0 -81
  203. package/dist/omegga/plugin/interface.d.ts +0 -30
  204. package/dist/omegga/plugin/plugin_jsonrpc_stdio.d.ts +0 -23
  205. package/dist/omegga/plugin/plugin_node_safe/proxyOmegga.d.ts +0 -145
  206. package/dist/omegga/plugin/plugin_node_safe/worker.d.ts +0 -1
  207. package/dist/omegga/plugin/plugin_node_safe/worker.js +0 -367
  208. package/dist/omegga/plugin/plugin_node_safe/worker.js.map +0 -1
  209. package/dist/omegga/plugin/plugin_node_safe.d.ts +0 -23
  210. package/dist/omegga/plugin/plugin_node_unsafe.d.ts +0 -13
  211. package/dist/omegga/plugin.d.ts +0 -63
  212. package/dist/omegga/server.d.ts +0 -166
  213. package/dist/omegga/types.d.ts +0 -49
  214. package/dist/omegga/types.js +0 -3
  215. package/dist/omegga/types.js.map +0 -1
  216. package/dist/omegga/wrapper.d.ts +0 -32
  217. package/dist/plugin.d.ts +0 -916
  218. package/dist/softconfig.d.ts +0 -67
  219. package/dist/tsconfig.tsbuildinfo +0 -1
  220. package/dist/updater/index.d.ts +0 -1
  221. package/dist/updater/index.js +0 -10
  222. package/dist/updater/index.js.map +0 -1
  223. package/dist/updater/steam.d.ts +0 -52
  224. package/dist/util/brdb.d.ts +0 -31
  225. package/dist/util/brick/checkBounds.d.ts +0 -8
  226. package/dist/util/brick/constants.d.ts +0 -18
  227. package/dist/util/brick/displayName.d.ts +0 -7
  228. package/dist/util/brick/getBounds.d.ts +0 -5
  229. package/dist/util/brick/getBrickSize.d.ts +0 -3
  230. package/dist/util/brick/getScaleAxis.d.ts +0 -3
  231. package/dist/util/brick/index.d.ts +0 -14
  232. package/dist/util/brick/rotate.d.ts +0 -16
  233. package/dist/util/brick/setOwnership.d.ts +0 -12
  234. package/dist/util/chat.d.ts +0 -23
  235. package/dist/util/color.d.ts +0 -13
  236. package/dist/util/file.d.ts +0 -5
  237. package/dist/util/index.d.ts +0 -183
  238. package/dist/util/map.d.ts +0 -6
  239. package/dist/util/pattern.d.ts +0 -1
  240. package/dist/util/time.d.ts +0 -39
  241. package/dist/util/uuid.d.ts +0 -3
  242. package/dist/util/wsl.d.ts +0 -2
  243. package/dist/version.d.ts +0 -2
  244. package/dist/webserver/backend/api.d.ts +0 -115
  245. package/dist/webserver/backend/calendar.d.ts +0 -5
  246. package/dist/webserver/backend/database.d.ts +0 -150
  247. package/dist/webserver/backend/index.d.ts +0 -30
  248. package/dist/webserver/backend/metrics.d.ts +0 -3
  249. package/dist/webserver/backend/types.d.ts +0 -114
  250. package/dist/webserver/backend/types.js +0 -3
  251. package/dist/webserver/backend/types.js.map +0 -1
  252. package/dist/webserver/backend/util.d.ts +0 -2
  253. package/dist/webserver/index.d.ts +0 -2
  254. package/dist/webserver/index.js +0 -9
  255. package/dist/webserver/index.js.map +0 -1
@@ -1,371 +1,426 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.PluginLoader = exports.PluginStorage = void 0;
7
- const logger_1 = __importDefault(require("../logger"));
8
- const softconfig_1 = __importDefault(require("../softconfig"));
9
- const fs_1 = __importDefault(require("fs"));
10
- const nedb_promises_1 = __importDefault(require("nedb-promises"));
11
- const path_1 = __importDefault(require("path"));
12
- const plugin_jsonrpc_stdio_1 = __importDefault(require("./plugin/plugin_jsonrpc_stdio"));
13
- const plugin_node_safe_1 = __importDefault(require("./plugin/plugin_node_safe"));
14
- const plugin_node_unsafe_1 = __importDefault(require("./plugin/plugin_node_unsafe"));
15
- // key-value storage for a plugin
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const logger = require("../logger.js");
4
+ const softconfig = require("../softconfig.js");
5
+ const Datastore = require("nedb-promises");
6
+ const fs = require("node:fs");
7
+ const path = require("node:path");
8
+ const plugin_jsonrpc_stdio = require("./plugin/plugin_jsonrpc_stdio.js");
9
+ const plugin_node_safe = require("./plugin/plugin_node_safe.js");
10
+ const plugin_node_unsafe = require("./plugin/plugin_node_unsafe.js");
16
11
  class PluginStorage {
17
- constructor(store, plugin) {
18
- this.store = store;
19
- this.plugin = plugin;
20
- this.name = plugin.getName();
12
+ constructor(store, plugin) {
13
+ this.store = store;
14
+ this.plugin = plugin;
15
+ this.name = plugin.getName();
16
+ }
17
+ // get the initial config values
18
+ getDefaultConfig() {
19
+ const doc = this.plugin.getDocumentation();
20
+ if (!doc) return {};
21
+ if (!doc.config) return {};
22
+ const config = {};
23
+ for (const k in doc.config) {
24
+ config[k] = doc.config[k].default;
21
25
  }
22
- // get the initial config values
23
- getDefaultConfig() {
24
- const doc = this.plugin.getDocumentation();
25
- // check if the documentation object exists and whether it has a config field
26
- if (!doc)
27
- return {};
28
- if (!doc.config)
29
- return {};
30
- // insert values from default config values
31
- const config = {};
32
- for (const k in doc.config) {
33
- config[k] = doc.config[k].default;
34
- }
35
- return config;
36
- }
37
- // set the config field for this plugin
38
- async setConfig(value) {
39
- await this.store.update({ type: 'config', plugin: this.name }, {
40
- $set: { value },
41
- }, { upsert: true });
42
- }
43
- // get the config for this plugin
44
- async getConfig() {
45
- const config = await this.store.findOne({
46
- type: 'config',
47
- plugin: this.name,
48
- });
49
- if (!config)
50
- return null;
51
- return config.value;
52
- }
53
- /** Wipes the configs for the plugin. */
54
- async wipeConfig() {
55
- await this.store.remove({ type: 'config', plugin: this.name }, { multi: true });
26
+ return config;
27
+ }
28
+ // set the config field for this plugin
29
+ async setConfig(value) {
30
+ await this.store.update(
31
+ { type: "config", plugin: this.name },
32
+ {
33
+ $set: { value }
34
+ },
35
+ { upsert: true }
36
+ );
37
+ }
38
+ // get the config for this plugin
39
+ async getConfig() {
40
+ const config = await this.store.findOne({
41
+ type: "config",
42
+ plugin: this.name
43
+ });
44
+ if (!config) return null;
45
+ return config.value;
46
+ }
47
+ /** Wipes the configs for the plugin. */
48
+ async wipeConfig() {
49
+ await this.store.remove(
50
+ { type: "config", plugin: this.name },
51
+ { multi: true }
52
+ );
53
+ }
54
+ /** Initializes the configs for the plugin into the database and sets default configs. */
55
+ async init() {
56
+ const defaultConf = this.getDefaultConfig();
57
+ const config = await this.getConfig();
58
+ await this.setConfig({
59
+ // use default config
60
+ ...defaultConf,
61
+ // override with actual config values
62
+ ...config ? config : {}
63
+ });
64
+ }
65
+ // get a stored object
66
+ async get(key) {
67
+ if (typeof key !== "string" || key.length === 0) return;
68
+ const obj = await this.store.findOne({
69
+ type: "store",
70
+ plugin: this.name,
71
+ key
72
+ });
73
+ if (!obj) return null;
74
+ return obj.value;
75
+ }
76
+ // delete a stored object
77
+ async delete(key) {
78
+ if (typeof key !== "string" || key.length === 0) return;
79
+ await this.store.remove({ type: "store", plugin: this.name, key }, {});
80
+ }
81
+ // clear all stored values
82
+ async wipe() {
83
+ await this.store.remove(
84
+ { type: "store", plugin: this.name },
85
+ { multi: true }
86
+ );
87
+ }
88
+ // count number of objects in store
89
+ async count() {
90
+ return await this.store.count({ type: "store", plugin: this.name });
91
+ }
92
+ // get keys of all objects in store
93
+ async keys() {
94
+ const objects = await this.store.find({
95
+ type: "store",
96
+ plugin: this.name
97
+ });
98
+ return objects.map((o) => o.key);
99
+ }
100
+ // set a stored value
101
+ async set(key, value) {
102
+ if (typeof key !== "string" || key.length === 0) return;
103
+ await this.store.update(
104
+ { type: "store", plugin: this.name, key },
105
+ { $set: { value } },
106
+ { upsert: true }
107
+ );
108
+ }
109
+ }
110
+ class PluginLoader {
111
+ constructor(workDir, omegga) {
112
+ this.path = path.join(workDir, softconfig.default.PLUGIN_PATH);
113
+ this.omegga = omegga;
114
+ this.store = Datastore.create({
115
+ filename: path.join(workDir, softconfig.default.DATA_PATH, softconfig.default.PLUGIN_STORE),
116
+ autoload: true
117
+ });
118
+ this.store.persistence.setAutocompactionInterval(1e3 * 60 * 5);
119
+ this.plugins = [];
120
+ logger.default.verbose("Loading plugin formats");
121
+ this.formats = [plugin_jsonrpc_stdio.default, plugin_node_safe.default, plugin_node_unsafe.default];
122
+ logger.default.verbose("Found plugin formats:", this.formats);
123
+ if (omegga) {
124
+ omegga.on("chatcmd:help", this.showHelp.bind(this));
125
+ omegga.on("cmd:plugins", this.showHelp.bind(this));
56
126
  }
57
- /** Initializes the configs for the plugin into the database and sets default configs. */
58
- async init() {
59
- // get default and configured values
60
- const defaultConf = this.getDefaultConfig();
61
- const config = await this.getConfig();
62
- await this.setConfig({
63
- // use default config
64
- ...defaultConf,
65
- // override with actual config values
66
- ...(config ? config : {}),
67
- });
127
+ }
128
+ // determine if this command is a command on the plugin
129
+ isCommand(cmd) {
130
+ return cmd === "plugins" || this.plugins.some((p) => p.isCommand(cmd));
131
+ }
132
+ // unload and load all installed plugins
133
+ async reload() {
134
+ let ok = true;
135
+ logger.default.verbose("Reloading plugins");
136
+ for (const p of this.plugins) {
137
+ try {
138
+ if (p.isLoaded()) await p.unload();
139
+ if (!p.isLoaded()) {
140
+ if (p.isEnabled()) {
141
+ logger.default.verbose(
142
+ "Loading plugin",
143
+ p.constructor.getFormat(),
144
+ p.getName().underline
145
+ );
146
+ ok = await p.load() || ok;
147
+ }
148
+ } else {
149
+ logger.default.errorp(
150
+ "Did not successfully unload plugin",
151
+ p.getName().brightRed.underline
152
+ );
153
+ ok = false;
154
+ }
155
+ } catch (err) {
156
+ logger.default.errorp(
157
+ "Error loading plugin",
158
+ p.getName().brightRed.underline,
159
+ err
160
+ );
161
+ ok = false;
162
+ }
68
163
  }
69
- // get a stored object
70
- async get(key) {
71
- if (typeof key !== 'string' || key.length === 0)
72
- return;
73
- const obj = await this.store.findOne({
74
- type: 'store',
75
- plugin: this.name,
76
- key,
77
- });
78
- if (!obj)
79
- return null;
80
- return obj.value;
164
+ logger.default.verbose("Finished reloading plugins");
165
+ return ok;
166
+ }
167
+ // stop all plugins from running
168
+ async unload() {
169
+ logger.default.verbose("Unloading plugins");
170
+ let ok = true;
171
+ for (const p of this.plugins) {
172
+ try {
173
+ if (p.isLoaded()) {
174
+ if (!await p.unload()) {
175
+ logger.default.errorp(
176
+ "Could not unloading plugin",
177
+ p.getName().brightRed.underline
178
+ );
179
+ ok = false;
180
+ continue;
181
+ } else {
182
+ logger.default.verbose("Unloaded", p.getName().underline);
183
+ }
184
+ }
185
+ } catch (e) {
186
+ logger.default.errorp(
187
+ "Error unloading plugin",
188
+ p.getName().brightRed.underline
189
+ );
190
+ ok = false;
191
+ }
81
192
  }
82
- // delete a stored object
83
- async delete(key) {
84
- if (typeof key !== 'string' || key.length === 0)
85
- return;
86
- await this.store.remove({ type: 'store', plugin: this.name, key }, {});
193
+ return ok;
194
+ }
195
+ /** Scans a plugin at the specified directory and create a Plugin object. */
196
+ async scanPlugin(dir) {
197
+ logger.default.verbose("Scanning plugin", dir.underline);
198
+ if (!fs.existsSync(dir)) {
199
+ logger.default.errorp("Plugin directory does not exist", dir.brightRed.underline);
200
+ return;
87
201
  }
88
- // clear all stored values
89
- async wipe() {
90
- await this.store.remove({ type: 'store', plugin: this.name }, { multi: true });
202
+ if (!fs.statSync(dir).isDirectory()) {
203
+ logger.default.errorp("Plugin must be a directory", dir.brightRed.underline);
204
+ return;
91
205
  }
92
- // count number of objects in store
93
- async count() {
94
- return await this.store.count({ type: 'store', plugin: this.name });
206
+ const PluginFormat = this.formats.find((f) => f.canLoad(dir));
207
+ if (!PluginFormat) {
208
+ logger.default.errorp("Missing plugin format for", dir);
209
+ return;
95
210
  }
96
- // get keys of all objects in store
97
- async keys() {
98
- const objects = await this.store.find({
99
- type: 'store',
100
- plugin: this.name,
101
- });
102
- return objects.map(o => o.key);
211
+ try {
212
+ const plugin = PluginFormat && new PluginFormat(dir, this.omegga);
213
+ if (!plugin.getDocumentation()) {
214
+ logger.default.errorp("Missing/invalid plugin documentation for", dir);
215
+ return;
216
+ }
217
+ const storage = new PluginStorage(this.store, plugin);
218
+ await storage.init();
219
+ plugin.setStorage(storage);
220
+ return plugin;
221
+ } catch (e) {
222
+ logger.default.errorp(
223
+ "Error loading plugin",
224
+ dir.brightRed.underline,
225
+ PluginFormat,
226
+ e
227
+ );
103
228
  }
104
- // set a stored value
105
- async set(key, value) {
106
- if (typeof key !== 'string' || key.length === 0)
107
- return;
108
- await this.store.update({ type: 'store', plugin: this.name, key }, { $set: { value } }, { upsert: true });
229
+ }
230
+ static calculateLoadOrder(unsortedPlugins) {
231
+ if (!unsortedPlugins.length) {
232
+ return [];
109
233
  }
110
- }
111
- exports.PluginStorage = PluginStorage;
112
- /*
113
- Plugin Loader and Scanner
114
- Allows omegga to scan in plugins and load them
115
- */
116
- class PluginLoader {
117
- constructor(workDir, omegga) {
118
- this.path = path_1.default.join(workDir, softconfig_1.default.PLUGIN_PATH);
119
- this.omegga = omegga;
120
- this.store = nedb_promises_1.default.create({
121
- filename: path_1.default.join(workDir, softconfig_1.default.DATA_PATH, softconfig_1.default.PLUGIN_STORE),
122
- autoload: true,
123
- });
124
- this.store.persistence.setAutocompactionInterval(1000 * 60 * 5);
125
- this.plugins = [];
126
- logger_1.default.verbose('Loading plugin formats');
127
- this.formats = [plugin_jsonrpc_stdio_1.default, plugin_node_safe_1.default, plugin_node_unsafe_1.default];
128
- logger_1.default.verbose('Found plugin formats:', this.formats);
129
- if (omegga) {
130
- // soon to be deprecated !help
131
- omegga.on('chatcmd:help', this.showHelp.bind(this));
132
- // use /plugins to get help
133
- omegga.on('cmd:plugins', this.showHelp.bind(this));
234
+ const depGraph = unsortedPlugins.reduce((graph, plugin) => {
235
+ const name = plugin.getName();
236
+ const {
237
+ dependencies = {},
238
+ loadAfter = [],
239
+ loadBefore = []
240
+ } = plugin.pluginConfig ?? {};
241
+ graph[name] ??= {};
242
+ graph[name].plugin ??= plugin;
243
+ graph[name].behind ??= [];
244
+ graph[name].inDegree ??= 0;
245
+ for (const depName of [...loadAfter, ...Object.keys(dependencies)]) {
246
+ graph[name].behind.push(depName);
247
+ graph[depName] ??= {};
248
+ graph[depName].inDegree ??= 0;
249
+ graph[depName].inDegree++;
250
+ }
251
+ for (const depName of [...loadBefore]) {
252
+ graph[depName] ??= {};
253
+ graph[depName].behind ??= [];
254
+ graph[depName].behind.push(name);
255
+ graph[name].inDegree++;
256
+ }
257
+ return graph;
258
+ }, {});
259
+ let zeros = Object.entries(depGraph).reduce(
260
+ (arr, [name, { inDegree }]) => {
261
+ if (inDegree === 0) {
262
+ arr.push(name);
134
263
  }
135
- }
136
- // determine if this command is a command on the plugin
137
- isCommand(cmd) {
138
- return cmd === 'plugins' || this.plugins.some(p => p.isCommand(cmd));
139
- }
140
- // unload and load all installed plugins
141
- async reload() {
142
- let ok = true;
143
- logger_1.default.verbose('Reloading plugins');
144
- for (const p of this.plugins) {
145
- try {
146
- // unload the plugin if it's loaded
147
- if (p.isLoaded())
148
- await p.unload();
149
- // load the plugin if it successfully unloaded
150
- if (!p.isLoaded()) {
151
- // only load it if it is enabled
152
- if (p.isEnabled()) {
153
- logger_1.default.verbose('Loading plugin', p.constructor.getFormat(), p.getName().underline);
154
- ok = (await p.load()) || ok;
155
- }
156
- }
157
- else {
158
- logger_1.default.errorp('Did not successfully unload plugin', p.getName().brightRed.underline);
159
- ok = false;
160
- }
161
- }
162
- catch (err) {
163
- logger_1.default.errorp('Error loading plugin', p.getName().brightRed.underline, err);
164
- ok = false;
165
- }
264
+ return arr;
265
+ },
266
+ []
267
+ );
268
+ const result = [];
269
+ while (zeros.length) {
270
+ result.unshift([...zeros]);
271
+ const nextZero = [];
272
+ for (const node of zeros) {
273
+ for (const neighbor of depGraph[node].behind) {
274
+ depGraph[neighbor].inDegree--;
275
+ if (depGraph[neighbor].inDegree === 0) {
276
+ nextZero.push(neighbor);
277
+ }
166
278
  }
167
- logger_1.default.verbose('Finished reloading plugins');
168
- return ok;
279
+ }
280
+ zeros = nextZero;
169
281
  }
170
- // stop all plugins from running
171
- async unload() {
172
- logger_1.default.verbose('Unloading plugins');
173
- let ok = true;
174
- // potentually use Promise.all
175
- for (const p of this.plugins) {
176
- try {
177
- // unload the plugin if it's loaded
178
- if (p.isLoaded()) {
179
- if (!(await p.unload())) {
180
- logger_1.default.errorp('Could not unloading plugin', p.getName().brightRed.underline);
181
- ok = false;
182
- continue;
183
- }
184
- else {
185
- logger_1.default.verbose('Unloaded', p.getName().underline);
186
- }
187
- }
188
- }
189
- catch (e) {
190
- logger_1.default.errorp('Error unloading plugin', p.getName().brightRed.underline);
191
- ok = false;
192
- }
193
- }
194
- return ok;
282
+ if (Object.values(depGraph).some(({ inDegree }) => inDegree > 0)) {
283
+ logger.default.verbose(
284
+ "Cyclic or missing dependencies detected in plugins. Could not resolve load order."
285
+ );
286
+ return [];
195
287
  }
196
- /** Scans a plugin at the specified directory and create a Plugin object. */
197
- async scanPlugin(dir) {
198
- logger_1.default.verbose('Scanning plugin', dir.underline);
199
- if (!fs_1.default.existsSync(dir)) {
200
- logger_1.default.errorp('Plugin directory does not exist', dir.brightRed.underline);
201
- return;
202
- }
203
- if (!fs_1.default.statSync(dir).isDirectory()) {
204
- logger_1.default.errorp('Plugin must be a directory', dir.brightRed.underline);
205
- return;
206
- }
207
- // find a plugin format that can load in this plugin
208
- const PluginFormat = this.formats.find(f => f.canLoad(dir));
209
- // let users know if there's a missing plugin format
210
- if (!PluginFormat) {
211
- logger_1.default.errorp('Missing plugin format for', dir);
212
- return;
213
- }
214
- try {
215
- // create the plugin format
216
- const plugin = PluginFormat && new PluginFormat(dir, this.omegga);
217
- if (!plugin.getDocumentation()) {
218
- logger_1.default.errorp('Missing/invalid plugin documentation for', dir);
219
- return;
220
- }
221
- // create its storage
222
- const storage = new PluginStorage(this.store, plugin);
223
- await storage.init();
224
- // load the storage in
225
- plugin.setStorage(storage);
226
- // if there is a plugin format, create the plugin instance (but don't load yet)
227
- return plugin;
228
- }
229
- catch (e) {
230
- // if a plugin format fails to load, prevent omegga from dying
231
- logger_1.default.errorp('Error loading plugin', dir.brightRed.underline, PluginFormat, e);
232
- }
288
+ return result.map(
289
+ (interchangeablePlugins) => interchangeablePlugins.map((name) => depGraph[name].plugin).sort((a, b) => a.getName().localeCompare(b.getName()))
290
+ ).flat().sort((a, b) => {
291
+ const A = a.pluginConfig?.loadPriority;
292
+ const B = b.pluginConfig?.loadPriority;
293
+ const aU = A == null;
294
+ const bU = B == null;
295
+ if (aU && bU) return 0;
296
+ if (aU) return B <= 0 ? 1 : -1;
297
+ if (bU) return A <= 0 ? -1 : 1;
298
+ return A - B;
299
+ });
300
+ }
301
+ /** Scans plugin directory for plugins and builds their documentation. */
302
+ async scan() {
303
+ logger.default.verbose("Scanning plugin directory");
304
+ if (!fs.existsSync(this.path)) {
305
+ logger.default.verbose("Plugin directory is missing");
306
+ return false;
233
307
  }
234
- /** Scans plugin directory for plugins and builds their documentation. */
235
- async scan() {
236
- logger_1.default.verbose('Scanning plugin directory');
237
- // plugin directory doesn't exist
238
- if (!fs_1.default.existsSync(this.path)) {
239
- logger_1.default.verbose('Plugin directory is missing');
240
- return false;
241
- }
242
- // make sure there are no plugins running
243
- if (this.plugins.some(p => p.isLoaded())) {
244
- logger_1.default.errorp('Cannot re-scan plugins while a plugin is loaded');
245
- return false;
246
- }
247
- // find all directories in the plugin path
248
- this.plugins = (await Promise.all(fs_1.default
249
- .readdirSync(this.path)
250
- .map(dir => path_1.default.join(this.path, dir)) // convert from local paths
251
- .map(async (dir) => this.scanPlugin(dir))))
252
- // remove plugins without formats
253
- .filter(p => p);
254
- logger_1.default.verbose('Finished scanning plugin directory');
255
- logger_1.default.verbose(`Found ${this.plugins.length} plugins`, this.plugins.map(p => p.getName()));
256
- this.buildDocumentation();
257
- return true;
308
+ if (this.plugins.some((p) => p.isLoaded())) {
309
+ logger.default.errorp("Cannot re-scan plugins while a plugin is loaded");
310
+ return false;
258
311
  }
259
- // show helptext
260
- showHelp(player, ...args) {
261
- var _a, _b;
262
- // send the message to the player
263
- const send = (msg) => this.omegga.whisper(player, msg);
264
- // available commands and documentation from the plugin system
265
- const commands = (_a = this.commands) !== null && _a !== void 0 ? _a : {};
266
- const docs = (_b = this.documentation) !== null && _b !== void 0 ? _b : {};
267
- const splitHelper = (objs) => {
268
- const lines = [];
269
- while (objs.length > 0) {
270
- const [item] = objs.splice(0, 1);
271
- if (!lines.length || lines[lines.length - 1].length + item.length > 150)
272
- lines.push(item);
273
- else
274
- lines[lines.length - 1] += ', ' + item;
275
- }
276
- return lines;
277
- };
278
- // no arguments
279
- if (!args.length) {
280
- send('"Use <code>/plugins plugin</>, <code>/plugins !command</>, <code>/plugins /command</> for more information"');
281
- const plugins = Object.keys(docs).map(d => `<color=\\"${docs[d]._plugin.isLoaded() ? 'aaffaa' : 'aaaaaa'}\\">${d}</>`);
282
- if (!plugins.length) {
283
- send('"<b>No Installed Plugins</>"');
284
- }
285
- else {
286
- const lines = splitHelper(plugins);
287
- send(`"<b>Installed Plugins</>: ${lines[0]}"`);
288
- for (let i = 1; i < lines.length; i++)
289
- send(`"${lines[i]}"`);
290
- }
291
- // plugin or command argument
312
+ const unsortedPlugins = (await Promise.all(
313
+ fs.readdirSync(this.path).map((dir) => path.join(this.path, dir)).map(async (dir) => this.scanPlugin(dir))
314
+ )).filter((p) => p);
315
+ this.plugins = PluginLoader.calculateLoadOrder(unsortedPlugins);
316
+ logger.default.verbose("Finished scanning plugin directory");
317
+ logger.default.verbose(
318
+ `Found ${this.plugins.length} plugins.Loading in this order: `,
319
+ this.plugins.map((p) => p.getName())
320
+ );
321
+ this.buildDocumentation();
322
+ return true;
323
+ }
324
+ // show helptext
325
+ showHelp(player, ...args) {
326
+ const send = (msg) => this.omegga.whisper(player, msg);
327
+ const commands = this.commands ?? {};
328
+ const docs = this.documentation ?? {};
329
+ const splitHelper = (objs) => {
330
+ const lines = [];
331
+ while (objs.length > 0) {
332
+ const [item] = objs.splice(0, 1);
333
+ if (!lines.length || lines[lines.length - 1].length + item.length > 150)
334
+ lines.push(item);
335
+ else lines[lines.length - 1] += ", " + item;
336
+ }
337
+ return lines;
338
+ };
339
+ if (!args.length) {
340
+ send(
341
+ '"Use <code>/plugins plugin</>, <code>/plugins !command</>, <code>/plugins /command</> for more information"'
342
+ );
343
+ const plugins = Object.keys(docs).map(
344
+ (d) => `<color=\\"${docs[d]._plugin.isLoaded() ? "aaffaa" : "aaaaaa"}\\">${d}</>`
345
+ );
346
+ if (!plugins.length) {
347
+ send('"<b>No Installed Plugins</>"');
348
+ } else {
349
+ const lines = splitHelper(plugins);
350
+ send(`"<b>Installed Plugins</>: ${lines[0]}"`);
351
+ for (let i = 1; i < lines.length; i++) send(`"${lines[i]}"`);
352
+ }
353
+ } else if (args.length > 0) {
354
+ const target = args.join(" ").toLowerCase();
355
+ if (target in docs) {
356
+ const doc = docs[target];
357
+ const desc = doc.description || "no description";
358
+ const color = doc._plugin.isLoaded() ? "aaffaa" : "aaaaaa";
359
+ send(
360
+ `"<b>Plugin</> <code><color=\\"${color}\\">${doc.name}</></>: ${desc}"`
361
+ );
362
+ if (doc.author)
363
+ send(`"<b>Author</>: <color=\\"c4d7f5\\"><b>${doc.author}</></>"`);
364
+ if (doc.commands && doc.commands.length > 0) {
365
+ const lines = splitHelper(
366
+ doc.commands.map((c) => `<code>${c.name}</>`)
367
+ );
368
+ send(`"<b>Commands</>: ${lines[0]}"`);
369
+ for (let i = 1; i < lines.length; i++) send(`"${lines[i]}"`);
292
370
  }
293
- else if (args.length > 0) {
294
- const target = args.join(' ').toLowerCase();
295
- // argument is a plugin; render description, author, and commands
296
- if (target in docs) {
297
- const doc = docs[target];
298
- const desc = doc.description || 'no description';
299
- const color = doc._plugin.isLoaded() ? 'aaffaa' : 'aaaaaa';
300
- send(`"<b>Plugin</> <code><color=\\"${color}\\">${doc.name}</></>: ${desc}"`);
301
- if (doc.author)
302
- send(`"<b>Author</>: <color=\\"c4d7f5\\"><b>${doc.author}</></>"`);
303
- if (doc.commands && doc.commands.length > 0) {
304
- const lines = splitHelper(doc.commands.map(c => `<code>${c.name}</>`));
305
- send(`"<b>Commands</>: ${lines[0]}"`);
306
- for (let i = 1; i < lines.length; i++)
307
- send(`"${lines[i]}"`);
308
- }
309
- // argument is a command
310
- }
311
- else if (target in commands) {
312
- const cmd = commands[target];
313
- const desc = cmd.description || 'no description';
314
- const example = cmd.example || 'no example';
315
- const color = cmd._plugin.isLoaded() ? 'aaffaa' : 'aaaaaa';
316
- send(`"<b>Command</> <code><color=\\"${color}\\">${cmd.name}</></>: ${desc}"`);
317
- send(`"<b>Example</>: <code>${example}</>"`);
318
- if (cmd.args && cmd.args.length > 0) {
319
- send('"<b>Arguments</>:"');
320
- for (const arg of cmd.args) {
321
- const desc = arg.description || 'no description';
322
- send(`"- <code>${arg.name}</>${arg.required ? ' (required)' : ''}: ${desc}"`);
323
- }
324
- }
325
- else {
326
- send('"<b>Arguments</>: None"');
327
- }
328
- // user takes the helptext literally
329
- }
330
- else if (args[0] === '!command' ||
331
- args[0] === '/command' ||
332
- args[0] === 'plugin') {
333
- send('"Use <code>/plugins [name of plugin]</> or <code>/plugins [name of !command or /command]</> for more help for the respective plugin or command"');
334
- // argument is not found
335
- }
336
- else {
337
- send('"Could not find that command or plugin"');
338
- }
339
- // too many arguments
340
- }
341
- else {
342
- send('"Use <code>/plugins</> to list plugins and <code>/plugins plugin</> or <code>!help !command or /command</> for more information"');
371
+ } else if (target in commands) {
372
+ const cmd = commands[target];
373
+ const desc = cmd.description || "no description";
374
+ const example = cmd.example || "no example";
375
+ const color = cmd._plugin.isLoaded() ? "aaffaa" : "aaaaaa";
376
+ send(
377
+ `"<b>Command</> <code><color=\\"${color}\\">${cmd.name}</></>: ${desc}"`
378
+ );
379
+ send(`"<b>Example</>: <code>${example}</>"`);
380
+ if (cmd.args && cmd.args.length > 0) {
381
+ send('"<b>Arguments</>:"');
382
+ for (const arg of cmd.args) {
383
+ const desc2 = arg.description || "no description";
384
+ send(
385
+ `"- <code>${arg.name}</>${arg.required ? " (required)" : ""}: ${desc2}"`
386
+ );
387
+ }
388
+ } else {
389
+ send('"<b>Arguments</>: None"');
343
390
  }
391
+ } else if (args[0] === "!command" || args[0] === "/command" || args[0] === "plugin") {
392
+ send(
393
+ '"Use <code>/plugins [name of plugin]</> or <code>/plugins [name of !command or /command]</> for more help for the respective plugin or command"'
394
+ );
395
+ } else {
396
+ send('"Could not find that command or plugin"');
397
+ }
398
+ } else {
399
+ send(
400
+ '"Use <code>/plugins</> to list plugins and <code>/plugins plugin</> or <code>!help !command or /command</> for more information"'
401
+ );
344
402
  }
345
- // generate documentation from loaded plugins
346
- buildDocumentation() {
347
- this.documentation = {};
348
- this.commands = {};
349
- for (const plugin of this.plugins) {
350
- // make sure this plugin has docs
351
- const doc = JSON.parse(JSON.stringify(plugin.getDocumentation()));
352
- if (!doc)
353
- continue;
354
- const name = plugin.getName();
355
- if (!name)
356
- continue;
357
- // add the documentation into a dictionary
358
- this.documentation[name.toLowerCase()] = doc;
359
- doc._plugin = plugin;
360
- // add all the commands into a dictionary
361
- if (doc.commands) {
362
- for (const cmd of doc.commands) {
363
- this.commands[cmd.name.toLowerCase()] = cmd;
364
- cmd._plugin = plugin;
365
- }
366
- }
403
+ }
404
+ // generate documentation from loaded plugins
405
+ buildDocumentation() {
406
+ this.documentation = {};
407
+ this.commands = {};
408
+ for (const plugin of this.plugins) {
409
+ const doc = JSON.parse(JSON.stringify(plugin.getDocumentation()));
410
+ if (!doc) continue;
411
+ const name = plugin.getName();
412
+ if (!name) continue;
413
+ this.documentation[name.toLowerCase()] = doc;
414
+ doc._plugin = plugin;
415
+ if (doc.commands) {
416
+ for (const cmd of doc.commands) {
417
+ this.commands[cmd.name.toLowerCase()] = cmd;
418
+ cmd._plugin = plugin;
367
419
  }
420
+ }
368
421
  }
422
+ }
369
423
  }
370
424
  exports.PluginLoader = PluginLoader;
371
- //# sourceMappingURL=plugin.js.map
425
+ exports.PluginStorage = PluginStorage;
426
+ //# sourceMappingURL=plugin.js.map