Mod Creation/Migrating from Scripts and Extensions

This guide is for mod creators that have previously created a userscript or extension and want to migrate it to be compatible with the new mod system. This guide will not cover new features, will skim over implementation details, and may not follow best practices for the sake of quickly getting a migrated mod up and running. For a more in-depth view, consider supplementing with the Mod Creation/Essentials guide.

Metadata

Whether the mod being migrated was a userscript or extension, the majority of metadata that was previously defined in the respective locations (userscript comment block or extension manifest.json file) will instead be entered in the mod.io profile page for the mod. This includes the name, author (the uploading mod.io account), description, tags, and versioning.

For the limited metadata managed within the mod's files, there is a required manifest.json file. This file must be at the root of the mod's packaged contents.

Userscript

Userscripts should define a single "load" property within the manifest, with a string value pointing to the script's location relative to the manifest.json file. For example, given the following folder structure:

  • my-mod (root folder)
    • manifest.json
    • script.js

The manifest.json should simply be:

{
  "load": "script.js"
}

Extension

Extensions that previously defined icons in the manifest should now define a single value for the "icon" property instead. This icon file will be used in-game and displayed no larger than 38px x 38px by default.

Content scripts / styles that were effectively an entry point to the extension should now be defined as the "load" property within the manifest, with an array of strings for a value with each entry being the script/stylesheet's location relative to the manifest.json file. For example, given the following folder structure:

  • my-mod (root folder)
    • icons
      • my-icon-48.png
    • sources
      • contentScript.js
    • styles
      • mainStyle.css
    • manifest.json

And a previous manifest.json of (irrelevant properties stripped):

{
  "icons": {
    "48": "icons/my-icon-48.png"
  },
  "content_scripts": [
    {
      "js": ["sources/contentScript.js"],
      "css": ["styles/mainStyle.css"]
    }
  ]
}

The new manifest.json would be:

{
  "icon": "my-icon-48.png",
  "load": ["sources/contentScript.js", "styles/mainStyle.css"]
}

The "Loading Loop"

Both userscripts and extensions would often end up with a funky loop that is some variation of the following in order to wait until the game has loaded into a character to perform actions:

var loadInterval = setInterval(() => {
  var isGameLoaded = window.isLoaded && !window.currentlyCatchingUp;

  if (isGameLoaded) {
    clearInterval(loadInterval);
    // Inject script element or execute code...
  }
}, 500);

With the new mod system's context API, that's no longer necessary. Instead, the script should use a game lifecycle hook, with the most comparable being onInterfaceReady:

mod.register(ctx => {
  ctx.onInterfaceReady(() => {
    // Code here will only get executed after the game, character, and
    // offline progress has been loaded.
  });
});

You can learn more about the various game lifecycle hooks in the Mod Creation/Essentials guide.

Loading Packaged Resources

This section is specific to extensions, as this isn't a concept (commonly) supported in userscripts. If the extension being migrated over contained scripts, stylesheets, images, audio, or other files that weren't automatically loaded as part of the content_scripts but utilized during runtime, chances are those resources were retrieved using the browser.runtime.getURL (or chrome.runtime.getURL) method. Instead, the migrated mod should rely on the new mod context API's method, getResourceUrl. This method takes in a string value that is the requested resource's location relative to the manifest.json (root) of the mod package.

There are also helper methods for combining getResourceUrl with common follow-up tasks. One such helper for injecting a script into the page is loadScript. It's important to note that getResourceUrl is synchronous while all combined helper methods are asynchronous and return a promise.

For example, given the following folder structure:

  • my-mod (root folder)
    • assets
      • icon.png
    • scripts
      • entryScript.js
      • helper.js
    • manifest.json

And assuming entryScript.js was loaded as part of the manifest's "load" property, the entryScript.js could retrieve and use or load the icon.png and helper.js with the following:

mod.register(async (ctx) => {
  var iconUrl = ctx.getResourceUrl('assets/icon.png');
  var iconElement = document.createElement('img');
  iconElement.src = iconUrl;

  await ctx.loadScript('scripts/helper.js');
  // Now the contents of helper.js have been injected and executed
});

Use JavaScript modules or want to learn more about the various resource loading methods? Check out the Mod Creation/Essentials guide.

Next Steps

Hopefully the mod has been successfully migrated and is working with the new mod system at this point. But that's only the beginning - explore all of the new APIs and techniques available to you in the other Official Mod Making Guides: