Package Format
This is the reference for package.yaml, the file that describes a SharpMUSH package. For what
packages are and how to install them, start with Softcode Packages.
Package Declarations covers the roles, settings, wiki pages,
attribute entries and other blocks a package brings beside its objects, and
Application, Plugin and UI Packages the other kinds of package.
A package is a folder with a package.yaml in it. The file describes the result you want, not a
script: there is no ordering, no @create lines, and no dbrefs. Objects are named with
{{ref}} tokens, which the installer turns into real dbrefs when the package is applied. The server
compares that description with the live game, a wizard reviews the changes, and only then is anything
written. Upgrades merge with your local edits, every apply is recorded, and an install can be rolled
back.
A complete example
Section titled “A complete example”Every top-level key a softcode package can use, with the format version that added it:
format: "1.10" # format version (omit = 1; 1.2 adds roles and permissions, 1.3 settings, 1.4 wiki pages, 1.5 attribute entries, 1.6 the engine range, 1.7 plugins, 1.8 attribute indexes, 1.9 attribute watches, 1.10 category)package: myrddins-bbs # required: lowercase letters, digits and hyphens, up to 64 charactersversion: 2.4.1 # required: a version number (see Versions below)authors: [Myrddin] # optional: a list, or one stringdescription: "Bulletin Board" # optionallicense: MIT # optional: an SPDX licence name is recommendedhomepage: https://example.com # optionalkeywords: [bbs, boards] # optional: up to 5, used for browsing and searchingcategory: Community # optional (1.10): the group the setup wizard lists it under (see Category)convention_prefix: BBS_ # optional: the attribute name prefix you use; advisory, not enforcedrequires_server: ">=2.0" # optional: the lowest SharpMUSH version it runs onverified_server: "2.4" # optional: the newest SharpMUSH version it was tested on (1.6)maximum_server: "2" # optional, only for a known break: the newest it runs on (1.6)plugins: [scene] # optional (1.7): plugins its code calls, by plugin id (see Plugins a package calls)
depends: # optional: other packages this one needs - volund-core: ">=1.0" # short form: id and version range - some-util # just an id: any released version - http-handler: ">=1.0" # provides the GET router bbs_routes adds a route to - package: who-where # full form: adds where to find it (see Source hints) version: ">=1.0" source: repo: https://github.com/SharpMUSH/SharpMUSH-Packages path: who-where/ branch: main
conflicts: # optional: can't be installed alongside these - legacy-bbs: "<2.0"
replaces: myrddins-board # optional: the id this package used to have
configure: # optional: questions the installer is asked bbs_storage: label: "Object that stores posts" type: dbref # dbref (default) | string | number | boolean board_name: label: "Board display name" type: string default: "Community Board" # defaults are allowed for every type except dbref
objects: # required: at least one object - ref: bbs_global # required: a name for this object, unique in the package (lowercase) type: thing # required: thing | room | exit | player name: BBS Global Object # required: its name in the game parent: "{{bbs_parent}}" # optional: one ref location: "{{?bbs_storage}}" # optional for things; required for exits previous_refs: [bbs_main] # optional: names this object had before (for renames) flags: [no_command] # optional: object flags powers: [Send_OOB] # optional: object powers locks: # optional: locks by lock type use: "{{bbs_parent}}" attributes: # optional: attributes to set CMD_+BBREAD: # full form: a value and attribute flags value: |- $+bbread *:@pemit %#=[u({{bbs_parent}}/FN_READ,%0)] flags: [] FN_HEADER: "[repeat(=,78)]" # short form: quoted one-liners only DATA_TITLE: |- {{?board_name}}
- ref: bbs_parent type: thing name: BBS Parent Object
- ref: bbs_routes # an attach entry: manages attributes on an object it didn't create target: "{{$http_handler}}" attributes: GET`BBS: |- @respond/type application/json; think [u({{bbs_parent}}/FN_JSON)]
categories: # optional (1.2): categories for the roles and permissions below roles: - name: Boards description: Roles for the people who run the boards. permissions: - name: Boards description: Moderating the bulletin boards.
permissions: # optional (1.2): custom permissions the softcode checks - name: bbs.moderate category: Boards # declared above, or already in the game description: Edit or remove anyone's post.
roles: # optional (1.2): roles that set custom permissions only - slug: bbs-moderator name: Board Moderator category: Boards color: "#4fc3c8" priority: 11 permissions: bbs.moderate: allow
settings: # optional (1.3): configuration options, as @config/set takes them noisy_whisper: "yes"
attributes: # optional (1.5): attribute entries, as @attribute makes them BBS`READ: flags: [no_command, wizard] index: true # optional (1.8): an index, as @attribute/index makes itThe sections below cover the manifest itself. The blocks from categories: on are in
Package Declarations, and kind:, application: and binaries: in
Application, Plugin and UI Packages.
Format versions
Section titled “Format versions”format: names the version of this format the manifest is written for. A server warns about a newer
minor version and ignores the keys it doesn’t know; it refuses a newer major version. This server reads
format 1.10.
| Format | Adds |
|---|---|
| 1 | The manifest on this page |
| 1.1 | Application packages |
| 1.2 | categories:, permissions: and roles: |
| 1.3 | settings: |
| 1.4 | wiki: and locale: |
| 1.5 | attributes: |
| 1.6 | verified_server and maximum_server |
| 1.7 | plugins: |
| 1.8 | An attribute entry’s index: |
| 1.9 | An attribute entry’s watch: |
| 1.10 | category: |
Write "1.10" in quotes, so YAML tools don’t read it as 1.1.
Writing softcode in YAML
Section titled “Writing softcode in YAML”Write every softcode value as a block scalar (|-). A block scalar keeps backslashes, brackets,
$patterns, % substitutions and #dbrefs exactly as written. Single quotes are fine for trivial
one-liners (double any ' inside). Never use unquoted or double-quoted values for code. Indent block
scalars with spaces (tabs are a YAML error), and use |- unless you want a newline at the end.
Literal brackets and parentheses
Section titled “Literal brackets and parentheses”The escaping rules are softcode’s own, not the package format’s: write %[ %] %( %) for
literal brackets and parentheses. [[]x[]] is not an escape for anything, and an empty [] is
refused, sometimes without saying so. Two places this catches people in a package:
- A bare
(insideheader(...),footer(...)orline(...)ends the call early, so the title is cut short and the)appears after the line. - A
+helptopic is evaluated before it is shown, so+help/list [<source>]loses its brackets. Write%[<source>%]or\[<source>\].
Refs: {{...}} tokens, never dbrefs
Section titled “Refs: {{...}} tokens, never dbrefs”| Syntax | Kind | What it points at |
|---|---|---|
{{name}} | internal | Another object in this package |
{{pkg/name}} | cross-package | An object from package pkg, which must be listed under depends: |
{{$name}} | well-known | A server setting: room_zero, master_room, player_start, god, package_manager, http_handler, event_handler (a server may add more) |
{{?name}} | configure | The installer’s answer to a configure: question |
Refs go in parent:, location: and destination: (exactly one token, in quotes, because a bare
{ confuses YAML), and anywhere inside attribute and lock values.
What installed code looks like. Installed code never contains raw dbrefs. In attribute values,
each token becomes [v(PM`REFS`NAME)], and the installer keeps a PM`REFS`NAME attribute on
every object whose code uses that ref, holding the object it points at (or the configure answer).
Refs to another package are grouped under its id (PM`REFS`WHO-WHERE`WW_FUNCTIONS). That keeps
installed code readable and lets an admin point a ref somewhere else by editing one attribute; that
local change survives upgrades. parent:, location:, destination: and lock strings are not
evaluated as code, so they get the dbref directly.
The PM` attribute tree is reserved: a package may not define attributes under it. Ref names must
also be unique across kinds within a package, so an object storage and a configure question
storage would clash.
These rules are checked when the manifest is read:
- Ref names ignore case (
{{BBS_PARENT}}is{{bbs_parent}}); write them in lowercase. - Every
{{...}}token must be a valid ref that resolves. A malformed token, an internal ref to an object that isn’t in the package, an unknown well-known name, a{{?ref}}with noconfigure:entry, or a{{pkg/...}}ref to a package not independs:is an error. - To write a literal
{{in code, double it:{{{{gives{{. Ordinary softcode brace groups such as@switch x={{a},{b}}are fine as they are; only exactly-double braces around a simple name are read as refs. - Only dbref configure answers can be used in
parent:,location:anddestination:.
Where objects go
Section titled “Where objects go”- Exits need
location:(the room they leave from) anddestination:. - Rooms may not have
location:, and only exits havedestination:. - Things and players may have
location:; without one they start in the Package Manager wizard’s inventory.
Flags, powers and locks
Section titled “Flags, powers and locks”flags: and powers: are lists of names, as @set and @power take them; locks: maps a lock
type to its key. An object that needs a power for its work declares it, rather than leaving an
administrator to grant it by hand. plus-help’s librarian, for example, reads HELP trees on objects
it doesn’t own and writes command links. Here it is with a flag and a lock added to show those keys
too:
objects: - ref: librarian type: thing name: Help Librarian location: "{{$master_room}}" flags: [no_command] powers: [See_All, Send_OOB] locks: use: "=me"Flags, powers and attribute flags merge on upgrade the way attributes do. One the new version adds is set, one it drops is cleared, and one an administrator cleared by hand stays cleared while the package still declares it. Flags and powers the package never declared are left alone.
Managing attributes on an object you didn’t create
Section titled “Managing attributes on an object you didn’t create”Some packages need to manage softcode on a core object they don’t own, such as the HTTP handler or
the master room. An entry with target: is an attach entry: the package manages only the
attributes it lists, and never creates, changes or destroys the object.
objects: - ref: handler target: "{{$http_handler}}" # an existing object to manage attributes on attributes: GET: |- think routed- Only
ref,targetandattributesare allowed: notype,name,parent,location,destination,flagsorlocks. - The target must already exist. It can be a
{{$well_known}}object, a{{?configure}}answer, or{{dependency/ref}}: an object another package provides, which must be listed underdepends:. A ref to this same package is not allowed; put those attributes on the object directly. - The package records the attributes it manages (so upgrades merge them and uninstalling removes them), but not the object, so uninstalling leaves it in place.
- A package can’t be uninstalled while another is attached to it. If package B attaches to an
object package A created, A can’t be uninstalled until B is removed (or the uninstall is forced).
http-handlerandprofile-handlerare the standard example:profile-handleradds its routes underhttp-handler’s attributes and depends on it.
Versions and ranges
Section titled “Versions and ranges”Versions are MAJOR[.MINOR[.PATCH]][-prerelease]: 2.4.1, 1.0, 3.0.0-beta.1. Prerelease
ordering follows SemVer 2.0.0 exactly:
alpha < alpha.1 < alpha.beta < beta < beta.2 < beta.11 < rc.1 < release. Build metadata (+...)
is not supported.
Ranges use >=, >, <=, <, = and bare versions (an exact match). Clauses separated by spaces
or commas must all hold. ^1.2 and ~1.2 are not supported; write >=1.2, and add <2.0 only
once you know 2.0 breaks your package (Versioning Packages).
Prereleases are never picked unless you ask for one. >=1.0 does not match 2.0.0-beta. A
prerelease only matches when a clause names a prerelease of the same major.minor.patch
(>=1.2.3-alpha matches 1.2.3-beta but not 1.3.0-beta).
A package can’t depend on itself, ids can’t repeat, and an id can’t be in both depends: and
conflicts:. provides:, recommends: and suggests: are reserved for a future format version.
Source hints
Section titled “Source hints”A dependency can say where to find it, so a missing dependency becomes “requires who-where >=1.0,
fetch it from this repository?” instead of a dead end. Use the full form (a package: key marks it,
which is also why package can’t be a dependency’s id):
depends: - package: who-where version: ">=1.0" # optional; leave out for any released version source: # optional repo: https://github.com/SharpMUSH/SharpMUSH-Packages path: who-where/ # optional: the package's folder in a repository of several branch: main # optional: defaults to the repository's default branchsource: can also be just a repository address. The hint is only advice: the installer shows it
(with the usual warning for repositories that aren’t configured remotes), and the admin decides
whether to fetch it. It never overrides a remote already configured for the same package.
Renames
Section titled “Renames”For objects, list their old ref names in previous_refs:. An upgrade then treats the change as a
rename (same dbref) instead of destroying and recreating the object, which would break everything
pointing at the old dbref. For packages, replaces: old-id carries the install history across a
package rename.
Errors and warnings
Section titled “Errors and warnings”The manifest is checked as a whole, and every problem is reported with its exact place
(for example objects[2].attributes.FN_READ). SharpMUSH.PackageTool runs the same checks on your
own machine.
Errors (the manifest is refused): a missing or invalid package, version or objects; invalid
ids, ref names or object types; repeated refs, attributes or dependency ids; any invalid or
unresolvable {{token}}; parent loops; exits without a location or destination (or a location on a
room, or a destination on anything but an exit); configure type mistakes (a default for a dbref, a
non-dbref answer in a placement field); a package depending on itself, or the same id in both
depends: and conflicts:; an invalid kind; an application package without an application:
block or with objects: (and the reverse for a softcode package); an invalid application slug, type,
role or zone; a format version whose major number is newer than the server supports; an upper bound
or exact version in requires_server, a verified_server that is not a version, a maximum_server
that is not one to three numbers, or a maximum_server below requires_server. In the format
1.2 and 1.3 blocks: an invalid category, permission or role name; a built-in permission or a system
role; a role priority outside 1-29 or a colour that isn’t #rrggbb; a role permission other than
allow or deny; a configuration option that doesn’t exist or that a package can’t set, a value it
doesn’t take, or a ref for an option that isn’t a dbref; any of these blocks in a plugin or UI package.
In format 1.4: a page that isn’t namespace:slug in the main, help or category namespace, a page
listed twice, a page with both or neither of file and markdown, or a link that doesn’t name its page in
full. In the format 1.5 attributes: block: a name @attribute would refuse, a flag that isn’t an
attribute flag, a limit that doesn’t compile, both a limit and an enum, an empty enum, a
delimiter longer than one character or a choice that contains it, a retroactive other than true
or false, or the block in a manifest whose format is below 1.5. In format 1.7: a plugins: entry
that isn’t a valid plugin id. In format 1.8: an index: that isn’t true, false or the options
@attribute/index takes, or one on an entry with internal in its flags. In format 1.9: a watch: that isn’t true, false or
the attribute wildcards @attribute/watch takes. In format 1.10: a category: longer
than 40 characters.
Warnings (the manifest is accepted): unknown keys (likely typos); reserved keys (provides,
recommends, suggests, configure pattern); configure questions nothing uses; more than 5
keywords; a format version whose minor number is newer than the server supports; a verified_server
below requires_server or above maximum_server; a plugin listed twice in plugins:; an index: in a
manifest whose format is below 1.8, which is ignored; a watch: in a manifest whose format is below
1.9, which is ignored; a category: in a manifest whose format
is below 1.10, which is ignored; a permission or
role naming a category the package doesn’t declare, or a role allowing a permission the package
doesn’t declare (the game must already have it, or the install stops).
Repositories and the bundled catalogue
Section titled “Repositories and the bundled catalogue”Repositories, release tags and the packages built into the server are covered in Softcode Packages.
Who owns installed objects
Section titled “Who owns installed objects”Installed objects belong to the Package Manager wizard (#7, set by the package_manager
option). The game records which package created each object and manages each attribute, including
each attribute’s value as installed. That is what makes upgrades a real three-way merge: the original,
your live edits and the new version. Every apply is recorded with the manifest, your configure
answers and the previous values of everything it changed, so an install or upgrade can be rolled
back, and upgrades never ask configure questions you have already answered.
Example packages
Section titled “Example packages”These examples are checked against the format by SharpMUSH’s own tests, so they always match what the installer accepts. They are published in the official package repository, SharpMUSH-Packages, which you can add as a remote under Admin → Packages → Remotes.
| Package | What it shows |
|---|---|
who-where | Several objects, internal refs, parents, flags and attribute trees (CMD`WHO, FUN`WHERE`<SWITCH>) |
plus-help | Object powers (See_All, Send_OOB), and the librarian other packages attach their +help topics to |
scene | A custom permission (scene.close) placed in the game’s own Staff category |
http-handler, profile-handler | Attach entries on the HTTP handler, and one package attaching to another |
scene-help, scene-config | Bridge packages: two attach entries joining scene to plus-help, and one joining it to plus-config (see Bridge Packages) |
scene-verse | Changing another package’s output without editing it: scene details and +scene filters attached to the Scene Logger (see Scene Details) |
chargen + chargen-app | A portal application: the softcode routes in one package, the application registered by another, with a configurable role |
jobs + jobs-app | A complete system: commands added with @command/add and @hook/override/inline, a role and permission category, eight permissions and eight roles declared by the package, per-object hooks, +help topics, and a portal application driven by the same code |
messages | A configuration option (messages_object) pointed at the package’s own object (format 1.3) |
bboards | An attribute entry (BBOARD, wizard and no_command by default) declared in the manifest (format 1.5) |
hello-ui | A plugin package: a compiled plugin that adds a portal page |
builder-graph | A UI package: a hosted HTML and JavaScript page |
Four smaller packages, written for learning the format, are in the SharpMUSH Guide repository under
Examples/Packages/. None of them ships with the server.
| Package | What it shows |
|---|---|
hello-world | The smallest package: one object, one command, and a +help topic |
starter-area | Rooms and exits (location: and destination:) |
bbs-lite | Dependencies with source hints, typed configure questions, cross-package refs, conflicts, locks and prerelease versions |
coast-guide | A content pack: wiki pages only, one of them pinned to the wiki home (format 1.4) |
hello-world, slightly shortened:
format: 1package: hello-worldversion: 1.0.2authors: [SharpMUSH]description: "Minimal example package: one object, one command."license: MITkeywords: [example, greeting]
depends: - package: plus-help version: ">=1.0"
objects: - ref: hello_global type: thing name: Hello World Object attributes: CMD`HELLO: |- $+hello:@pemit %#=Hello, [name(%#)]!
# +help in its smallest form: a HELP root describing the set, one leaf per topic, # and one attach entry telling the +help librarian where to look. - ref: hello_help type: thing name: Hello Help attributes: HELP: |- The example package. HELP`HELLO: |- `+hello` says hello. That is the entire package.
- ref: hello_help_registration target: "{{plus-help/librarian}}" attributes: SRC`HELLO-WORLD: |- {{hello_help}}After installing it, players get a +hello command that greets them by name.
Authoring tips
Section titled “Authoring tips”- Write substitutions bare:
%0,%q<name>. Brackets are for function calls only. - Prefer
firstof()to nestedif()calls for “first one that works” choices. - Name attributes as trees,
<kind>`<section>`<Name>:CMD`BBPOST,FUN`DISPLAY`LIST,DATA`CONFIG`<NAME>. - Keep function objects
no_command, and put$commandpatterns on a separate global object (seewho-where). - Never hardcode a dbref. Use
{{?configure}}for objects that differ between games and{{$well_known}}for ones every game has. - Quote fields that are only a ref (
parent: "{{x}}"); a bare leading{is YAML syntax.