--- a/net/minecraft/server/level/ServerEntity.java +++ b/net/minecraft/server/level/ServerEntity.java @@ -6,7 +6,9 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Iterator; import java.util.List; +import java.util.Objects; import java.util.Set; import java.util.function.Consumer; import java.util.stream.Stream; @@ -26,7 +28,6 @@ import net.minecraft.network.protocol.game.ClientboundUpdateAttributesPacket; import net.minecraft.network.protocol.game.VecDeltaCodec; import net.minecraft.network.syncher.SynchedEntityData; -import net.minecraft.util.Mth; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.LivingEntity; @@ -40,10 +41,18 @@ import net.minecraft.world.phys.Vec3; import org.slf4j.Logger; +// CraftBukkit start +import net.minecraft.server.network.ServerPlayerConnection; +import net.minecraft.util.Mth; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerVelocityEvent; +// CraftBukkit end + public class ServerEntity { + private static final Logger LOGGER = LogUtils.getLogger(); private static final int TOLERANCE_LEVEL_ROTATION = 1; - private static final double TOLERANCE_LEVEL_POSITION = 7.6293945E-6F; + private static final double TOLERANCE_LEVEL_POSITION = 7.62939453125E-6D; public static final int FORCED_POS_UPDATE_PERIOD = 60; private static final int FORCED_TELEPORT_PERIOD = 400; private final ServerLevel level; @@ -55,21 +64,27 @@ private int yRotp; private int xRotp; private int yHeadRotp; - private Vec3 ap = Vec3.ZERO; + private Vec3 ap; private int tickCount; private int teleportDelay; - private List lastPassengers = Collections.emptyList(); + private List lastPassengers; private boolean wasRiding; private boolean wasOnGround; @Nullable private List> trackedDataValues; + // CraftBukkit start + private final Set trackedPlayers; - public ServerEntity(ServerLevel level, Entity entity, int updateInterval, boolean trackDelta, Consumer> broadcast) { - this.level = level; - this.broadcast = broadcast; + public ServerEntity(ServerLevel worldserver, Entity entity, int i, boolean flag, Consumer> consumer, Set trackedPlayers) { + this.trackedPlayers = trackedPlayers; + // CraftBukkit end + this.ap = Vec3.ZERO; + this.lastPassengers = Collections.emptyList(); + this.level = worldserver; + this.broadcast = consumer; this.entity = entity; - this.updateInterval = updateInterval; - this.trackDelta = trackDelta; + this.updateInterval = i; + this.trackDelta = flag; this.positionCodec.setBase(entity.trackingPosition()); this.yRotp = Mth.floor(entity.getYRot() * 256.0F / 360.0F); this.xRotp = Mth.floor(entity.getXRot() * 256.0F / 360.0F); @@ -79,149 +94,191 @@ } public void sendChanges() { - List passengers = this.entity.getPassengers(); - if (!passengers.equals(this.lastPassengers)) { - this.broadcast.accept(new ClientboundSetPassengersPacket(this.entity)); - removedPassengers(passengers, this.lastPassengers) - .forEach( - entity -> { - if (entity instanceof ServerPlayer serverPlayer1) { - serverPlayer1.connection - .teleport(serverPlayer1.getX(), serverPlayer1.getY(), serverPlayer1.getZ(), serverPlayer1.getYRot(), serverPlayer1.getXRot()); - } - } - ); - this.lastPassengers = passengers; + List list = this.entity.getPassengers(); + + if (!list.equals(this.lastPassengers)) { + this.broadcastAndSend(new ClientboundSetPassengersPacket(this.entity)); // CraftBukkit + removedPassengers(list, this.lastPassengers).forEach((entity) -> { + if (entity instanceof ServerPlayer) { + ServerPlayer entityplayer = (ServerPlayer) entity; + + entityplayer.connection.teleport(entityplayer.getX(), entityplayer.getY(), entityplayer.getZ(), entityplayer.getYRot(), entityplayer.getXRot()); + } + + }); + this.lastPassengers = list; } - if (this.entity instanceof ItemFrame itemFrame && this.tickCount % 10 == 0) { - ItemStack item = itemFrame.getItem(); - if (item.getItem() instanceof MapItem) { - Integer mapId = MapItem.getMapId(item); - MapItemSavedData savedData = MapItem.getSavedData(mapId, this.level); - if (savedData != null) { - for (ServerPlayer serverPlayer : this.level.players()) { - savedData.tickCarriedBy(serverPlayer, item); - Packet updatePacket = savedData.getUpdatePacket(mapId, serverPlayer); - if (updatePacket != null) { - serverPlayer.connection.send(updatePacket); + Entity entity = this.entity; + + if (entity instanceof ItemFrame) { + ItemFrame entityitemframe = (ItemFrame) entity; + + if (true || this.tickCount % 10 == 0) { // CraftBukkit - Moved below, should always enter this block + ItemStack itemstack = entityitemframe.getItem(); + + if (this.tickCount % 10 == 0 && itemstack.getItem() instanceof MapItem) { // CraftBukkit - Moved this.tickCounter % 10 logic here so item frames do not enter the other blocks + Integer integer = MapItem.getMapId(itemstack); + MapItemSavedData worldmap = MapItem.getSavedData(integer, this.level); + + if (worldmap != null) { + Iterator iterator = this.trackedPlayers.iterator(); // CraftBukkit + + while (iterator.hasNext()) { + ServerPlayer entityplayer = iterator.next().getPlayer(); // CraftBukkit + + worldmap.tickCarriedBy(entityplayer, itemstack); + Packet packet = worldmap.getUpdatePacket(integer, entityplayer); + + if (packet != null) { + entityplayer.connection.send(packet); + } } } } - } - this.sendDirtyEntityData(); + this.sendDirtyEntityData(); + } } if (this.tickCount % this.updateInterval == 0 || this.entity.hasImpulse || this.entity.getEntityData().isDirty()) { + int i; + int j; + if (this.entity.isPassenger()) { - int floor = Mth.floor(this.entity.getYRot() * 256.0F / 360.0F); - int floor1 = Mth.floor(this.entity.getXRot() * 256.0F / 360.0F); - boolean flag = Math.abs(floor - this.yRotp) >= 1 || Math.abs(floor1 - this.xRotp) >= 1; + i = Mth.floor(this.entity.getYRot() * 256.0F / 360.0F); + j = Mth.floor(this.entity.getXRot() * 256.0F / 360.0F); + boolean flag = Math.abs(i - this.yRotp) >= 1 || Math.abs(j - this.xRotp) >= 1; + if (flag) { - this.broadcast.accept(new ClientboundMoveEntityPacket.Rot(this.entity.getId(), (byte)floor, (byte)floor1, this.entity.onGround())); - this.yRotp = floor; - this.xRotp = floor1; + this.broadcast.accept(new ClientboundMoveEntityPacket.Rot(this.entity.getId(), (byte) i, (byte) j, this.entity.onGround())); + this.yRotp = i; + this.xRotp = j; } this.positionCodec.setBase(this.entity.trackingPosition()); this.sendDirtyEntityData(); this.wasRiding = true; } else { - this.teleportDelay++; - int floor = Mth.floor(this.entity.getYRot() * 256.0F / 360.0F); - int floor1 = Mth.floor(this.entity.getXRot() * 256.0F / 360.0F); - Vec3 vec3 = this.entity.trackingPosition(); - boolean flag1 = this.positionCodec.delta(vec3).lengthSqr() >= 7.6293945E-6F; - Packet packet = null; + ++this.teleportDelay; + i = Mth.floor(this.entity.getYRot() * 256.0F / 360.0F); + j = Mth.floor(this.entity.getXRot() * 256.0F / 360.0F); + Vec3 vec3d = this.entity.trackingPosition(); + boolean flag1 = this.positionCodec.delta(vec3d).lengthSqr() >= 7.62939453125E-6D; + Packet packet1 = null; boolean flag2 = flag1 || this.tickCount % 60 == 0; - boolean flag3 = Math.abs(floor - this.yRotp) >= 1 || Math.abs(floor1 - this.xRotp) >= 1; + boolean flag3 = Math.abs(i - this.yRotp) >= 1 || Math.abs(j - this.xRotp) >= 1; boolean flag4 = false; boolean flag5 = false; + if (this.tickCount > 0 || this.entity instanceof AbstractArrow) { - long l = this.positionCodec.encodeX(vec3); - long l1 = this.positionCodec.encodeY(vec3); - long l2 = this.positionCodec.encodeZ(vec3); - boolean flag6 = l < -32768L || l > 32767L || l1 < -32768L || l1 > 32767L || l2 < -32768L || l2 > 32767L; - if (flag6 || this.teleportDelay > 400 || this.wasRiding || this.wasOnGround != this.entity.onGround()) { - this.wasOnGround = this.entity.onGround(); - this.teleportDelay = 0; - packet = new ClientboundTeleportEntityPacket(this.entity); - flag4 = true; - flag5 = true; - } else if ((!flag2 || !flag3) && !(this.entity instanceof AbstractArrow)) { - if (flag2) { - packet = new ClientboundMoveEntityPacket.Pos( - this.entity.getId(), (short)((int)l), (short)((int)l1), (short)((int)l2), this.entity.onGround() - ); + long k = this.positionCodec.encodeX(vec3d); + long l = this.positionCodec.encodeY(vec3d); + long i1 = this.positionCodec.encodeZ(vec3d); + boolean flag6 = k < -32768L || k > 32767L || l < -32768L || l > 32767L || i1 < -32768L || i1 > 32767L; + + if (!flag6 && this.teleportDelay <= 400 && !this.wasRiding && this.wasOnGround == this.entity.onGround()) { + if ((!flag2 || !flag3) && !(this.entity instanceof AbstractArrow)) { + if (flag2) { + packet1 = new ClientboundMoveEntityPacket.Pos(this.entity.getId(), (short) ((int) k), (short) ((int) l), (short) ((int) i1), this.entity.onGround()); + flag4 = true; + } else if (flag3) { + packet1 = new ClientboundMoveEntityPacket.Rot(this.entity.getId(), (byte) i, (byte) j, this.entity.onGround()); + flag5 = true; + } + } else { + packet1 = new ClientboundMoveEntityPacket.PosRot(this.entity.getId(), (short) ((int) k), (short) ((int) l), (short) ((int) i1), (byte) i, (byte) j, this.entity.onGround()); flag4 = true; - } else if (flag3) { - packet = new ClientboundMoveEntityPacket.Rot(this.entity.getId(), (byte)floor, (byte)floor1, this.entity.onGround()); flag5 = true; } } else { - packet = new ClientboundMoveEntityPacket.PosRot( - this.entity.getId(), (short)((int)l), (short)((int)l1), (short)((int)l2), (byte)floor, (byte)floor1, this.entity.onGround() - ); + this.wasOnGround = this.entity.onGround(); + this.teleportDelay = 0; + packet1 = new ClientboundTeleportEntityPacket(this.entity); flag4 = true; flag5 = true; } } - if ((this.trackDelta || this.entity.hasImpulse || this.entity instanceof LivingEntity && ((LivingEntity)this.entity).isFallFlying()) - && this.tickCount > 0) { - Vec3 deltaMovement = this.entity.getDeltaMovement(); - double d = deltaMovement.distanceToSqr(this.ap); - if (d > 1.0E-7 || d > 0.0 && deltaMovement.lengthSqr() == 0.0) { - this.ap = deltaMovement; + if ((this.trackDelta || this.entity.hasImpulse || this.entity instanceof LivingEntity && ((LivingEntity) this.entity).isFallFlying()) && this.tickCount > 0) { + Vec3 vec3d1 = this.entity.getDeltaMovement(); + double d0 = vec3d1.distanceToSqr(this.ap); + + if (d0 > 1.0E-7D || d0 > 0.0D && vec3d1.lengthSqr() == 0.0D) { + this.ap = vec3d1; this.broadcast.accept(new ClientboundSetEntityMotionPacket(this.entity.getId(), this.ap)); } } - if (packet != null) { - this.broadcast.accept(packet); + if (packet1 != null) { + this.broadcast.accept(packet1); } this.sendDirtyEntityData(); if (flag4) { - this.positionCodec.setBase(vec3); + this.positionCodec.setBase(vec3d); } if (flag5) { - this.yRotp = floor; - this.xRotp = floor1; + this.yRotp = i; + this.xRotp = j; } this.wasRiding = false; } - int floorx = Mth.floor(this.entity.getYHeadRot() * 256.0F / 360.0F); - if (Math.abs(floorx - this.yHeadRotp) >= 1) { - this.broadcast.accept(new ClientboundRotateHeadPacket(this.entity, (byte)floorx)); - this.yHeadRotp = floorx; + i = Mth.floor(this.entity.getYHeadRot() * 256.0F / 360.0F); + if (Math.abs(i - this.yHeadRotp) >= 1) { + this.broadcast.accept(new ClientboundRotateHeadPacket(this.entity, (byte) i)); + this.yHeadRotp = i; } this.entity.hasImpulse = false; } - this.tickCount++; + ++this.tickCount; if (this.entity.hurtMarked) { - this.broadcastAndSend(new ClientboundSetEntityMotionPacket(this.entity)); + // CraftBukkit start - Create PlayerVelocity event + boolean cancelled = false; + + if (this.entity instanceof ServerPlayer) { + Player player = (Player) this.entity.getBukkitEntity(); + org.bukkit.util.Vector velocity = player.getVelocity(); + + PlayerVelocityEvent event = new PlayerVelocityEvent(player, velocity.clone()); + this.entity.level().getCraftServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + cancelled = true; + } else if (!velocity.equals(event.getVelocity())) { + player.setVelocity(event.getVelocity()); + } + } + + if (!cancelled) { + this.broadcastAndSend(new ClientboundSetEntityMotionPacket(this.entity)); + } + // CraftBukkit end this.entity.hurtMarked = false; } + } private static Stream removedPassengers(List initialPassengers, List currentPassengers) { - return currentPassengers.stream().filter(entity -> !initialPassengers.contains(entity)); + return currentPassengers.stream().filter((entity) -> { + return !initialPassengers.contains(entity); + }); } public void removePairing(ServerPlayer player) { this.entity.stopSeenByPlayer(player); - player.connection.send(new ClientboundRemoveEntitiesPacket(this.entity.getId())); + player.connection.send(new ClientboundRemoveEntitiesPacket(new int[]{this.entity.getId()})); } public void addPairing(ServerPlayer player) { - List> list = new ArrayList<>(); + List> list = new ArrayList(); + + Objects.requireNonNull(list); this.sendPairingData(player, list::add); player.connection.send(new ClientboundBundlePacket(list)); this.entity.startSeenByPlayer(player); @@ -229,24 +286,36 @@ public void sendPairingData(ServerPlayer player, Consumer> consumer) { if (this.entity.isRemoved()) { - LOGGER.warn("Fetching packet for removed entity {}", this.entity); + // CraftBukkit start - Remove useless error spam, just return + // EntityTrackerEntry.LOGGER.warn("Fetching packet for removed entity {}", this.entity); + return; + // CraftBukkit end } - Packet addEntityPacket = this.entity.getAddEntityPacket(); + Packet packet = this.entity.getAddEntityPacket(); + this.yHeadRotp = Mth.floor(this.entity.getYHeadRot() * 256.0F / 360.0F); - consumer.accept(addEntityPacket); + consumer.accept(packet); if (this.trackedDataValues != null) { consumer.accept(new ClientboundSetEntityDataPacket(this.entity.getId(), this.trackedDataValues)); } boolean flag = this.trackDelta; + if (this.entity instanceof LivingEntity) { - Collection syncableAttributes = ((LivingEntity)this.entity).getAttributes().getSyncableAttributes(); - if (!syncableAttributes.isEmpty()) { - consumer.accept(new ClientboundUpdateAttributesPacket(this.entity.getId(), syncableAttributes)); + Collection collection = ((LivingEntity) this.entity).getAttributes().getSyncableAttributes(); + + // CraftBukkit start - If sending own attributes send scaled health instead of current maximum health + if (this.entity.getId() == player.getId()) { + ((ServerPlayer) this.entity).getBukkitEntity().injectScaledMaxHealth(collection, false); } + // CraftBukkit end - if (((LivingEntity)this.entity).isFallFlying()) { + if (!collection.isEmpty()) { + consumer.accept(new ClientboundUpdateAttributesPacket(this.entity.getId(), collection)); + } + + if (((LivingEntity) this.entity).isFallFlying()) { flag = true; } } @@ -258,19 +327,30 @@ if (this.entity instanceof LivingEntity) { List> list = Lists.newArrayList(); + EquipmentSlot[] aenumitemslot = EquipmentSlot.values(); + int i = aenumitemslot.length; - for (EquipmentSlot equipmentSlot : EquipmentSlot.values()) { - ItemStack itemBySlot = ((LivingEntity)this.entity).getItemBySlot(equipmentSlot); - if (!itemBySlot.isEmpty()) { - list.add(Pair.of(equipmentSlot, itemBySlot.copy())); + for (int j = 0; j < i; ++j) { + EquipmentSlot enumitemslot = aenumitemslot[j]; + ItemStack itemstack = ((LivingEntity) this.entity).getItemBySlot(enumitemslot); + + if (!itemstack.isEmpty()) { + list.add(Pair.of(enumitemslot, itemstack.copy())); } } if (!list.isEmpty()) { consumer.accept(new ClientboundSetEquipmentPacket(this.entity.getId(), list)); } + ((LivingEntity) this.entity).detectEquipmentUpdatesPublic(); // CraftBukkit - SPIGOT-3789: sync again immediately after sending } + // CraftBukkit start - MC-109346: Fix for nonsensical head yaw + if (this.entity instanceof ServerPlayer) { + consumer.accept(new ClientboundRotateHeadPacket(this.entity, (byte) Mth.floor(this.entity.getYHeadRot() * 256.0F / 360.0F))); + } + // CraftBukkit end + if (!this.entity.getPassengers().isEmpty()) { consumer.accept(new ClientboundSetPassengersPacket(this.entity)); } @@ -279,33 +359,49 @@ consumer.accept(new ClientboundSetPassengersPacket(this.entity.getVehicle())); } - if (this.entity instanceof Mob mob && mob.isLeashed()) { - consumer.accept(new ClientboundSetEntityLinkPacket(mob, mob.getLeashHolder())); + Entity entity = this.entity; + + if (entity instanceof Mob) { + Mob entityinsentient = (Mob) entity; + + if (entityinsentient.isLeashed()) { + consumer.accept(new ClientboundSetEntityLinkPacket(entityinsentient, entityinsentient.getLeashHolder())); + } } + } private void sendDirtyEntityData() { - SynchedEntityData entityData = this.entity.getEntityData(); - List> list = entityData.packDirty(); + SynchedEntityData datawatcher = this.entity.getEntityData(); + List> list = datawatcher.packDirty(); + if (list != null) { - this.trackedDataValues = entityData.getNonDefaultValues(); + this.trackedDataValues = datawatcher.getNonDefaultValues(); this.broadcastAndSend(new ClientboundSetEntityDataPacket(this.entity.getId(), list)); } if (this.entity instanceof LivingEntity) { - Set dirtyAttributes = ((LivingEntity)this.entity).getAttributes().getDirtyAttributes(); - if (!dirtyAttributes.isEmpty()) { - this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), dirtyAttributes)); + Set set = ((LivingEntity) this.entity).getAttributes().getDirtyAttributes(); + + if (!set.isEmpty()) { + // CraftBukkit start - Send scaled max health + if (this.entity instanceof ServerPlayer) { + ((ServerPlayer) this.entity).getBukkitEntity().injectScaledMaxHealth(set, false); + } + // CraftBukkit end + this.broadcastAndSend(new ClientboundUpdateAttributesPacket(this.entity.getId(), set)); } - dirtyAttributes.clear(); + set.clear(); } + } private void broadcastAndSend(Packet packet) { this.broadcast.accept(packet); if (this.entity instanceof ServerPlayer) { - ((ServerPlayer)this.entity).connection.send(packet); + ((ServerPlayer) this.entity).connection.send(packet); } + } }