diff --git a/paper-server/patches/sources/net/minecraft/world/entity/Entity.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/Entity.java.patch index a4078fcb41..7fed99e9e5 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/Entity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/Entity.java.patch @@ -485,15 +485,15 @@ } } -@@ -587,9 +874,25 @@ +@@ -587,7 +874,23 @@ } public final void igniteForSeconds(float seconds) { - this.igniteForTicks(Mth.floor(seconds * 20.0F)); + // CraftBukkit start + this.igniteForSeconds(seconds, true); - } - ++ } ++ + public final void igniteForSeconds(float f, boolean callEvent) { + if (callEvent) { + EntityCombustEvent event = new EntityCombustEvent(this.getBukkitEntity(), f); @@ -507,11 +507,9 @@ + } + // CraftBukkit end + this.igniteForTicks(Mth.floor(f * 20.0F)); -+ } -+ + } + public void igniteForTicks(int ticks) { - if (this.remainingFireTicks < ticks) { - this.setRemainingFireTicks(ticks); @@ -610,7 +913,7 @@ } @@ -550,15 +548,17 @@ if (!this.level().isClientSide() || this.isControlledByLocalInstance()) { Entity.MovementEmission entity_movementemission = this.getMovementEmission(); -@@ -1133,6 +1458,20 @@ - return SoundEvents.GENERIC_SPLASH; - } +@@ -1131,8 +1456,22 @@ + protected SoundEvent getSwimHighSpeedSplashSound() { + return SoundEvents.GENERIC_SPLASH; ++ } ++ + // CraftBukkit start - Add delegate methods + public SoundEvent getSwimSound0() { + return this.getSwimSound(); -+ } -+ + } + + public SoundEvent getSwimSplashSound0() { + return this.getSwimSplashSound(); + } @@ -646,7 +646,7 @@ public void awardKillScore(Entity entityKilled, DamageSource damageSource) { if (entityKilled instanceof ServerPlayer) { CriteriaTriggers.ENTITY_KILLED_PLAYER.trigger((ServerPlayer) entityKilled, this, damageSource); -@@ -1889,16 +2263,22 @@ +@@ -1889,74 +2263,133 @@ } public boolean saveAsPassenger(CompoundTag nbt) { @@ -672,7 +672,19 @@ return true; } } -@@ -1909,54 +2289,98 @@ + } + ++ // Paper start - Entity serialization api ++ public boolean serializeEntity(CompoundTag compound) { ++ List pass = new java.util.ArrayList<>(this.getPassengers()); ++ this.passengers = ImmutableList.of(); ++ boolean result = save(compound); ++ this.passengers = ImmutableList.copyOf(pass); ++ return result; ++ } ++ // Paper end - Entity serialization api + public boolean save(CompoundTag nbt) { + return this.isPassenger() ? false : this.saveAsPassenger(nbt); } public CompoundTag saveWithoutId(CompoundTag nbt) { @@ -791,7 +803,7 @@ } ListTag nbttaglist; -@@ -1972,10 +2396,10 @@ +@@ -1972,10 +2405,10 @@ nbttaglist.add(StringTag.valueOf(s)); } @@ -804,7 +816,7 @@ if (this.isVehicle()) { nbttaglist = new ListTag(); iterator = this.getPassengers().iterator(); -@@ -1984,17 +2408,41 @@ +@@ -1984,17 +2417,41 @@ Entity entity = (Entity) iterator.next(); CompoundTag nbttagcompound1 = new CompoundTag(); @@ -849,7 +861,7 @@ } catch (Throwable throwable) { CrashReport crashreport = CrashReport.forThrowable(throwable, "Saving entity NBT"); CrashReportCategory crashreportsystemdetails = crashreport.addCategory("Entity being saved"); -@@ -2079,7 +2527,69 @@ +@@ -2079,7 +2536,69 @@ } } else { throw new IllegalStateException("Entity has invalid position"); @@ -919,7 +931,7 @@ } catch (Throwable throwable) { CrashReport crashreport = CrashReport.forThrowable(throwable, "Loading entity NBT"); CrashReportCategory crashreportsystemdetails = crashreport.addCategory("Entity being loaded"); -@@ -2101,6 +2611,12 @@ +@@ -2101,6 +2620,12 @@ return entitytypes.canSerialize() && minecraftkey != null ? minecraftkey.toString() : null; } @@ -932,7 +944,7 @@ protected abstract void readAdditionalSaveData(CompoundTag nbt); protected abstract void addAdditionalSaveData(CompoundTag nbt); -@@ -2153,9 +2669,23 @@ +@@ -2153,9 +2678,23 @@ if (stack.isEmpty()) { return null; } else { @@ -957,7 +969,7 @@ world.addFreshEntity(entityitem); return entityitem; } -@@ -2184,7 +2714,16 @@ +@@ -2184,7 +2723,16 @@ if (this.isAlive() && this instanceof Leashable leashable) { if (leashable.getLeashHolder() == player) { if (!this.level().isClientSide()) { @@ -975,7 +987,7 @@ leashable.removeLeash(); } else { leashable.dropLeash(); -@@ -2200,6 +2739,13 @@ +@@ -2200,6 +2748,13 @@ if (itemstack.is(Items.LEAD) && leashable.canHaveALeashAttachedToIt()) { if (!this.level().isClientSide()) { @@ -989,7 +1001,7 @@ leashable.setLeashedTo(player, true); } -@@ -2265,7 +2811,7 @@ +@@ -2265,7 +2820,7 @@ } public boolean showVehicleHealth() { @@ -998,7 +1010,7 @@ } public boolean startRiding(Entity entity, boolean force) { -@@ -2273,7 +2819,7 @@ +@@ -2273,7 +2828,7 @@ return false; } else if (!entity.couldAcceptPassenger()) { return false; @@ -1007,7 +1019,7 @@ return false; } else { for (Entity entity1 = entity; entity1.vehicle != null; entity1 = entity1.vehicle) { -@@ -2285,11 +2831,32 @@ +@@ -2285,11 +2840,32 @@ if (!force && (!this.canRide(entity) || !entity.canAddPassenger(this))) { return false; } else { @@ -1041,7 +1053,7 @@ this.vehicle = entity; this.vehicle.addPassenger(this); entity.getIndirectPassengersStream().filter((entity2) -> { -@@ -2314,19 +2881,30 @@ +@@ -2314,19 +2890,30 @@ } public void removeVehicle() { @@ -1074,7 +1086,7 @@ protected void addPassenger(Entity passenger) { if (passenger.getVehicle() != this) { throw new IllegalStateException("Use x.startRiding(y), not y.addPassenger(x)"); -@@ -2349,21 +2927,53 @@ +@@ -2349,21 +2936,53 @@ } } @@ -1134,7 +1146,7 @@ } protected boolean canAddPassenger(Entity passenger) { -@@ -2464,7 +3074,7 @@ +@@ -2464,7 +3083,7 @@ if (teleporttransition != null) { ServerLevel worldserver1 = teleporttransition.newLevel(); @@ -1143,7 +1155,7 @@ this.teleport(teleporttransition); } } -@@ -2547,7 +3157,7 @@ +@@ -2547,7 +3166,7 @@ } public boolean isCrouching() { @@ -1152,7 +1164,7 @@ } public boolean isSprinting() { -@@ -2563,7 +3173,7 @@ +@@ -2563,7 +3182,7 @@ } public boolean isVisuallySwimming() { @@ -1161,7 +1173,7 @@ } public boolean isVisuallyCrawling() { -@@ -2571,6 +3181,13 @@ +@@ -2571,6 +3190,13 @@ } public void setSwimming(boolean swimming) { @@ -1175,7 +1187,7 @@ this.setSharedFlag(4, swimming); } -@@ -2609,6 +3226,7 @@ +@@ -2609,6 +3235,7 @@ @Nullable public PlayerTeam getTeam() { @@ -1183,7 +1195,7 @@ return this.level().getScoreboard().getPlayersTeam(this.getScoreboardName()); } -@@ -2624,8 +3242,12 @@ +@@ -2624,8 +3251,12 @@ return this.getTeam() != null ? this.getTeam().isAlliedTo(team) : false; } @@ -1197,7 +1209,7 @@ } public boolean getSharedFlag(int index) { -@@ -2644,7 +3266,7 @@ +@@ -2644,7 +3275,7 @@ } public int getMaxAirSupply() { @@ -1206,7 +1218,7 @@ } public int getAirSupply() { -@@ -2652,7 +3274,18 @@ +@@ -2652,7 +3283,18 @@ } public void setAirSupply(int air) { @@ -1226,7 +1238,7 @@ } public int getTicksFrozen() { -@@ -2679,11 +3312,40 @@ +@@ -2679,11 +3321,40 @@ public void thunderHit(ServerLevel world, LightningBolt lightning) { this.setRemainingFireTicks(this.remainingFireTicks + 1); @@ -1269,7 +1281,7 @@ } public void onAboveBubbleCol(boolean drag) { -@@ -2713,7 +3375,7 @@ +@@ -2713,7 +3384,7 @@ this.resetFallDistance(); } @@ -1278,7 +1290,7 @@ return true; } -@@ -2818,7 +3480,7 @@ +@@ -2818,7 +3489,7 @@ public String toString() { String s = this.level() == null ? "~NULL~" : this.level().toString(); @@ -1287,7 +1299,7 @@ } public final boolean isInvulnerableToBase(DamageSource damageSource) { -@@ -2850,8 +3512,57 @@ +@@ -2850,8 +3521,57 @@ public Entity teleport(TeleportTransition teleportTarget) { Level world = this.level(); @@ -1345,7 +1357,7 @@ ServerLevel worldserver1 = teleportTarget.newLevel(); boolean flag = worldserver1.dimension() != worldserver.dimension(); -@@ -2918,10 +3629,19 @@ +@@ -2918,10 +3638,19 @@ gameprofilerfiller.pop(); return null; } else { @@ -1366,7 +1378,7 @@ Iterator iterator1 = list1.iterator(); while (iterator1.hasNext()) { -@@ -2947,7 +3667,7 @@ +@@ -2947,7 +3676,7 @@ } private void sendTeleportTransitionToRidingPlayers(TeleportTransition teleportTarget) { @@ -1375,7 +1387,7 @@ Iterator iterator = this.getIndirectPassengers().iterator(); while (iterator.hasNext()) { -@@ -2995,9 +3715,17 @@ +@@ -2995,9 +3724,17 @@ } protected void removeAfterChangingDimensions() { @@ -1396,7 +1408,7 @@ } } -@@ -3006,11 +3734,26 @@ +@@ -3006,11 +3743,26 @@ return PortalShape.getRelativePosition(portalRect, portalAxis, this.position(), this.getDimensions(this.getPose())); } @@ -1423,7 +1435,7 @@ if (from.dimension() == Level.END && to.dimension() == Level.OVERWORLD) { Iterator iterator = this.getPassengers().iterator(); -@@ -3134,10 +3877,16 @@ +@@ -3134,10 +3886,16 @@ return (Boolean) this.entityData.get(Entity.DATA_CUSTOM_NAME_VISIBLE); } @@ -1443,7 +1455,7 @@ return entity != null; } -@@ -3187,7 +3936,7 @@ +@@ -3187,7 +3945,7 @@ /** @deprecated */ @Deprecated protected void fixupDimensions() { @@ -1452,7 +1464,7 @@ EntityDimensions entitysize = this.getDimensions(entitypose); this.dimensions = entitysize; -@@ -3196,7 +3945,7 @@ +@@ -3196,7 +3954,7 @@ public void refreshDimensions() { EntityDimensions entitysize = this.dimensions; @@ -1461,7 +1473,7 @@ EntityDimensions entitysize1 = this.getDimensions(entitypose); this.dimensions = entitysize1; -@@ -3258,10 +4007,29 @@ +@@ -3258,10 +4016,29 @@ } public final void setBoundingBox(AABB boundingBox) { @@ -1493,7 +1505,7 @@ return this.getDimensions(pose).eyeHeight(); } -@@ -3335,7 +4103,7 @@ +@@ -3335,7 +4112,7 @@ } @Nullable @@ -1502,7 +1514,7 @@ return null; } -@@ -3373,20 +4141,34 @@ +@@ -3373,20 +4150,34 @@ } private Stream getIndirectPassengersStream() { @@ -1537,7 +1549,7 @@ return () -> { return this.getIndirectPassengersStream().iterator(); }; -@@ -3399,6 +4181,7 @@ +@@ -3399,6 +4190,7 @@ } public boolean hasExactlyOnePlayerPassenger() { @@ -1545,7 +1557,7 @@ return this.countPlayerPassengers() == 1; } -@@ -3435,7 +4218,7 @@ +@@ -3435,7 +4227,7 @@ } public boolean isControlledByLocalInstance() { @@ -1554,7 +1566,7 @@ if (entityliving instanceof Player entityhuman) { return entityhuman.isLocalPlayer(); -@@ -3445,7 +4228,7 @@ +@@ -3445,7 +4237,7 @@ } public boolean isControlledByClient() { @@ -1563,7 +1575,7 @@ return entityliving != null && entityliving.isControlledByClient(); } -@@ -3463,7 +4246,7 @@ +@@ -3463,7 +4255,7 @@ return new Vec3((double) f1 * d2 / (double) f3, 0.0D, (double) f2 * d2 / (double) f3); } @@ -1572,17 +1584,18 @@ return new Vec3(this.getX(), this.getBoundingBox().maxY, this.getZ()); } -@@ -3489,8 +4272,37 @@ +@@ -3488,9 +4280,38 @@ + public int getFireImmuneTicks() { return 1; } - ++ + // CraftBukkit start + private final CommandSource commandSource = new CommandSource() { + + @Override + public void sendSystemMessage(Component message) { + } -+ + + @Override + public CommandSender getBukkitSender(CommandSourceStack wrapper) { + return Entity.this.getBukkitEntity(); @@ -1611,7 +1624,7 @@ } public void lookAt(EntityAnchorArgument.Anchor anchorPoint, Vec3 target) { -@@ -3551,6 +4363,11 @@ +@@ -3551,6 +4372,11 @@ vec3d = vec3d.add(vec3d1); ++k1; } @@ -1623,7 +1636,7 @@ } } } -@@ -3613,7 +4430,7 @@ +@@ -3613,7 +4439,7 @@ return new ClientboundAddEntityPacket(this, entityTrackerEntry); } @@ -1632,7 +1645,7 @@ return this.type.getDimensions(); } -@@ -3714,7 +4531,39 @@ +@@ -3714,7 +4540,39 @@ return this.getZ((2.0D * this.random.nextDouble() - 1.0D) * widthScale); } @@ -1672,7 +1685,7 @@ if (this.position.x != x || this.position.y != y || this.position.z != z) { this.position = new Vec3(x, y, z); int i = Mth.floor(x); -@@ -3732,6 +4581,12 @@ +@@ -3732,6 +4590,12 @@ this.levelCallback.onMove(); } @@ -1685,7 +1698,7 @@ } public void checkDespawn() {} -@@ -3818,8 +4673,16 @@ +@@ -3818,8 +4682,16 @@ @Override public final void setRemoved(Entity.RemovalReason reason) { @@ -1703,7 +1716,7 @@ } if (this.removalReason.shouldDestroy()) { -@@ -3827,8 +4690,8 @@ +@@ -3827,8 +4699,8 @@ } this.getPassengers().forEach(Entity::stopRiding); @@ -1714,7 +1727,7 @@ } public void unsetRemoved() { -@@ -3887,7 +4750,7 @@ +@@ -3887,7 +4759,7 @@ } public Vec3 getKnownMovement() { @@ -1723,7 +1736,7 @@ if (entityliving instanceof Player entityhuman) { if (this.isAlive()) { -@@ -3962,4 +4825,14 @@ +@@ -3962,4 +4834,14 @@ void accept(Entity entity, double x, double y, double z); } diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java index 888a75423a..b13c947d2c 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java @@ -1083,6 +1083,18 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { } // Paper end - tracked players API + // Paper start - raw entity serialization API + @Override + public boolean spawnAt(Location location, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason) { + Preconditions.checkNotNull(location, "location cannot be null"); + Preconditions.checkNotNull(reason, "reason cannot be null"); + this.entity.setLevel(((CraftWorld) location.getWorld()).getHandle()); + this.entity.setPos(location.getX(), location.getY(), location.getZ()); + this.entity.setRot(location.getYaw(), location.getPitch()); + return !this.entity.valid && this.entity.level().addFreshEntity(this.entity, reason); + } + // Paper end - raw entity serialization API + // Paper start - missing entity api @Override public boolean isInvisible() { // Paper - moved up from LivingEntity diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java b/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java index 6f780b76eb..d7698f8ae5 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/util/CraftMagicNumbers.java @@ -510,7 +510,33 @@ public final class CraftMagicNumbers implements UnsafeValues { return CraftItemStack.asCraftMirror(net.minecraft.world.item.ItemStack.parse(MinecraftServer.getServer().registryAccess(), compound).orElseThrow()); } - private byte[] serializeNbtToBytes(CompoundTag compound) { + @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, "only CraftEntities can be serialized"); + + net.minecraft.nbt.CompoundTag compound = new net.minecraft.nbt.CompoundTag(); + ((org.bukkit.craftbukkit.entity.CraftEntity) entity).getHandle().serializeEntity(compound); + return serializeNbtToBytes(compound); + } + + @Override + public org.bukkit.entity.Entity deserializeEntity(byte[] data, org.bukkit.World world, boolean preserveUUID) { + Preconditions.checkNotNull(data, "null cannot be deserialized"); + Preconditions.checkArgument(data.length > 0, "cannot deserialize nothing"); + + net.minecraft.nbt.CompoundTag compound = deserializeNbtFromBytes(data); + int dataVersion = compound.getInt("DataVersion"); + compound = (net.minecraft.nbt.CompoundTag) MinecraftServer.getServer().fixerUpper.update(References.ENTITY, new Dynamic<>(NbtOps.INSTANCE, compound), dataVersion, this.getDataVersion()).getValue(); + if (!preserveUUID) { + // Generate a new UUID so we don't have to worry about deserializing the same entity twice + compound.remove("UUID"); + } + return net.minecraft.world.entity.EntityType.create(compound, ((org.bukkit.craftbukkit.CraftWorld) world).getHandle(), net.minecraft.world.entity.EntitySpawnReason.LOAD) + .orElseThrow(() -> new IllegalArgumentException("An ID was not found for the data. Did you downgrade?")).getBukkitEntity(); + } + + private byte[] serializeNbtToBytes(net.minecraft.nbt.CompoundTag compound) { compound.putInt("DataVersion", getDataVersion()); java.io.ByteArrayOutputStream outputStream = new java.io.ByteArrayOutputStream(); try {