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/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java index 9430d4e270cd5ebabeffde0e28f0a96657ce0262..c50089b21d3b258ae4b3f0a6b2625fb129764fee 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java @@ -1265,5 +1265,12 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { public boolean isRidableInWater() { return getHandle().rideableUnderWater(); } + + @Override + public boolean spawnAt(Location location, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason) { + entity.level = ((CraftWorld) location.getWorld()).getHandle(); + entity.absMoveTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); + return !entity.valid && entity.level.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 72cfffd80ad76abe7cb16bc9133730338c07b6f6..09b99e3465ae142081f01c483116c1d1adb34842 100644 --- a/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +++ b/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java @@ -407,9 +407,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 CompoundTag()), true); + } + + public byte[] serializeNbtToBytes(CompoundTag compound, boolean addDataVersion) { + if (addDataVersion) compound.putInt("DataVersion", getDataVersion()); java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream(); - CompoundTag compound = (item instanceof CraftItemStack ? ((CraftItemStack) item).handle : CraftItemStack.asNMSCopy(item)).save(new CompoundTag()); - compound.putInt("DataVersion", getDataVersion()); + // Purpur end try { net.minecraft.nbt.NbtIo.writeCompressed( compound, @@ -427,21 +432,52 @@ public final class CraftMagicNumbers implements UnsafeValues { Preconditions.checkNotNull(data, "null cannot be deserialized"); Preconditions.checkArgument(data.length > 0, "cannot deserialize nothing"); + // Purpur start - rework NBT <-> bytes + CompoundTag compound = deserializeNbtFromBytes(data, true); + Dynamic converted = DataFixers.getDataFixer().update(References.ITEM_STACK, new Dynamic<>(NbtOps.INSTANCE, compound), compound.getInt("DataVersion"), getDataVersion()); + return net.minecraft.world.item.ItemStack.of((CompoundTag) converted.getValue()).asBukkitMirror(); + } + + public CompoundTag deserializeNbtFromBytes(byte[] data, boolean verifyDataVersion) { + // Purpur end try { CompoundTag compound = net.minecraft.nbt.NbtIo.readCompressed( 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 = DataFixers.getDataFixer().update(References.ITEM_STACK, new Dynamic(NbtOps.INSTANCE, compound), dataVersion, getDataVersion()); - return CraftItemStack.asCraftMirror(net.minecraft.world.item.ItemStack.of((CompoundTag) 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"); + CompoundTag compound = new CompoundTag(); + compound.putString("id", ((org.bukkit.craftbukkit.entity.CraftEntity) entity).getHandle().getMinecraftKeyString()); + return serializeNbtToBytes(((org.bukkit.craftbukkit.entity.CraftEntity) entity).getHandle().saveWithoutId(compound), true); + } + + @Override + public org.bukkit.entity.Entity deserializeEntity(byte[] data, org.bukkit.World world) { + CompoundTag compound = deserializeNbtFromBytes(data, true); + Dynamic converted = DataFixers.getDataFixer().update(References.ENTITY, new Dynamic<>(NbtOps.INSTANCE, compound), compound.getInt("DataVersion"), getDataVersion()); + compound = (CompoundTag) converted.getValue(); + compound.remove("UUID"); // Make the server make a new UUID for this entity; makes entities always spawnable. + return net.minecraft.world.entity.EntityType.create(compound, ((org.bukkit.craftbukkit.CraftWorld) world).getHandle()) + .orElseThrow(() -> new IllegalArgumentException("unknown ID was found for the data; did you downgrade?")) + .getBukkitEntity(); + } + // Purpur end + @Override public String getTranslationKey(Material mat) { if (mat.isBlock()) {