Your first plugin
This tutorial installs a community plugin, configures it and uses it from a Lua script, end to
end. Part 1 uses Moongate.Plugins.Notifications with its log channel: it needs no account and
sends nothing outside your machine. Part 2 repeats the steps with Moongate.Plugins.OpenAi, which
needs an API key.
You need:
- a prepared server root (
mgctl init, see First start) and a game master account with a character in the world; mgctlfrom the same release as the server:mgctl --versionprints the version, and a plugin is built for exactly one;- an internet connection for
mgctlto reach Codeberg.
The commands below use /srv/moongate as the root. Replace it with yours.
Part 1: Notifications
Section titled “Part 1: Notifications”1. Stop the server and install
Section titled “1. Stop the server and install”The server reads plugins/ once, at start, so stop it first. Then:
mgctl plugin install Moongate.Plugins.Notifications --root-directory /srv/moongateWith a plugin id, mgctl looks in the community repository and picks the newest build made for
your Moongate. It shows what it found and asks before it installs:
Plugin Moongate.Plugins.Notifications 0.2.0Built for Moongate 0.17.0Found in Codeberg moongate/moongate-pluginsSHA-256 a483f510ed545d1bdcee50a0874e8d518375e60aaaeddf7c85a18381f263a268A plugin runs inside the server with all its permissions: install it only from a source you trust.Install it? [y/N]Answer y. Check the result:
mgctl plugin list --root-directory /srv/moongatePlugin Version Built for OriginMoongate.Plugins.Notifications 0.2.0 0.17.0 packageThe plugin is now the folder /srv/moongate/plugins/Moongate.Plugins.Notifications/.
2. Start once, then configure
Section titled “2. Start once, then configure”Start the server and stop it again. The plugin appends its [notifications] table to
config/moongate.toml at its first start, switched off. Open the file and make the table say
this, keeping what the plugin appended if it differs:
[notifications]enabled = true
[notifications.channels.audit]type = "log"A channel is where notifications go. log writes one line of the server log for each
notification; Discord and webhook channels are configured the same way, with an address
that you keep in an environment variable (Telegram and SMTP come from their own plugins). The keys are on the plugin’s own page, in
its repository.
3. Publish a notification from Lua
Section titled “3. Publish a notification from Lua”The plugin adds a Lua module, notify. To try it you need something that runs a script: a small
NPC that rings the staff bell when a player says “alarm”.
Create templates/mobiles/tutorial_bell.toml:
[[mobile]]id = "tutorial_bell"base_id = "male"name = "Bell Ringer"script_id = "tutorial_bell"notoriety = "invulnerable"Create scripts/mobiles/tutorial_bell.lua:
tutorial_bell = {}
function tutorial_bell.on_speech(serial, speaker, text) if not text:lower():find("alarm", 1, true) then return end
local sent = notify.send{ topic = "staff.tutorial", level = "info", title = "The bell rang", message = "A player said the word to the bell ringer.", }
if sent then npc.say(serial, "The staff has been told.") else npc.say(serial, "Nobody wanted that notification.") endendThe message does not carry what the player typed: a notification leaves the server for the channel’s owner, so publish what the script knows, not what players write.
4. Try it
Section titled “4. Try it”- Start the server and look for
Disk plugin Moongate.Plugins.Notifications: ...in the log: it is the line that tells you the plugin loaded. - Log in with your game master character, run
.spawn tutorial_belland target a spot. - Say “alarm” within 15 cells of the NPC.
- The NPC answers “The staff has been told.” and the server log holds the notification:
Notification staff.tutorial: The bell rang - A player said the word to the bell ringer.
When the NPC answers “Nobody wanted that notification.”, notify.send returned false:
| Cause | Fix |
|---|---|
enabled = false |
Set enabled = true and restart |
| No channel wants the notification | Add a channel; with topics or min_level on it, make sure staff.tutorial and info pass |
| An argument is bad | The log has a warning that names it |
Part 2: OpenAI
Section titled “Part 2: OpenAI”The same four steps, for an NPC that answers a player in character. What players type goes to OpenAI, or to the server you point the plugin at: the plugin is off until you turn it on.
-
Install. With the server stopped:
Terminal window mgctl plugin install Moongate.Plugins.OpenAi --root-directory /srv/moongate -
Configure. Start once and stop, then edit the
[openai]table. Put the key in an environment variable of the server, not in the file, and name the variable:[openai]enabled = trueapi_key_env = "OPENAI_API_KEY"model = "your-model" # required: the model your provider offers[openai.personas.fabbro]prompt = "You are Mario, the blacksmith of Britain. You are gruff and proud of your work."A persona is a prompt you name; the script picks one by its id.
base_urlpoints the plugin at any OpenAI-compatible server, a local model included. -
Use it. Create
templates/mobiles/tutorial_mario.tomlwithid = "tutorial_mario",name = "Mario"andscript_id = "tutorial_mario", as in part 1, andscripts/mobiles/tutorial_mario.lua:tutorial_mario = {}function tutorial_mario.on_speech(serial, speaker, text)if not text:lower():find("mario", 1, true) thenreturnendnpc.look_at(serial, speaker)openai.ask{persona = "fabbro",npc = serial,player = speaker,text = text,callback = function(answer, reason)if answer thennpc.say(serial, answer)elsenpc.say(serial, "(no answer: " .. tostring(reason) .. ")")endend}endThe callback runs later, on a turn of the game loop, with the line to say or
niland a reason. The plugin says nothing by itself: what the NPC does without an answer is the script’s choice. Printing the reason, as here, is for the trial; a shard would stay silent. -
Try it. Start the server, run
.spawn tutorial_mario, target a spot and say “Mario, what do you forge?” within 15 cells. The reasons you can meet (disabled,persona,cooldown,rate,budget,timeout,empty, among others) are listed on the plugin’s page.
Update and remove
Section titled “Update and remove”Install over the old one to update, and remove to delete; both leave the settings in
config/moongate.toml:
mgctl plugin install Moongate.Plugins.OpenAi --root-directory /srv/moongatemgctl plugin remove Moongate.Plugins.OpenAi --root-directory /srv/moongateAfter a server update, install the build of each plugin made for the new version: the server refuses one built for another.
Where next
Section titled “Where next”- Install and manage plugins: Docker, your own repository, SQL migrations and what to do when the server does not start.
- mgctl: what
plugin installchecks. - Mobile scripts: the hooks an NPC script can define.
- Writing a plugin: build one yourself.
Made with ❤️ by Tom
