From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Mariell Hoversholm Date: Sat, 9 Jan 2021 21:22:58 +0100 Subject: [PATCH] Add unsafe Entity serialization API This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . diff --git a/src/main/java/net/minecraft/world/entity/EntityTypes.java b/src/main/java/net/minecraft/world/entity/EntityTypes.java index e1ef642f06078202fb782ac16b268e6429c5e202..778c981d4458957533fc3ac44095051b279c74e3 100644 --- a/src/main/java/net/minecraft/world/entity/EntityTypes.java +++ b/src/main/java/net/minecraft/world/entity/EntityTypes.java @@ -534,6 +534,7 @@ public class EntityTypes { return this.bf.create(this, world); } + public static Optional loadEntityFixedData(NBTTagCompound nbtTagCompound, World world) { return a(nbtTagCompound, world); } // Purpur - OBFHELPER public static Optional a(NBTTagCompound nbttagcompound, World world) { return SystemUtils.a(a(nbttagcompound).map((entitytypes) -> { return entitytypes.a(world); diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java index a80f664d2cf713fd751421be3735e2f4779f0056..6c37bf58bd269c2d7e1c84e5791e8245e4c75aea 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java @@ -1200,5 +1200,12 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { public boolean isRidableInWater() { return getHandle().isRidableInWater(); } + + @Override + public boolean spawnAt(Location location, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason) { + entity.world = ((CraftWorld) location.getWorld()).getHandle(); + entity.setLocation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + return !entity.valid && entity.world.addEntity(entity, spawnReason); + } // Purpur end } diff --git a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java index 38e1bd3893b9dfcf1bac1333d06b4e9792793ec6..05d0d2e5bb10bb002c8d7d4783a144ec35e4550f 100644 --- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java @@ -390,9 +390,14 @@ public final class CraftMagicNumbers implements UnsafeValues { Preconditions.checkNotNull(item, "null cannot be serialized"); Preconditions.checkArgument(item.getType() != Material.AIR, "air cannot be serialized"); + // Purpur start - rework NBT <-> bytes + return serializeNbtToBytes(CraftItemStack.asNMSCopy(item).save(new NBTTagCompound()), true); + } + + public byte[] serializeNbtToBytes(NBTTagCompound compound, boolean addDataVersion) { + if (addDataVersion) compound.setInt("DataVersion", getDataVersion()); java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream(); - NBTTagCompound compound = (item instanceof CraftItemStack ? ((CraftItemStack) item).getHandle() : CraftItemStack.asNMSCopy(item)).save(new NBTTagCompound()); - compound.setInt("DataVersion", getDataVersion()); + // Purpur end try { net.minecraft.nbt.NBTCompressedStreamTools.writeNBT( compound, @@ -405,26 +410,58 @@ public final class CraftMagicNumbers implements UnsafeValues { return outputStream.toByteArray(); } + public static DynamicOpsNBT getDynamicOpsNbtInstance() { return DynamicOpsNBT.a; } // Purpur - OBFHELPER - keeping out of the class because it's FULL of decompile errors @Override public ItemStack deserializeItem(byte[] data) { Preconditions.checkNotNull(data, "null cannot be deserialized"); Preconditions.checkArgument(data.length > 0, "cannot deserialize nothing"); + // Purpur start - rework NBT <-> bytes + NBTTagCompound compound = deserializeNbtFromBytes(data, true); + Dynamic converted = DataConverterRegistry.getDataFixer().update(DataConverterTypes.ITEM_STACK, new Dynamic<>(getDynamicOpsNbtInstance(), compound), compound.getInt("DataVersion"), getDataVersion()); // TODO: obfhelper + return net.minecraft.world.item.ItemStack.fromCompound((NBTTagCompound) converted.getValue()).asBukkitMirror(); + } + + public NBTTagCompound deserializeNbtFromBytes(byte[] data, boolean verifyDataVersion) { + // Purpur end try { NBTTagCompound compound = net.minecraft.nbt.NBTCompressedStreamTools.readNBT( new java.io.ByteArrayInputStream(data) ); + if (verifyDataVersion) { // Purpur int dataVersion = compound.getInt("DataVersion"); Preconditions.checkArgument(dataVersion <= getDataVersion(), "Newer version! Server downgrades are not supported!"); - Dynamic converted = DataConverterRegistry.getDataFixer().update(DataConverterTypes.ITEM_STACK, new Dynamic(DynamicOpsNBT.a, compound), dataVersion, getDataVersion()); - return CraftItemStack.asCraftMirror(net.minecraft.world.item.ItemStack.fromCompound((NBTTagCompound) converted.getValue())); + } // Purpur + return compound; // Purpur } catch (IOException ex) { com.destroystokyo.paper.util.SneakyThrow.sneaky(ex); throw new RuntimeException(); } } + // Purpur start + @Override + public byte[] serializeEntity(org.bukkit.entity.Entity entity) { + Preconditions.checkNotNull(entity, "null cannot be serialized"); + Preconditions.checkArgument(entity instanceof org.bukkit.craftbukkit.entity.CraftEntity, "non-CraftEntity cannot be serialized"); + NBTTagCompound compound = new NBTTagCompound(); + compound.setString("id", ((org.bukkit.craftbukkit.entity.CraftEntity) entity).getHandle().getMinecraftKeyString()); + return serializeNbtToBytes(((org.bukkit.craftbukkit.entity.CraftEntity) entity).getHandle().save(compound), true); + } + + @Override + public org.bukkit.entity.Entity deserializeEntity(byte[] data, org.bukkit.World world) { + NBTTagCompound compound = deserializeNbtFromBytes(data, true); + Dynamic converted = DataConverterRegistry.getDataFixer().update(DataConverterTypes.ENTITY, new Dynamic<>(getDynamicOpsNbtInstance(), compound), compound.getInt("DataVersion"), getDataVersion()); + compound = (NBTTagCompound) converted.getValue(); + compound.remove("UUID"); // Make the server make a new UUID for this entity; makes entities always spawnable. + return net.minecraft.world.entity.EntityTypes.loadEntityFixedData(compound, ((org.bukkit.craftbukkit.CraftWorld) world).getHandle()) + .orElseThrow(() -> new IllegalArgumentException("unknown ID was found for the data; did you downgrade?")) + .getBukkitEntity(); + } + // Pupur end + @Override public String getTranslationKey(Material mat) { if (mat.isBlock()) {