package net.glowstone.io.entity;
import net.glowstone.GlowWorld;
import net.glowstone.entity.GlowEntity;
import net.glowstone.entity.passive.GlowChicken;
import net.glowstone.io.nbt.NbtSerialization;
import net.glowstone.util.nbt.CompoundTag;
import org.bukkit.Location;
import org.bukkit.World;
import java.util.HashMap;
import java.util.Map;
/**
* The class responsible for mapping entity types to their storage methods
* and reading and writing entity data using those storage methods.
*/
public final class EntityStorage {
private EntityStorage() {
}
/**
* A table which maps entity ids to compound readers. This is generally used to map
* stored entities to actual entities.
*/
private static final Map<String, EntityStore<?>> idTable = new HashMap<>();
/**
* A table which maps entities to stores. This is generally used to map
* entities being stored.
*/
private static final Map<Class<? extends GlowEntity>, EntityStore<?>> classTable = new HashMap<>();
/**
* Populates the maps with stores.
*/
static {
bind(new PlayerStore());
// LivingEntities - Passive Entities
bind(new BatStore());
bind(new AgeableStore<>(GlowChicken.class, "Chicken"));
bind(new HorseStore());
bind(new PigStore());
bind(new RabbitStore());
bind(new SheepStore());
bind(new ItemStore());
bind(new TNTPrimedStorage());
bind(new ItemFrameStore());
}
/**
* Binds a store by adding entries for it to the tables.
* @param store The store object.
* @param <T> The type of entity.
*/
private static <T extends GlowEntity> void bind(EntityStore<T> store) {
idTable.put(store.getId(), store);
classTable.put(store.getType(), store);
}
/**
* Load a new entity in the given world from the given data tag.
* @param world The target world.
* @param compound The tag to load from.
* @return The newly constructed entity.
* @throws IllegalArgumentException if there is an error in the data.
*/
public static GlowEntity loadEntity(GlowWorld world, CompoundTag compound) {
// look up the store by the tag's id
if (!compound.isString("id")) {
throw new IllegalArgumentException("Entity has no type");
}
EntityStore<?> store = idTable.get(compound.getString("id"));
if (store == null) {
throw new IllegalArgumentException("Unknown entity type to load: \"" + compound.getString("id") + "\"");
}
// verify that, if the tag contains a world, it's correct
World checkWorld = NbtSerialization.readWorld(world.getServer(), compound);
if (checkWorld != null && checkWorld != world) {
throw new IllegalArgumentException("Entity in wrong world: stored in " + world + " but data says " + checkWorld);
}
// find out the entity's location
Location location = NbtSerialization.listTagsToLocation(world, compound);
if (location == null) {
throw new IllegalArgumentException("Entity has no location");
}
// create the entity instance and read the rest of the data
return createEntity(store, location, compound);
}
/**
* Helper method to call EntityStore methods for type safety.
*/
private static <T extends GlowEntity> T createEntity(EntityStore<T> store, Location location, CompoundTag compound) {
T entity = store.createEntity(location, compound);
store.load(entity, compound);
return entity;
}
/**
* Finds a store by entity class, throwing an exception if not found.
*/
private static EntityStore<?> find(Class<? extends GlowEntity> clazz, String type) {
EntityStore<?> store = classTable.get(clazz);
//Dragonet-Add Check parent class
if (store == null) {
store = classTable.get(clazz.getSuperclass());
}
//Dragonet-End
if (store == null) {
// todo: maybe try to look up a parent class's store if one isn't found
throw new IllegalArgumentException("Unknown entity type to " + type + ": " + clazz);
}
return store;
}
/**
* Unsafe-cast an unknown EntityStore to the base type.
*/
@SuppressWarnings("unchecked")
private static EntityStore<GlowEntity> getBaseStore(EntityStore<?> store) {
return ((EntityStore<GlowEntity>) store);
}
/**
* Save an entity's data to the given compound tag.
* @param entity The entity to save.
* @param compound The target tag.
*/
public static void save(GlowEntity entity, CompoundTag compound) {
// look up the store for the entity
EntityStore<?> store = find(entity.getClass(), "save");
// EntityStore knows how to save world and location information
getBaseStore(store).save(entity, compound);
}
/**
* Load an entity's data from the given compound tag.
* @param entity The target entity.
* @param compound The tag to load from.
*/
public static void load(GlowEntity entity, CompoundTag compound) {
// look up the store for the entity
EntityStore<?> store = find(entity.getClass(), "load");
// work out the entity's location, using its current location if unavailable
World world = NbtSerialization.readWorld(entity.getServer(), compound);
if (world == null) {
world = entity.getWorld();
}
Location location = NbtSerialization.listTagsToLocation(world, compound);
if (location != null) {
entity.teleport(location);
}
// read the rest of the entity's information
getBaseStore(store).load(entity, compound);
}
}