Menu Modules
Menu Modules
From initial connection to the screens and mods your users interact with, the entire experience is made up of menu entries — And all menu entries found within menu.hjson are backed by Menu Modules. For basic menus, a standard handler is implemented requiring no code. However, if you would like to create a menu that has custom handling, you will very likely be inheriting from from MenuModule
. More on this below.
Remember that ENiGMA does not impose any stucture to your system! The “flow” of all
menu.hjson
entries is up to you!
If the
module
entry is not present in amenu.hjson
entry, the system automatically uses standard_menu.js.
Creating a New Module
At the highest level, to create a new custom menu or mod, inherit from MenuModule
and expose it via the getModule
exported method:
// my_fancy_module.js
exports.getModule = class MyFancyModule extends MenuModule {
constructor(options) {
super(options);
}
};
Next, override the appropriate methods to add some functionality! Below is an example fragment overriding just initSequence()
:
initSequence() {
async.series(
[
callback => {
// call base method
return this.beforeArt(callback);
},
callback => {
// a private method to display a main "page"
return this._displayMainPage(false, callback);
},
],
() => {
this.finishedLoading();
}
);
}
Remember that all menus within ENiGMA are created by inheriting from
MenuModule
. Take a look at existing examples such as WFC, NUA, MRC and more!
ModuleInfo
To register your module with the system, include a moduleInfo
declaration in your exports. The following members are available:
Field | Required | Description |
---|---|---|
name |
Short name of the module | |
desc |
Long description of this module | |
author |
Author(s) of module | |
packageName |
Defines a reverse DNS style package name. Can be used in conjunction with the getModDatabasePath() call form database.js to interact with a database specific to your module (See example below) |
Example:
exports.moduleInfo = {
name: 'Super Dope Mod',
desc: '...a super dope mod, duh.',
author: 'You!',
packageName: `com.myname.foo.super-dope-mod`,
};
Per-Mod Databases
Custom mods often need their own data persistence. This can be acheived with getModDatabsePath()
and your moduleInfo
’s packageName
.
Example:
self.database = getTransactionDatabase(
new sqlite3.Database(getModDatabasePath(moduleInfo), callback)
);
Given the packageName
above, a database will be created at the following location:
$enigma-bbs/db/mods/com.myname.foo.super-dope-mod.sqlite3
Menu Methods
Form handler methods specified by @method:someName
in your menu.hjson
entries map to those found in your module’s menuMethods
object. That is, this.menuMethods
and have the following signature (formData, extraArgs, cb)
. For example, consider the following menu.hjson
fragment:
actionKeys: [
{
keys: [ "a", "shift + a" ]
action: @method:toggleAvailable
}
]
We can handle this in our module as such:
exports.getModule = class MyFancyModule extends MenuModule {
constructor(options) {
super(options);
this.menuMethods = {
toggleAvailable: (formData, extraArgs, cb) => {
// ...do something fancy...
return cb(null);
}
};
}
}
MenuModule Lifecycle
Below is a very high level diagram showing the basic lifecycle of a MenuModule.
Methods indicated above with ()
in their name such as enter()
are overridable when inheriting form MenuModule
.
-
enter()
is the first to be called. There is no callback. The default implementation is to simply callthis.initSequence()
. -
displayQueuedInterruptions(callback)
is called, and if interruptions are allowed for this menu, any that may be queued will be displayed first. -
beforeArt(callback)
is called before any art is displayed. The default implementation will set emulated baud rate, and clear the screen if either are requested by the menu’sconfig
block. -
mciReady(mciData, callback)
is called when art is loaded and MCI codes are initialized. The default implementation of a customMenuModule
simply continues. See also standardMCIReadyHandler.
MenuModule Helper Methods
Many helper methods exist and are available to code inheriting from MenuModule
. Below are some examples. Poke around at menu_module.js to discover more!
Views & View Controller
displayAsset(name | Buffer, options, callback)
:
Display an asset by name
or by supplying an Buffer
.
options
is an optional Object with any of the following properties:
-
clearScreen
(Boolean): Should the screen be cleared first? -
encoding
(String): Encoding ofBuffer
if used. Defaults tocp437
. -
font
(String): SyncTERM style font to use. -
trailingLF
(Boolean): Should a trailing LF be allowed? -
startRow
(Number): Row in which to start drawing at
prepViewController(name, formId, mciMap, callback)
:
Prepares the menu’s View Controller for a form of name
and formId
using the supplied mciMap
. callback
has the following siguature: (err, viewController, created)
where created
is true
if a new View Controller was made.
prepViewControllerWithArt()
displayArtAndPrepViewController()
setViewText()
getView()
updateCustomViewTextsWithFilter()
refreshPredefinedMciViewsByCode()
Validation
validateMCIByViewIds()
validateConfigFields()
Date/Time Helpers
The following methods take a single input to specify style, defaulting to short
. If your menu or theme config
block specifies a cooresponding value such as dateFormat
or dateTimeFormat
, that value will be used, else standard fallbacks apply:
getDateFormat()
getTimeFormat()
getDateTimeFormat()
Misc
promptForInput()
standardMCIReadyHandler(mciData, callback)
:
This is a standard and commonly used mciReady()
implementation:
mciReady(mciData, cb) {
return this.standardMCIReadyHandler(mciData, cb);
}
Where mciData
is a Object mapping MCI codes such as TL2
to their properties:
-
SGR
: Graphics rendition -
focusSGR
(Only present if art contained both, ie:TL2^[0;mTL2
) -
position
(Array of Number): Position in [Row, Column] order -
args
(Array): Any arguments to the MCI code -
code
(String): The code itself, such asTL
-
id
(Number): The MCI code’s ID such as1
Search the code for the above methods to see how they are used in the base system!
Custom Mods
Most mods will also derive from MenuModule
. Some things to be aware of:
- Custom mods that bring in their own dependencies must also include their own
package.json
and other Node requirements - Be sure to use
packageName
andgetModDatabasePath()
for any (database) peristence needs. - Custom mods in
mods/the_mod_name/
and theMenuModule
entry point must be within a file of the same name:mods/the_mod_name/the_mod_name.js
- To import ENiGMA modules
require()
from../../core/