EntityData memento

Hi there !

As you may know, I’m creating an entity editor based on Zay-ES. This editor has a “run” option, that allow the user to activate the game logic and make the entities live.

With this feature, I also have a “pause” option, which is only a matter of shuting down the logic processors. Now, I want the “stop” option.

By stopping, I would like to came back in the past of the entity data, before running. I think the memento pattern would do the job : OriginatorEntityData would provide a state, and would be able to accept a previously provided state.

I’m not sure how I should do that. I think I need to copy/paste the code from DefaultEntityData and add the getState() method I need. But what to save into the entity data? is there an existing entity data (serializable for exemple) that would help? Or do you guys see another solution to my need?

Thanks !

So you have some entity data state that you want to keep from one run to the next? Or a fresh copy every time?

…a fresh copy is at least easy enough. The other definitely requires more effort.

I’d have to think about what the easiest approach would be. I’d be interested to know more about your setup.

I already use this extension of the DefaultEntityData

Shortly, it maintains a tree of entity nodes by listening the CRUD events (I didn’t use the ObservableEntityData methods because I wasn’t able to catch creation and deletion events). This tree contains all entities with all components, alowing inspection to the entity editor.

Nothing more about the entity data.

In the editor, I only maintain one instance of this entity data. The user add, delete and change components of it’s entities and at some point, he wants to run. More specifically, he want’s to activate the systems (I call them processors) that alterate the data.

At that moment, the game logic begin to apply change to the entity data. What I need is to restore the entity data in the state it was before the game logic begin to act. The user-drawn state.

Just a note here: an entity doesn’t really exist until it has a component… it’s just an ID after all. Sure, the sequence gets incremented as a convenience but you could literally pull an ID out of thin air and start using it if you knew what you were doing. Likewise, an entity is never deleted. It may not have any components anymore but you can always use the ID.

Regarding your other, if you are already tracking the full state of your ES then you should be able to clone snapshots, I guess. If your components are all immutable then you don’t have to clone them… just the data structures inside your entity nodes or whatever.

It’s an interesting point, given that when the user clic on “create entity”, my implementtion gives a first Naming component, indeed. For the deletion it’s more murky because the editor forbid an Id to be reused after the user has clicked on “delete entity” and obvisously won’t draw all deleted entites in the list.

I will think about it again some day.

Components are all immutable, yes.

My first appoach was to clone the tree that I build by change observation, and create a whole new entity data from a tree state. But it’s not a trivial task and I was wondering if there was an easier way to clone the components lists and reassign them.

Don’t you have an implementation that is able to “save” the current state of entity data and serialize it ? It could help me to see how it’s done.

All of my persistent entity data implementations use the database version. I don’t yet have a case where I need to save an in-memory ES… though I agree the use-case is real.

It wouldn’t be hard to make DefaultEntityData serializable if the user’s components were also serializable.

in my case, every component is serialisable (actually on json format with FasterXML lib), because I already save entities as blueprints on disk.

So the contract is to have only immutable primitives (or immutable serialisable classes) in components.

A typical component (they all have this shape) :

public class Naming implements EntityComponent {
	private final String name;
	
	public Naming() {
		name = "Unnamed";
	}
	public Naming(@JsonProperty("name")String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}
}

And a typical blueprint JSON serialisation :

{
  "name" : "Sun",
  "comps" : [ {
    "class" : "model.ES.component.Naming",
    "name" : "Sun"
  }, {
    "class" : "model.ES.component.visuals.Lighting",
    "color" : {
      "a" : 255,
      "r" : 255,
      "g" : 255,
      "b" : 255
    },
    "intensity" : 1.5,
    "distance" : "Infinity",
    "innerAngle" : 0.0,
    "outerAngle" : 0.0,
    "shadowIntensity" : {
      "value" : 1.0
    },
    "activation" : {
      "value" : 1.0
    }
  }, {
    "class" : "model.ES.component.motion.SpaceStance",
    "position" : {
      "x" : 0.0,
      "y" : 0.0,
      "z" : 0.0
    },
    "direction" : {
      "x" : -1.0,
      "y" : -1.0,
      "z" : -3.0
    }
  } ],
  "children" : [ ]
}

@pspeed I could use a hint here, given that you wrote it wouldn’t be hard :smile:

No emergency, though.

Without giving it too much thought… you might be able to get away with a little trickery.

Whenever components are set on an entity, it’s ALWAYS done through the setComponent() method. (At least for sure in DefaultEntityData.) Even the setComponents() method delegates to setComponent().

So, one thing you could do is intercept that call and just keep track of the component class in a Set or something. (DefaultEntityData doesn’t provide access to the component classes though it could. I’m trying to provide a solution that works with that code as it is today.)

From there, you can get any of the ComponentHandlers any time you want. That basically lets you introspect the whole set of all components. You can do whatever you want with it at that point.

tl;dr:
Extend DefaultEntityData
Intercept setComponent() to save the component classes accessed.
Use that to access the component handlers to save/load/whatever the state whenever you want.

Quick and dirty implementation. An observer extends EntityComponentListener and listen for all changes in the ObservableEntityData :

public class EntityDataObserver implements EntityComponentListener {
	public Map<EntityId, Map<Class<? extends EntityComponent>, EntityComponent>> entities = new HashMap<>();

	@Override
	public void componentChange(EntityChange change) {
		if(!entities.containsKey(change.getEntityId()))
			entities.put(change.getEntityId(), new HashMap<>());
		
		Map<Class<? extends EntityComponent>, EntityComponent> components = entities.get(change.getEntityId());
		if(change.getComponent() == null)
			components.remove(change.getComponentType());
		else
			components.put(change.getComponentType(), change.getComponent());
	}
}

At run, we remove the observer from the entity data. It stores the state juste before the logic start.

At stop, we ask the entity data to erase all its components (with an ugly trick) and to set all the components stored in the observer.

public void setState(Map<EntityId, Map<Class<? extends EntityComponent>, EntityComponent>> entities){
	// we remove all entities that we can find
	// this trick seems ugly...
	long l = createEntity().getId();
	for(long i = 0; i <= l; i++)
		removeEntity(new EntityId(i));
	
	// then we set all components that have been stored by the observer
	for(EntityId eid : entities.keySet()){
		Map<Class<? extends EntityComponent>, EntityComponent> components = entities.get(eid);
		for(EntityComponent comp : components.values())
			setComponent(eid, comp);
	}
	// will it work with a new instance of entity data, where the entity Ids havn't already been created??
	// it remains to be tested
}

It’s working pretty well, although it takes time to rebuild the entity data (maybe because of the node structure of my own that runs behind and refresh everything each time something happens)

Will it work in deserialisation situation, with a new instance of EntityData where the entity Ids have never been created? I can’t say for now since it’s hard to test in my current setup.

Thanks @pspeed to have indicated me the right direction.

You will have to be careful about entity ID collision if you have a fresh entity data. It might be worth having your own EntityIdGenerator so that you can control what the ‘next entity ID’ will be in this case.

here is a cleaner implementation of the SavableEntityData if it can help somebody. I’m not sure about the clone thing, though.

Tell me if you want PR.

And thanks !

/**
 * An extended EntityData able to give snapshot states (memento) and reset to a previously created state.
 * @author Benoît
 *
 */
public class SavableEntityData extends DefaultEntityData implements Cloneable{
	public Map<EntityId, Map<Class<? extends EntityComponent>, EntityComponent>> entities = new HashMap<>();
	
	@Override
	protected void entityChange(EntityChange change) {
		super.entityChange(change);
		
		if(!entities.containsKey(change.getEntityId()))
			entities.put(change.getEntityId(), new HashMap<>());
		
		Map<Class<? extends EntityComponent>, EntityComponent> components = entities.get(change.getEntityId());
		if(change.getComponent() == null)
			components.remove(change.getComponentType());
		else
			components.put(change.getComponentType(), change.getComponent());
	}

	/**
	 * Create a new immutable state from that point.
	 * With this method, components are assumed immutable too, to avoid time consuming cloning.
	 * For mutable components, use createClonedMemento() method.
	 * @return the entity data state
	 */
	public EntityDataMemento createMemento(){
		Map<EntityId, Map<Class<? extends EntityComponent>, EntityComponent>> res = new HashMap<>(entities);
		for(EntityId eid : res.keySet())
			res.put(eid, Collections.unmodifiableMap(new HashMap<>(res.get(eid))));
		
		return new EntityDataMemento(new HashMap<>(res), this);
	}

	/**
	 * Create a new immutable state from that point.
	 * This method is made to save component by clone, in the case component are not immutable.
	 * Component must override the Object.clone() method with a public visibility.
	 * @return the entity data state
	 * @throws InvocationTargetException 
	 * @throws IllegalArgumentException 
	 * @throws IllegalAccessException 
	 * @throws SecurityException 
	 * @throws NoSuchMethodException 
	 */
	public EntityDataMemento createClonedMemento() throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException{
		Method clone = Object.class.getMethod("clone");
		Map<EntityId, Map<Class<? extends EntityComponent>, EntityComponent>> res = new HashMap<>(entities);
		for(EntityId eid : res.keySet()){
			Map<Class<? extends EntityComponent>, EntityComponent> comps = new HashMap<>(res.get(eid));
			for(Class<? extends EntityComponent> compClass : comps.keySet())
				comps.put(compClass, (EntityComponent)(clone.invoke(comps.get(compClass))));
			res.put(eid, Collections.unmodifiableMap(new HashMap<>(comps)));
		}
		
		return new EntityDataMemento(new HashMap<>(res), this);
	}
	
	/**
	 * Reset to a previous state. 
	 * @param memento
	 */
	public void setMemento(EntityDataMemento memento){
		if(memento.getOriginator() != this)
			throw new IllegalArgumentException(SavableEntityData.class.getSimpleName() + " only accept memento it has created himself.");
		// we clean the entity data up to the last created entity ID
		long l = createEntity().getId();
		for(long i = 0; i <= l; i++)
			removeEntity(new EntityId(i));
		
		// then we set all components stored in the memento
		for(EntityId eid : memento.getState().keySet()){
			Map<Class<? extends EntityComponent>, EntityComponent> components = memento.getState().get(eid);
			for(EntityComponent comp : components.values())
				setComponent(eid, comp);
		}
	}
}

.

public class EntityDataMemento {
	private final SavableEntityData originator;
	private final Map<EntityId, Map<Class<? extends EntityComponent>, EntityComponent>> state;
	
	public EntityDataMemento(Map<EntityId, Map<Class<? extends EntityComponent>, EntityComponent>> state, SavableEntityData originator) {
		this.state = state;
		this.originator = originator;
	}

	public Map<EntityId, Map<Class<? extends EntityComponent>, EntityComponent>> getState() {
		return state;
	}

	public SavableEntityData getOriginator() {
		return originator;
	}
}
1 Like

Well, I’m glad it works for you but as a general ‘savable entity data’, I’m not sure I’ll use it. But thanks for the offer.

You effectively keep two copies of everything since the components are already easily available from the ComponentHandlers as I mentioned in an earlier post. You have a requirement (I guess) to keep your entities together when persisted… but as a general “save all the data” solution it’s simpler just to write out the values of each component handler.

I can get the component handlers with getHandler(Class) but since the handlers map is private and not accessible, I can’t get the key list and I don’t know which component classes are currently in use.

That would require to “scan” the handlers map with all possible component classes, which the EntityData has no clue (or I miss something?)

Of course I could re-write the DefaultEntityData class but I would like to avoid it.

The code remains available here if somebody needs :slight_smile:

I addresses this already:

keeping the component Class yes, I missed that. It’s a good idea, and wil allow to avoid storing things twice.

Thanks !