Appearance
Your first extension
This walks through making an extension with nothing but the MMO Run server download, from an empty folder to a .pck other hosts can install. Style packs work the same way; the differences are at the end.
What you need
- The dedicated server for your platform from mmo.run (
mmorun-server). It runs your work in progress and has the authoring commands built in. - The client (or the launcher) to join your test world and see the result.
- A text editor. Extensions are GDScript and JSON. Any editor works; one with GDScript highlighting helps.
1. Make a test world
sh
./mmorun-server --world ./worlds/test --port 27015 --consoleThe first run creates the world in ./worlds/test. Join it from the client at 127.0.0.1:27015; the first account to log in becomes the owner. Stop the server with /stop in the console (or Ctrl+C).
2. Write the extension
Extensions under development live in the world's packs/ folder, one folder per extension. The folder name is the extension's id.
worlds/test/packs/hello/
extension.json
config.schema.json
server.gdextension.json:
json
{
"id": "hello",
"name": "Hello",
"version": "1.0.0",
"api_version": [1],
"author": "you",
"description": "Greets players.",
"server": "server.gd",
"config": "config.schema.json",
"client_code": false,
"permissions": ["commands", "store"]
}config.schema.json (hosts get a settings form built from it):
json
{
"title": "Hello",
"type": "object",
"properties": {
"greeting": {"type": "string", "title": "Greeting", "default": "Welcome, {name}!", "x-live": true}
}
}server.gd:
gdscript
extends ServerExtension
func on_register(reg: Registrar) -> void:
reg.command("hello", "player", _cmd_hello, "Say hello.")
func on_ready(_ctx: Dictionary) -> void:
on("character:enter_world", func(p: Dictionary) -> void:
notify(p["peer_id"], str(cfg("greeting")).replace("{name}", p["entity"].name)))
func _cmd_hello(ctx: Dictionary, _args: PackedStringArray) -> String:
var n := int(store_get("count", 0)) + 1
store_set("count", n)
return "Hello! Used %d times." % n3. Run it
sh
./mmorun-server --world ./worlds/test --port 27015 --console --enable hello--enable turns the extension on for this world and remembers it, so later runs don't need the flag. The server log says extension hello loaded from folder ... and lists hello in the ready. line. Join, and you'll see the greeting; type /hello in chat.
To change something, edit the files and restart the server. Clients that join afterwards download the new version on their own.
When something goes wrong, the reason is in the console and in worlds/test/logs/server.log. Script errors name the file and line. A manifest problem (a typo in JSON, an id that does not match the folder name) is logged and the extension is skipped.
You can also turn extensions on and off from the launcher: open the world's Extensions tab and your folder shows up with a folder badge.
4. Add more
- A client half (
"client": "client.gd","client_code": true) for panels, effects and input. Players download it when they join, so keep it to presentation: the server decides everything that matters. - Content under
content/<kind>/*.json: items, zones, quests, NPCs, or kinds you register yourself. - Art under
assets/, loaded withasset("...")so style packs can replace it. - Tables in
migrations/001_init.sqlwhen key-value storage is not enough.
Everything available is in the Extension API.
5. Check it
sh
./mmorun-server --validate ./worlds/test/packs/helloThis loads your extension the way a server would, checks every content file against its schema (yours included), prints each problem with its file, and exits non-zero if there are any. Run it before you share.
6. Share it
sh
./mmorun-server --pack ./worlds/test/packs/hello
# packed 3 files from .../hello -> ./hello.pck (sha256 ...)That hello.pck is the whole extension. A host installs it by putting it in their world's packs/ folder and turning it on in the launcher's Extensions tab (or with --enable hello). Players never install anything: joining a world downloads the packs it runs and checks them against the hashes the server announces.
Bump version in extension.json whenever you share a new build. Other extensions depend on yours by version range, so follow semver: breaking changes to your events, commands or config keys get a new major version.
Style packs
A style pack is a folder too, with stylepack.json instead of extension.json and no code:
worlds/test/packs/ember/
stylepack.json {"id": "ember", "name": "Ember", "version": "1.0.0", "author": "you", "covers": ["ui"]}
theme.json {"colors": {"accent": "#ff5522"}}Make a world that uses it with --stylepack ember the first time you run it, or set "style_pack": "ember" in an existing world's world.json. Anything the pack does not include falls back to the default art, so you can start with one color and grow from there. --pack ./worlds/test/packs/ember writes ember.stylepack.pck. The full format is in Style packs.