Skip to content

Rollbacks

Daniil Pankevich edited this page May 26, 2025 · 30 revisions

Massive classes

For each data structure like SparseSet, DataSet<T>, or Entities, there is a corresponding Massive version:

  • MassiveSparseSet
  • MassiveDataSet<T>
  • MassiveEntities

The Massive versions extend the functionality of their base classes, providing the same API with added rollback capabilities.

To use rollbacks with a World, use the MassiveWorld class:

var world = new MassiveWorld();

It is fully backward-compatible with the standard World class, just like all other Massive classes are with their standard counterparts.

Save/Rollback API

Here’s how to use the Massive classes for saving and rolling back frames.
The term "frames" refers to snapshots of states.

Saving the current state

Use the SaveFrame() method to save the current state:

var set = new MassiveSparseSet();

// Make changes.
set.Assign(0);
set.Assign(1);

// Save the current state.
set.SaveFrame();

Rolling back to a previous state

To rollback to the most recent saved state, use the Rollback(0) method:

// Make more changes.
set.Unassign(0);
set.Assign(2);

// Restore everything to the last SaveFrame() call.
set.Rollback(0);

// You can call this repeatedly, it does not erase the last saved frame.
set.Rollback(0);

Rolling back beyond the last saved frame

To rollback further than the last saved frame, pass a value greater than 0 to Rollback(frames):

// Make more changes again.
set.Unassign(0);
set.Assign(2);

// Save the state.
set.SaveFrame();

// Restore to the previous SaveFrame() call.
// This erases the most recent saved frame.
set.Rollback(1);

Checking available rollback frames

To check how many frames you can rollback, use the CanRollbackFrames property:

var canRollbackFrames = set.CanRollbackFrames;

// Calling Rollback() with this value restores to the oldest saved frame.
// Using a value greater than this will throw an error.
set.Rollback(canRollbackFrames);

Note: CanRollbackFrames can be negative if there are no saved frames. Calling Rollback() in this state will throw an error. To avoid this, ensure at least one saved frame exists after creating a Massive object:

var set = new MassiveSparseSet();
set.SaveFrame(); // Save an initial empty frame to enable rollbacks.

Managed components

In Massive, any type can be used as a component. But there are some differences in their rollbacks.

For components, prefer using types that can be simply copied by value, as managed types are more complicated to work with.

Rollbacks for managed components

By default, managed components are copied by value, which means the references themselves are copied, not the actual data they point to. As a result, the component's data isn’t preserved, and rollbacks won’t work correctly.

To use rollbacks with managed types, Massive provides the ICopyable<T> interface. This allows you to define exactly how a component is copied.
Here’s an example:

public struct Inventory : ICopyable<Inventory>
{
	public List<int> Items;

	public void CopyTo(ref Inventory other)
	{
		other.Items = new List<int>(Items);
	}
}

In this example, the CopyTo method creates a new list for each copy, which works but isn’t very efficient.
To improve this, we can reuse an existing list like this:

public void CopyTo(ref Inventory other)
{
	other.Items ??= new List<int>();
	other.Items.Clear();
	other.Items.AddRange(Items);
}

To use built-in managed types as rollback components, such as string, List<T>, Type, etc., it is recommended to create a wrapper component that implements ICopyable and name it according to the specific use case of the component.