Skip to content

Softcode Packages

Global softcode has traditionally been distributed as a .mush file you paste into a client and hope for the best: the dbrefs have to line up, there is no record of what was installed, and there is no way to upgrade without clobbering local edits.

SharpMUSH still accepts .mush files: paste one in as you would on any other MUSH. It also offers packages, which are safer and much easier to upgrade and maintain. A package is a declarative description of softcode — the objects that should exist, and the attributes, flags, locks and parents they carry — installed through the admin panel at /admin/packages.

Packages are desired state, not a script. There is no ordering, no @create lines, and crucially no dbrefs: object identity is symbolic and resolved to real dbrefs at install time.

That makes a plan/apply model possible:

  1. Plan. The server compares the manifest against your live database and computes a changeset.

  2. Review. A wizard shows you exactly what will be created, changed or removed. Nothing has been written yet.

  3. Apply. Only now does anything change, and the result is recorded as a revision.

Because every apply is a revision, an install can be rolled back. And because upgrades three-way-merge against the baseline stored at install time, the local customizations you made to a package’s attributes survive an upgrade instead of being overwritten.

Dependencies are resolved against your installed packages, and version constraints are checked before anything is planned. A package can also say which SharpMUSH versions it runs on: the review screen refuses one that needs a newer server, and warns about one that has not been tested on yours. Versioning Packages covers what each warning means and how to set the range when you write a package.

Games change the packages they install: a header in the game’s own words, a rule added to a list, a lock that lets the game’s staff role in. An upgrade keeps those changes. For every attribute and lock it compares three values: what the package installed, what the game has now, and what the new version ships.

If only the package changed an attribute, the new value is written. If only the game changed it, the game's value is kept. If both changed it on different lines, the lines are merged. If both changed the same lines, the review page asks, with Keep mine preselected.

The review page starts with Changed by this game. It lists every attribute and lock the game changed since the package installed it, and what this upgrade does with each one:

The Changed by this game card for lore-notes 1.1.0. FNHEADER, DATAINTRO and the Use lock say Both changed it: choose below. DATA`RULES says Merged: the package changed other lines.

Everything not on that list takes the new version. To see only the listed attributes, turn on Only what this game changed above the attribute list.

When the game and the package changed different lines of a value, the upgrade writes both changes. Here the game added a third rule and the new version reworded the first, so the upgrade writes the reworded first rule followed by the game’s added one:

DATA`RULES marked Merge. Base: One note per topic, Keep notes short. Live adds Sign every note. New rewords the first line to One topic per note. The merged value, which the upgrade writes, has all three lines with the new first line.

When both changed the same lines, or both changed a value that is one line long, the review page shows the three values side by side. Keep mine is already chosen, so applying without looking never overwrites the game’s work. Take theirs writes the new version’s value. Edit merged value opens an editor, and for a value of several lines it starts from the merge with each clash marked:

DATA`INTRO marked conflict: ModifyModify. Below the three values, Merged, clashes marked shows the shared first line, then the game's two lines and the package's two lines, each between conflict markers. Keep mine is selected.

Delete the marker lines and keep what you want from each side. The editor holds the value the upgrade writes.

Locks work the same way:

Lock conflicts: the Use lock was FLAG^WIZARD as installed, is FLAG^ROYALTY in the game, and is FLAG^WIZARD|FLAG^STAFF in the new version. Keep mine is selected.

Whatever you choose, the new version’s value becomes the base for the next upgrade. An attribute you kept is listed as the game’s own and is not asked about again until a later version changes it too. Wiki pages a package ships merge the same way.

A package can carry an AINSTALL attribute, which runs after a first install, and an AUPDATE attribute, which runs after an upgrade. The last card on the review page, Runs after the apply, shows that code for each object as it will read once the apply has written it. When you change a choice above, the card shows the code that choice leaves. It also lists any hook the game put on one of the package’s objects itself, since the apply runs that too.

The Runs after the apply card for lore-notes 1.1.0. notes/AUPDATE holds @trigger me/STARTUP.

Add a repository under Admin → Packages → Remotes. The official one is:

https://github.com/SharpMUSH/SharpMUSH-Packages

Then browse, pick a package and a version, review the changeset, and apply.

Browse lists a repository’s packages by category and subcategory. Each row says what the package ships (softcode, wiki pages, an application, a plugin), its version, and whether it is installed. A bridge sits under the first package in its depends: that the same repository holds, behind a line that counts that package’s add-ons, and names the two packages it joins. Search, the Ships filter and the category buttons narrow the list. Queue install adds a package to the queue at the bottom of the page and turns into Queued; select it again to take the package back out. Queueing a bridge also queues the packages it joins, and what those need, when they are neither installed nor queued yet: from the same repository, or from the packages that ship with the server. Review queued changes opens the queue as one batch to apply. A step with settings to choose asks for them first; any other step can be applied as it stands, and Full review opens its changeset. If the server refuses the batch, the result says why and nothing is changed; Review again reviews the same changes as they stand now.

Browse, in the Game System category under Fate: the Fate sheet, queued, with its four add-ons open. Each add-on is a bridge, such as Fate sheet: +help topics, which joins sheet-fate and plus-help.

Dependency graph shows the packages the filters leave as a graph, together with every package they need, directly or through others, whether or not this repository lists it. Packages that need nothing are on the left, and each package is one column right of the furthest package it needs, joined by a line to each one. Packages that need nothing and that nothing needs are listed below the graph instead. A bridge has a dashed border, and the border colour says whether a package is installed, queued, available, or needed but not in this repository. Select a package to highlight what it needs and what needs it, through every step; both are listed below the graph. The Dependencies tab under Admin → Packages draws the installed packages the same way.

The dependency graph filtered to "fate", with Fate sheet selected. Fate sheet needs nothing; its add-ons, the toolkits built on it and their add-ons are highlighted, and the rest is dimmed.

A git repository holds one or more packages. Its root may have an index.yaml so browsing is fast; without one, the repository is searched for package.yaml files. An entry is a folder path, or a mapping with path and optionally name, package, version and description, so the portal can list packages without reading every manifest (an id may appear only once):

name: SharpMUSH Packages
description: Official softcode packages.
packages:
- path: who-where/
name: Who/Where
package: who-where
version: 1.2.0
description: +who and +where commands
- starter-area/

Releases are git tags named <package-folder>/v<version>, for example who-where/v1.2.0. The version list in the portal is the tag list, installing a version uses its tag, and the latest commit is the development channel. Never move or delete a release tag; publish a fix as a new version. The installer records the commit it applied and warns loudly if a tag it installed from later points somewhere else.

The official repository also carries a curated community directory, so games can discover third-party package repos from the admin panel along with their trust tier.

Some packages are built into the server and appear in the portal under the reserved remote name bundled, with no download and no configured remote. Three separate things can be true of them:

  • Shipped: the package is in the server and can be installed from the catalogue at any time.
  • Installed on first start: a new game gets it without being asked. http-handler, profile-handler, room-contents, comm-feed, plus-help, plus-config, scene, scene-help, scene-config, scene-here, onboarding, onboarding-help and onboarding-config are. They are why a fresh game already has a working HTTP handler, +help, +config, a scene system and a getting-started checklist. A bridge such as scene-help or jobs-help is installed wherever both of the packages it joins are, as soon as they are (see Bridge Packages).
  • Ticked at setup: the setup wizard offers it ticked, so a new game gets it unless the administrator unticks it, but a game that is already running does not get it when the server is upgraded. The World map is. Once removed, it is no longer offered ticked.

Every other bundled package is shipped but not installed: the bulletin boards, +who, +status, the watch list, +finger and +notify, for example, add commands a game may not want, a game chooses whether it wants a verse and content warnings named on its scenes, and a new game reads its connect screen and MOTDs from stored text until an administrator chooses the Messages object (in the setup wizard or on the package’s page).

A bundled package may only depend on other bundled packages, so installing it never needs the network. Anything installed from the catalogue records bundled:sharpmush as its source, and is upgraded in place when a newer server ships a newer version.

The setup wizard offers every bundled package in its last step, grouped by what the packages are for: Essentials, Web portal, Scenes, Community, Staff, Characters and MUD clients, with any package that names no category last, under Uncategorized. Each group says how many of its packages are ticked. A package that builds on another one in its group, such as a portal page for a package’s commands, is listed under that package. What a ticked package needs is ticked with it.

Step 4 of 4, Choose your packages: the Essentials group, 2 of 4 chosen, lists softcode-editor, +help with +config under it, and Messages object; the Web portal group follows

The bundled packages use these categories (a package’s category:, described in Package Declarations):

CategoryPackages
Essentialssoftcode-editor, plus-help, plus-config, messages, onboarding, onboarding-help, onboarding-config
Web portalhttp-handler, profile-handler, room-contents, comm-feed
Scenesscene, scene-help, scene-config, scene-here, radio-scene, scene-verse, scene-safety, highlights, highlights-app, time, timeline, scene-availability, summary
Communitywiki-reader, radio, bboards, bboards-app, who-where, status, watch, discord, notifications, notifications-app
Staffjobs, jobs-app, health, health-jobs
Characterschargen, chargen-app, finger, info, roster, roster-jobs, relationships, relationships-scene, relationships-app, relationships-card, alts, onboarding-chargen
MUD clientsgmcp, gmcp-mudlet
Uncategorizeddiscord-channels, discord-scenes

Each package’s -help and -config bridges, such as jobs-help and jobs-config, are in its category and listed under it. The wizard ticks them with it.

The setup wizard's Staff group, 0 of 4 chosen: Jobs with Jobs in the portal listed under it, and health with health-jobs listed under it

A package can carry a README.md beside its package.yaml. The Packages page shows it: each row under Installed, and each plugin under Plugins that came from a package, has a README button that opens it in a dialog, rendered from Markdown. Browse shows the same file before you install.

The Installed tab of the Packages page, each row with a README button between Check and the revision history

The README dialog for room-contents, showing its README.md rendered from Markdown

The README you see on the Packages page is the one the installed version came with. It is read from the same commit, upload or bundled copy as the manifest, and kept with the revision the install records, so it needs no network and doesn’t change when the repository does:

  • an upgrade replaces it with the new version’s README, or removes it if the new version has none;
  • a rollback brings back the README of the revision it restores;
  • uninstalling the package removes it.

A bundled package without a README of its own shows one written from its manifest: its description, whether a new game gets it, what it requires and what it creates.

A README is written for the package’s folder, so links and pictures that point into that folder (../profile-handler/, docs/setup.md, screenshot.png) are not served by the game. The dialog shows such a link as plain text and such a picture as its description. Web links (https://…), links to a heading on the same page (#usage) and wiki links ([[Main:home]]) still work. Raw HTML is removed, as on wiki pages.

For example, this README.md in a package folder:

# coast-guide
Travel notes for the coast road. Install it, then read [[Main:coast-road]].
See [the page format](docs/format.md) and [SharpMUSH](https://sharpmush.com).

shows a heading, a wiki link to the installed page, the words “the page format” without a link, and a working link to sharpmush.com.

A README larger than 256 KB, or one that is not UTF-8 text, is left out; the package still installs.

If the code already exists in your game, export it rather than writing YAML by hand:

@package/scan <objects>

is a read-only report — it shows the manifest that would be produced, including anything it cannot resolve, without touching the database. When the report looks right:

@package <objects>=<package-id>[,<version>[,<description>]]

writes the package out. The /admin/packages/author page covers the same ground in the portal.

A package is a directory containing a package.yaml:

format: 1.2
package: myrddins-bbs
version: 2.4.1
authors: [Myrddin]
description: "Bulletin Board"
license: MIT
depends:
- who-where: ">=1.0"
configure:
board_name:
label: "Board display name"
type: string
default: "Community Board"
objects:
- ref: bbs_global
type: thing
name: BBS Global Object
flags: [no_command]
attributes:
CMD_+BBREAD:
value: |-
$+bbread *:@pemit %#=[u({{bbs_parent}}/FN_READ,%0)]
flags: []
DATA_TITLE: |-
{{?board_name}}
- ref: bbs_parent
type: thing
name: BBS Parent Object
permissions:
- name: bbs.moderate
category: Staff
description: Edit or remove anyone's post.
roles:
- slug: bbs-moderator
name: Board Moderator
category: Staff
permissions:
bbs.moderate: allow

ref names are intra-package identifiers; {{token}} references are resolved to real dbrefs when the package is applied. configure declares typed parameters the installer is asked for, so a package can adapt to a game without being forked. permissions and roles bring the custom permission the board’s softcode checks with permission(), and a role that holds it, so a game hands out moderation with @role instead of editing code. A package can also bring its own categories and set configuration options.

Write attribute values as block scalars (|-), as above; Writing softcode in YAML says why.

The Package Format reference covers every field, the rules for refs and versions, and a set of example packages, from the bundled systems such as jobs to the learning examples in the SharpMUSH Guide, a one-object hello-world and a dependency-carrying bbs-lite. Package Declarations covers the roles, settings and other blocks a package can bring.

SharpMUSH.PackageTool validates a manifest locally, before you push.

Packages come in kinds. kind: softcode is the default described above; kind: application registers a Dynamic Application — a portal page backed by a softcode schema — kind: plugin distributes a compiled C# plugin, and kind: ui a hosted HTML and JavaScript page. See Application, Plugin and UI Packages.