Skip to content

World Data Storage

UnlimitedHugs edited this page May 2, 2020 · 2 revisions

Deprecation notice

The recent versions of Rimworld now offer more convenient and performant ways of storing world-specific data: WorldComponent and GameComponent. It is recommend to transition your stored data to one of those, as UtilityWorldObjects will be removed in the next major HugsLib update (to Rimworld 1.2, presumably).
Here's a WorldComponent Example:

// 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>()

UtilityWorldObject-s are a convenient and reliable way to store map-independent mod data within a save file.
Before A16 MapComponent-s used to be the go-to method for receiving events and storing mod data. With the advent of simultaneously active maps, however, they are no longer reliable- every active map will create its own instance of a component and any components are lost when the player abandons a map.

Enter UtilityWorldObject- an invisible object attached to the world map, that is reliably loaded and saved with the game. When requesting a UWO from the library, either the existing object of the matching type is returned, or a new one is created and injected into the world.
To allow the custom object to save its data, ExposeData must be overridden and the necessary fields exposed using Scribe methods, as usual. ExposeData will be called when the game is loaded and saved.
Example:

public class StorageTest : ModBase {
	public override string ModIdentifier {
		get { return "StorageTest"; }
	}

	public override void WorldLoaded() {
		var obj = UtilityWorldObjectManager.GetUtilityWorldObject<WorldDataStore>();
		Logger.Message(obj.testInt.ToString());
		Logger.Message(obj.testString);
		obj.testInt++;
		obj.testString += "+";
	}

	private class WorldDataStore : UtilityWorldObject {
		public int testInt;
		public string testString;
			
		public override void PostAdd() {
			base.PostAdd();
			testInt = 1;
			testString = "+";
		}

		public override void ExposeData() {
			base.ExposeData();
			Scribe_Values.LookValue(ref testInt, "testInt", 0);
			Scribe_Values.LookValue(ref testString, "testString", "");
		}
	}
}

WorldObject.PostAdd is an optional override and is called the first time the object is added to the world.

MapComponent extensions

WorldObjects, however, are not necessarily the ultimate storage solution- MapComponent-s are still useful for storing data that should live and die with a Map. The library adds some extension methods to make working with them easier:

MapComponentUtility.EnsureIsActive

A extension method for MapComponent, designed to be called in its constructor. It injects the map component into the map if it is not already present. This is useful for allowing a mod to work on saved games where it was not active at the moment of map creation. At the moment, Rimworld will instantiate a MapComponent, but it will not be added to the map if the map is being loaded from a save.
Calling this method ensures that the MapComponent will receive calls and is saved with the map.

public class InjectedMapComponent : MapComponent {
	public InjectedMapComponent(Map map) : base(map) {
		this.EnsureIsActive();
	}
}

MapComponentUtility.GetMapComponent

An extension method for Map that is useful during the ModBase.MapLoaded event. It retrieves the first MapComponent of the provided type from the map.