So it has been a while since the entity system was added to Terasology, so I thought I'ld do a review of where it is at, what needs to be improved, and where to from here. Feel free to add any thoughts or observations.
What works well
Quite a lot really, but some highlights
you could do
What works well
- I feel that the general principle of having entities with components works very well in terms of enabling reuse and single responsibility.
- The ease of serialization is nice. Once the necessary types are set up to be handled, components can be created easily and persistence is handled automatically.
- Prefabs work nicely, allowing new entities to be described without having to recompile. The ability to update prefabs and have those changes apply automatically to existing entities is a nice bonus for being able to apply changes over time, although it has some limits.
- The event system has some good potential, especially for mods. Still need to work out when it is best to use events and when it is best to work with components directly - this may come down to how individual gametypes and mods decide to make use of the features.
- The Component <-> System split is a bit of an interesting pattern, but does allow some things that would be impossible otherwise, such as systems do batch updates.
Quite a lot really, but some highlights
- The entity system isn't set up for multithreading yet (no protection around obtaining/updating/saving components)
- Prefabs need a bit of work - particularly around nested prefabs (i.e. the ability to specify the contents of a player's inventory in the prefab) and inheritance (which I'm not sure works correctly).
- While JSON is a nice starting point for the prefabs, may want to support some simplifications and extensions. For instance, being able to specify an array of size X without listing X values, or reducing need for quotes (note: GSON supports a non-strict parser that allows there to be no quotes around element names, and = instead of : among other things).
- It might be nice for at least some events to be asynchronous - that is not immediately dispatched and dealt with. This will be important when multiplayer is introduced, because some events would be sent to the server/clients.
- Regarding serialization, currently there is a default handler for unknown types that tries to handle them as java beans. Unfortunately this hides the use of unhandled types, so it may be best to drop this default behaviour while retaining the system for explicitly marked types (with an annotation, or via registration call).
- Performance could be improved by switching some of the reflection usage with bytecode manipulation - particularly the event system.
- At the moment when an entity's component is requested, the actual stored component is returned. This has the issue that it can be changed and not saved, but the changes will still take effect. This can allow sloppy behaviour around component usage, where they are not saved - and saving the component is the only way to notify systems when they have changed. I am considering cloning the components and providing the clone instead, although this will require the performance improvements from the bytecode manipulation. On the plus side this gets halfway towards supporting multithreading too.
- There has to be a better way to store entities/components than a HashMap.
- I have to admit when I set up the component and componentSystem packages - I got it wrong. Components and their systems should live together in packages related to their function. I have migrated a few components and component systems to better places when working on related functionality (physics, input, rendering, world), probably will attend to the rest when doing the mod/package system work.
- It would be nice to use reflection to automatically inject services from the CoreRegistry into ComponentSystems. So instead of doing
Code:
private LocalPlayer localPlayer;
private CameraTargetSystem cameraTargetSystem;
private Timer timer;
private WorldProvider worldProvider;
@Override
public void initialise() {
worldProvider = CoreRegistry.get(WorldProvider.class);
localPlayer = CoreRegistry.get(LocalPlayer.class);
timer = CoreRegistry.get(Timer.class);
cameraTargetSystem = CoreRegistry.get(CameraTargetSystem.class);
}
Code:
@In private LocalPlayer localPlayer;
@In private CameraTargetSystem cameraTargetSystem;
@In private Timer timer;
@In private WorldProvider worldProvider;
- Perhaps allow the use of custom events in addition to the generic ChangedComponentEvent when saving a component. These events would be extensions to ChangeComponentEvent.
- The ability to have component systems registered automatically through an annotation is nice, but still need a way in place for mods to remove/replace systems as desired.
- Some of the interfaces, such as event receivers and entity iterators could be improved to return relevant components rather than requiring them to be fetched again from the entity ref.
- Need to look into associating entities with chunk regions and loading/unloading them with their chunks. There is some complexity here around entities that aren't associated with chunks, and references to entities in unloaded chunks.
- Obviously entity systems are a somewhat different approach to the standard OO. In particular the lack of encapsulation can be disconcerting, although I don't think this is too great a weakness. Some of the engine components may do things a little differently - particularly the LocationComponent - but this is more an exception than the rule.
- Camera system still needs to be moved over to be a driven from a component, possibly allowing multiple cameras.