Component System and Minecraft

I (and others in the development channel) have been stewing with the idea of managing Entities with components. Anyone who has ever worked with a modern game engine will have experience with this in some way or another. The premise is given your basic Entity, you add or “attach” objects or components to give the Entity functionality. It intrinsically means that all entities are fundamentally the same and only components make them unique from another.

Obviously code speaks better than words, here is an example of an Entity/Component relationship:

API Goodness:

interface Component<T extends ComponentHolder> {
    T getHolder();
}

interface ComponentHolder<T extends Component> {
    T add(Class<T> clazz);

    @Nullable
    T get(Class<T> clazz);
     
    Collection<T> getAll();
}

interface EntityComponent extends Component<Entity> {
    Metadata getData();    
}

interface Entity extends ComponentHolder<EntityComponent> {

}

interface Health extends EntityComponent {
    double getHealth();
    void setHealth(double health);
}

Minor implementation stuffs:

// Might be its own class, might be on Minecraft's EntityLiving?
class HealthComponent implements Health {        
    // If its own class, can have a way to set this
    Entity entity;

    @Override
    public Entity getHolder() {
        return entity;
    }

    @Override
    public Metadata getData() {
        return entity.getData();
    }

    @Override
    public double getHealth() {
        // If data doesn't exist, return a fallback
        getData().get("health", 50.5);
    }

    @Override
    public void setHealth() {
        getData().set("health", 50.5);
    }
}

Phew, that is just basic stuff (and certaintly has room for improvement). The biggest change in this system is we move away from this:

interface Entity {
}

interface EntityLiving extends Entity {
}

…which is a top down design. Have the “parts” that make up Minecraft’s Enderman or Creeper separated into their own segments makes extending them a breeze. Even better, it makes adding new entities very easy in the future (if/when we support client-side content).

As with all structures, there are cons. In this case, it comes down to incompatibility with existing code. We’ve gotten hints from Mojang at the prospect of Minecraft converting to a component structure but obviously we have no ETAs nor a guarantee. At the same point, staying the course with a top-down design means that should Minecraft switch to components, its an API-break.

So, weighing all the factors…what is everyone’s thoughts on the matter? This decision needs to be made soon-ish before we are at the point where we break plugins “correcting” this.

Components or not?

15 Likes

What exactly is the Metadata object supposed to be? I don’t want to latch on to details of an example but that seems pretty central to your design.

I should have touched on it

Its basically a mapping of some identifier + some value

For instance, health is really some value arbitrarily changed. I would key it to “health” and save the value with it.

I am welcome to suggestions on handling and storing this data.

What would be the plan for adding these to MC entities?

The core [Sponge]Entity would have a reference to the MC entity and then each component would pass calls to the MC entity?

In theory, if MC went down the same route, there would be a SpongeEntity with a collection of components referencing a MC entity with a collection of components.

I think the plan was to monkey patch implementations of the Sponge interfaces onto the actual MC entity objects so we’d be storing the components directly in them. Since all the components would be interfaces in Sponge if MC had any that matched we could just drop theirs in directly. If not we’d have wrappers (and perhaps deprecate them and move to MCs component setup) which would be alright.

1 Like

A Component is only a data container:
http://t-machine.org/index.php/2007/11/11/entity-systems-are-the-future-of-mmog-development-part-2/

As my understanding the ES-System interact with the MC entity :wink:

I really like this idea, and it would also be super cool to extend this component system for the AI. AI behaviors could be regarded as just components on special entities, and it would allow for mix-and-match AI with custom mobs.

The AI, based on my code skimming, appears to be a component system already. There’s an AI for all sorts of small tasks, instead of just one AI for an entity. I actually saw that when I was interested in improving the dog pathfinding, although I never got too far into it.

@Amaranth brought up a good point about Artemis (read about it here) and I think I can revise Components to not contain any logic (just data driven accessors). We can also extend this system to Blocks/Items…

API:

interface Block extends ComponentHolder<BlockComponent> {
    String getID();
}

interface BlockComponent extends Component<Block> {
}

interface Book extends BlockComponent {
    String getTitle();
}

We also need the Systems concept @IDragonfire brought up. I’ll keep conceptualizing this tonight and start pushing code to a branch.

2 Likes

Now the question I have is should it be entity.get(“component”) or entity.get(Component.class);

The class one. Strings are like magic numbers, only more complicated.

2 Likes

After thinking about this for a while, I don’t like the idea of having the entities themselves be able to get their components. It should either be static on the component or on another class. This way we’ll be able to manage context of entity component systems, which I think could be useful. E.g with what was previously suggested

Only allows for one semantic ‘context’. Meanwhile, if we do:

context.get(entity, Component.class);

This will allow for different semantic ‘contexts’ isolated from mod to mod that may not be useful for the server. So the server will have a context:

server.context.get(entity, Health.class).getHealth();

And a plugin can have a context of its own:

ComponentContext cc = new ComponentContext();
cc.set(entity, new Health());
cc.get(entity, Health.class).getHealth();

Alternate names could be ComponentSystem, etc. And in addition, it would also manage entities and such, and would have an APi like:

cc.getEntity(myUUID);
cc.getEntities();

I think that would be a perfect abstraction in terms of dealing with the component system in-game.

1 Like

Love the feedback, keep it coming!

game.getSystem(HealthSystem.class).get(entity).setHealth(10);

This allows a plugin to define its own system. setHealth would fire off an event…?

Throwing out ideas based on above…

API:

interface Game {
    <T extends ComponentSystem> getSystem(Class<T> clazz);
    <T extends ComponentSystem> registerSystem(Class<T> clazz);
}

interface Component {
    Game getGame();
}

interface ComponentSystem<B, C extends Component> {
    C set(B instance, C type);

    C get(B instance);
}

interface Health extends Component {
    double getHealth();
    void setHealth(double value);
}

Impl:

class HealthImpl implements Health {
    private double health;

    public double getHealth() {
        return health;
    }

    public void setHealth(double value) {
        this.health = health;
     }
}

class HealthSystem extends ComponentSystem<Entity, Health> {
    public Health set(B instance, C type) {
        // Set in the system if not present or overwrite?
       return health;    
    }

    public Health get(Entity instance) {
        // Check in the system if the entity has health...
       return health;
    }
}

Bring on feedback!

(Generics are probably not right sooooo)

I like the tracking of data idea … (previous, current, next)
Not sure if that put to much logic in the Components …
Should an API not hide this system?
Also everything can change anything silent…
Last Caller wins?
If you use the system, we can trigger events, inform other systems, …
(debug, track, …)

First commit done:

3 Likes

@Zidane: You saw this pull request?

A draft,
white = current system
blue = PR from jacklin213
orange = my idea for Components

3 Likes

Let me just crosspost this from github.

Since when does an entity system has an entity type?

In an ideal ES an entity should be nothing more than an integer, which works but would be inconvenient if you want to get its components - thats why its often a class storing its ID and the manager it came from.

A “type” generally does not exist. There is an “EntityFactory” that you call to create your entities when you have your specific entity types (creeper, skeleton, player, vehicle, item…). The getID for the type component is redundant when you just add a component thats holds the model-ID, something like “Renderable” or “Model”. This current system is kind of mixing the OOP and ES approach together, which can quickly go into chaos imo …

Since the Mod-maker should be able to create entities as well, the EntityFactory is just a helper class to not write the same code over and over and over. We could add some more methods to it as well though. Some example code:

public interface EntityCreator {
  Entity createEntity(EntityFactory factory, EntitySystem system);
}

public interface EntityFactory {
  // General Vanilla mobs - we could even leave those out...
  Entity createCreeper();
  Entity createSkeleton();
  // ...
 
  // Helper methods
  void addLivingAttributes(Entity entity); // Adds position, velocity and health

  // Custom entity support
  void addEntitySupport(String id, EntityCreator creator);
  Entity createEntity(String id);
}

Example usage:

public class MinirobotEntityCreator implements EntityCreator {
  @Override
  public Entity createEntity(EntityFactory factory, EntitySystem system) {
    Entity entity = system.createEntity();
    factory.addLivingAttributes(entity);

    // Add custom and other components ...
    entity.addComponent(SmartMovement.class);
    entity.addComponent(Storage.class);
  }
}
1 Like

I have some questions @Zidane

  1. Component<H> must be Component<H extends ComponentHolder> or?
  2. What purpose has the ComponentHolder? In my eyes the Component interface mark the Component.
  3. You plan a interface for each mob type oO

For all that miss the commit, here a class diagram:
blue = Entity
orange = Component
green = System

There is almost no need to store the exact monster type as a component. Systems should not use it, and in the case you do need a specific entity type(for what purpose I would have no idea) you could have a simple NameComponent/TypeComponent which stores the name of the entity. There is no need to optimize this, because it should (almost) never be used. I could see it used by plugin developers that don’t understand the ECS structure well, but it should be discouraged.

In an ECS you never want to ask “is this entity a specific type”. Instead you will want to ask “Does this entity have this aspect” and aspects are represented by components.

For the rendering/model type sent to the client you would have a different component. And you should never use that component to see what kind of entity it is. It should only be used to tell the client what to render.

the components that extend the base entitycomponent as well (Once again, I would vote for them being classes as well, since they only store values, and have nothing to do with the logic)

I would argue against that. How it is stored can be an important implementation detail. e.g. with the RenderComponent it makes sense to store the internal value as an int or enum that is used for the package sent to the client, but for the API it makes no sense to expose it like that. You would want to expose it as a string and convert it to the int/enum value when it is set. The render system would use the int/enum value instead of converting it every time it is needed.

Fair enough, I actually agree. Bear in mind I’m thinking about all those people used to Bukkit because this will easily trip them up if ECS is a new concept to them. This is an easy solution that makes both camps happy…

interface ModelComponent extends Component {
    String getModel();        
}

public enum VanillaModel extends ModelComponent {
    ENDERMAN(0, "enderman")

    private final int id;
    private final String identifier;

    public VanillaModel(int id, String identifier) {
        this.id = id;
        this.identifier = identifier;
    }
    
    @Override
    public String getModel() {
        return identifier;
    }
}
5 Likes