/* * This file is part of LanternServer, licensed under the MIT License (MIT). * * Copyright (c) LanternPowered <https://www.lanternpowered.org> * Copyright (c) SpongePowered <https://www.spongepowered.org> * Copyright (c) contributors * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the Software), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package org.lanternpowered.server.data.io.store.entity; import org.lanternpowered.server.data.io.store.IdentifiableObjectStore; import org.lanternpowered.server.data.io.store.ObjectSerializer; import org.lanternpowered.server.data.io.store.ObjectStore; import org.lanternpowered.server.data.io.store.ObjectStoreRegistry; import org.lanternpowered.server.entity.LanternEntity; import org.lanternpowered.server.entity.LanternEntityType; import org.spongepowered.api.Sponge; import org.spongepowered.api.data.DataQuery; import org.spongepowered.api.data.DataView; import org.spongepowered.api.data.MemoryDataContainer; import org.spongepowered.api.data.persistence.InvalidDataException; import org.spongepowered.api.entity.EntityType; import java.util.HashMap; import java.util.Map; import java.util.UUID; public class EntitySerializer implements ObjectSerializer<LanternEntity> { private static final DataQuery DATA_VERSION = DataQuery.of("DataVersion"); private static final DataQuery ID = DataQuery.of("id"); @Override public LanternEntity deserialize(DataView dataView) throws InvalidDataException { String id0 = dataView.getString(ID).get(); final String id; // Fast fail if the data isn't old if (dataView.getInt(DATA_VERSION).orElse(0) < 704) { id = fixEntityId(dataView, id0); } else { id = id0; } dataView.remove(ID); final LanternEntityType entityType = (LanternEntityType) Sponge.getRegistry().getType(EntityType.class, id).orElseThrow( () -> new InvalidDataException("Unknown entity id: " + id)); //noinspection unchecked final ObjectStore<LanternEntity> store = (ObjectStore) ObjectStoreRegistry.get().get(entityType.getEntityClass()).get(); final UUID uniqueId; if (store instanceof IdentifiableObjectStore) { uniqueId = ((IdentifiableObjectStore) store).deserializeUniqueId(dataView); } else { uniqueId = UUID.randomUUID(); } //noinspection unchecked final LanternEntity entity = (LanternEntity) entityType.getEntityConstructor().apply(uniqueId); store.deserialize(entity, dataView); return entity; } @Override public DataView serialize(LanternEntity object) { final DataView dataView = new MemoryDataContainer(DataView.SafetyMode.NO_DATA_CLONED); dataView.set(ID, object.getType().getId()); //noinspection unchecked final ObjectStore<LanternEntity> store = (ObjectStore) ObjectStoreRegistry.get().get(object.getClass()).get(); store.serialize(object, dataView); if (store instanceof IdentifiableObjectStore) { ((IdentifiableObjectStore) store).serializeUniqueId(dataView, object.getUniqueId()); } return dataView; } private static final String[] HORSE_ENTITY_IDS = { "minecraft:horse", "minecraft:donkey", "minecraft:mule", "minecraft:zombie_horse", "minecraft:skeleton_horse" }; private static final DataQuery HORSE_TYPE = DataQuery.of("Type"); private static final String[] SKELETON_ENTITY_IDS = { "minecraft:skeleton", "minecraft:wither_skeleton", "minecraft:stray" }; private static final DataQuery SKELETON_TYPE = DataQuery.of("SkeletonType"); private static final String[] MINECART_ENTITY_IDS = { "minecraft:minecart", "minecraft:chest_minecart", "minecraft:furnace_minecart", "minecraft:tnt_minecart", "minecraft:spawner_minecart", "minecraft:hopper_minecart", "minecraft:commandblock_minecart" }; private static final DataQuery MINECART_TYPE = DataQuery.of("Type"); private static final DataQuery ZOMBIE_TYPE = DataQuery.of("ZombieType"); private static final Map<String, String> OLD_TO_NEW_ID_MAPPINGS = new HashMap<>(); private static void put(String newId, String oldId) { OLD_TO_NEW_ID_MAPPINGS.put(oldId, newId); } static { put("minecraft:area_effect_cloud", "AreaEffectCloud"); put("minecraft:armor_stand", "ArmorStand"); put("minecraft:arrow", "Arrow"); put("minecraft:bat", "Bat"); put("minecraft:blaze", "Blaze"); put("minecraft:boat", "Boat"); put("minecraft:cave_spider", "CaveSpider"); put("minecraft:chest_minecart", "MinecartChest"); put("minecraft:chicken", "Chicken"); put("minecraft:commandblock_minecart", "MinecartCommandBlock"); put("minecraft:cow", "Cow"); put("minecraft:creeper", "Creeper"); put("minecraft:donkey", "Donkey"); put("minecraft:dragon_fireball", "DragonFireball"); put("minecraft:egg", "ThrownEgg"); put("minecraft:elder_guardian", "ElderGuardian"); put("minecraft:ender_crystal", "EnderCrystal"); put("minecraft:ender_dragon", "EnderDragon"); put("minecraft:ender_pearl", "ThrownEnderpearl"); put("minecraft:enderman", "Enderman"); put("minecraft:endermite", "Endermite"); put("minecraft:eye_of_ender_signal", "EyeOfEnderSignal"); put("minecraft:falling_block", "FallingSand"); put("minecraft:fireball", "Fireball"); put("minecraft:fireworks_rocket", "FireworksRocketEntity"); put("minecraft:furnace_minecart", "MinecartFurnace"); put("minecraft:ghast", "Ghast"); put("minecraft:giant", "Giant"); put("minecraft:guardian", "Guardian"); put("minecraft:hopper_minecart", "MinecartHopper"); put("minecraft:horse", "Horse"); put("minecraft:husk", "Husk"); put("minecraft:item", "Item"); put("minecraft:item_frame", "ItemFrame"); put("minecraft:leash_knot", "LeashKnot"); put("minecraft:magma_cube", "LavaSlime"); put("minecraft:minecart", "MinecartRideable"); put("minecraft:mooshroom", "MushroomCow"); put("minecraft:mule", "Mule"); put("minecraft:ocelot", "Ozelot"); put("minecraft:painting", "Painting"); put("minecraft:pig", "Pig"); put("minecraft:polar_bear", "PolarBear"); put("minecraft:potion", "ThrownPotion"); put("minecraft:rabbit", "Rabbit"); put("minecraft:sheep", "Sheep"); put("minecraft:shulker", "Shulker"); put("minecraft:shulker_bullet", "ShulkerBullet"); put("minecraft:silverfish", "Silverfish"); put("minecraft:skeleton", "Skeleton"); put("minecraft:skeleton_horse", "SkeletonHorse"); put("minecraft:slime", "Slime"); put("minecraft:small_fireball", "SmallFireball"); put("minecraft:snowball", "Snowball"); put("minecraft:snowman", "SnowMan"); put("minecraft:spawner_minecart", "MinecartSpawner"); put("minecraft:spectral_arrow", "SpectralArrow"); put("minecraft:spider", "Spider"); put("minecraft:squid", "Squid"); put("minecraft:stray", "Stray"); put("minecraft:tnt", "PrimedTnt"); put("minecraft:tnt_minecart", "MinecartTNT"); put("minecraft:villager", "Villager"); put("minecraft:villager_golem", "VillagerGolem"); put("minecraft:witch", "Witch"); put("minecraft:wither", "WitherBoss"); put("minecraft:wither_skeleton", "WitherSkeleton"); put("minecraft:wither_skull", "WitherSkull"); put("minecraft:wolf", "Wolf"); put("minecraft:xp_bottle", "ThrownExpBottle"); put("minecraft:xp_orb", "XPOrb"); put("minecraft:zombie", "Zombie"); put("minecraft:zombie_horse", "ZombieHorse"); put("minecraft:zombie_pigman", "PigZombie"); put("minecraft:zombie_villager", "ZombieVillager"); } public static String fixEntityId(DataView dataView, String id) { // Separate the horse entities if (id.equals("EntityHorse")) { final int type = dataView.getInt(HORSE_TYPE).get(); dataView.remove(HORSE_TYPE); return HORSE_ENTITY_IDS[type < 0 ? 0 : type >= HORSE_ENTITY_IDS.length ? 0 : type]; // Separate the skeleton entities } else if (id.equals("Skeleton")) { final int type = dataView.getInt(SKELETON_TYPE).orElse(0); dataView.remove(SKELETON_TYPE); return SKELETON_ENTITY_IDS[type < 0 ? 0 : type >= SKELETON_ENTITY_IDS.length ? 0 : type]; // Separate the minecart entities } else if (id.equals("Minecart")) { final int type = dataView.getInt(MINECART_TYPE).get(); dataView.remove(MINECART_TYPE); return MINECART_ENTITY_IDS[type < 0 ? 0 : type >= MINECART_ENTITY_IDS.length ? 0 : type]; } else if (id.equals("Zombie")) { final int type = dataView.getInt(ZOMBIE_TYPE).orElse(0); // Profession if (type > 0 && type <= 5) { dataView.set(ZombieVillagerStore.PROFESSION, type - 1); return "minecraft:zombie_villager"; } else if (type == 6) { return "minecraft:husk"; } else { return "minecraft:zombie"; } } final String id1 = OLD_TO_NEW_ID_MAPPINGS.get(id); return id1 == null ? id : id1; } }