Skip to content

Simple Mod Framework v3 #677

Description

@atampy25

Development of version 3.0.0 of SMF is currently in progress. As a new major version, it brings with it breaking changes which allow flexibility in improving the SMF experience. Here's a summary of the changes made so far.

TL;DR: v3 of the Simple Mod Framework:

  • deploys mods in seconds (1.5 seconds per mod in fact, on average)
  • supports Linux natively
  • supports every version of the WoA engine (not just HITMAN 3)
  • fixes all existing errors/bugs in v2
  • brings major improvements to the mod development experience and can catch many common issues
  • makes scripting significantly more powerful, allowing for interesting and more compatible mods
  • comes with breaking changes that will break a small portion of mods and require some getting used to for mod developers

Breaking Changes

Update checking overhaul

Update URLs have been removed, in favour of a new top-level url key in the manifest. All mods must now specify a URL that links to their mod page. This link will be shown in the GUI and also used for mod update checking.

If the URL links to a page on Nexus Mods (https://nexusmods.com/hitman3/mods/453), users will be notified when the Nexus version changes, and will have to manually redownload the mod to receive updates. This means that while Nexus update checking is now possible, it's still better to use one of the alternative URLs which provide fully automatic updating.

If the URL links to a GitHub repository (https://github.com/Notexe/Portable-Chair), users will be notified of new releases (assuming the release is tagged with a version like 1.0.0 - no v at the start) and the framework will be able to automatically update the mod.

If the URL links to a ModWorkshop page (https://modworkshop.net/mod/45444), users will be notified of new releases and the framework will be able to automatically update the mod. Unfortunately, ModWorkshop doesn't support a proper changelog system, so the framework won't be able to provide a list of changes to the user.

{
    "id": "Notex.PortableChair",
-   "updateCheck": "https://github.com/Notexe/Portable-Chair/releases/latest/download/updates.json"
+   "url": "https://github.com/Notexe/Portable-Chair"
}

Framework data moved to data key

Properties like contentFolders and localisation have now been moved into the top-level data key (this applies to options as well, so the option now has a data key instead of directly having contentFolders).

{
    "id": "Notex.PortableChair",
-   "contentFolders": ["content"],
+   "data": {
+       "contentFolders": ["content"]
+   }
}

Options overhaul

Mod options have been completely overhauled. The easiest way by far to understand the new options system will be through the use of a schema and an editor like VS Code, but here are the basics.

Options now require IDs. These generally follow a kebab-case convention (i.e. my-option).

Most options now have the ability to be referenced within content files. This is done through a simple find-and-replace. "bla": "#{option:the-option-id}" will be transformed into "bla": true for a boolean option, or "bla": 1 for a number, etc. If a find-and-replace is inside of a string, like "bla": "something #{option:the-option-id} something", it will be replaced inline, like "bla": "something 1 something".

Option groups now exist, which are sections of options that can be displayed based on a condition (such as another option being on or off, or another mod being enabled).

Option tooltips have been replaced with descriptions, which may or may not be displayed differently, depending on how the GUI is ultimately designed.

Removal of thumbs

The thumbs manifest key no longer exists. It was previously used by some mods to disable dynamic resources to make REPO mods work online. Instead, the framework now decides automatically when to disable dynamic resources, and gives the user control over whether to allow this.

Automatic dependency porting

The dependencies key has been removed. Necessary resources are now automatically made available to whatever files they are referenced in, without having to manually specify dependencies to port.

You can, if absolutely necessary, port resources manually using the portResources key:

{
-   "dependencies": [
-       "00123456789ABCDE",
-       { "runtimeID": "00123456789ABCDE", "toChunk": 0, "portFromChunk1": true }
-   ],
+   "portResources": [
+       "00123456789ABCDE",
+       { "resource": "00123456789ABCDE", "forPartition": "chunk0" }
+   ]
}

This is unnecessary if the porting is just so a dependency will work; SMF v3 will port those automatically.

Only resources inaccessible by the given partition will be ported, an improvement over v2's unnecessary porting of some non-chunk0/1 dependencies.

Mod ID version range syntax change

The requirements, incompatibilities, loadBefore and loadAfter keys in v2 accept specific version ranges for the referenced mods. These are now mandatory, and the syntax for defining them has changed.

{
-   "requirements": [
-       ["SomeoneElse.TheirMod"],
-       ["SomeoneElse.TheirOtherMod", "^1.3.1"]
-   ],
+   "requirements": ["SomeoneElse.TheirMod@1.0.0", "SomeoneElse.TheirOtherMod@1.3.1"]
}

As before, the meaning of version ranges is interpreted according to standard convention, so 1.3.1 is equivalent to ^1.3.1, meaning "any version above 1.3.1 which is still major version 1". By making versions mandatory, this prevents mods from having obsolete incompatibilities defined that other mods can do nothing about, and additionally encourages mods to correctly use semantic versioning.

Packagedefinition changes

The packagedefinition key has been renamed to packageDefinition and support for custom partitions has been removed, meaning the type key in its entries has been removed as well (since it would always be entity).

Additionally, the framework now automatically adds scenes and bricks specified by entity.json files to packagedefinition, removing the need for explicitly specifying them most of the time.

blobsFolders renamed to blobFolders

Self-explanatory.

Condition changes and script overhaul

Scripts and conditions have both been changed to use the Rune language. This means they are now fully sandboxed and therefore safe for users; the framework will no longer give a warning when scripts are used in a mod. Scripts are now significantly more powerful as well, with the ability to dynamically alter game files based on any condition you wish.

The script interface has changed significantly.

Old

export async function analysis()
export async function preDeploy()
export async function postDeploy()
export const cachePolicy

New

fn data(config: Config, data: ManifestData) -> ManifestData;
fn operations(config: Config, data: ManifestData) -> Vec<(String, Operation)>;

The first function, data, receives the config and the manifest data (after options have been merged into it), and has the opportunity to modify the manifest data. For example, it can add a content folder based on a complex condition. The second function, operations, receives the same information but has the ability to return a series of (String, Operation) tuples. At least one of the two must be defined.

The mention of Operation may be confusing. It's due to the revamp of framework internals, which will be talked about later.

Removal of delta special file type

The XYZ.delta special file type has been removed due to it being largely useless and highly brittle to any change in the file. It was not used in any mod on the Nexus.

QuickEntity 3.2

A new version of QuickEntity is also under development, and should include improvements to the patch format as well as some behind-the-scenes changes for memory and performance improvements.

Removal of all QuickEntity files prior to version 3.2

Support for QuickEntity files (entity.json and entity.patch.json) which use a version prior to 3.2 has been removed. The framework will automatically upgrade these files when adding v2 mods.

Mod archive format change

Mod archives (e.g. ZIP) no longer include a folder inside them which contains the manifest. Instead, the manifest.json file exists at the root of the archive.

Localisation format change

The format for specifying manifest localisation has changed to keep multiple translations of the same string together.

{
    "localisation": {
-        "english": {
-            "UI_XYZ": "Some localisation"
-        },
-        "french": {
-            "UI_XYZ": "Some localisation"
-        }
+        "UI_XYZ": {
+            "english": "Some localisation",
+            "french": "Some localisation"
+        }
    }
}

New material JSON format

The old RPKG Tool material.json file format has been replaced with a new, simpler material format where material entities (MATT/MATB; now material.entity.json) and material instances (MATI; material.json) are separated. This is the format GlacierKit previews MATI files in already.

Like the other changes, this will be automatically upgraded by the mod manager.

Chunk folders are now named by partition name

The chunk folders inside a content folder are now named after the partition they represent to make it clearer what each chunk contains.

  content
- -> chunk0
+ -> super
- -> chunk1
+ -> base

Localisation overrides replaced with new localisation.patch.json special file type

The localisationOverrides manifest key was previously the only manifest key which patches a specific file. This task is better suited to content files (as seen with the other .patch.json files), so there is a new localisation.patch.json filetype following the format:

{
    "id": "[assembly:/localization/hitman6/conversations/ui/pro/online/repository/outfits_hero_s3.sweetmenutext].pc_localized-textlist",
    "lines": {
        "CHAR_REWARD_SAPIENZA_HERO_SUPERFAN_M_NAME": {
            "french": "Fan ultime",
            "german": "Superfan"
        }
    }
}

When automatically upgrading an older mod, the mod manager will place these new files in the first available content folder (which is part of the option/top-level manifest data). If no content folder is available, the mod manager will create one.

Texture changes

texture.tga files have been separated into two parts: the texture metadata in a new texture.json format and the texture itself as a PNG, TGA or DDS file with the same name. This also means you no longer have to specify the texture hash in the filename, allowing descriptive names like suit_diffuse.texture.json/suit_diffuse.texture.png.

supportedPlatforms format

The supportedPlatforms key now accepts version-platform pairs in the format h3-steam. You can additionally specify a version only, to allow deploying on any platform of that version (e.g. h2 to allow HITMAN 2 on Steam or Epic).

Improvements

So, you've heard all the reasons why the new update will require changes to your mods. What's the benefit? The framework has been completely redesigned.

Graph model

The framework no longer simply deploys mods one by one. All mods are now deployed, all at once. You might naturally ask whether this impacts the load order functionality; deployment order is still respected because the framework now represents each deploy as a dependency graph.

Essentially, instead of deploying files, the framework now applies operations like ApplyQNPatch or OverwriteMaterial in parallel across a graph which represents deploy order as dependencies (a later mod in the order depends on an earlier mod's patches finishing in order to perform its patches).

This also allows for more granular caching of data (such as specific manifest keys rather than the whole manifest being re-deployed). The framework will also now be able to store the whole cache in a flat, compressed folder, instead of the multi-gigabyte sprawling cache folder it previously had.

This is also where those two functions from the script interface come in. An Operation is in effect a node in the dependency graph, and the String its ID. A mod which wants to apply a dynamically-built patch, for example, can return an ApplyQNPatch operation. The ID is used for caching, which is another thing the framework could not previously do for scripts.

Safety and error reporting

In addition to scripts now being safe, other improvements have been made. Internal framework data structures can no longer be constructed at all with invalid data, meaning that a manifest with an invalid path will now immediately throw an error with a clear message explaining why.

Many conditions that can cause issues in-game are now caught immediately by the framework and prevented; for example, a dependency which is missing or a texture which is the wrong size and will cause infinite loading screens.

Speed

The framework can now deploy all supported mods on the Nexus in a total of 11 minutes. The cache additionally now deploys all cached files in a fraction of a second, meaning no matter the size of your mod list, adding a new mod will only take however long that mod takes to deploy.

Platform support

v3 is intended to have first-class native Linux support, distributed alongside the Windows build.

Game support

The Simple Mod Framework now supports all game/engine versions in the World of Assassination trilogy, meaning you can now design and deploy mods against HITMAN™ (2016), HITMAN 2 or HITMAN 3.

A note on the GUI

The Mod Manager is also receiving a redesign as part of this update. Much of it is similar, but it has been given a cleaner and faster interface and now uses Tauri rather than Electron.

So what?

This post has been made to ask for your feedback. If you have any issues with the ideas above, any ideas of your own, any new features you'd like to see added or any issues you have with current SMF, feel free to share below!

Also, polls will be running on some changes in the Glacier 2 Modding discord server, which you can join here.

Other note

If you'd like to play around with the current iteration of the new manifest format, you can use this JSON schema with VS Code or in an online editor.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions