Skip to content

Extensibility of the EntityTypeContainer system

itsmeow edited this page Jan 4, 2022 · 2 revisions

About

The EntityTypeContainer class (and its builders) are designed to be extended and added to. You can also extend EntityRegistrarHandler to include shorthand for new EntityTypeContainer subclasses. IMDLib natively includes an extended EntityTypeContainer!

Example in IMDLib

Example in Better Animals Plus

How to Extend

Because of the complicated generic typing nature of builders and wrappers, I created a way to properly chain said extensions infinitely. Here is a chart of the inheritance that I made.

Essentially, you make a chain of abstract versions of your container's builder, then define an implementation with the proper built definitions, such that generic type chain is intact and getImplementation() does not return the parent class' type.

I'll stop with the gibberish now and give you some concrete examples.

Define your parent class

This is the class that will be used in the end, your extension of EntityTypeContainer.

In this example, I will be using EntityTypeContainerExample. It will require that the entity extends ExampleEntity.

public class EntityTypeContainerExample<T extends ExampleEntity> extends EntityTypeContainer<T> {
    
    protected int customValue;

    private EntityTypeContainerExample(ExampleEntityTypeDefinition<T> def) {
        this.customValue = def.getCustomValue();
    }

    public int getCustomValue() {
        return customValue;
    }
}

This class will simply retrieve a custom value from the builder and expose it via a method, but you can imagine the possibilities with this system, for example overriding the custom configuration methods to add a configuration field based on this value.

Now, there is an undefined ExampleEntityTypeDefinition. I created these interfaces so that the constructor's argument list would be of manageable size due to the sheer amount of data to pass in.

This class will simply accept a builder and expose fields on the builder.

public class ExampleEntityTypeDefinition<T extends ExampleEntity> extends EntityTypeDefinition<T> {
    AbstractEntityBuilderExample<T, ?, ?> builder;

    public ExampleEntityTypeDefinition(AbstractEntityBuilderExample<T, ?, ?> builder) {
       super(builder);
       this.builder = builder;
    }

    public int getCustomValue() {
       return builder.customValue;
    }

}

As a side note, I like to put all of these classes as protected or public static inner classes of the main EntityTypeContainer, so as not to clutter the filesystem.

Now, we need to define the abstract builder. This will hold all of the extended logic for the container.

public abstract class AbstractEntityBuilderExample<T extends ExampleEntity, C extends EntityTypeContainerExample<T>, B extends AbstractEntityBuilderExample<T, C, B>> extends AbstractEntityBuilder<T, C, B> {
    protected int customValue;

    protected AbstractEntityBuilderExample(Class<T> entityClass, EntityType.IFactory<T> factory, String entityNameIn, Supplier<AttributeModifierMap.MutableAttribute> attributeMap, String modid) {
        super(entityClass, factory, entityNameIn, attributeMap, modid);
    }

    public B customValue(int customValue) {
        this.customValue = customValue;
        return getImplementation();
    }

}

Finally, we define the implementation of this abstract builder.

public class EntityTypeContainerExampleBuilder<T extends ExampleEntity> extends AbstractEntityBuilderExample<T, EntityTypeContainerExample<T>, EntityTypeContainerExampleBuilder<T>> {

    protected EntityTypeContainerExampleBuilder(Class<T> entityClass, EntityType.IFactory<T> factory, String entityNameIn, Supplier<AttributeModifierMap.MutableAttribute> attributeMap, String modid) {
        super(entityClass, factory, entityNameIn, attributeMap, modid);
    }

    @Override
    public EntityTypeContainerExample<T> rawBuild() {
        return new EntityTypeContainerExample<>(new ExampleEntityTypeDefinition<>(this));
    }

    @Override
    public EntityTypeContainerExampleBuilder<T> getImplementation() {
        return this;
    }

    public static <T extends ExampleEntity> EntityTypeContainerExampleBuilder<T> create(Class<T> entityClass, EntityType.IFactory<T> factory, String entityNameIn, Supplier<AttributeModifierMap.MutableAttribute> attributeMap, String modid) {
        return new EntityTypeContainerExampleBuilder<>(entityClass, factory, entityNameIn, attributeMap, modid);
    }

}

Now, you can get a builder via the create method on the EntityTypeContainerExampleBuilder class.