| package net.glowstone.io.entity; |
| |
| import net.glowstone.GlowWorld; |
| import net.glowstone.entity.GlowEntity; |
| import net.glowstone.entity.monster.GlowBlaze; |
| import net.glowstone.entity.monster.GlowCaveSpider; |
| import net.glowstone.entity.monster.GlowGiant; |
| import net.glowstone.entity.monster.GlowHusk; |
| import net.glowstone.entity.monster.GlowMagmaCube; |
| import net.glowstone.entity.monster.GlowSilverfish; |
| import net.glowstone.entity.monster.GlowSkeleton; |
| import net.glowstone.entity.monster.GlowSlime; |
| import net.glowstone.entity.monster.GlowSnowman; |
| import net.glowstone.entity.monster.GlowSpider; |
| import net.glowstone.entity.monster.GlowStray; |
| import net.glowstone.entity.monster.GlowWitch; |
| import net.glowstone.entity.monster.GlowWitherSkeleton; |
| import net.glowstone.entity.objects.GlowMinecart; |
| import net.glowstone.entity.passive.GlowCow; |
| import net.glowstone.entity.passive.GlowDonkey; |
| import net.glowstone.entity.passive.GlowLlama; |
| import net.glowstone.entity.passive.GlowMooshroom; |
| import net.glowstone.entity.passive.GlowMule; |
| import net.glowstone.entity.passive.GlowPolarBear; |
| import net.glowstone.entity.passive.GlowSkeletonHorse; |
| import net.glowstone.entity.passive.GlowSquid; |
| import net.glowstone.entity.passive.GlowZombieHorse; |
| import net.glowstone.entity.projectile.GlowEgg; |
| import net.glowstone.entity.projectile.GlowEnderPearl; |
| import net.glowstone.entity.projectile.GlowFireball; |
| import net.glowstone.entity.projectile.GlowLingeringPotion; |
| import net.glowstone.entity.projectile.GlowSnowball; |
| import net.glowstone.entity.projectile.GlowSpectralArrow; |
| import net.glowstone.entity.projectile.GlowSplashPotion; |
| import net.glowstone.entity.projectile.GlowThrownExpBottle; |
| import net.glowstone.entity.projectile.GlowTippedArrow; |
| import net.glowstone.entity.projectile.GlowWitherSkull; |
| import net.glowstone.io.nbt.NbtSerialization; |
| import net.glowstone.util.nbt.CompoundTag; |
| import org.bukkit.Location; |
| import org.bukkit.World; |
| import org.bukkit.entity.EntityType; |
| import org.jetbrains.annotations.NonNls; |
| |
| 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 { |
| |
| /** |
| * 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<>(); |
| private static final CompoundTag EMPTY_TAG = new CompoundTag(); |
| |
| /* |
| * Populates the maps with stores. |
| */ |
| static { |
| bind(new PlayerStore()); |
| |
| // LivingEntities - Passive Entities |
| bind(new BatStore()); |
| bind(new ChickenStore()); |
| bind(new PigStore()); |
| bind(new RabbitStore()); |
| bind(new SheepStore()); |
| bind(new OcelotStore()); |
| bind(new WolfStore()); |
| bind(new VillagerStore()); |
| bind(new AgeableStore<>(GlowCow.class, EntityType.COW, GlowCow::new)); |
| bind(new AgeableStore<>(GlowMooshroom.class, EntityType.MUSHROOM_COW, GlowMooshroom::new)); |
| bind(new WaterMobStore<>(GlowSquid.class, EntityType.SQUID, GlowSquid::new)); |
| bind(new AgeableStore<>(GlowPolarBear.class, EntityType.POLAR_BEAR, GlowPolarBear::new)); |
| bind(new AbstractHorseStore<>(GlowZombieHorse.class, EntityType.ZOMBIE_HORSE, |
| GlowZombieHorse::new)); |
| bind(new AbstractHorseStore<>(GlowSkeletonHorse.class, EntityType.SKELETON_HORSE, |
| GlowSkeletonHorse::new)); |
| bind(new ChestedHorseStore<>(GlowLlama.class, EntityType.LLAMA, GlowLlama::new)); |
| bind(new ChestedHorseStore<>(GlowMule.class, EntityType.MULE, GlowMule::new)); |
| bind(new ChestedHorseStore<>(GlowDonkey.class, EntityType.DONKEY, GlowDonkey::new)); |
| bind(new HorseStore()); |
| bind(new ParrotStore()); |
| |
| // LivingEntities - Hostile Entities |
| bind(new CreeperStore()); |
| bind(new EndermanStore()); |
| bind(new EndermiteStore()); |
| bind(new GhastStore()); |
| bind(new GuardianStore()); |
| bind(new ElderGuardianStore()); |
| bind(new IronGolemStore()); |
| bind(new SlimeStore<>(GlowSlime.class, EntityType.SLIME, GlowSlime::new)); |
| bind(new SlimeStore<>(GlowMagmaCube.class, EntityType.MAGMA_CUBE, GlowMagmaCube::new)); |
| bind(new ZombieStore<>()); |
| bind(new ZombieStore<>(GlowHusk.class, EntityType.HUSK, GlowHusk::new)); |
| bind(new MonsterStore<>(GlowSkeleton.class, EntityType.SKELETON, GlowSkeleton::new)); |
| bind(new MonsterStore<>(GlowStray.class, EntityType.STRAY, GlowStray::new)); |
| bind(new MonsterStore<>(GlowWitherSkeleton.class, EntityType.WITHER_SKELETON, |
| GlowWitherSkeleton::new)); |
| bind(new MonsterStore<>(GlowBlaze.class, EntityType.BLAZE, GlowBlaze::new)); |
| bind(new MonsterStore<>(GlowCaveSpider.class, EntityType.CAVE_SPIDER, GlowCaveSpider::new)); |
| bind(new MonsterStore<>(GlowSpider.class, EntityType.SPIDER, GlowSpider::new)); |
| bind(new MonsterStore<>(GlowSnowman.class, EntityType.SNOWMAN, GlowSnowman::new)); |
| bind(new MonsterStore<>(GlowGiant.class, EntityType.GIANT, GlowGiant::new)); |
| bind(new MonsterStore<>(GlowSilverfish.class, EntityType.SILVERFISH, GlowSilverfish::new)); |
| bind(new MonsterStore<>(GlowWitch.class, EntityType.WITCH, GlowWitch::new)); |
| bind(new ShulkerStore()); |
| bind(new WitherStore()); |
| bind(new VexStore()); |
| bind(new VindicatorStore()); |
| bind(new EvokerStore()); |
| bind(new EnderDragonStore()); |
| bind(new ZombieVillagerStore()); |
| |
| bind(new AreaEffectCloudStore()); |
| bind(new ArmorStandStore()); |
| bind(new FallingBlockStore()); |
| bind(new ItemFrameStore()); |
| bind(new ItemStore()); |
| bind(new TntPrimedStorage()); |
| bind(new EnderCrystalStore()); |
| bind(new BoatStore()); |
| for (GlowMinecart.MinecartType type : GlowMinecart.MinecartType.values()) { |
| if (type != null) { |
| bind(new MinecartStore(type)); |
| } |
| } |
| bind(new ProjectileStore<>(GlowSnowball.class, "snowball", GlowSnowball::new)); |
| bind(new ProjectileStore<>(GlowEgg.class, "egg", GlowEgg::new)); |
| bind(new ProjectileStore<>(GlowEnderPearl.class, "ender_pearl", GlowEnderPearl::new)); |
| bind(new ProjectileStore<>(GlowThrownExpBottle.class, "xp_bottle", |
| GlowThrownExpBottle::new)); |
| bind(new SplashPotionStore<>(GlowSplashPotion.class, "splash_potion", |
| GlowSplashPotion::new)); |
| bind(new SplashPotionStore<>(GlowLingeringPotion.class, "lingering_potion", |
| GlowLingeringPotion::new)); |
| final FireballStore<GlowFireball> fireballStore |
| = new FireballStore<>(GlowFireball.class, "fireball", GlowFireball::new); |
| bind(fireballStore); |
| idTable.put("small_fireball", fireballStore); // NON-NLS |
| bind(new FireballStore<>(GlowWitherSkull.class, "wither_skull", GlowWitherSkull::new)); |
| bind(new ArrowStore<>(GlowSpectralArrow.class, "spectral_arrow", GlowSpectralArrow::new)); |
| bind(new PaintingStore()); |
| bind(new ExperienceOrbStore()); |
| bind(new FireworkStore()); |
| |
| // Normal and tipped arrows use same storage |
| final NormalTippedArrowStore arrowStore = new NormalTippedArrowStore(); |
| bind(arrowStore); |
| classTable.put(GlowTippedArrow.class, arrowStore); |
| } |
| |
| private EntityStorage() { |
| } |
| |
| /** |
| * Creates an entity of the given Glowstone class, by deserializing an empty tag. |
| * |
| * @param clazz the type of entity |
| * @param <T> the type of entity |
| * @param location the entity's initial location |
| */ |
| @SuppressWarnings("unchecked") |
| public static <T extends GlowEntity> T create(Class<T> clazz, Location location) { |
| return (T) find(clazz, clazz.getSimpleName()).createEntity(location, EMPTY_TAG); |
| } |
| |
| /** |
| * Binds a store by adding entries for it to the tables. |
| * |
| * @param store The store object. |
| * @param <T> The type of entity. |
| */ |
| public static <T extends GlowEntity> void bind(EntityStore<T> store) { |
| idTable.put(store.getEntityType(), 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"); |
| } |
| String id = compound.getString("id"); |
| if (id.startsWith("minecraft:")) { // NON-NLS |
| id = id.substring("minecraft:".length()); // NON-NLS |
| } |
| EntityStore<?> store = idTable.get(id); |
| if (store == null) { |
| throw new UnknownEntityTypeException(compound); |
| } |
| |
| // 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, @NonNls String type) { |
| EntityStore<?> store = classTable.get(clazz); |
| 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); |
| } |
| |
| } |