HugsLib 7.0 for Rimworld 1.1 update guide - UnlimitedHugs/RimworldHugsLib GitHub Wiki

This is a brief guide to ease the transition to HugsLib 7.0 for Rimworld 1.1.

Despite the earlier warning about pending breaking changes, this HugsLib update should still be fully API-compatible with all dependent mods. Despite not breaking outright, to work correctly with the new version, mods may need minor changes- which I'd like to highlight in this document.

The various HugsLib wiki pages will be updated with the new changes eventually, but for now this page should serve as a reference.

Critical changes

Harmony no longer distributed with HugsLib, must be loaded as a separate mod

Due to critical compatibility issues, the Harmony assembly file can no longer be distributed with HugsLib. Harmony is now available as a separate mod that must be installed by the players along with HugsLib.
If your mod has a dependency on HugsLib, you can add another dependency on the Harmony mod, but that is not strictly necessary- HugsLib already takes care of that. When publishing, it's not recommended to include the Harmony dll file with your mod, as it would not be used anyway.
Harmony in standalone mod form can be downloaded here: https://github.com/pardeike/HarmonyRimWorld/releases

Changes to SettingHandleConvertible values must be saved manually

This used to be the case before, but now simply calling ModSettingsManager.SaveChanges() will not save custom handle values- assuming no other setting values were modified. This happens because SettingHandle has no way to detect changes in a reference-type value.

Long story short- call SettingHandle.ForceSaveChanges() on the handle that contains your custom setting value type after making changes to it.

ModBase.SettingsChanged is now only called for the mod that "owns" the modified setting handles

Ownership, in this case, means the ModBase instance that was given the ModSettingsPack (assigned to the ModBase.Settings property) from which the modified handles were created. As a reminder, SettingsChanged is called both when the Mod Settings window closes, as well as when settings are manually saved using ModSettingsManager.SaveChanges(). See the Additions section below if you need to be notified of settings changes in any HugsLib mod, like before.

Mod Update News defs must be copied to another folder to be processed

Due to the update news system becoming available to non-HugsLib mods, all news defs must now be loaded from the /News folder in the root folder of your mod (Rimworld/Mods/YourMod/News). I recommend copying, rather than moving, the def XML files there to allow your news to be displayed on both the 1.0 and 1.1 versions of your mod- assuming you are making use of the new versioned folders system. News item images should also be moved to the /News folder- more on that below.

The Harmony ID for all automatically-patched HugsLib mods is now their PackageId

This used to be HugsLib.ModIdentifier before, but the new package ids are a much better fit for a unique identifier. You can still return false in ModBase.HarmonyAutoPatch and use your own identifier, if you like. Note: the original, non-lowercase, ModContentPack.PackageIdPlayerFacing, is used.

Minor changes

SettingHandle.NeverVisible no longer makes a setting immune to being reset

If you want reset immunity, set SettingHandle.CanBeReset to false. This also works for visible settings and disables their reset option via the right-click menu. The only way to reset a NeverVisible setting from the game is by using the Reset All button in the Mod Settings window.

Mod Update News images should be moved under the /News folder

While optional, this is a good idea, because images in the Textures folder are always loaded on game startup in their entirety. When it comes to update news images, 99 times out of 100 that is wasted memory and CPU time. Placing images under /News, however, results in them being loaded only when the update news window is opened, and unloaded after the window is closed.

When referencing an image (e.g. <content>img:images/fancy</content>) in an UpdateFeatureDef, it will first be looked for under the /News folder (Mods/YourMod/News/images/fancy.png) and if not found, under the /Textures folder (Mods/YourMod/Textures/images/fancy.png). This way you can reuse some asset that is used elsewhere in your mod, or even the base game.

When moving images used in news items available in the 1.0 version of your mod, it's a good idea to remove them from the news content, so they won't be replaced with "missing texture" images when those news items are displayed. Alternatively, you could leave the 1.0 news images under /Textures and put any new ones under /News.

UpdateFeatureDef.content sections are now trimmed of whitespace

This makes writing news content more comfortable, as you can add line breaks and indents between your sections. These are automatically removed when loaded, unless you specify <trimWhitespace>false</trimWhitespace> in the news def. This can be useful if you are using whitespace to affect the layout of a news item.

Update news are no longer restricted by the version of your mod

The UpdateFeatureDef.assemblyVersion is still present, but us used only to sort news items and remember the latest version displayed to the player.

Removed attribute callback system (DetectableAttributeHandler)

Did you know we had that? I bet you didn't. I was not using it and apparently no one else was, so- good riddance.

Deprecated members

Some things have been marked as deprecated. These still fully work, but should be replaced when possible, as they will be removed during the next major update (Rimworld 1.2).

  • ModBase.EarlyInitalize
    Added a new method to fix the typo.

  • MapComponentUtility.EnsureIsActive Map components are now automatically injected by the game, including the ones added after map creation.

  • MapComponentUtility.GetMapComponent Base game now has Verse.Map.GetComponent<T>() which does the same thing.

  • UtilityWorldObject Yes, I think it is time for that. The idea is to transition to GameComponent and WorldComponent added by the base game. They are inherently better suited for the task, and should be slightly faster to access. Not to mention that UtilityWorldObject is just a hack added back in the Alpha 16 days to allow us to store data common to all maps. Here is a basic conversion example:

    // definition
    public class WorldData : UtilityWorldObject {
        private int data;
        public override void ExposeData() {
            base.ExposeData();
            Scribe_Values.Look(ref data, "data");
        }
    }
    // access
    var worldData = UtilityWorldObjectManager.GetUtilityWorldObject<WorldData>();

    Becomes:

    // definition
    public class WorldData : WorldComponent {
        private int data;
        public WorldData(World world) : base(world) {
        }
        public override void ExposeData() {
            Scribe_Values.Look(ref data, "data");
        }
    }
    // access
    var worldData = Find.World.GetComponent<WorldData>()

    GameComponent is instantiated at the storyteller selection screen, while WorldComponent is created when the world map is generated. Both save and load properly, as far as my testing shows. Slight gotcha: Your GameComponent constructor must declare a Game parameter, despite its base class having a parameterless constructor. Let me know if you have any issues with the transition- we could make tweaks to address them.

Additions

ModBase.LogIdentifier and ModBase.SettingsIdentifier

The first is now used to identify ModBase mods in the logs, and the second to uniquely identify their HugsLib mod settings. Both properties are optional and can be replaced with ModBase.ModIdentifier (which is now optional, as well). If neither is defined, the mods PackageId is used as the identifier.

ModSettingManager.BeforeModSettingsSaved and AfterModSettingsSaved events

The Before event is useful for identifying the mods that are saving settings and the specific handles that are being saved. Use ModSettingManager.ModSettingsPacks and ModSettingsPack.Handles to enumerate them. When SettingHandle.HasUnsavedChanges is set to true, it is part of the reason settings are being saved, as ``ModSettingManagerwould not raise the event if none of the handles had unsaved changes. TheAfter` event is raised when the settings XML file has been flushed to disk, and all of the handles will have had their `HasUnsavedChanges` property reset to false.

PackageIds for all loaded mods have been added to the log publisher

Very useful for dependencies and load order. The Harmony Id of a mod might differ from its Package Id- consult the "Harmony versions present" line of the log for a list of all Harmony Ids.

Conclusion

This should about cover it. Please report any issues you find and feel free to ask if you have any questions- I am happy to talk to you about stuff (and things). You can find me on Discord, post on the forum, or even email me if you like.

Also, if you would like any hooks added to the library, feel free to tell me. Patching the library with Harmony is a good start to get your thing working, but patches (and reflection) are liable to break when I change the internals of the library. However, anything added to the public API will still work after I update things on my end.

Take care, and thank you for your time.

-Hugs

⚠️ **GitHub.com Fallback** ⚠️