Skip to content

Instantly share code, notes, and snippets.

@dualspiral
Last active August 29, 2015 14:06
Show Gist options
  • Save dualspiral/a6861a154e2b98ed3828 to your computer and use it in GitHub Desktop.
Save dualspiral/a6861a154e2b98ed3828 to your computer and use it in GitHub Desktop.
PoC for an Entity Builder
/**
* Builds an entity. Usage for a plugin developer:
*
* EntityBuilder.forEntity("minecraft:creeper").with(healthComponentObject).without(OtherComponent.class).build();
*
* An EntityValidator class would have been created prior to use. See the relevant file.
*/
public final class EntityBuilder {
static {
// Register Minecraft entities here, or create an internal method to create them.
}
/**
* The map of Mob IDs to default components/validation. This probably could (should) be elsewhere - implementation detail,
* here for context.
*/
private static Map<String, EntityValidator> entityMap = new HashMap<String, EntityValidator>();
/**
* Registers a entity for use with the EntityBuilder.
*/
public static void registerEntity(String id, EntityValidator validator) {
if (id.startsWith("minecraft:")) {
throw new InvalidStateException("You cannot replace entities in the Minecraft id namespace.")
}
if (validator == null) {
// We could just provide an empty validator instead.
throw new InvalidArgumentException("The validator must not be null");
}
entityMap.put(id, validator);
}
/**
* Returns a builder for an entity that represents a mob
*/
public static EntityBuilder forEntity(String id) {
if (!entityMap.containsKey(id)) {
// Not registered in Sponge.
throw new InvalidArgumentException();
}
EntityBuilder builder = new EntityBuilder(id);
entityMap.get(id).defaults(builder);
return builder;
}
public static EntityBuilder forEntity(VanillaEntity entity) {
return forEntity(entity.getID());
}
// Instance
private final String id;
/**
* Stores the components with their types. We use the types to avoid duplication.
*/
private final Map<Class<? extends EntityComponent>, EntityComponent> components = new Map<Class<? extends EntityComponent>, EntityComponent>();
public EntityBuilder(String id) {
this.id = id;
}
/**
* Gets the currently added component of the specified type, or {@code null} if the component
* is not a part of this builder. Mostly used for validation.
*/
public EntityComponent get(Class<? extends EntityComponent> clazz) {
return components.get(clazz);
}
/**
* Adds the component, replacing any previous component of the same type.
*
* The builder could just have seperate "withHealth", "withSpeed" etc. methods, but that would
* make the Builder less flexible.
*/
public EntityBuilder with(EntityComponent component) {
components.put(component.getClass(), component);
return this;
}
/**
* Removes the component of a specified type from the builder.
* Users should note that removing a default component may cause the
* builder to be in an invalid state and will throw an exception upon
* building.
*/
public EntityBuilder without(Class<? extends EntityComponent> type) {
// We probably don't want to ever remove a couple of types, such as components
// that specify how to render the entity. We might not even want this method,
// but the idea is devs can re-use builders to create entities over and over,
// and sometimes may need to remove a component.
components.remove(type);
return this;
}
/**
* Validates the entity that has been built, and if valid, creates the entity.
*/
public Entity build() {
// Validate that the supplied entity is valid
if (!entityMap.get(id).validate(this)) {
throw new InvalidStateException("The entity is not valid,");
}
// Code here to create the entity from the components and the ID.
}
}
/**
* This interface defines the default entity characteristics for the EntityBuilder,
* and the allowed configuration of the entity in question.
*
* I couldn't think of a better name for the interface, but as it does more than "validate"
* then the name could be better.
*/
public interface EntityValidator {
/**
* Adds the default components for the entity this represents.
*/
void defaults(EntityBuilder builder);
/**
* Validates that the requested entity can be built in the current configuration.
*
* An alternative is to have this method start creating the object, and throw an exception
* if the builder is in an invalid state.
*
* @returns {@code true} if the entity is valid, otherwise {@code false}.
*/
boolean validate(EntityBuilder builder);
}
public enum VanillaEntity {
CREEPER("minecraft:creeper"),
SPIDER("minecraft:spider"),
CAVE_SPIDER("minecraft:cave_spider"),
...
private final String id;
private VanillaEntity(String id) {
this.id = id;
}
public String getID() {
return this.id;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment