Mod Creation/Migrating from Scripts and Extensions: Difference between revisions

From Melvor Idle
(Migrating your script or extension to the new mod format? Use these guidelines for a frustration-free experience.)
 
(Use SyntaxHighlight)
 
(2 intermediate revisions by 2 users not shown)
Line 2: Line 2:


== Metadata ==
== 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 <code>manifest.json</code> 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.
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 <code>manifest.json</code> 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.


Line 7: Line 8:


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


Line 14: Line 16:


The <code>manifest.json</code> should simply be:
The <code>manifest.json</code> should simply be:
  <nowiki>{
 
  <syntaxhighlight lang="js" line>{
   "load": "script.js"
   "load": "script.js"
}</nowiki>
}</syntaxhighlight>


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


Line 33: Line 37:


And a previous <code>manifest.json</code> of (irrelevant properties stripped):
And a previous <code>manifest.json</code> of (irrelevant properties stripped):
  <nowiki>{
 
  <syntaxhighlight lang="js" line>{
   "icons": {
   "icons": {
     "48": "icons/my-icon-48.png"
     "48": "icons/my-icon-48.png"
Line 43: Line 48:
     }
     }
   ]
   ]
}</nowiki>
}</syntaxhighlight>


The new <code>manifest.json</code> would be:
The new <code>manifest.json</code> would be:
  <nowiki>{
 
  <syntaxhighlight lang="js" line>{
   "icon": "my-icon-48.png",
   "icon": "my-icon-48.png",
   "load": ["sources/contentScript.js", "styles/mainStyle.css"]
   "load": ["sources/contentScript.js", "styles/mainStyle.css"]
}</nowiki>
}</syntaxhighlight>


== The "Loading Loop" ==
== 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:
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:
  <nowiki>var loadInterval = setInterval(() => {
 
  <syntaxhighlight lang="js" line>var loadInterval = setInterval(() => {
   var isGameLoaded = window.isLoaded && !window.currentlyCatchingUp;
   var isGameLoaded = window.isLoaded && !window.currentlyCatchingUp;


Line 60: Line 68:
     // Inject script element or execute code...
     // Inject script element or execute code...
   }
   }
}, 500);</nowiki>
}, 500);</syntaxhighlight>


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 <code>onInterfaceReady</code>:
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 <code>onInterfaceReady</code>:
  <nowiki>mod.register(ctx => {
 
  <syntaxhighlight lang="js" line>mod.register(ctx => {
   ctx.onInterfaceReady(() => {
   ctx.onInterfaceReady(() => {
     // Code here will only get executed after the game, character, and
     // Code here will only get executed after the game, character, and
     // offline progress has been loaded.
     // offline progress has been loaded.
   });
   });
});</nowiki>
});</syntaxhighlight>


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


== Loading Packaged Resources ==
== 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 <code>content_scripts</code> but utilized during runtime, chances are those resources were retrieved using the <code>browser.runtime.getURL</code> (or <code>chrome.runtime.getURL</code>) method. Instead, the migrated mod should rely on the new mod context API's method, <code>getResourceUrl</code>. This method takes in a string value that is the requested resource's location relative to the manifest.json (root) of the mod package.
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 <code>content_scripts</code> but utilized during runtime, chances are those resources were retrieved using the <code>browser.runtime.getURL</code> (or <code>chrome.runtime.getURL</code>) method. Instead, the migrated mod should rely on the new mod context API's method, <code>getResourceUrl</code>. This method takes in a string value that is the requested resource's location relative to the manifest.json (root) of the mod package.


Line 88: Line 98:


And assuming <code>entryScript.js</code> was loaded as part of the manifest's <code>"load"</code> property, the <code>entryScript.js</code> could retrieve and use or load the <code>icon.png</code> and <code>helper.js</code> with the following:
And assuming <code>entryScript.js</code> was loaded as part of the manifest's <code>"load"</code> property, the <code>entryScript.js</code> could retrieve and use or load the <code>icon.png</code> and <code>helper.js</code> with the following:
  <nowiki>mod.register(async (ctx) => {
 
  <syntaxhighlight lang="js" line>mod.register(async (ctx) => {
   var iconUrl = ctx.getResourceUrl('assets/icon.png');
   var iconUrl = ctx.getResourceUrl('assets/icon.png');
   var iconElement = document.createElement('img');
   var iconElement = document.createElement('img');
Line 95: Line 106:
   await ctx.loadScript('scripts/helper.js');
   await ctx.loadScript('scripts/helper.js');
   // Now the contents of helper.js have been injected and executed
   // Now the contents of helper.js have been injected and executed
});</nowiki>
});</syntaxhighlight>


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


== Next Steps ==
== 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:
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:
* [[Mod Creation/Getting Started]]
* [[Mod Creation/Getting Started]]
Line 105: Line 117:
* [[Mod Creation/Mod Context API Reference]]
* [[Mod Creation/Mod Context API Reference]]
* [[Mod Creation/Sidebar API Reference]]
* [[Mod Creation/Sidebar API Reference]]
{{ModGuideNav}}
{{Menu}}

Latest revision as of 00:24, 2 January 2023

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: