omegga 1.0.25 → 1.0.28

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 (36) hide show
  1. package/README.md +973 -947
  2. package/dist/cli/index.d.ts +5 -0
  3. package/dist/cli/plugin.d.ts +10 -0
  4. package/dist/cli/plugin.js +138 -1
  5. package/dist/cli/plugin.js.map +1 -1
  6. package/dist/cli/terminal.js +3 -0
  7. package/dist/cli/terminal.js.map +1 -1
  8. package/dist/main.js +58 -9
  9. package/dist/main.js.map +1 -1
  10. package/dist/omegga/commandInjector.js +15 -0
  11. package/dist/omegga/commandInjector.js.map +1 -1
  12. package/dist/omegga/matchers/index.js +3 -0
  13. package/dist/omegga/matchers/index.js.map +1 -1
  14. package/dist/omegga/matchers/minigameJoin.d.ts +9 -0
  15. package/dist/omegga/matchers/minigameJoin.js +35 -0
  16. package/dist/omegga/matchers/minigameJoin.js.map +1 -0
  17. package/dist/omegga/plugin/plugin_jsonrpc_stdio.js +1 -1
  18. package/dist/omegga/plugin/plugin_jsonrpc_stdio.js.map +1 -1
  19. package/dist/omegga/plugin/plugin_node_safe/proxyOmegga.d.ts +5 -5
  20. package/dist/omegga/plugin/plugin_node_safe/proxyOmegga.js +2 -2
  21. package/dist/omegga/plugin/plugin_node_safe/proxyOmegga.js.map +1 -1
  22. package/dist/omegga/plugin/plugin_node_safe/worker.d.ts +1 -0
  23. package/dist/omegga/plugin/plugin_node_safe/worker.js +11 -6
  24. package/dist/omegga/plugin/plugin_node_safe/worker.js.map +1 -1
  25. package/dist/omegga/plugin.d.ts +8 -2
  26. package/dist/omegga/plugin.js +76 -55
  27. package/dist/omegga/plugin.js.map +1 -1
  28. package/dist/omegga/server.js +1 -4
  29. package/dist/omegga/server.js.map +1 -1
  30. package/dist/omegga/types.d.ts +1 -0
  31. package/dist/plugin.d.ts +7 -0
  32. package/dist/plugin.js.map +1 -1
  33. package/package.json +2 -1
  34. package/public/app.bundle.js.map +1 -1
  35. package/public/auth.bundle.js.map +1 -1
  36. package/templates/safe-ts/omegga.d.ts +8 -0
package/README.md CHANGED
@@ -1,947 +1,973 @@
1
- # Omegga
2
-
3
- Read the README first before asking questions! [Join the discord](https://discord.gg/UcdwTYhS75) to browse plugins and get support.
4
-
5
- Omegga wraps brickadia's server console to provide interactivity and utility via plugins.
6
-
7
- ## Screenshots
8
-
9
- [<img src="https://i.imgur.com/AqJF2T0.png" width="512"/>](https://i.imgur.com/AqJF2T0.png)
10
- [<img src="https://i.imgur.com/vGjKoB6.png" width="512"/>](https://i.imgur.com/vGjKoB6.png)
11
- [<img src="https://i.imgur.com/EhT1GBR.png" width="512"/>](https://i.imgur.com/EhT1GBR.png)
12
- [<img src="https://i.imgur.com/PLwgVlx.png" width="512"/>](https://i.imgur.com/PLwgVlx.png)
13
- [<img src="https://i.imgur.com/bCnQ5Pb.png" width="512"/>](https://i.imgur.com/bCnQ5Pb.png)
14
-
15
- ## Install
16
-
17
- You can run omegga in the [Windows Subsystem for Linux](#wsl) (I recommend Ubuntu) or on an actual linux install.
18
-
19
- <font size="5" color="red">Do not install omegga or run brickadia/omegga as root/superuser</font>:
20
-
21
- - running `whoami` should NOT print "root"
22
- - your terminal prompt should NOT end with #
23
- - you should NOT be typing `sudo npm i -g omegga`
24
- - running `echo $EUID` should NOT print "0"
25
-
26
- If any of the above are true, [create a new user](#creating-a-new-user) and continue from there.
27
-
28
- ### Quick Setup (automatically download launcher)
29
-
30
- 1. Install linux if you haven't already ([Windows Install](#wsl))
31
-
32
- 2. If you type `whoami` and it says "root", [create a new user](#creating-a-new-user) and come back. This step is usually only necessary for people using a VPS.
33
-
34
- 3. Run these commands (Installs a node installer, installs node, installs omegga):
35
-
36
- ```sh
37
- curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash
38
- . ~/.nvm/nvm.sh
39
- nvm install 16
40
- npm i -g omegga
41
- ```
42
-
43
- If you get an error like "gyp info find Python using Python version 3.8.10 found at /usr/bin/python3" you need to install python3: `sudo apt install python3` and re-run `npm i -g omegga`
44
- If you get an error like "gyp ERR! stack Error: not found: make" you need to install build-essential: `sudo apt install build-essential` and re-run `npm i -g omegga`
45
- If you get an error like "Unable to fetch some archives, maybe run apt-get update" you need to run `sudo apt update && sudo apt upgrade` before running your original command.
46
-
47
- If you are having trouble installing with nvm and are running Ubuntu/Debian, run the following commands (installs node, installs omegga) instead or install node&npm from [NodeSource Binary Distributions](https://github.com/nodesource/distributions/blob/master/README.md).
48
-
49
- ```sh
50
- curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash -
51
- sudo apt-get install -y nodejs
52
- npm i -g omegga
53
- ```
54
-
55
- Then head over to [Running Omegga](#running)!
56
-
57
- ### Manual Setup (you install stuff)
58
-
59
- Omegga depends on:
60
-
61
- - linux
62
- - [Windows Install](https://docs.microsoft.com/en-us/windows/wsl/install-win10#manual-installation-steps) (WSL 1 or WSL 2)
63
- - [Windows Ubuntu](https://www.microsoft.com/en-us/p/ubuntu/9nblggh4msv6)
64
- - Node v14+ ([ubuntu/deb](https://github.com/nodesource/distributions/blob/master/README.md#installation-instructions))
65
- - One of:
66
- - `tar` (most linuxes come with this, though you can `sudo apt install tar`)
67
- - [Brickadia linux launcher](https://brickadia.com/download)
68
-
69
- Omegga is installed as a global npm package
70
-
71
- npm i -g omegga
72
-
73
- Alternatively, you can use a development/local omegga.
74
-
75
- # clone omegga
76
- git clone https://github.com/brickadia-community/omegga.git && cd omegga
77
-
78
- # install dependencies
79
- npm i
80
-
81
- # point development omegga to global npm bin
82
- npm link
83
-
84
- # build the web ui, build omegga's typescript, and the plugin omegga.d.ts
85
- npm run dist
86
-
87
- If you accidentally install both from Github and `npm i -g omegga`, you can run `npm unlink omegga` to stop npm from using the git one.
88
-
89
- If you have EACCES errors on WSL, see the [troubleshooting](#troubleshooting) section for a potential fix.
90
-
91
- ### WSL
92
-
93
- These are simple instructions to get Windows System for Linux installed.
94
-
95
- **Note**: WSL 2 at the moment requires the [wsl2binds plugin](https://github.com/Meshiest/omegga-wsl2binds). You can install it with `omegga install gh:meshiest/wsl2binds`
96
-
97
- To enable WSL, run this in powershell as an administrator:
98
-
99
- ```powershell
100
- dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
101
- ```
102
-
103
- Then in the Microsoft Store, download a linux:
104
-
105
- - [Ubuntu](https://www.microsoft.com/en-us/p/ubuntu/9nblggh4msv6)
106
-
107
- [More Advanced Instructions here](https://docs.microsoft.com/en-us/windows/wsl/install-win10#manual-installation-steps) if the above is not sufficient.
108
-
109
- To set WSL version from 2 to 1:
110
-
111
- 1. Check WSL version with `wsl -l -v` in cmd
112
- 2. In Administrator cmd, run `wsl --set-version <distribution name> 1` where `<distribution name>` is `Ubuntu`, `Debian`, etc. (From the NAME section of the previous command)
113
-
114
- ### Creating a New User
115
-
116
- If you are running as root (terminal prompt ends with '#' instead of '$' or running `whoami` says "root"), create a new user.
117
-
118
- The following commands will create a user named `brickadia`. Feel free to replace it to `user` or your own name.
119
-
120
- ```sh
121
- # create the user
122
- useradd -m brickadia
123
- # set the new user's password
124
- passwd brickadia
125
- # allow "sudo apt install ...." to work in this user
126
- usermod -aG sudo brickadia
127
- # become this user
128
- su brickadia
129
- ```
130
-
131
- ## Running
132
-
133
- It's recommend to create a folder first _before_ starting your server:
134
-
135
- mkdir myServer && cd myServer
136
-
137
- To start a server, simply type the following in a linux shell after install:
138
-
139
- omegga
140
-
141
- Omegga will prompt for credentials as necessary and only stores the auth tokens brickadia generates on login. **Omegga does not store your password**
142
-
143
- ## Updating
144
-
145
- Omegga will tell you when it's out of date. You can update with this command:
146
-
147
- npm i -g omegga
148
-
149
- ## Configuration
150
-
151
- - CLI config via `omegga config`
152
- - Omegga config is located in a generated `omegga-config.yml`
153
- - Plugin config is handled by plugin or inside the web-ui's plugins tab.
154
-
155
- Example available `omegga-config.yml` fields
156
-
157
- ```yaml
158
- omegga:
159
- port: 8080
160
- webui: true
161
- https: true
162
- debug: false
163
- server:
164
- port: 7777
165
- map: Plate
166
- branch: unstable:unstable-server
167
- ```
168
-
169
- Note: `BRANCH-server` branches download only server data
170
-
171
- ## Troubleshooting
172
-
173
- Narrow down where the issue might be with the following options:
174
-
175
- - If your brickadia is crashing and omegga works, type `/debug` into omegga console or run with `omegga --debug`
176
- - If your omegga isn't starting, run with `omegga --verbose`
177
- - If a plugin is crashing, message the plugin developer
178
- - If you are on Ubuntu and the output of `which npm` is `/bin/npm`, run `sudo apt purge nodejs` and restart install instructions from `nvm install 16`.
179
- - If you're getting an EACCES error when running `npm i -g omegga`:
180
- 1. First, try [this](https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally).
181
- 2. If that doesn't work, try this horrible bodge method for WSL:
182
- 1. Set your WSL to WSL 2
183
- 2. `npm i -g omegga`
184
- 3. Set your WSL back to WSL 1
185
- - If you're getting a "gyp ERR! stack Error: not found: make" install [build-essential](https://wiki.gnucash.org/wiki/Install_Build_Tools)
186
-
187
- ## Uninstalling
188
-
189
- ```sh
190
- # uninstall omegga
191
- npm uninstall -g omegga
192
-
193
- # remove omegga config
194
- rm -rf ~/.config/omegga
195
-
196
- # remove brickadia installs
197
- rm -rf ~/.local/share/brickadia-launcher
198
-
199
- # potentially remove extra brickadia config
200
- rm ~/.config/Epic
201
- ```
202
-
203
- You will have to delete your omegga data folders manually
204
-
205
- # Planned Features
206
-
207
- - [ ] web interface (mostly done)
208
- - [x] reload plugins
209
- - [x] enable/disable plugins live
210
- - [x] live plugin reloading/unloading state
211
- - [x] browse chat history
212
- - [x] manage plugins config
213
- - [x] start/stop server
214
- - [x] chat with players
215
- - [ ] view recent console logs
216
- - [x] view server status
217
- - [x] multiple users
218
- - [ ] roles for each user
219
- - [ ] chatcmd history
220
- - [x] track players kicked/banned
221
- - [ ] automated/scheduled server restarting (when no one is on)
222
- - [x] terminal interface
223
- - [x] reload plugins
224
- - [x] chat with players
225
- - [x] view recent console logs
226
- - [x] view server status
227
- - [ ] metrics
228
- - [ ] bricks over time charts
229
- - [x] player online time tracking
230
- - [x] chat logs
231
- - [ ] chats/hour tracking
232
- - [x] plugins in other languages via JSON RPC over stdio
233
- - [ ] LogWrangler impl for other languages
234
- - [x] events sent JSON RPC
235
- - [ ] sandboxed node plugins (more secure, more stable)
236
- - [x] running in own thread (worker)
237
- - [x] running in own vm
238
- - [x] can `require`
239
- - [x] partial omegga spec (events, some features)
240
- - [x] full omegga spec
241
- - [ ] _good_ access restrictions (ask user for permission)
242
- - [x] plugin installation by `omegga install gh:user/repo`
243
- - [x] plugin updates by `omegga update`
244
- - [ ] server config bundling (making it easier to transfer configs)
245
- - [ ] omegga.server.json
246
- - [ ] list of installed omegga plugins, versions, and download urls
247
- - [ ] list of roles, bans, role assignments
248
-
249
- # Plugins
250
-
251
- Plugins are located in the `plugins` directory in an omegga config folder
252
-
253
- Plugins are most easily developed in Javascript at the moment using the Node VM Plugins and Node Plugins. You can use JSON RPC Plugins to write plugins in other languages.
254
-
255
- ## Installing Plugins
256
-
257
- ### CLI Installation
258
-
259
- You can install plugins with the `omegga install https://github.com/user/repo` command.
260
-
261
- You can install plugins using a shorthand `omegga install gh:user/repo` which will install the plugin located at `https://github.com/user/omegga-repo`
262
-
263
- ### Manual Installation
264
-
265
- Usually you can run `git clone https://github.com/user/repo` inside your `plugins` folder (created when you run `omegga` for the first time):
266
-
267
- - `cd plugins` to navigate to plugins folder
268
- - Make sure to read the plugin's README file for after-install instructions
269
-
270
- ## Updating Plugins
271
-
272
- Plugins can be updated with `omegga update` or `omegga update pluginName anotherPluginName`
273
-
274
- Plugins may also need to be updated based on the project's README file.
275
-
276
- ## Creating Plugins
277
-
278
- Plugins can be created manually using the file structure described below, or they can be initialized automatically using `omegga init-plugin`.
279
- Follow the prompts and your plugin will be generated for you.
280
-
281
- The plugin types are as follows:
282
-
283
- - `safe` (default), the standard Node VM plugin type
284
- - `unsafe`, an unsafe plugin with raw access to internal Omegga APIs
285
- - `rust`, an RPC plugin that makes use of the [omegga-rs](https://github.com/voximity/omegga-rs) Rust interface
286
- - `rpc`, a plugin that will interface another executable using RPC over STDIN/STDOUT
287
-
288
- ## Plugin Structure
289
-
290
- All plugins are located in a `plugins` directory where you are running Omegga:
291
-
292
- - `plugins/myPlugin` - plugin folder (required)
293
- - `plugins/myPlugin/doc.json` - plugin information (required)
294
- - `plugins/myPlugin/plugin.json` - plugin version information, validated with `omegga check` (optional, for now)
295
- - `plugins/myPlugin/setup.sh` - plugin setup script, run after installed by `omegga install` (optional)
296
- - `plugins/myPlugin/disable.omegga` - empty file only present if the plugin should be disabled (optional)
297
-
298
- Every plugin requires a `doc.json` file to document which briefly describes the plugin and its commands.
299
-
300
- ### `doc.json` (example)
301
-
302
- ```json
303
- {
304
- "name": "My Plugin",
305
- "description": "Example Plugin",
306
- "author": "cake",
307
- "config": {
308
- "example-text": {
309
- "description": "This is an example text input",
310
- "default": "default value",
311
- "type": "string"
312
- },
313
- "example-password": {
314
- "description": "This is example text input hidden as a password",
315
- "default": "hidden password value",
316
- "type": "password"
317
- },
318
- "example-number": {
319
- "description": "This is an example numerical input",
320
- "default": 5,
321
- "type": "number"
322
- },
323
- "example-bool": {
324
- "description": "This is an example boolean input",
325
- "default": false,
326
- "type": "boolean"
327
- }
328
- },
329
- "commands": [
330
- {
331
- "name": "!ping",
332
- "description": "sends a pong to the sender",
333
- "example": "!ping foo bar",
334
- "args": [
335
- {
336
- "name": "args",
337
- "description": "random filler arguments",
338
- "required": false
339
- }
340
- ]
341
- },
342
- {
343
- "name": "!pos",
344
- "description": "announces player position",
345
- "example": "!pos",
346
- "args": []
347
- }
348
- ]
349
- }
350
- ```
351
-
352
- ## Plugin Config
353
-
354
- This is an example config section of a `doc.json`. The web ui provides an interface for editing these configs.
355
-
356
- ```json
357
- {
358
- "config": {
359
- "example-text": {
360
- "description": "This is an example text input",
361
- "default": "default value",
362
- "type": "string"
363
- },
364
- "example-password": {
365
- "description": "This is example text input hidden as a password",
366
- "default": "hidden password value",
367
- "type": "password"
368
- },
369
- "example-number": {
370
- "description": "This is an example numerical input",
371
- "default": 5,
372
- "type": "number"
373
- },
374
- "example-bool": {
375
- "description": "This is an example boolean input",
376
- "default": false,
377
- "type": "boolean"
378
- },
379
- "example-list": {
380
- "description": "This is an example list input. List type can be string, password, number, or enum",
381
- "type": "list",
382
- "itemType": "string",
383
- "default": ["hello"]
384
- },
385
- "example-enum": {
386
- "description": "This is an example enum/dropdown input",
387
- "type": "enum",
388
- "options": ["foo", "bar", "baz", 1, 2, 3],
389
- "default": "foo"
390
- },
391
- "example-enum-list": {
392
- "description": "This is an example list of enums.",
393
- "type": "list",
394
- "itemType": "enum",
395
- "options": ["foo", "bar", "baz"],
396
- "default": ["foo"]
397
- },
398
- "example-players-list": {
399
- "description": "This is an example list of players.",
400
- "type": "players",
401
- "default": [
402
- {
403
- "id": "fa577b9e-f2be-493f-a30a-3789b02ba70b",
404
- "name": "Aware"
405
- }
406
- ]
407
- },
408
- "example-role": {
409
- "description": "This is an example role dropdown",
410
- "type": "role",
411
- "default": "Admin"
412
- }
413
- }
414
- }
415
- ```
416
-
417
- That config section would generate the following default config:
418
-
419
- ```json
420
- {
421
- "example-text": "default value",
422
- "example-password": "hidden password value",
423
- "example-number": 5,
424
- "example-bool": false,
425
- "example-list": ["hello"],
426
- "example-enum": "foo",
427
- "example-enum-list": ["foo"],
428
- "example-players-list": [
429
- { "id": "fa577b9e-f2be-493f-a30a-3789b02ba70b", "name": "Aware" }
430
- ]
431
- }
432
- ```
433
-
434
- This is provided to plugins in the constructor or the RPC init function.
435
-
436
- ## Plugin File
437
-
438
- This is an example `plugin.json`, located inside a plugin folder. The plugin file helps omegga know if the plugin is compatible with the current installation. Plugin files can be validated with the `omegga check` command.
439
-
440
- ```json
441
- {
442
- "formatVersion": 1,
443
- "omeggaVersion": ">=0.1.32",
444
- "emitConfig": "config.json"
445
- }
446
- ```
447
-
448
- - `formatVersion` - indicates the plugin file format version
449
- - `omeggaVersion` - indicates compatible omegga versions ([semver cheatsheet](https://www.npmjs.com/package/semver#user-content-ranges))
450
- - `emitConfig` - optional, a path to a json file where plugin config will be saved to before the plugin starts.
451
-
452
- ## Plugin Store
453
-
454
- All plugins have the capability to get/set values in a very lightweight "database"
455
-
456
- The following **asynchronous** methods are provided:
457
-
458
- | Method | Arguments | Description |
459
- | -------------- | ------------------------- | ---------------------------------------- |
460
- | `store.get` | key (string) | Get an object from plugin store |
461
- | `store.set` | key (string), value (any) | Store an object in plugin store |
462
- | `store.delete` | key (string) | Remove an object from plugin store |
463
- | `store.wipe` | _none_ | Remove all objects from plugin store |
464
- | `store.count` | _none_ | Count number of objects in plugin store |
465
- | `store.keys` | _none_ | Get keys for all objects in plugin store |
466
-
467
- ### Example usage:
468
-
469
- ```javascript
470
- // simple add function
471
- async function add() {
472
- const a = await store.get('foo');
473
- const b = await store.get('bar');
474
- await store.set('baz', a + b);
475
- await store.delete('foo');
476
- await store.delete('bar');
477
- }
478
-
479
- (async () => {
480
- // store foo and bar in the plugin store
481
- await Promise.all([store.set('foo', 5), store.set('bar', 2)]);
482
-
483
- // add foo and bar
484
- await add();
485
-
486
- // baz should be equal to 7
487
- console.log('assert', (await store.get('baz')) === 7);
488
-
489
- // demo of storing an object
490
- await store.set('example object', {
491
- foo: 'you can store objects in the store too',
492
- bar: "just don't expect it to work with anything recursive (cannot serialize)",
493
- });
494
- })();
495
- ```
496
-
497
- For Node Plugins, the `store` is the third argument passed into the constructor. For JSONRPC Plugins, the `"store.get"`/etc. methods can be used.
498
-
499
- **JSONRPC Note:** `store.set` has an array of arguments (`[key, value]`)
500
-
501
- ## Node VM Plugins
502
-
503
- Node VM Plugins are what you should be using. They are run inside a VM inside a Worker. This means when they crash, they do not crash the whole server, and they can in the future have locked down permissions (disable filesystem access, etc.).
504
-
505
- These plugins receive a "proxy" reference to `omegga` and have limited reach for what they can touch.
506
-
507
- Register custom `/commands` by returning `{registeredCommands: ['foo', 'bar']}` (registers command `/foo` and `/bar`) in the `async init()` method.
508
-
509
- By defining an `async pluginEvent(event, from, ...args)` method in your plugin class, you can respond to events from other plugins, where `from` is the name of the other plugin, `event` is the name of the custom event, and `args` is an array of any passed arguments.
510
-
511
- ### Globals
512
-
513
- - `OMEGGA_UTIL` - access to the `src/util/index.js` module
514
- - `Omegga` - access to the "proxy" omegga
515
- - `console.log` - and other variants (`console.error`, `console.info`) print specialized output to console
516
-
517
- ### Folder Structure
518
-
519
- In a `plugins` directory create the following folder structure:
520
-
521
- - `plugins/myPlugin` - plugin folder (required)
522
- - `plugins/myPlugin/omegga.plugin.js` - js plugin main file (required)
523
- - `plugins/myPlugin/doc.json`
524
- - `plugins/myPlugin/access.json` - plugin access information (required, but doesn't have to have anything right now). this will contain what things the vm will need to access
525
-
526
- ### `access.json` (examples)
527
-
528
- Access to any builtin modules (`fs`, `path`, etc.)
529
-
530
- ```json
531
- ["*"]
532
- ```
533
-
534
- Access to nothing - only the code in the `omegga.plugin.js`
535
-
536
- ```json
537
- []
538
- ```
539
-
540
- Access to only `fs`, (`const fs = require('fs');`)
541
-
542
- ```json
543
- ["fs"]
544
- ```
545
-
546
- ### `omegga.plugin.js` (example)
547
-
548
- ```javascript
549
- class PluginName {
550
- // the constructor also contains an omegga if you don't want to use the global one
551
- // config and store variables are optional but provide access to the plugin data store
552
- constructor(omegga, config, store) {
553
- this.omegga = omegga;
554
- this.config = config;
555
- this.store = store;
556
- console.info('constructed my plugin!');
557
- }
558
-
559
- async init() {
560
- Omegga.on('chatcmd:ping', (name, ...args) => {
561
- Omegga.broadcast(`pong @ ${name} + ${args.length} args`);
562
- }).on('chatcmd:pos', async name => {
563
- const [x, y, z] = await Omegga.getPlayer(name).getPosition();
564
- Omegga.broadcast(`<b>${name}</> is at ${x} ${y} ${z}`);
565
- });
566
- }
567
-
568
- async stop() {
569
- // any remove events are not necessary because the VM removes the code
570
- }
571
- }
572
-
573
- module.exports = PluginName;
574
- ```
575
-
576
- ### `omegga.plugin.ts` (example)
577
-
578
- Be sure to put `.build/` and `node_modules/` in your `.gitignore`
579
-
580
- **Requires a `tsconfig.json`**:
581
-
582
- ```json
583
- {
584
- "compilerOptions": {
585
- "noEmit": true,
586
- "esModuleInterop": true,
587
- "moduleResolution": "node",
588
- "resolveJsonModule": true,
589
- "target": "es2020",
590
- "baseUrl": ".",
591
- "paths": {
592
- "omegga/*": ["node_modules/omegga/dist/*"]
593
- }
594
- }
595
- }
596
- ```
597
-
598
- `omegga.plugin.ts`:
599
-
600
- ```ts
601
- import type { OmeggaPlugin, OL, PS, PC } from 'omegga/plugin';
602
-
603
- type Config = { foo: string };
604
- type Storage = { bar: string };
605
-
606
- export default class Plugin implements OmeggaPlugin<Config, Storage> {
607
- omegga: OL;
608
- config: PC<Config>;
609
- store: PS<Storage>;
610
-
611
- constructor(omegga: OL, config: PC<Config>, store: PS<Storage>) {
612
- this.omegga = omegga;
613
- this.config = config;
614
- this.store = store;
615
- }
616
-
617
- async init() {
618
- // Write your plugin!
619
- this.omegga.on('cmd:test', (speaker: string) => {
620
- this.omegga.broadcast(`Hello, ${speaker}!`);
621
- });
622
-
623
- return { registeredCommands: ['test'] };
624
- }
625
-
626
- async stop() {
627
- // Anything that needs to be cleaned up...
628
- }
629
- }
630
- ```
631
-
632
- ## Node Plugins
633
-
634
- Node plugins are effectively `require`'d into omegga. They have the potential to crash the entire service through uncaught exceptions and also can be insecure. Develop and run these at your own risk - your server stability may suffer.
635
-
636
- These plugins receive a direct reference to the `omegga` that wraps the brickadia server. As a result, they can directly modify how omegga runs.
637
-
638
- Cleanup is important as code can still be running after the plugin is unloaded resulting in strange and undefined behavior. Make sure to run `clearInterval` and `clearTimeout`
639
-
640
- Register custom `/commands` by returning `{registeredCommands: ['foo', 'bar']}` (registers command `/foo` and `/bar`) in the `async init()` method.
641
-
642
- ### Globals
643
-
644
- - `OMEGGA_UTIL` - access to the `src/util/index.js` module
645
-
646
- ### Folder Structure
647
-
648
- In a `plugins` directory create the following folder structure:
649
-
650
- - `plugins/myPlugin` - plugin folder (required)
651
- - `plugins/myPlugin/doc.json`
652
- - `plugins/myPlugin/omegga.main.js` - js plugin main file (required)
653
-
654
- ### `omegga.main.js` (example)
655
-
656
- ```javascript
657
- class PluginName {
658
- // config and store variables are optional but provide access to the plugin data store
659
- constructor(omegga, config, store) {
660
- this.omegga = omegga;
661
- this.config = config;
662
- this.store = store;
663
- }
664
-
665
- async init() {
666
- this.omegga
667
- .on('chatcmd:ping', (name, ...args) => {
668
- this.omegga.broadcast(`pong @ ${name} + ${args.length} args`);
669
- })
670
- .on('chatcmd:pos', async name => {
671
- const [x, y, z] = await this.omegga.getPlayer(name).getPosition();
672
- this.omegga.broadcast(`<b>${name}</> is at ${x} ${y} ${z}`);
673
- });
674
- }
675
-
676
- async stop() {
677
- this.omegga
678
- .removeAllListeners('chatcmd:ping')
679
- .removeAllListeners('chatcmd:pos');
680
- }
681
- }
682
-
683
- module.exports = PluginName;
684
- ```
685
-
686
- ## JSON RPC Plugins
687
-
688
- JSON RPC Plugins let you use any language you desire, as long as you can run it from a single executable file. They follow the [JSON-RPC 2.0 Specification](https://www.jsonrpc.org/specification)
689
-
690
- The server communicates with the plugin by sending messages to `stdin` and expects responses in `stdout`. All `stderr` is printed to the console.
691
-
692
- Register custom `/commands` by returning `{registeredCommands: ['foo', 'bar']}` (registers command `/foo` and `/bar`) in the `init` method.
693
-
694
- ### Omegga Methods (You can access these)
695
-
696
- | Method | Arguments | Description | Returns |
697
- | ------------------------------ | ---------------------------------------------------- | ------------------------------------------------------------------- | --------------------------------------------------------------- |
698
- | `log` | line (string) | Prints message to omegga console | |
699
- | `error` | line (string) | Same as `log` but with different colors | |
700
- | `info` | line (string) | Same as `log` but with different colors | |
701
- | `debug` | line (string) | Same as `log` but with different colors | |
702
- | `warn` | line (string) | Same as `log` but with different colors | |
703
- | `trace` | line (string) | Same as `log` but with different colors | |
704
- | `store.get` | key (string) | Get an object from plugin store | Object |
705
- | `store.set` | [key (string), value (any)] | Store an object in plugin store | |
706
- | `store.delete` | key (string) | Remove an object from plugin store | |
707
- | `store.wipe` | _none_ | Remove all objects from plugin store | |
708
- | `store.count` | _none_ | Count number of objects in plugin store | Integer |
709
- | `store.keys` | _none_ | Get keys for all objects in plugin store | List of Strings |
710
- | `exec` | cmd (string) | Writes a console command to Brickadia | |
711
- | `writeln` | cmd (string) | Same as `exec` | |
712
- | `broadcast` | line (string) | Broadcasts a message to the server | |
713
- | `whisper` | {target: string, line: string} | Sends a message to a specific client | |
714
- | `middlePrint` | {target: string, line: string} | Sends a middle print message to a specific client | |
715
- | `getPlayers` | _none_ | Online players | List of Players |
716
- | `getAllPlayerPositions` | _none_ | An array of objects with fields `pos` and `player`. | List of { _Player Object_(...), _Position_(...), isDead(bool) } |
717
- | `getRoleSetup` | _none_ | Server roles | _JSON Data_ |
718
- | `getBanList` | _none_ | List of bans | _JSON Data_ |
719
- | `getSaves` | _none_ | Saves in the saves directory | List Strings |
720
- | `getSavePath` | name (string) | The path to a specific save | String |
721
- | `getSaveData` | _none_ | Current save as brs-js data | _BRS Object_ |
722
- | `clearBricks` | {target: string, quiet: bool} | Clears a specific player's bricks | |
723
- | `clearAllBricks` | quiet | Clears all bricks on the server | |
724
- | `saveBricks` | name (string) | Save bricks to a save named `name` | |
725
- | `loadBricks` | {name: string, offX, offY, offY, quiet: bool} | Load bricks of save named `name` | |
726
- | `loadBricksOnPlayer` | {name: string, player: string, offX, offY, offY} | Load bricks of save named `name` on player clipboard | |
727
- | `readSaveData` | name (string) | Parses save into a brs-js save object, returns the object | _BRS Object_ |
728
- | `loadSaveData` | {data: object, offX, offY, offY, quiet: bool} | Builds brs file from data, loads the file | |
729
- | `loadSaveDataOnPlayer` | {data: object, player: string, offX, offY, offY} | Builds brs file from data, loads the file onto a player's clipboard | |
730
- | `changeMap` | map (string) | Change map to specified map name, returns success | Boolean |
731
- | `player.get` | target (string) | Gets the player by their name or UUID. | `{name, id, controller, state, host: bool}` |
732
- | `player.getRoles` | target (string) | Target's roles | |
733
- | `player.getPermissions` | target (string) | Target's permissions | List of Strings |
734
- | `player.getNameColor` | target (string) | Target's name color | _RGB Hex Object_ (int, int ,int) |
735
- | `player.getPosition` | target (string) | Target's position | _Position Object_ |
736
- | `player.getGhostBrick` | target (string) | Target's ghost brick | {targetGrid, location(_Location_), orientation} |
737
- | `player.getPaint` | target (string) | Target's current paint selection | {materialIndex, materialAlpha, material, color)} |
738
- | `player.getTemplateBounds` | target (string) | Target's template/selection bounds | {minBound, maxBound, Center} |
739
- | `player.getTemplateBoundsData` | target (string) | Target's template/selection as brs-js save data | _Brick Object_ |
740
- | `player.loadSaveData` | {target, data, offX, offY, offZ} | Loads brs-js save data to the targets clipboard | |
741
- | `player.loadDataAtGhostBrick` | {target, data, rotate=true, offX, offY, offZ, quiet} | Loads brs-js save data at the target's selection bounds | |
742
- | `plugin.get` | target (string) | Gets info on the target plugin | Object |
743
- | `plugin.emit` | [target (string), event (string), ...args (any)] | Emit a custom event to the target plugin | |
744
-
745
- ### Plugin Methods (You implement these)
746
-
747
- | Method | Arguments | Description | Required |
748
- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- |
749
- | `init` | config object | Returns a start result, called on plugin start | &#9745; |
750
- | `stop` | _none_ | Returns _something_, called on plugin stop | &#9745; |
751
- | `bootstrap` | [{ omegga info (`host`, `version`, etc) }] | Run when plugin is started for base data | |
752
- | `plugin:players:raw` | [[... [player `name`, `id`, `controller`, `state`]]] | Lists players on the server | |
753
- | `plugin:emit` | [event, from, ...args] | Fired when another plugin sends an event | |
754
- | `line` | [brickadiaLog string] | A brickadia console log | |
755
- | `start` | [{map}] | On brickadia server start | |
756
- | `host` | [{name, id}] | When the host is detected | |
757
- | `version` | [-1 or the CL number] | When the version is detected | |
758
- | `unauthorized` | _none_ | On brickadia server fails an auth check | |
759
- | `join` | [{name, id, state, controller}] | Run when a player joins | |
760
- | `leave` | [{name, id, state, controller}] | Run when a player leaves | |
761
- | `cmd:command` | [playerName, ...args] | Runs when a player runs a `/command args` | |
762
- | `chatcmd:command` | [playerName, ...args] | Runs when a player runs a `!command args` | |
763
- | `chat` | [playerName, message] | Runs when a player sends a chat message | |
764
- | `interact` | {brick_asset: string;player: { id: string; name: string; controller: string; pawn: string };position: [number, number, number];} | Runs when a player clicks a brick with an interact component. `data` is parsed JSON if `line` (from interact component) starts with "json:{"your": "json"}`. Uses interact log field | |
765
- | `event:NAME` | [&gt;player from click&lt;, ...args] | Runs when an interact component has `event:NAME: arg1,arg2,arg\,3, | |
766
- | `mapchange` | \[{map}\] | Runs when the map changes | |
767
- | `autorestart` | [autorestart config] | Runs server has an autorestart scheduled | |
768
-
769
- ### Folder Structure
770
-
771
- In a `plugins` directory create the following folder structure:
772
-
773
- - `plugins/myPlugin` - plugin folder (required)
774
- - `plugins/myPlugin/doc.json`
775
- - `plugins/myPlugin/omegga_plugin` - executable plugin file (required)
776
-
777
- ### `omegga_plugin` (example, node javascript)
778
-
779
- ```javascript
780
- #!/usr/bin/env node
781
-
782
- const readline = require('readline');
783
- const { EventEmitter } = require('events');
784
- const {
785
- JSONRPCServer,
786
- JSONRPCServerAndClient,
787
- JSONRPCClient,
788
- } = require('json-rpc-2.0');
789
-
790
- // events
791
- const ev = new EventEmitter();
792
-
793
- // stdio handling
794
- const rl = readline.createInterface({
795
- input: process.stdin,
796
- output: process.stdout,
797
- terminal: false,
798
- });
799
-
800
- // rpc "server and client" for responding/receiving messages
801
- const rpc = new JSONRPCServerAndClient(
802
- new JSONRPCServer(),
803
- // the client outputs JSON to console
804
- new JSONRPCClient(async blob => console.log(JSON.stringify(blob)))
805
- );
806
-
807
- // on stdin, pass into rpc
808
- rl.on('line', line => {
809
- try {
810
- rpc.receiveAndSend(JSON.parse(line));
811
- } catch (e) {
812
- console.error(e);
813
- }
814
- });
815
-
816
- // regexes for matching brickadia console logs
817
- const GENERIC_LINE_REGEX =
818
- /^(\[(?<date>\d{4}\.\d\d.\d\d-\d\d.\d\d.\d\d:\d{3})\]\[\s*(?<counter>\d+)\])?(?<generator>\w+): (?<data>.+)$/;
819
- const LOG_LINE_REGEX =
820
- /\[(?<date>\d{4}\.\d\d.\d\d-\d\d.\d\d.\d\d:\d{3})\]\[\s*(?<counter>\d+)\](?<rest>.*)$/;
821
-
822
- ev.on('line', line => {
823
- const logMatch = line.match(LOG_LINE_REGEX);
824
- if (!logMatch) return;
825
- const {
826
- groups: { rest },
827
- } = logMatch;
828
- const dataMatch = rest.match(GENERIC_LINE_REGEX);
829
- if (dataMatch) ev.emit('logData', dataMatch.groups);
830
- else ev.emit('logLine', rest);
831
- });
832
-
833
- // list of players
834
- let players;
835
-
836
- // get a player by name
837
- const getPlayer = name => players.find(p => p.name === name);
838
-
839
- // watch console logs for a pattern, then remove the listener
840
- function watch(exec, pattern) {
841
- return new Promise(resolve => {
842
- function listener(line) {
843
- const match = line.match(pattern);
844
- // listener removes itself on a match
845
- if (match) {
846
- ev.off('logLine', listener);
847
- resolve(match.groups);
848
- }
849
- }
850
- // add the listener
851
- ev.on('logLine', listener);
852
-
853
- // run the console command
854
- rpc.notify('writeln', exec);
855
- });
856
- }
857
-
858
- // get a player's position
859
- async function getPlayerPos(name) {
860
- const player = getPlayer(name);
861
- if (!player) return;
862
-
863
- // get player position from player controller
864
- const pawnRegExp = new RegExp(
865
- `BP_PlayerController_C .+?PersistentLevel\\.${player.controller}\.Pawn = BP_FigureV2_C'.+?:PersistentLevel.(?<pawn>BP_FigureV2_C_\\d+)'`
866
- );
867
- const { pawn } = await watch(
868
- `GetAll BP_PlayerController_C Pawn Name=${player.controller}`,
869
- pawnRegExp
870
- );
871
-
872
- // get player position from pawn
873
- const posRegExp = new RegExp(
874
- `CapsuleComponent .+?PersistentLevel\\.${pawn}\\.CollisionCylinder\\.RelativeLocation = \\(X=(?<x>[\\d\\.-]+),Y=(?<y>[\\d\\.-]+),Z=(?<z>[\\d\\.-]+)\\)`
875
- );
876
- const { x, y, z } = await watch(
877
- `GetAll SceneComponent RelativeLocation Name=CollisionCylinder Outer=${pawn}`,
878
- posRegExp
879
- );
880
-
881
- return [x, y, z].map(Number);
882
- }
883
-
884
- // emit a console log
885
- const log = (...args) => rpc.notify('log', args.join(' '));
886
-
887
- // when available players updates - plugin:players:raw is emitted
888
- rpc.addMethod('plugin:players:raw', ([playerArr]) => {
889
- // update the players list
890
- players = playerArr.map(p => ({
891
- name: p[0],
892
- id: p[1],
893
- controller: p[2],
894
- state: p[3],
895
- }));
896
- });
897
-
898
- // ping command
899
- rpc.addMethod('chatcmd:ping', ([name, ...args]) => {
900
- rpc.notify('broadcast', `pong @ ${name} + ${args.length} args`);
901
- });
902
-
903
- // player position command
904
- rpc.addMethod('chatcmd:pos', async ([name]) => {
905
- log('player', name, 'requests position');
906
- const [x, y, z] = await getPlayerPos(name);
907
- rpc.notify('broadcast', `<b>${name}</> is at ${x} ${y} ${z}`);
908
- });
909
-
910
- // pass lines into the event emitter
911
- rpc.addMethod('line', ([line]) => {
912
- ev.emit('line', line);
913
- });
914
-
915
- // receive config object in init
916
- rpc.addMethod('init', async ([config]) => ({ registeredCommands: [] }));
917
- rpc.addMethod('stop', async () => 'ok');
918
- ```
919
-
920
- # Extra Features
921
-
922
- ## Environment Variables
923
-
924
- `omegga` accepts the following environment variables:
925
-
926
- - `BRICKADIA_USER` - brickadia auth username (on first start)
927
- - `BRICKADIA_PASS` - brickadia auth password (on first start)
928
- - `BRICKADIA_PORT` - brickadia server port (on config creation)
929
- - `OMEGGA_PORT` - omegga webserver port (on config creation)
930
-
931
- ## Config
932
-
933
- Default config values (including hidden ones)
934
-
935
- ```yaml
936
- omegga:
937
- port: 8080 # web-ui port
938
- webui: true # enable web-ui
939
- plugins: true # enable plugins
940
- singleUser: false # disable web-ui auth users
941
- https: true # enable https for web-ui
942
- debug: false # debug logging
943
- server:
944
- port: 7777 # game server port
945
- map: Plate # map name
946
- branch: unstable:unstable-server # branch alias:branch name
947
- ```
1
+ # Omegga
2
+
3
+ Read the README first before asking questions! [Join the discord](https://discord.gg/UcdwTYhS75) to browse plugins and get support.
4
+
5
+ Omegga wraps brickadia's server console to provide interactivity and utility via plugins.
6
+
7
+ ## Screenshots
8
+
9
+ [<img src="https://i.imgur.com/AqJF2T0.png" width="512"/>](https://i.imgur.com/AqJF2T0.png)
10
+ [<img src="https://i.imgur.com/vGjKoB6.png" width="512"/>](https://i.imgur.com/vGjKoB6.png)
11
+ [<img src="https://i.imgur.com/EhT1GBR.png" width="512"/>](https://i.imgur.com/EhT1GBR.png)
12
+ [<img src="https://i.imgur.com/PLwgVlx.png" width="512"/>](https://i.imgur.com/PLwgVlx.png)
13
+ [<img src="https://i.imgur.com/bCnQ5Pb.png" width="512"/>](https://i.imgur.com/bCnQ5Pb.png)
14
+
15
+ ## Install
16
+
17
+ You can run omegga in the [Windows Subsystem for Linux](#wsl) (I recommend Ubuntu) or on an actual linux install.
18
+
19
+ <font size="5" color="red">Do not install omegga or run brickadia/omegga as root/superuser</font>:
20
+
21
+ - running `whoami` should NOT print "root"
22
+ - your terminal prompt should NOT end with #
23
+ - you should NOT be typing `sudo npm i -g omegga`
24
+ - running `echo $EUID` should NOT print "0"
25
+ - if you type `pwd` it should NOT print "/root" (type `cd` to navigate to your user's home dir)
26
+
27
+ If any of the above are true, [create a new user](#creating-a-new-user) and continue from there.
28
+
29
+ ### Quick Setup (automatically download launcher)
30
+
31
+ 1. Install linux if you haven't already ([Windows Install](#wsl))
32
+
33
+ 2. If you type `whoami` and it says "root", [create a new user](#creating-a-new-user) and come back. This step is usually only necessary for people using a VPS.
34
+
35
+ 3. Run these commands (Installs a node installer, installs node, installs omegga):
36
+
37
+ ```sh
38
+ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash
39
+ . ~/.nvm/nvm.sh
40
+ nvm install 16
41
+ npm i -g omegga
42
+ ```
43
+
44
+ 4. Head over to [Running Omegga](#running) or troubleshoot below.
45
+
46
+ ### Quick Setup Troubleshooting
47
+
48
+
49
+ - If you get "`sh: 28: cd: can't cd to .`", you need to be in `bash` (and probably type `cd` to navigate out of root directory):
50
+
51
+ ```sh
52
+ bash # use bash instead of sh
53
+ cd # navigate home
54
+ ```
55
+
56
+ - If you get an error like "`gyp info find Python using Python version 3.8.10 found at /usr/bin/python3`" you need to install python3:
57
+
58
+ ```sh
59
+ sudo apt install python3
60
+ npm i -g omegga
61
+ ```
62
+
63
+ - If you get an error like "`gyp ERR! stack Error: not found: make`" you need to install build-essential:
64
+ ```sh
65
+ sudo apt install build-essential
66
+ npm i -g omegga # re-run omegga install
67
+ ```
68
+
69
+ - If you get an error like "`Unable to fetch some archives, maybe run apt-get update`" you need to run this before running your original command:
70
+ ```sh
71
+ sudo apt update && sudo apt upgrade
72
+ ```
73
+
74
+ - If you are having trouble installing with nvm and are running **Ubuntu/Debian**, run the following commands (installs node, installs omegga) instead or install node&npm from [NodeSource Binary Distributions](https://github.com/nodesource/distributions/blob/master/README.md).
75
+
76
+ ```sh
77
+ curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash -
78
+ sudo apt-get install -y nodejs
79
+ npm i -g omegga
80
+ ```
81
+
82
+ ### Manual Setup (you install stuff)
83
+
84
+ Omegga depends on:
85
+
86
+ - linux
87
+ - [Windows Install](https://docs.microsoft.com/en-us/windows/wsl/install-win10#manual-installation-steps) (WSL 1 or WSL 2)
88
+ - [Windows Ubuntu](https://www.microsoft.com/en-us/p/ubuntu/9nblggh4msv6)
89
+ - Node v14+ ([ubuntu/deb](https://github.com/nodesource/distributions/blob/master/README.md#installation-instructions))
90
+ - One of:
91
+ - `tar` (most linuxes come with this, though you can `sudo apt install tar`)
92
+ - [Brickadia linux launcher](https://brickadia.com/download)
93
+
94
+ Omegga is installed as a global npm package
95
+
96
+ npm i -g omegga
97
+
98
+ Alternatively, you can use a development/local omegga.
99
+
100
+ # clone omegga
101
+ git clone https://github.com/brickadia-community/omegga.git && cd omegga
102
+
103
+ # install dependencies
104
+ npm i
105
+
106
+ # point development omegga to global npm bin
107
+ npm link
108
+
109
+ # build the web ui, build omegga's typescript, and the plugin omegga.d.ts
110
+ npm run dist
111
+
112
+ If you accidentally install both from Github and `npm i -g omegga`, you can run `npm unlink omegga` to stop npm from using the git one.
113
+
114
+ If you have EACCES errors on WSL, see the [troubleshooting](#troubleshooting) section for a potential fix.
115
+
116
+ ### WSL
117
+
118
+ These are simple instructions to get Windows System for Linux installed.
119
+
120
+ **Note**: WSL 2 at the moment requires the [wsl2binds plugin](https://github.com/Meshiest/omegga-wsl2binds). You can install it with `omegga install gh:meshiest/wsl2binds`
121
+
122
+ To enable WSL, run this in powershell as an administrator:
123
+
124
+ ```powershell
125
+ dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
126
+ ```
127
+
128
+ Then in the Microsoft Store, download a linux:
129
+
130
+ - [Ubuntu](https://www.microsoft.com/en-us/p/ubuntu/9nblggh4msv6)
131
+
132
+ [More Advanced Instructions here](https://docs.microsoft.com/en-us/windows/wsl/install-win10#manual-installation-steps) if the above is not sufficient.
133
+
134
+ To set WSL version from 2 to 1:
135
+
136
+ 1. Check WSL version with `wsl -l -v` in cmd
137
+ 2. In Administrator cmd, run `wsl --set-version <distribution name> 1` where `<distribution name>` is `Ubuntu`, `Debian`, etc. (From the NAME section of the previous command)
138
+
139
+ ### Creating a New User
140
+
141
+ If you are running as root (terminal prompt ends with '#' instead of '$' or running `whoami` says "root"), create a new user.
142
+
143
+ The following commands will create a user named `brickadia`. Feel free to replace it to `user` or your own name.
144
+
145
+ ```sh
146
+ # create the user
147
+ useradd -m brickadia
148
+ # set the new user's password
149
+ passwd brickadia
150
+ # allow "sudo apt install ...." to work in this user
151
+ usermod -aG sudo brickadia
152
+ # become this user
153
+ su brickadia
154
+ ```
155
+
156
+ ## Running
157
+
158
+ It's recommend to create a folder first _before_ starting your server:
159
+
160
+ mkdir myServer && cd myServer
161
+
162
+ To start a server, simply type the following in a linux shell after install:
163
+
164
+ omegga
165
+
166
+ Omegga will prompt for credentials as necessary and only stores the auth tokens brickadia generates on login. **Omegga does not store your password**
167
+
168
+ ## Updating
169
+
170
+ Omegga will tell you when it's out of date. You can update with this command:
171
+
172
+ npm i -g omegga
173
+
174
+ ## Configuration
175
+
176
+ - CLI config via `omegga config`
177
+ - Omegga config is located in a generated `omegga-config.yml`
178
+ - Plugin config is handled by plugin or inside the web-ui's plugins tab.
179
+
180
+ Example available `omegga-config.yml` fields
181
+
182
+ ```yaml
183
+ omegga:
184
+ port: 8080
185
+ webui: true
186
+ https: true
187
+ debug: false
188
+ server:
189
+ port: 7777
190
+ map: Plate
191
+ branch: unstable:unstable-server
192
+ ```
193
+
194
+ Note: `BRANCH-server` branches download only server data
195
+
196
+ ## Troubleshooting
197
+
198
+ Narrow down where the issue might be with the following options:
199
+
200
+ - If your brickadia is crashing and omegga works, type `/debug` into omegga console or run with `omegga --debug`
201
+ - If your omegga isn't starting, run with `omegga --verbose`
202
+ - If a plugin is crashing, message the plugin developer
203
+ - If you are on Ubuntu and the output of `which npm` is `/bin/npm`, run `sudo apt purge nodejs` and restart install instructions from `nvm install 16`.
204
+ - If you're getting an EACCES error when running `npm i -g omegga`:
205
+ 1. First, try [this](https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally).
206
+ 2. If that doesn't work, try this horrible bodge method for WSL:
207
+ 1. Set your WSL to WSL 2
208
+ 2. `npm i -g omegga`
209
+ 3. Set your WSL back to WSL 1
210
+ - If you're getting a "gyp ERR! stack Error: not found: make" install [build-essential](https://wiki.gnucash.org/wiki/Install_Build_Tools)
211
+
212
+ ## Uninstalling
213
+
214
+ ```sh
215
+ # uninstall omegga
216
+ npm uninstall -g omegga
217
+
218
+ # remove omegga config
219
+ rm -rf ~/.config/omegga
220
+
221
+ # remove brickadia installs
222
+ rm -rf ~/.local/share/brickadia-launcher
223
+
224
+ # potentially remove extra brickadia config
225
+ rm ~/.config/Epic
226
+ ```
227
+
228
+ You will have to delete your omegga data folders manually
229
+
230
+ # Planned Features
231
+
232
+ - [ ] web interface (mostly done)
233
+ - [x] reload plugins
234
+ - [x] enable/disable plugins live
235
+ - [x] live plugin reloading/unloading state
236
+ - [x] browse chat history
237
+ - [x] manage plugins config
238
+ - [x] start/stop server
239
+ - [x] chat with players
240
+ - [ ] view recent console logs
241
+ - [x] view server status
242
+ - [x] multiple users
243
+ - [ ] roles for each user
244
+ - [ ] chatcmd history
245
+ - [x] track players kicked/banned
246
+ - [ ] automated/scheduled server restarting (when no one is on)
247
+ - [x] terminal interface
248
+ - [x] reload plugins
249
+ - [x] chat with players
250
+ - [x] view recent console logs
251
+ - [x] view server status
252
+ - [ ] metrics
253
+ - [ ] bricks over time charts
254
+ - [x] player online time tracking
255
+ - [x] chat logs
256
+ - [ ] chats/hour tracking
257
+ - [x] plugins in other languages via JSON RPC over stdio
258
+ - [ ] LogWrangler impl for other languages
259
+ - [x] events sent JSON RPC
260
+ - [ ] sandboxed node plugins (more secure, more stable)
261
+ - [x] running in own thread (worker)
262
+ - [x] running in own vm
263
+ - [x] can `require`
264
+ - [x] partial omegga spec (events, some features)
265
+ - [x] full omegga spec
266
+ - [ ] _good_ access restrictions (ask user for permission)
267
+ - [x] plugin installation by `omegga install gh:user/repo`
268
+ - [x] plugin updates by `omegga update`
269
+ - [ ] server config bundling (making it easier to transfer configs)
270
+ - [ ] omegga.server.json
271
+ - [ ] list of installed omegga plugins, versions, and download urls
272
+ - [ ] list of roles, bans, role assignments
273
+
274
+ # Plugins
275
+
276
+ Plugins are located in the `plugins` directory in an omegga config folder
277
+
278
+ Plugins are most easily developed in Javascript at the moment using the Node VM Plugins and Node Plugins. You can use JSON RPC Plugins to write plugins in other languages.
279
+
280
+ ## Installing Plugins
281
+
282
+ ### CLI Installation
283
+
284
+ You can install plugins with the `omegga install https://github.com/user/repo` command.
285
+
286
+ You can install plugins using a shorthand `omegga install gh:user/repo` which will install the plugin located at `https://github.com/user/omegga-repo`
287
+
288
+ ### Manual Installation
289
+
290
+ Usually you can run `git clone https://github.com/user/repo` inside your `plugins` folder (created when you run `omegga` for the first time):
291
+
292
+ - `cd plugins` to navigate to plugins folder
293
+ - Make sure to read the plugin's README file for after-install instructions
294
+
295
+ ## Updating Plugins
296
+
297
+ Plugins can be updated with `omegga update` or `omegga update pluginName anotherPluginName`
298
+
299
+ Plugins may also need to be updated based on the project's README file.
300
+
301
+ ## Creating Plugins
302
+
303
+ Plugins can be created manually using the file structure described below, or they can be initialized automatically using `omegga init-plugin`.
304
+ Follow the prompts and your plugin will be generated for you.
305
+
306
+ The plugin types are as follows:
307
+
308
+ - `safe` (default), the standard Node VM plugin type
309
+ - `unsafe`, an unsafe plugin with raw access to internal Omegga APIs
310
+ - `rust`, an RPC plugin that makes use of the [omegga-rs](https://github.com/voximity/omegga-rs) Rust interface
311
+ - `rpc`, a plugin that will interface another executable using RPC over STDIN/STDOUT
312
+
313
+ ## Plugin Structure
314
+
315
+ All plugins are located in a `plugins` directory where you are running Omegga:
316
+
317
+ - `plugins/myPlugin` - plugin folder (required)
318
+ - `plugins/myPlugin/doc.json` - plugin information (required)
319
+ - `plugins/myPlugin/plugin.json` - plugin version information, validated with `omegga check` (optional, for now)
320
+ - `plugins/myPlugin/setup.sh` - plugin setup script, run after installed by `omegga install` (optional)
321
+ - `plugins/myPlugin/disable.omegga` - empty file only present if the plugin should be disabled (optional)
322
+
323
+ Every plugin requires a `doc.json` file to document which briefly describes the plugin and its commands.
324
+
325
+ ### `doc.json` (example)
326
+
327
+ ```json
328
+ {
329
+ "name": "My Plugin",
330
+ "description": "Example Plugin",
331
+ "author": "cake",
332
+ "config": {
333
+ "example-text": {
334
+ "description": "This is an example text input",
335
+ "default": "default value",
336
+ "type": "string"
337
+ },
338
+ "example-password": {
339
+ "description": "This is example text input hidden as a password",
340
+ "default": "hidden password value",
341
+ "type": "password"
342
+ },
343
+ "example-number": {
344
+ "description": "This is an example numerical input",
345
+ "default": 5,
346
+ "type": "number"
347
+ },
348
+ "example-bool": {
349
+ "description": "This is an example boolean input",
350
+ "default": false,
351
+ "type": "boolean"
352
+ }
353
+ },
354
+ "commands": [
355
+ {
356
+ "name": "!ping",
357
+ "description": "sends a pong to the sender",
358
+ "example": "!ping foo bar",
359
+ "args": [
360
+ {
361
+ "name": "args",
362
+ "description": "random filler arguments",
363
+ "required": false
364
+ }
365
+ ]
366
+ },
367
+ {
368
+ "name": "!pos",
369
+ "description": "announces player position",
370
+ "example": "!pos",
371
+ "args": []
372
+ }
373
+ ]
374
+ }
375
+ ```
376
+
377
+ ## Plugin Config
378
+
379
+ This is an example config section of a `doc.json`. The web ui provides an interface for editing these configs.
380
+
381
+ ```json
382
+ {
383
+ "config": {
384
+ "example-text": {
385
+ "description": "This is an example text input",
386
+ "default": "default value",
387
+ "type": "string"
388
+ },
389
+ "example-password": {
390
+ "description": "This is example text input hidden as a password",
391
+ "default": "hidden password value",
392
+ "type": "password"
393
+ },
394
+ "example-number": {
395
+ "description": "This is an example numerical input",
396
+ "default": 5,
397
+ "type": "number"
398
+ },
399
+ "example-bool": {
400
+ "description": "This is an example boolean input",
401
+ "default": false,
402
+ "type": "boolean"
403
+ },
404
+ "example-list": {
405
+ "description": "This is an example list input. List type can be string, password, number, or enum",
406
+ "type": "list",
407
+ "itemType": "string",
408
+ "default": ["hello"]
409
+ },
410
+ "example-enum": {
411
+ "description": "This is an example enum/dropdown input",
412
+ "type": "enum",
413
+ "options": ["foo", "bar", "baz", 1, 2, 3],
414
+ "default": "foo"
415
+ },
416
+ "example-enum-list": {
417
+ "description": "This is an example list of enums.",
418
+ "type": "list",
419
+ "itemType": "enum",
420
+ "options": ["foo", "bar", "baz"],
421
+ "default": ["foo"]
422
+ },
423
+ "example-players-list": {
424
+ "description": "This is an example list of players.",
425
+ "type": "players",
426
+ "default": [
427
+ {
428
+ "id": "fa577b9e-f2be-493f-a30a-3789b02ba70b",
429
+ "name": "Aware"
430
+ }
431
+ ]
432
+ },
433
+ "example-role": {
434
+ "description": "This is an example role dropdown",
435
+ "type": "role",
436
+ "default": "Admin"
437
+ }
438
+ }
439
+ }
440
+ ```
441
+
442
+ That config section would generate the following default config:
443
+
444
+ ```json
445
+ {
446
+ "example-text": "default value",
447
+ "example-password": "hidden password value",
448
+ "example-number": 5,
449
+ "example-bool": false,
450
+ "example-list": ["hello"],
451
+ "example-enum": "foo",
452
+ "example-enum-list": ["foo"],
453
+ "example-players-list": [
454
+ { "id": "fa577b9e-f2be-493f-a30a-3789b02ba70b", "name": "Aware" }
455
+ ]
456
+ }
457
+ ```
458
+
459
+ This is provided to plugins in the constructor or the RPC init function.
460
+
461
+ ## Plugin File
462
+
463
+ This is an example `plugin.json`, located inside a plugin folder. The plugin file helps omegga know if the plugin is compatible with the current installation. Plugin files can be validated with the `omegga check` command.
464
+
465
+ ```json
466
+ {
467
+ "formatVersion": 1,
468
+ "omeggaVersion": ">=0.1.32",
469
+ "emitConfig": "config.json"
470
+ }
471
+ ```
472
+
473
+ - `formatVersion` - indicates the plugin file format version
474
+ - `omeggaVersion` - indicates compatible omegga versions ([semver cheatsheet](https://www.npmjs.com/package/semver#user-content-ranges))
475
+ - `emitConfig` - optional, a path to a json file where plugin config will be saved to before the plugin starts.
476
+
477
+ ## Plugin Store
478
+
479
+ All plugins have the capability to get/set values in a very lightweight "database"
480
+
481
+ The following **asynchronous** methods are provided:
482
+
483
+ | Method | Arguments | Description |
484
+ | -------------- | ------------------------- | ---------------------------------------- |
485
+ | `store.get` | key (string) | Get an object from plugin store |
486
+ | `store.set` | key (string), value (any) | Store an object in plugin store |
487
+ | `store.delete` | key (string) | Remove an object from plugin store |
488
+ | `store.wipe` | _none_ | Remove all objects from plugin store |
489
+ | `store.count` | _none_ | Count number of objects in plugin store |
490
+ | `store.keys` | _none_ | Get keys for all objects in plugin store |
491
+
492
+ ### Example usage:
493
+
494
+ ```javascript
495
+ // simple add function
496
+ async function add() {
497
+ const a = await store.get('foo');
498
+ const b = await store.get('bar');
499
+ await store.set('baz', a + b);
500
+ await store.delete('foo');
501
+ await store.delete('bar');
502
+ }
503
+
504
+ (async () => {
505
+ // store foo and bar in the plugin store
506
+ await Promise.all([store.set('foo', 5), store.set('bar', 2)]);
507
+
508
+ // add foo and bar
509
+ await add();
510
+
511
+ // baz should be equal to 7
512
+ console.log('assert', (await store.get('baz')) === 7);
513
+
514
+ // demo of storing an object
515
+ await store.set('example object', {
516
+ foo: 'you can store objects in the store too',
517
+ bar: "just don't expect it to work with anything recursive (cannot serialize)",
518
+ });
519
+ })();
520
+ ```
521
+
522
+ For Node Plugins, the `store` is the third argument passed into the constructor. For JSONRPC Plugins, the `"store.get"`/etc. methods can be used.
523
+
524
+ **JSONRPC Note:** `store.set` has an array of arguments (`[key, value]`)
525
+
526
+ ## Node VM Plugins
527
+
528
+ Node VM Plugins are what you should be using. They are run inside a VM inside a Worker. This means when they crash, they do not crash the whole server, and they can in the future have locked down permissions (disable filesystem access, etc.).
529
+
530
+ These plugins receive a "proxy" reference to `omegga` and have limited reach for what they can touch.
531
+
532
+ Register custom `/commands` by returning `{registeredCommands: ['foo', 'bar']}` (registers command `/foo` and `/bar`) in the `async init()` method.
533
+
534
+ By defining an `async pluginEvent(event, from, ...args)` method in your plugin class, you can respond to events from other plugins, where `from` is the name of the other plugin, `event` is the name of the custom event, and `args` is an array of any passed arguments.
535
+
536
+ ### Globals
537
+
538
+ - `OMEGGA_UTIL` - access to the `src/util/index.js` module
539
+ - `Omegga` - access to the "proxy" omegga
540
+ - `console.log` - and other variants (`console.error`, `console.info`) print specialized output to console
541
+
542
+ ### Folder Structure
543
+
544
+ In a `plugins` directory create the following folder structure:
545
+
546
+ - `plugins/myPlugin` - plugin folder (required)
547
+ - `plugins/myPlugin/omegga.plugin.js` - js plugin main file (required)
548
+ - `plugins/myPlugin/doc.json`
549
+ - `plugins/myPlugin/access.json` - plugin access information (required, but doesn't have to have anything right now). this will contain what things the vm will need to access
550
+
551
+ ### `access.json` (examples)
552
+
553
+ Access to any builtin modules (`fs`, `path`, etc.)
554
+
555
+ ```json
556
+ ["*"]
557
+ ```
558
+
559
+ Access to nothing - only the code in the `omegga.plugin.js`
560
+
561
+ ```json
562
+ []
563
+ ```
564
+
565
+ Access to only `fs`, (`const fs = require('fs');`)
566
+
567
+ ```json
568
+ ["fs"]
569
+ ```
570
+
571
+ ### `omegga.plugin.js` (example)
572
+
573
+ ```javascript
574
+ class PluginName {
575
+ // the constructor also contains an omegga if you don't want to use the global one
576
+ // config and store variables are optional but provide access to the plugin data store
577
+ constructor(omegga, config, store) {
578
+ this.omegga = omegga;
579
+ this.config = config;
580
+ this.store = store;
581
+ console.info('constructed my plugin!');
582
+ }
583
+
584
+ async init() {
585
+ Omegga.on('chatcmd:ping', (name, ...args) => {
586
+ Omegga.broadcast(`pong @ ${name} + ${args.length} args`);
587
+ }).on('chatcmd:pos', async name => {
588
+ const [x, y, z] = await Omegga.getPlayer(name).getPosition();
589
+ Omegga.broadcast(`<b>${name}</> is at ${x} ${y} ${z}`);
590
+ });
591
+ }
592
+
593
+ async stop() {
594
+ // any remove events are not necessary because the VM removes the code
595
+ }
596
+ }
597
+
598
+ module.exports = PluginName;
599
+ ```
600
+
601
+ ### `omegga.plugin.ts` (example)
602
+
603
+ Be sure to put `.build/` and `node_modules/` in your `.gitignore`
604
+
605
+ **Requires a `tsconfig.json`**:
606
+
607
+ ```json
608
+ {
609
+ "compilerOptions": {
610
+ "noEmit": true,
611
+ "esModuleInterop": true,
612
+ "moduleResolution": "node",
613
+ "resolveJsonModule": true,
614
+ "target": "es2020",
615
+ "baseUrl": ".",
616
+ "paths": {
617
+ "omegga/*": ["node_modules/omegga/dist/*"]
618
+ }
619
+ }
620
+ }
621
+ ```
622
+
623
+ `omegga.plugin.ts`:
624
+
625
+ ```ts
626
+ import type { OmeggaPlugin, OL, PS, PC } from 'omegga/plugin';
627
+
628
+ type Config = { foo: string };
629
+ type Storage = { bar: string };
630
+
631
+ export default class Plugin implements OmeggaPlugin<Config, Storage> {
632
+ omegga: OL;
633
+ config: PC<Config>;
634
+ store: PS<Storage>;
635
+
636
+ constructor(omegga: OL, config: PC<Config>, store: PS<Storage>) {
637
+ this.omegga = omegga;
638
+ this.config = config;
639
+ this.store = store;
640
+ }
641
+
642
+ async init() {
643
+ // Write your plugin!
644
+ this.omegga.on('cmd:test', (speaker: string) => {
645
+ this.omegga.broadcast(`Hello, ${speaker}!`);
646
+ });
647
+
648
+ return { registeredCommands: ['test'] };
649
+ }
650
+
651
+ async stop() {
652
+ // Anything that needs to be cleaned up...
653
+ }
654
+ }
655
+ ```
656
+
657
+ ## Node Plugins
658
+
659
+ Node plugins are effectively `require`'d into omegga. They have the potential to crash the entire service through uncaught exceptions and also can be insecure. Develop and run these at your own risk - your server stability may suffer.
660
+
661
+ These plugins receive a direct reference to the `omegga` that wraps the brickadia server. As a result, they can directly modify how omegga runs.
662
+
663
+ Cleanup is important as code can still be running after the plugin is unloaded resulting in strange and undefined behavior. Make sure to run `clearInterval` and `clearTimeout`
664
+
665
+ Register custom `/commands` by returning `{registeredCommands: ['foo', 'bar']}` (registers command `/foo` and `/bar`) in the `async init()` method.
666
+
667
+ ### Globals
668
+
669
+ - `OMEGGA_UTIL` - access to the `src/util/index.js` module
670
+
671
+ ### Folder Structure
672
+
673
+ In a `plugins` directory create the following folder structure:
674
+
675
+ - `plugins/myPlugin` - plugin folder (required)
676
+ - `plugins/myPlugin/doc.json`
677
+ - `plugins/myPlugin/omegga.main.js` - js plugin main file (required)
678
+
679
+ ### `omegga.main.js` (example)
680
+
681
+ ```javascript
682
+ class PluginName {
683
+ // config and store variables are optional but provide access to the plugin data store
684
+ constructor(omegga, config, store) {
685
+ this.omegga = omegga;
686
+ this.config = config;
687
+ this.store = store;
688
+ }
689
+
690
+ async init() {
691
+ this.omegga
692
+ .on('chatcmd:ping', (name, ...args) => {
693
+ this.omegga.broadcast(`pong @ ${name} + ${args.length} args`);
694
+ })
695
+ .on('chatcmd:pos', async name => {
696
+ const [x, y, z] = await this.omegga.getPlayer(name).getPosition();
697
+ this.omegga.broadcast(`<b>${name}</> is at ${x} ${y} ${z}`);
698
+ });
699
+ }
700
+
701
+ async stop() {
702
+ this.omegga
703
+ .removeAllListeners('chatcmd:ping')
704
+ .removeAllListeners('chatcmd:pos');
705
+ }
706
+ }
707
+
708
+ module.exports = PluginName;
709
+ ```
710
+
711
+ ## JSON RPC Plugins
712
+
713
+ JSON RPC Plugins let you use any language you desire, as long as you can run it from a single executable file. They follow the [JSON-RPC 2.0 Specification](https://www.jsonrpc.org/specification)
714
+
715
+ The server communicates with the plugin by sending messages to `stdin` and expects responses in `stdout`. All `stderr` is printed to the console.
716
+
717
+ Register custom `/commands` by returning `{registeredCommands: ['foo', 'bar']}` (registers command `/foo` and `/bar`) in the `init` method.
718
+
719
+ ### Omegga Methods (You can access these)
720
+
721
+ | Method | Arguments | Description | Returns |
722
+ | ------------------------------ | ---------------------------------------------------- | ------------------------------------------------------------------- | --------------------------------------------------------------- |
723
+ | `log` | line (string) | Prints message to omegga console | |
724
+ | `error` | line (string) | Same as `log` but with different colors | |
725
+ | `info` | line (string) | Same as `log` but with different colors | |
726
+ | `debug` | line (string) | Same as `log` but with different colors | |
727
+ | `warn` | line (string) | Same as `log` but with different colors | |
728
+ | `trace` | line (string) | Same as `log` but with different colors | |
729
+ | `store.get` | key (string) | Get an object from plugin store | Object |
730
+ | `store.set` | [key (string), value (any)] | Store an object in plugin store | |
731
+ | `store.delete` | key (string) | Remove an object from plugin store | |
732
+ | `store.wipe` | _none_ | Remove all objects from plugin store | |
733
+ | `store.count` | _none_ | Count number of objects in plugin store | Integer |
734
+ | `store.keys` | _none_ | Get keys for all objects in plugin store | List of Strings |
735
+ | `exec` | cmd (string) | Writes a console command to Brickadia | |
736
+ | `writeln` | cmd (string) | Same as `exec` | |
737
+ | `broadcast` | line (string) | Broadcasts a message to the server | |
738
+ | `whisper` | {target: string, line: string} | Sends a message to a specific client | |
739
+ | `middlePrint` | {target: string, line: string} | Sends a middle print message to a specific client | |
740
+ | `getPlayers` | _none_ | Online players | List of Players |
741
+ | `getAllPlayerPositions` | _none_ | An array of objects with fields `pos` and `player`. | List of { _Player Object_(...), _Position_(...), isDead(bool) } |
742
+ | `getRoleSetup` | _none_ | Server roles | _JSON Data_ |
743
+ | `getBanList` | _none_ | List of bans | _JSON Data_ |
744
+ | `getSaves` | _none_ | Saves in the saves directory | List Strings |
745
+ | `getSavePath` | name (string) | The path to a specific save | String |
746
+ | `getSaveData` | _none_ | Current save as brs-js data | _BRS Object_ |
747
+ | `clearBricks` | {target: string, quiet: bool} | Clears a specific player's bricks | |
748
+ | `clearAllBricks` | quiet | Clears all bricks on the server | |
749
+ | `saveBricks` | name (string) | Save bricks to a save named `name` | |
750
+ | `loadBricks` | {name: string, offX, offY, offY, quiet: bool} | Load bricks of save named `name` | |
751
+ | `loadBricksOnPlayer` | {name: string, player: string, offX, offY, offY} | Load bricks of save named `name` on player clipboard | |
752
+ | `readSaveData` | name (string) | Parses save into a brs-js save object, returns the object | _BRS Object_ |
753
+ | `loadSaveData` | {data: object, offX, offY, offY, quiet: bool} | Builds brs file from data, loads the file | |
754
+ | `loadSaveDataOnPlayer` | {data: object, player: string, offX, offY, offY} | Builds brs file from data, loads the file onto a player's clipboard | |
755
+ | `changeMap` | map (string) | Change map to specified map name, returns success | Boolean |
756
+ | `player.get` | target (string) | Gets the player by their name or UUID. | `{name, id, controller, state, host: bool}` |
757
+ | `player.getRoles` | target (string) | Target's roles | |
758
+ | `player.getPermissions` | target (string) | Target's permissions | List of Strings |
759
+ | `player.getNameColor` | target (string) | Target's name color | _RGB Hex Object_ (int, int ,int) |
760
+ | `player.getPosition` | target (string) | Target's position | _Position Object_ |
761
+ | `player.getGhostBrick` | target (string) | Target's ghost brick | {targetGrid, location(_Location_), orientation} |
762
+ | `player.getPaint` | target (string) | Target's current paint selection | {materialIndex, materialAlpha, material, color)} |
763
+ | `player.getTemplateBounds` | target (string) | Target's template/selection bounds | {minBound, maxBound, Center} |
764
+ | `player.getTemplateBoundsData` | target (string) | Target's template/selection as brs-js save data | _Brick Object_ |
765
+ | `player.loadSaveData` | {target, data, offX, offY, offZ} | Loads brs-js save data to the targets clipboard | |
766
+ | `player.loadDataAtGhostBrick` | {target, data, rotate=true, offX, offY, offZ, quiet} | Loads brs-js save data at the target's selection bounds | |
767
+ | `plugin.get` | target (string) | Gets info on the target plugin | Object |
768
+ | `plugin.emit` | [target (string), event (string), ...args (any)] | Emit a custom event to the target plugin | |
769
+
770
+ ### Plugin Methods (You implement these)
771
+
772
+ | Method | Arguments | Description | Required |
773
+ | -------------------- | -------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- |
774
+ | `init` | config object | Returns a start result, called on plugin start | &#9745; |
775
+ | `stop` | _none_ | Returns _something_, called on plugin stop | &#9745; |
776
+ | `bootstrap` | [{ omegga info (`host`, `version`, etc) }] | Run when plugin is started for base data | |
777
+ | `plugin:players:raw` | [[... [player `name`, `id`, `controller`, `state`]]] | Lists players on the server | |
778
+ | `plugin:emit` | [event, from, ...args] | Fired when another plugin sends an event | |
779
+ | `line` | [brickadiaLog string] | A brickadia console log | |
780
+ | `start` | [{map}] | On brickadia server start | |
781
+ | `host` | [{name, id}] | When the host is detected | |
782
+ | `version` | [-1 or the CL number] | When the version is detected | |
783
+ | `unauthorized` | _none_ | On brickadia server fails an auth check | |
784
+ | `join` | [{name, id, state, controller}] | Run when a player joins | |
785
+ | `leave` | [{name, id, state, controller}] | Run when a player leaves | |
786
+ | `cmd:command` | [playerName, ...args] | Runs when a player runs a `/command args` | |
787
+ | `chatcmd:command` | [playerName, ...args] | Runs when a player runs a `!command args` | |
788
+ | `chat` | [playerName, message] | Runs when a player sends a chat message | |
789
+ | `interact` | {brick_asset: string;player: { id: string; name: string; controller: string; pawn: string };position: [number, number, number];} | Runs when a player clicks a brick with an interact component. `data` is parsed JSON if `line` (from interact component) starts with "json:{"your": "json"}`. Uses interact log field | |
790
+ | `event:NAME` | [&gt;player from click&lt;, ...args] | Runs when an interact component has `event:NAME: arg1,arg2,arg\,3, | |
791
+ | `mapchange` | \[{map}\] | Runs when the map changes | |
792
+ | `autorestart` | [autorestart config] | Runs server has an autorestart scheduled | |
793
+ | `minigamejoin` | {player: {name, id}; minigameName: string} | Runs when a player joins a minigame. Note that minigameName is not unique between minigames. minigameName will be null if player leaves all minigames. This will run before `join` | |
794
+
795
+ ### Folder Structure
796
+
797
+ In a `plugins` directory create the following folder structure:
798
+
799
+ - `plugins/myPlugin` - plugin folder (required)
800
+ - `plugins/myPlugin/doc.json`
801
+ - `plugins/myPlugin/omegga_plugin` - executable plugin file (required)
802
+
803
+ ### `omegga_plugin` (example, node javascript)
804
+
805
+ ```javascript
806
+ #!/usr/bin/env node
807
+
808
+ const readline = require('readline');
809
+ const { EventEmitter } = require('events');
810
+ const {
811
+ JSONRPCServer,
812
+ JSONRPCServerAndClient,
813
+ JSONRPCClient,
814
+ } = require('json-rpc-2.0');
815
+
816
+ // events
817
+ const ev = new EventEmitter();
818
+
819
+ // stdio handling
820
+ const rl = readline.createInterface({
821
+ input: process.stdin,
822
+ output: process.stdout,
823
+ terminal: false,
824
+ });
825
+
826
+ // rpc "server and client" for responding/receiving messages
827
+ const rpc = new JSONRPCServerAndClient(
828
+ new JSONRPCServer(),
829
+ // the client outputs JSON to console
830
+ new JSONRPCClient(async blob => console.log(JSON.stringify(blob)))
831
+ );
832
+
833
+ // on stdin, pass into rpc
834
+ rl.on('line', line => {
835
+ try {
836
+ rpc.receiveAndSend(JSON.parse(line));
837
+ } catch (e) {
838
+ console.error(e);
839
+ }
840
+ });
841
+
842
+ // regexes for matching brickadia console logs
843
+ const GENERIC_LINE_REGEX =
844
+ /^(\[(?<date>\d{4}\.\d\d.\d\d-\d\d.\d\d.\d\d:\d{3})\]\[\s*(?<counter>\d+)\])?(?<generator>\w+): (?<data>.+)$/;
845
+ const LOG_LINE_REGEX =
846
+ /\[(?<date>\d{4}\.\d\d.\d\d-\d\d.\d\d.\d\d:\d{3})\]\[\s*(?<counter>\d+)\](?<rest>.*)$/;
847
+
848
+ ev.on('line', line => {
849
+ const logMatch = line.match(LOG_LINE_REGEX);
850
+ if (!logMatch) return;
851
+ const {
852
+ groups: { rest },
853
+ } = logMatch;
854
+ const dataMatch = rest.match(GENERIC_LINE_REGEX);
855
+ if (dataMatch) ev.emit('logData', dataMatch.groups);
856
+ else ev.emit('logLine', rest);
857
+ });
858
+
859
+ // list of players
860
+ let players;
861
+
862
+ // get a player by name
863
+ const getPlayer = name => players.find(p => p.name === name);
864
+
865
+ // watch console logs for a pattern, then remove the listener
866
+ function watch(exec, pattern) {
867
+ return new Promise(resolve => {
868
+ function listener(line) {
869
+ const match = line.match(pattern);
870
+ // listener removes itself on a match
871
+ if (match) {
872
+ ev.off('logLine', listener);
873
+ resolve(match.groups);
874
+ }
875
+ }
876
+ // add the listener
877
+ ev.on('logLine', listener);
878
+
879
+ // run the console command
880
+ rpc.notify('writeln', exec);
881
+ });
882
+ }
883
+
884
+ // get a player's position
885
+ async function getPlayerPos(name) {
886
+ const player = getPlayer(name);
887
+ if (!player) return;
888
+
889
+ // get player position from player controller
890
+ const pawnRegExp = new RegExp(
891
+ `BP_PlayerController_C .+?PersistentLevel\\.${player.controller}\.Pawn = BP_FigureV2_C'.+?:PersistentLevel.(?<pawn>BP_FigureV2_C_\\d+)'`
892
+ );
893
+ const { pawn } = await watch(
894
+ `GetAll BP_PlayerController_C Pawn Name=${player.controller}`,
895
+ pawnRegExp
896
+ );
897
+
898
+ // get player position from pawn
899
+ const posRegExp = new RegExp(
900
+ `CapsuleComponent .+?PersistentLevel\\.${pawn}\\.CollisionCylinder\\.RelativeLocation = \\(X=(?<x>[\\d\\.-]+),Y=(?<y>[\\d\\.-]+),Z=(?<z>[\\d\\.-]+)\\)`
901
+ );
902
+ const { x, y, z } = await watch(
903
+ `GetAll SceneComponent RelativeLocation Name=CollisionCylinder Outer=${pawn}`,
904
+ posRegExp
905
+ );
906
+
907
+ return [x, y, z].map(Number);
908
+ }
909
+
910
+ // emit a console log
911
+ const log = (...args) => rpc.notify('log', args.join(' '));
912
+
913
+ // when available players updates - plugin:players:raw is emitted
914
+ rpc.addMethod('plugin:players:raw', ([playerArr]) => {
915
+ // update the players list
916
+ players = playerArr.map(p => ({
917
+ name: p[0],
918
+ id: p[1],
919
+ controller: p[2],
920
+ state: p[3],
921
+ }));
922
+ });
923
+
924
+ // ping command
925
+ rpc.addMethod('chatcmd:ping', ([name, ...args]) => {
926
+ rpc.notify('broadcast', `pong @ ${name} + ${args.length} args`);
927
+ });
928
+
929
+ // player position command
930
+ rpc.addMethod('chatcmd:pos', async ([name]) => {
931
+ log('player', name, 'requests position');
932
+ const [x, y, z] = await getPlayerPos(name);
933
+ rpc.notify('broadcast', `<b>${name}</> is at ${x} ${y} ${z}`);
934
+ });
935
+
936
+ // pass lines into the event emitter
937
+ rpc.addMethod('line', ([line]) => {
938
+ ev.emit('line', line);
939
+ });
940
+
941
+ // receive config object in init
942
+ rpc.addMethod('init', async ([config]) => ({ registeredCommands: [] }));
943
+ rpc.addMethod('stop', async () => 'ok');
944
+ ```
945
+
946
+ # Extra Features
947
+
948
+ ## Environment Variables
949
+
950
+ `omegga` accepts the following environment variables:
951
+
952
+ - `BRICKADIA_USER` - brickadia auth username (on first start)
953
+ - `BRICKADIA_PASS` - brickadia auth password (on first start)
954
+ - `BRICKADIA_PORT` - brickadia server port (on config creation)
955
+ - `OMEGGA_PORT` - omegga webserver port (on config creation)
956
+
957
+ ## Config
958
+
959
+ Default config values (including hidden ones)
960
+
961
+ ```yaml
962
+ omegga:
963
+ port: 8080 # web-ui port
964
+ webui: true # enable web-ui
965
+ plugins: true # enable plugins
966
+ singleUser: false # disable web-ui auth users
967
+ https: true # enable https for web-ui
968
+ debug: false # debug logging
969
+ server:
970
+ port: 7777 # game server port
971
+ map: Plate # map name
972
+ branch: unstable:unstable-server # branch alias:branch name
973
+ ```