Skip to content

ModBase Reference

UnlimitedHugs edited this page May 4, 2020 · 5 revisions

The ModBase class

The easiest way to access the facilities and events HugsLib provides is to create a class that extends HugsLib.ModBase.
To access the type in Visual Studio the HugsLib dll must be added as a reference to your project. It's also recommended to add add the checker assembly to your mod to make sure the library is installed when the mod is loaded.

When a type extends ModBase, it will be automatically instantiated by HugsLib on game start. Make sure that your class has a parameterless constructor, which can be private if you prefer.

Properties

ModIdentifier

Each ModBase class needs to have a unique identifier. Provide yours by overriding the ModIdentifier property. The identifier will be used in the settings XML to store your settings, so avoid spaces and special characters. You will get an exception if you provide an improper identifier.

Logger

The Logger property allows a mod to write identifiable messages to the console. Error and Warning methods are also available. Calling:
Logger.Message("test");
will result in the following console output:
[ModIdentifier] test
Additionally, the Trace method of the logger will write a console message only if Rimworld is in Dev mode.

ModIsActive

Returns true if the mod is enabled in the Mods dialog. Disabled mods would not be loaded or instantiated, but if a mod was enabled, and then disabled in the Mods dialog this property will return false.
This property is no longer useful as of A17, since the game restarts when the mod configuration changes.

Settings

Returns the ModSettingsPack for your mod, from where you can get your SettingsHandles. See the wiki page of creating configurable settings for more information.

ModContentPack

Returns the ModContentPack for your mod. This can be used to access the name and PackageId, as well as loading custom files from your mod's directory.

HarmonyInstance

All assemblies that declare a class that extends ModBase are automatically assigned a HarmonyInstance and their Harmony patches are applied. This is where the Harmony instance for each ModBase instance is stored.

HarmonyAutoPatch

Override this and return false if you don't want a HarmonyInstance to be automatically created and the patches in your assembly applied. Having multiple ModBase classes in your assembly will produce warnings if their HarmonyAutoPatch is not disabled, but your assembly will only be patched once.

Events

To receive events from the HugsLib controller the extending class must override the methods provided by ModBase. The following are the events that can be received:

EarlyInitalize

Called during Verse.Mod instantiation, and only if your class has the [EarlyInit] attribute.
Nothing is yet loaded at this point, so you might want to place your initialization code in Initialize, instead- this method is mostly used for custom patching.
You will not receive any callbacks on Update, FixedUpdate, OnGUI and SettingsChanged until after the Initialize callback comes through.
Initialize will still be called at the normal time, regardless of the [EarlyInit] attribute.

Initialize

Called once when the mod is first initialized, closely after it is instantiated.
If the mods configuration changes, or Defs are reloaded, this method is not called again.

DefsLoaded

Called after all Defs are loaded.
This happens when game loading has completed, after Initialize is called. This is a good time to inject any procedural defs. Make sure you call HugsLib.InjectedDefHasher.GiveShortHasToDef on any defs you manually instantiate to avoid def collisions (it's a vanilla thing).
Since A17 it no longer matters where you initialize your settings handles, since the game automatically restarts both when the mod configuration or the language changes. This means that both Initialize and DefsLoaded are only ever called once per ModBase instance.

Update

Called on each frame.
Keep in mind that frame rate varies significantly, so this callback is recommended only to do any custom drawing.

FixedUpdate

Called on each physics update by Unity.
This is like Update, but independent of frame rate.

OnGUI

Called when the Unity GUI system is redrawn or receives an input event.
This is a good time to draw custom GUI overlays and controls.
Also useful for listening for input events, such as key strokes. Here's an example of a key binding listener:

if (Event.current.type == EventType.KeyDown) {
	if (KeyBindingDefOf.Misc1.JustPressed) {
		// do things
	}
}

Since A17 OnGUI will no longer be called during loading screens and will have UI scaling automatically applied.

SceneLoaded

Called after a Unity scene change. Receives a UnityEngine.SceneManagement.Scene type argument.
There are two scenes in Rimworld- Entry and Play, which stand for the menu, and the game itself. Use Verse.GenScene to check which scene has been loaded.
Note, that not everything may be initialized after the scene change, and the game may be in the middle of map loading or generation.

WorldLoaded

Called after the game has started and the world has been initialized.
Any maps may not have been initialized at this point.
This is a good place to get your UtilityWorldObjects with the data you store in the save file. See the appropriate wiki page on how to use those.
This is only called after the game has started, not on the "select landing spot" world map.

MapComponentsInitializing

Called during the initialization of a map, more exactly right after Verse.Map.ConstructComponents(). Receives a Verse.Map type argument.
This is a good place for sneaky business and getting access to data that is unavailable after map loading has completed.
This is right before the map is populated with data from a save file.

MapGenerated

Called right after a new map has finished generating. This is the equivalent of creating a MapComponent and overriding its MapGenerated method, but without the need to pollute save files with unnecessary map components.

MapLoaded

Called after map loading and generation is complete and after Verse.MapDrawer.RegenerateEverythingNow was executed. Receives a Verse.Map type argument.
This is a good place to run initialization code specific to a game map.
Note, that this method may be called zero or multiple times after loading a save, depending on how many maps the player has active at the moment.

MapDiscarded

Called after a map has been abandoned or otherwise made inaccessible. Works on player bases, encounter maps, destroyed faction bases, etc. This is a good place to clean up any map-related data in your World and UtilityWorldObjects to avoid bloating the save file.

Tick

Called during each tick, when a game is loaded. Receives an int argument, which is the number of the current tick.
Will be called even if the player is on the world map and no map is currently loaded.
Will not be called on the "select landing spot" world map.

SettingsChanged

Called after the player closes the Mod Settings dialog after changing any setting.
Note, that the setting changed may belong to another mod.

ApplicationQuit

Called before the game process shuts down. This is a good place to update any non-critical mod setting values or write any custom data files.
"Quit to OS", clicking the "X" button on the window, and pressing Alt+F4 all execute this event. There are still ways to forcibly terminate the game process, as well as the possibility of a crash, so this callback is not 100% reliable.
Modified mod settings are automatically saved after this call.

[EarlyInit] Attribute

Adding this attribute to you ModBase class will cause it to be instantiated at the earliest possible time- when Verse.Mod instances are being created. This also causes your Harmony patches to be applied earlier (assuming, your HarmonyAutoPatch is true), which makes this a great way to patch vanilla loading and Def generation code.
The EarlyInitalize method is called immediately after, but implementing it is optional. Keep in mind, that pretty much nothing outside of HugsLib is yet loaded at this point. You can already access your Settings object, but it might not be a good idea to create SettingHandles here, since translation strings have not been loaded yet. Instead, you can make use of ValueExists and PeekValue if you need to read one of your settings.

[EarlyInit]
public class YourModController : ModBase {
	public override string ModIdentifier {
		get { return "MyModId"; }
	}

	public override void EarlyInitalize() {
		Logger.Message("I am early!");
	}
}