From d300c94ec2b8fb9c8ce8e671fcdc1c0219ea5671 Mon Sep 17 00:00:00 2001 From: Owen1212055 <23108066+Owen1212055@users.noreply.github.com> Date: Wed, 7 Dec 2022 17:25:19 -0500 Subject: [PATCH] Properly resend entities This resolves some issues which caused entities to not be resent correctly. Entities that are interacted with need to be resent to the client, so we resend all the entity data to the player whilst making sure not to clear dirty entries from the tracker. This makes sure that values will be correctly updated to other players. This also adds utilities to aid in further preventing entity desyncs. This also also fixes the bug causing cancelling PlayerInteractEvent to cause items to continue to be used despite being cancelled on the server. For example, items being consumed but never finishing, shields being put up, etc. The underlying issue of this is that the client modifies their synced data values, and so we have to (forcibly) resend them in order for the client to reset their using item state. See: https://github.com/PaperMC/Paper/pull/1896 == AT == public net.minecraft.server.level.ChunkMap$TrackedEntity serverEntity --- .../syncher/SynchedEntityData.java.patch | 25 +- .../level/ServerPlayerGameMode.java.patch | 29 ++- .../ServerGamePacketListenerImpl.java.patch | 97 ++++---- .../server/players/PlayerList.java.patch | 70 +++--- .../minecraft/world/entity/Entity.java.patch | 235 ++++++++++-------- .../world/entity/LivingEntity.java.patch | 97 ++++---- .../world/entity/animal/Bucketable.java.patch | 7 +- .../item/component/Consumable.java.patch | 8 +- .../component/ConsumableListener.java.patch | 2 +- .../OminousBottleAmplifier.java.patch | 18 ++ .../SuspiciousStewEffects.java.patch | 4 +- .../craftbukkit/entity/CraftEntity.java | 6 +- .../craftbukkit/entity/CraftItemFrame.java | 6 +- 13 files changed, 358 insertions(+), 246 deletions(-) create mode 100644 paper-server/patches/sources/net/minecraft/world/item/component/OminousBottleAmplifier.java.patch diff --git a/paper-server/patches/sources/net/minecraft/network/syncher/SynchedEntityData.java.patch b/paper-server/patches/sources/net/minecraft/network/syncher/SynchedEntityData.java.patch index 98be950605..d2519790c5 100644 --- a/paper-server/patches/sources/net/minecraft/network/syncher/SynchedEntityData.java.patch +++ b/paper-server/patches/sources/net/minecraft/network/syncher/SynchedEntityData.java.patch @@ -1,10 +1,12 @@ --- a/net/minecraft/network/syncher/SynchedEntityData.java +++ b/net/minecraft/network/syncher/SynchedEntityData.java -@@ -51,7 +51,7 @@ +@@ -50,8 +50,8 @@ + } } - private SynchedEntityData.DataItem getItem(EntityDataAccessor key) { +- private SynchedEntityData.DataItem getItem(EntityDataAccessor key) { - return this.itemsById[key.id()]; ++ public SynchedEntityData.DataItem getItem(EntityDataAccessor key) { // Paper - public + return (SynchedEntityData.DataItem) this.itemsById[key.id()]; // CraftBukkit - decompile error } @@ -23,7 +25,7 @@ public boolean isDirty() { return this.isDirty; } -@@ -140,7 +147,7 @@ +@@ -140,10 +147,24 @@ if (!Objects.equals(from.serializer(), to.accessor.serializer())) { throw new IllegalStateException(String.format(Locale.ROOT, "Invalid entity data item type for field %d on entity %s: old=%s(%s), new=%s(%s)", to.accessor.id(), this.entity, to.value, to.value.getClass(), from.value, from.value.getClass())); } else { @@ -32,3 +34,20 @@ } } ++ // Paper start ++ // We need to pack all as we cannot rely on "non default values" or "dirty" ones. ++ // Because these values can possibly be desynced on the client. ++ @Nullable ++ public List> packAll() { ++ final List> list = new ArrayList<>(); ++ for (final DataItem dataItem : this.itemsById) { ++ list.add(dataItem.value()); ++ } ++ ++ return list; ++ } ++ // Paper end ++ + public static class DataItem { + + final EntityDataAccessor accessor; diff --git a/paper-server/patches/sources/net/minecraft/server/level/ServerPlayerGameMode.java.patch b/paper-server/patches/sources/net/minecraft/server/level/ServerPlayerGameMode.java.patch index 6f36548255..aba7ebeae1 100644 --- a/paper-server/patches/sources/net/minecraft/server/level/ServerPlayerGameMode.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/level/ServerPlayerGameMode.java.patch @@ -135,9 +135,9 @@ + // Update any tile entity data for this block + capturedBlockEntity = true; // Paper - Send block entities after destroy prediction + // CraftBukkit end -+ return; -+ } -+ + return; + } + + // CraftBukkit start + PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(this.player, Action.LEFT_CLICK_BLOCK, pos, direction, this.player.getInventory().getSelected(), InteractionHand.MAIN_HAND); + if (event.isCancelled()) { @@ -145,10 +145,10 @@ + // this.player.connection.send(new ClientboundBlockUpdatePacket(this.level, pos)); // Paper - Don't resync blocks + // Update any tile entity data for this block + capturedBlockEntity = true; // Paper - Send block entities after destroy prediction - return; - } ++ return; ++ } + // CraftBukkit end - ++ if (this.isCreative()) { this.destroyAndAck(pos, sequence, "creative destroy"); return; @@ -374,7 +374,7 @@ } } } -@@ -321,17 +514,62 @@ +@@ -321,17 +514,63 @@ } } @@ -431,6 +431,7 @@ + } + // Paper end - extend Player Interact cancellation + player.getBukkitEntity().updateInventory(); // SPIGOT-2867 ++ this.player.resyncUsingItem(this.player); // Paper - Properly cancel usable items + return (event.useItemInHand() != Event.Result.ALLOW) ? InteractionResult.SUCCESS : InteractionResult.PASS; + } else if (this.gameModeForPlayer == GameType.SPECTATOR) { + MenuProvider itileinventory = iblockdata.getMenuProvider(world, blockposition); @@ -439,7 +440,7 @@ return InteractionResult.CONSUME; } else { return InteractionResult.PASS; -@@ -359,7 +597,7 @@ +@@ -359,7 +598,7 @@ } } @@ -448,3 +449,15 @@ UseOnContext itemactioncontext = new UseOnContext(player, hand, hitResult); if (this.isCreative()) { +@@ -377,6 +616,11 @@ + + return enuminteractionresult; + } else { ++ // Paper start - Properly cancel usable items; Cancel only if cancelled + if the interact result is different from default response ++ if (this.interactResult && this.interactResult != cancelledItem) { ++ this.player.resyncUsingItem(this.player); ++ } ++ // Paper end - Properly cancel usable items + return InteractionResult.PASS; + } + } diff --git a/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch b/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch index b39c68012f..6c1745ae43 100644 --- a/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/network/ServerGamePacketListenerImpl.java.patch @@ -48,7 +48,7 @@ import net.minecraft.world.level.GameRules; import net.minecraft.world.level.GameType; import net.minecraft.world.level.Level; -@@ -192,12 +196,73 @@ +@@ -192,11 +196,72 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.BlockHitResult; @@ -59,7 +59,7 @@ import net.minecraft.world.phys.shapes.VoxelShape; +import org.bukkit.NamespacedKey; import org.slf4j.Logger; - ++ +// CraftBukkit start +import io.papermc.paper.adventure.ChatProcessor; // Paper +import io.papermc.paper.adventure.PaperAdventure; // Paper @@ -118,10 +118,9 @@ +import org.bukkit.inventory.InventoryView; +import org.bukkit.inventory.SmithingInventory; +// CraftBukkit end -+ + public class ServerGamePacketListenerImpl extends ServerCommonPacketListenerImpl implements ServerGamePacketListener, ServerPlayerConnection, TickablePacketListener { - static final Logger LOGGER = LogUtils.getLogger(); @@ -212,7 +277,9 @@ private int tickCount; private int ackBlockChangesUpTo = -1; @@ -339,7 +338,7 @@ boolean flag1 = entity.verticalCollisionBelow; if (entity instanceof LivingEntity) { -@@ -449,20 +607,73 @@ +@@ -449,19 +607,72 @@ d10 = d6 * d6 + d7 * d7 + d8 * d8; boolean flag2 = false; @@ -358,8 +357,8 @@ + this.player.absMoveTo(d0, d1, d2, this.player.getYRot(), this.player.getXRot()); // CraftBukkit this.send(ClientboundMoveVehiclePacket.fromEntity(entity)); return; - } - ++ } ++ + // CraftBukkit start - fire PlayerMoveEvent + Player player = this.getCraftPlayer(); + if (!this.hasMoved) { @@ -408,12 +407,11 @@ + this.justTeleported = false; + return; + } -+ } + } + // CraftBukkit end -+ + this.player.serverLevel().getChunkSource().move(this.player); entity.recordMovementThroughBlocks(new Vec3(d0, d1, d2), entity.position()); - Vec3 vec3d = new Vec3(entity.getX() - d0, entity.getY() - d1, entity.getZ() - d2); @@ -489,16 +700,17 @@ PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); if (packet.getId() == this.awaitingTeleport) { @@ -1264,7 +1262,7 @@ if (this.player.hasClientLoaded()) { this.ackBlockChangesUpTo(packet.getSequence()); ServerLevel worldserver = this.player.serverLevel(); -@@ -1296,6 +1993,47 @@ +@@ -1296,6 +1993,48 @@ this.player.absRotateTo(f, f1); } @@ -1301,6 +1299,7 @@ + } + + if (cancelled) { ++ this.player.resyncUsingItem(this.player); // Paper - Properly cancel usable items + this.player.getBukkitEntity().updateInventory(); // SPIGOT-2524 + return; + } @@ -1312,7 +1311,7 @@ InteractionResult enuminteractionresult = this.player.gameMode.useItem(this.player, worldserver, itemstack, enumhand); if (enuminteractionresult instanceof InteractionResult.Success) { -@@ -1321,7 +2059,7 @@ +@@ -1321,7 +2060,7 @@ Entity entity = packet.getEntity(worldserver); if (entity != null) { @@ -1321,7 +1320,7 @@ return; } } -@@ -1342,22 +2080,52 @@ +@@ -1342,22 +2081,52 @@ @Override public void onDisconnect(DisconnectionDetails info) { @@ -1378,7 +1377,7 @@ throw new IllegalArgumentException("Expected packet sequence nr >= 0"); } else { this.ackBlockChangesUpTo = Math.max(sequence, this.ackBlockChangesUpTo); -@@ -1367,7 +2135,17 @@ +@@ -1367,7 +2136,17 @@ @Override public void handleSetCarriedItem(ServerboundSetCarriedItemPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); @@ -1396,7 +1395,7 @@ if (this.player.getInventory().selected != packet.getSlot() && this.player.getUsedItemHand() == InteractionHand.MAIN_HAND) { this.player.stopUsingItem(); } -@@ -1376,11 +2154,18 @@ +@@ -1376,11 +2155,18 @@ this.player.resetLastActionTime(); } else { ServerGamePacketListenerImpl.LOGGER.warn("{} tried to set an invalid carried item", this.player.getName().getString()); @@ -1415,7 +1414,7 @@ Optional optional = this.unpackAndApplyLastSeen(packet.lastSeenMessages()); if (!optional.isEmpty()) { -@@ -1394,27 +2179,46 @@ +@@ -1394,27 +2180,46 @@ return; } @@ -1469,7 +1468,7 @@ ParseResults parseresults = this.parseCommand(command); if (this.server.enforceSecureProfile() && SignableCommand.hasSignableArguments(parseresults)) { -@@ -1431,30 +2235,58 @@ +@@ -1431,30 +2236,58 @@ if (!optional.isEmpty()) { this.tryHandleChat(packet.command(), () -> { @@ -1534,7 +1533,7 @@ } private void handleMessageDecodeFailure(SignedMessageChain.DecodeException exception) { -@@ -1530,14 +2362,20 @@ +@@ -1530,14 +2363,20 @@ return com_mojang_brigadier_commanddispatcher.parse(command, this.player.createCommandSourceStack()); } @@ -1560,7 +1559,7 @@ } } -@@ -1549,7 +2387,7 @@ +@@ -1549,7 +2388,7 @@ if (optional.isEmpty()) { ServerGamePacketListenerImpl.LOGGER.warn("Failed to validate message acknowledgements from {}", this.player.getName().getString()); @@ -1569,7 +1568,7 @@ } return optional; -@@ -1566,6 +2404,117 @@ +@@ -1566,6 +2405,117 @@ return false; } @@ -1687,7 +1686,7 @@ private PlayerChatMessage getSignedMessage(ServerboundChatPacket packet, LastSeenMessages lastSeenMessages) throws SignedMessageChain.DecodeException { SignedMessageBody signedmessagebody = new SignedMessageBody(packet.message(), packet.timeStamp(), packet.salt(), lastSeenMessages); -@@ -1573,15 +2522,44 @@ +@@ -1573,15 +2523,44 @@ } private void broadcastChatMessage(PlayerChatMessage message) { @@ -1738,7 +1737,7 @@ } -@@ -1592,7 +2570,7 @@ +@@ -1592,7 +2571,7 @@ synchronized (this.lastSeenMessages) { if (!this.lastSeenMessages.applyOffset(packet.offset())) { ServerGamePacketListenerImpl.LOGGER.warn("Failed to validate message acknowledgements from {}", this.player.getName().getString()); @@ -1747,7 +1746,7 @@ } } -@@ -1601,7 +2579,40 @@ +@@ -1601,7 +2580,40 @@ @Override public void handleAnimate(ServerboundSwingPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); @@ -1788,7 +1787,7 @@ this.player.swing(packet.getHand()); } -@@ -1609,6 +2620,29 @@ +@@ -1609,6 +2621,29 @@ public void handlePlayerCommand(ServerboundPlayerCommandPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); if (this.player.hasClientLoaded()) { @@ -1818,7 +1817,7 @@ this.player.resetLastActionTime(); Entity entity; PlayerRideableJumping ijumpable; -@@ -1616,6 +2650,11 @@ +@@ -1616,6 +2651,11 @@ switch (packet.getAction()) { case PRESS_SHIFT_KEY: this.player.setShiftKeyDown(true); @@ -1830,7 +1829,7 @@ break; case RELEASE_SHIFT_KEY: this.player.setShiftKeyDown(false); -@@ -1684,15 +2723,25 @@ +@@ -1684,15 +2724,25 @@ } if (i > 4096) { @@ -1857,7 +1856,7 @@ } public void sendDisguisedChatMessage(Component message, ChatType.Bound params) { -@@ -1703,6 +2752,18 @@ +@@ -1703,6 +2753,18 @@ return this.connection.getRemoteAddress(); } @@ -1876,7 +1875,7 @@ public void switchToConfig() { this.waitingForSwitchToConfig = true; this.removePlayerFromWorld(); -@@ -1718,9 +2779,17 @@ +@@ -1718,9 +2780,17 @@ @Override public void handleInteract(ServerboundInteractPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); @@ -1894,7 +1893,7 @@ this.player.resetLastActionTime(); this.player.setShiftKeyDown(packet.isUsingSecondaryAction()); -@@ -1731,22 +2800,61 @@ +@@ -1731,22 +2801,61 @@ AABB axisalignedbb = entity.getBoundingBox(); @@ -1918,7 +1917,7 @@ + + // Entity in bucket - SPIGOT-4048 and SPIGOT-6859a + if ((entity instanceof Bucketable && entity instanceof LivingEntity && origItem != null && origItem.asItem() == Items.WATER_BUCKET) && (event.isCancelled() || ServerGamePacketListenerImpl.this.player.getInventory().getSelected() == null || ServerGamePacketListenerImpl.this.player.getInventory().getSelected().getItem() != origItem)) { -+ entity.getBukkitEntity().update(ServerGamePacketListenerImpl.this.player); ++ entity.resendPossiblyDesyncedEntityData(ServerGamePacketListenerImpl.this.player); // Paper - The entire mob gets deleted, so resend it + ServerGamePacketListenerImpl.this.player.containerMenu.sendAllDataToRemote(); + } + @@ -1961,7 +1960,7 @@ } } -@@ -1755,19 +2863,20 @@ +@@ -1755,19 +2864,20 @@ @Override public void onInteraction(InteractionHand hand) { @@ -1985,7 +1984,7 @@ label23: { if (entity instanceof AbstractArrow) { -@@ -1785,17 +2894,41 @@ +@@ -1785,17 +2895,41 @@ } ServerGamePacketListenerImpl.this.player.attack(entity); @@ -2028,7 +2027,7 @@ } } -@@ -1809,7 +2942,7 @@ +@@ -1809,7 +2943,7 @@ case PERFORM_RESPAWN: if (this.player.wonGame) { this.player.wonGame = false; @@ -2037,7 +2036,7 @@ this.resetPosition(); CriteriaTriggers.CHANGED_DIMENSION.trigger(this.player, Level.END, Level.OVERWORLD); } else { -@@ -1817,11 +2950,11 @@ +@@ -1817,11 +2951,11 @@ return; } @@ -2052,7 +2051,7 @@ } } break; -@@ -1833,16 +2966,27 @@ +@@ -1833,16 +2967,27 @@ @Override public void handleContainerClose(ServerboundContainerClosePacket packet) { @@ -2082,7 +2081,7 @@ this.player.containerMenu.sendAllDataToRemote(); } else if (!this.player.containerMenu.stillValid(this.player)) { ServerGamePacketListenerImpl.LOGGER.debug("Player {} interacted with invalid menu {}", this.player, this.player.containerMenu); -@@ -1855,7 +2999,315 @@ +@@ -1855,7 +3000,315 @@ boolean flag = packet.getStateId() != this.player.containerMenu.getStateId(); this.player.containerMenu.suppressRemoteUpdates(); @@ -2399,7 +2398,7 @@ ObjectIterator objectiterator = Int2ObjectMaps.fastIterable(packet.getChangedSlots()).iterator(); while (objectiterator.hasNext()) { -@@ -1879,6 +3331,14 @@ +@@ -1879,6 +3332,14 @@ @Override public void handlePlaceRecipe(ServerboundPlaceRecipePacket packet) { @@ -2414,7 +2413,7 @@ PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); this.player.resetLastActionTime(); if (!this.player.isSpectator() && this.player.containerMenu.containerId == packet.containerId()) { -@@ -1900,9 +3360,43 @@ +@@ -1900,9 +3361,43 @@ ServerGamePacketListenerImpl.LOGGER.debug("Player {} tried to place impossible recipe {}", this.player, recipeholder.id().location()); return; } @@ -2459,7 +2458,7 @@ if (containerrecipebook_a == RecipeBookMenu.PostPlaceAction.PLACE_GHOST_RECIPE) { this.player.connection.send(new ClientboundPlaceGhostRecipePacket(this.player.containerMenu.containerId, craftingmanager_d.display().display())); } -@@ -1917,6 +3411,7 @@ +@@ -1917,6 +3412,7 @@ @Override public void handleContainerButtonClick(ServerboundContainerButtonClickPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); @@ -2467,7 +2466,7 @@ this.player.resetLastActionTime(); if (this.player.containerMenu.containerId == packet.containerId() && !this.player.isSpectator()) { if (!this.player.containerMenu.stillValid(this.player)) { -@@ -1945,7 +3440,44 @@ +@@ -1945,7 +3441,44 @@ boolean flag1 = packet.slotNum() >= 1 && packet.slotNum() <= 45; boolean flag2 = itemstack.isEmpty() || itemstack.getCount() <= itemstack.getMaxStackSize(); @@ -2512,7 +2511,7 @@ if (flag1 && flag2) { this.player.inventoryMenu.getSlot(packet.slotNum()).setByPlayer(itemstack); this.player.inventoryMenu.setRemoteSlot(packet.slotNum(), itemstack); -@@ -1964,7 +3496,19 @@ +@@ -1964,7 +3497,19 @@ @Override public void handleSignUpdate(ServerboundSignUpdatePacket packet) { @@ -2533,7 +2532,7 @@ this.filterTextPacket(list).thenAcceptAsync((list1) -> { this.updateSignText(packet, list1); -@@ -1972,6 +3516,7 @@ +@@ -1972,6 +3517,7 @@ } private void updateSignText(ServerboundSignUpdatePacket packet, List signText) { @@ -2541,7 +2540,7 @@ this.player.resetLastActionTime(); ServerLevel worldserver = this.player.serverLevel(); BlockPos blockposition = packet.getPos(); -@@ -1993,15 +3538,33 @@ +@@ -1993,15 +3539,33 @@ @Override public void handlePlayerAbilities(ServerboundPlayerAbilitiesPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); @@ -2576,7 +2575,7 @@ if (this.player.isModelPartShown(PlayerModelPart.HAT) != flag) { this.server.getPlayerList().broadcastAll(new ClientboundPlayerInfoUpdatePacket(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_HAT, this.player)); } -@@ -2012,7 +3575,7 @@ +@@ -2012,7 +3576,7 @@ public void handleChangeDifficulty(ServerboundChangeDifficultyPacket packet) { PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); if (this.player.hasPermissions(2) || this.isSingleplayerOwner()) { @@ -2585,7 +2584,7 @@ } } -@@ -2033,7 +3596,7 @@ +@@ -2033,7 +3597,7 @@ if (!Objects.equals(profilepublickey_a, profilepublickey_a1)) { if (profilepublickey_a != null && profilepublickey_a1.expiresAt().isBefore(profilepublickey_a.expiresAt())) { @@ -2594,7 +2593,7 @@ } else { try { SignatureValidator signaturevalidator = this.server.getProfileKeySignatureValidator(); -@@ -2045,8 +3608,8 @@ +@@ -2045,8 +3609,8 @@ this.resetPlayerChatState(remotechatsession_a.validate(this.player.getGameProfile(), signaturevalidator)); } catch (ProfilePublicKey.ValidationException profilepublickey_b) { @@ -2605,7 +2604,7 @@ } } -@@ -2058,7 +3621,7 @@ +@@ -2058,7 +3622,7 @@ if (!this.waitingForSwitchToConfig) { throw new IllegalStateException("Client acknowledged config, but none was requested"); } else { @@ -2614,7 +2613,7 @@ } } -@@ -2076,15 +3639,18 @@ +@@ -2076,15 +3640,18 @@ private void resetPlayerChatState(RemoteChatSession session) { this.chatSession = session; @@ -2636,7 +2635,7 @@ @Override public void handleClientTickEnd(ServerboundClientTickEndPacket packet) { -@@ -2115,4 +3681,17 @@ +@@ -2115,4 +3682,17 @@ InteractionResult run(ServerPlayer player, Entity entity, InteractionHand hand); } diff --git a/paper-server/patches/sources/net/minecraft/server/players/PlayerList.java.patch b/paper-server/patches/sources/net/minecraft/server/players/PlayerList.java.patch index 93555e74c6..06288fc4a0 100644 --- a/paper-server/patches/sources/net/minecraft/server/players/PlayerList.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/players/PlayerList.java.patch @@ -313,7 +313,7 @@ + ((ServerLevel)player.level()).getChunkSource().chunkMap.addEntity(player); // Paper - Fire PlayerJoinEvent when Player is actually ready; track entity now + // CraftBukkit end + -+ player.refreshEntityData(player); // CraftBukkit - BungeeCord#2321, send complete data to self on spawn ++ //player.refreshEntityData(player); // CraftBukkit - BungeeCord#2321, send complete data to self on spawn // Paper - THIS IS NOT NEEDED ANYMORE + + this.sendLevelInfo(player, worldserver1); + @@ -842,7 +842,24 @@ return entityplayer1; } -@@ -516,15 +924,32 @@ +@@ -505,26 +913,48 @@ + } + + public void sendActiveEffects(LivingEntity entity, ServerGamePacketListenerImpl networkHandler) { ++ // Paper start - collect packets ++ this.sendActiveEffects(entity, networkHandler::send); ++ } ++ public void sendActiveEffects(LivingEntity entity, java.util.function.Consumer> packetConsumer) { ++ // Paper end - collect packets + Iterator iterator = entity.getActiveEffects().iterator(); + + while (iterator.hasNext()) { + MobEffectInstance mobeffect = (MobEffectInstance) iterator.next(); + +- networkHandler.send(new ClientboundUpdateMobEffectPacket(entity.getId(), mobeffect, false)); ++ packetConsumer.accept(new ClientboundUpdateMobEffectPacket(entity.getId(), mobeffect, false)); // Paper - collect packets + } + } public void sendPlayerPermissionLevel(ServerPlayer player) { @@ -877,14 +894,10 @@ this.sendAllPlayerInfoIn = 0; } -@@ -537,9 +962,28 @@ - ServerPlayer entityplayer = (ServerPlayer) iterator.next(); +@@ -541,6 +971,25 @@ + + } - entityplayer.connection.send(packet); -+ } -+ -+ } -+ + // CraftBukkit start - add a world/entity limited version + public void broadcastAll(Packet packet, net.minecraft.world.entity.player.Player entityhuman) { + for (int i = 0; i < this.players.size(); ++i) { @@ -899,14 +912,15 @@ + public void broadcastAll(Packet packet, Level world) { + for (int i = 0; i < world.players().size(); ++i) { + ((ServerPlayer) world.players().get(i)).connection.send(packet); - } - - } ++ } ++ ++ } + // CraftBukkit end - ++ public void broadcastAll(Packet packet, ResourceKey dimension) { Iterator iterator = this.players.iterator(); -@@ -554,7 +998,7 @@ + +@@ -554,7 +1003,7 @@ } @@ -915,7 +929,7 @@ PlayerTeam scoreboardteam = source.getTeam(); if (scoreboardteam != null) { -@@ -573,7 +1017,7 @@ +@@ -573,7 +1022,7 @@ } } @@ -924,7 +938,7 @@ PlayerTeam scoreboardteam = source.getTeam(); if (scoreboardteam == null) { -@@ -619,7 +1063,7 @@ +@@ -619,7 +1068,7 @@ } public void deop(GameProfile profile) { @@ -933,7 +947,7 @@ ServerPlayer entityplayer = this.getPlayer(profile.getId()); if (entityplayer != null) { -@@ -629,6 +1073,11 @@ +@@ -629,6 +1078,11 @@ } private void sendPlayerPermissionLevel(ServerPlayer player, int permissionLevel) { @@ -945,7 +959,7 @@ if (player.connection != null) { byte b0; -@@ -643,36 +1092,53 @@ +@@ -643,36 +1097,53 @@ player.connection.send(new ClientboundEntityEventPacket(player, b0)); } @@ -1012,7 +1026,7 @@ if (entityplayer != player && entityplayer.level().dimension() == worldKey) { double d4 = x - entityplayer.getX(); double d5 = y - entityplayer.getY(); -@@ -687,10 +1153,12 @@ +@@ -687,10 +1158,12 @@ } public void saveAll() { @@ -1025,7 +1039,7 @@ } public UserWhiteList getWhiteList() { -@@ -712,15 +1180,19 @@ +@@ -712,15 +1185,19 @@ public void reloadWhiteList() {} public void sendLevelInfo(ServerPlayer player, ServerLevel world) { @@ -1049,7 +1063,7 @@ } player.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.LEVEL_CHUNKS_LOAD_START, 0.0F)); -@@ -729,8 +1201,16 @@ +@@ -729,8 +1206,16 @@ public void sendAllPlayerInfo(ServerPlayer player) { player.inventoryMenu.sendAllDataToRemote(); @@ -1067,7 +1081,7 @@ } public int getPlayerCount() { -@@ -746,6 +1226,7 @@ +@@ -746,6 +1231,7 @@ } public void setUsingWhiteList(boolean whitelistEnabled) { @@ -1075,7 +1089,7 @@ this.doWhiteList = whitelistEnabled; } -@@ -786,11 +1267,35 @@ +@@ -786,11 +1272,35 @@ } public void removeAll() { @@ -1113,7 +1127,7 @@ public void broadcastSystemMessage(Component message, boolean overlay) { this.broadcastSystemMessage(message, (entityplayer) -> { -@@ -819,24 +1324,43 @@ +@@ -819,24 +1329,43 @@ } public void broadcastChatMessage(PlayerChatMessage message, ServerPlayer sender, ChatType.Bound params) { @@ -1160,7 +1174,7 @@ } if (flag1 && sender != null) { -@@ -845,20 +1369,27 @@ +@@ -845,20 +1374,27 @@ } @@ -1193,7 +1207,7 @@ Path path = file2.toPath(); if (FileUtil.isPathNormalized(path) && FileUtil.isPathPortable(path) && path.startsWith(file.getPath()) && file2.isFile()) { -@@ -867,7 +1398,7 @@ +@@ -867,7 +1403,7 @@ } serverstatisticmanager = new ServerStatsCounter(this.server, file1); @@ -1202,7 +1216,7 @@ } return serverstatisticmanager; -@@ -875,13 +1406,13 @@ +@@ -875,13 +1411,13 @@ public PlayerAdvancements getPlayerAdvancements(ServerPlayer player) { UUID uuid = player.getUUID(); @@ -1218,7 +1232,7 @@ } advancementdataplayer.setPlayer(player); -@@ -932,15 +1463,39 @@ +@@ -932,15 +1468,39 @@ } public void reloadResources() { 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 f288f27212..a274089f56 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 @@ -302,7 +302,7 @@ } public boolean removeTag(String tag) { -@@ -362,20 +576,36 @@ +@@ -362,20 +576,68 @@ } public void kill(ServerLevel world) { @@ -330,18 +330,50 @@ + // CraftBukkit start + public void refreshEntityData(ServerPlayer to) { -+ List> list = this.getEntityData().getNonDefaultValues(); ++ List> list = this.entityData.packAll(); // Paper - Update EVERYTHING not just not default + -+ if (list != null) { ++ if (list != null && to.getBukkitEntity().canSee(this.getBukkitEntity())) { // Paper + to.connection.send(new ClientboundSetEntityDataPacket(this.getId(), list)); + } + } + // CraftBukkit end ++ // Paper start ++ // This method should only be used if the data of an entity could have become desynced ++ // due to interactions on the client. ++ public void resendPossiblyDesyncedEntityData(net.minecraft.server.level.ServerPlayer player) { ++ if (player.getBukkitEntity().canSee(this.getBukkitEntity())) { ++ ServerLevel world = (net.minecraft.server.level.ServerLevel)this.level(); ++ net.minecraft.server.level.ChunkMap.TrackedEntity tracker = world == null ? null : world.getChunkSource().chunkMap.entityMap.get(this.getId()); ++ if (tracker == null) { ++ return; ++ } ++ final net.minecraft.server.level.ServerEntity serverEntity = tracker.serverEntity; ++ final List> list = new java.util.ArrayList<>(); ++ serverEntity.sendPairingData(player, list::add); ++ player.connection.send(new net.minecraft.network.protocol.game.ClientboundBundlePacket(list)); ++ } ++ } ++ ++ // This method allows you to specifically resend certain data accessor keys to the client ++ public void resendPossiblyDesyncedDataValues(List> keys, ServerPlayer to) { ++ if (!to.getBukkitEntity().canSee(this.getBukkitEntity())) { ++ return; ++ } ++ ++ final List> values = new java.util.ArrayList<>(keys.size()); ++ for (final EntityDataAccessor key : keys) { ++ final SynchedEntityData.DataItem synchedValue = this.entityData.getItem(key); ++ values.add(synchedValue.value()); ++ } ++ ++ to.connection.send(new ClientboundSetEntityDataPacket(this.id, values)); ++ } ++ // Paper end + public boolean equals(Object object) { return object instanceof Entity ? ((Entity) object).id == this.id : false; } -@@ -385,22 +615,39 @@ +@@ -385,22 +647,39 @@ } public void remove(Entity.RemovalReason reason) { @@ -386,7 +418,7 @@ return this.getPose() == pose; } -@@ -417,6 +664,33 @@ +@@ -417,6 +696,33 @@ } public void setRot(float yaw, float pitch) { @@ -420,7 +452,7 @@ this.setYRot(yaw % 360.0F); this.setXRot(pitch % 360.0F); } -@@ -426,8 +700,8 @@ +@@ -426,8 +732,8 @@ } public void setPos(double x, double y, double z) { @@ -431,7 +463,7 @@ } protected final AABB makeBoundingBox() { -@@ -459,13 +733,29 @@ +@@ -459,13 +765,29 @@ } public void tick() { @@ -461,7 +493,7 @@ this.inBlockState = null; if (this.isPassenger() && this.getVehicle().isRemoved()) { this.stopRiding(); -@@ -475,7 +765,7 @@ +@@ -475,7 +797,7 @@ --this.boardingCooldown; } @@ -470,7 +502,7 @@ if (this.canSpawnSprintParticle()) { this.spawnSprintParticle(); } -@@ -502,7 +792,7 @@ +@@ -502,7 +824,7 @@ this.setRemainingFireTicks(this.remainingFireTicks - 1); } @@ -479,7 +511,7 @@ this.setTicksFrozen(0); this.level().levelEvent((Player) null, 1009, this.blockPosition, 1); } -@@ -514,6 +804,10 @@ +@@ -514,6 +836,10 @@ if (this.isInLava()) { this.lavaHurt(); this.fallDistance *= 0.5F; @@ -490,7 +522,7 @@ } this.checkBelowWorld(); -@@ -525,7 +819,7 @@ +@@ -525,7 +851,7 @@ world = this.level(); if (world instanceof ServerLevel worldserver) { if (this instanceof Leashable) { @@ -499,7 +531,7 @@ } } -@@ -537,7 +831,12 @@ +@@ -537,7 +863,12 @@ } public void checkBelowWorld() { @@ -513,7 +545,7 @@ this.onBelowWorld(); } -@@ -568,15 +867,32 @@ +@@ -568,15 +899,32 @@ public void lavaHurt() { if (!this.fireImmune()) { @@ -548,7 +580,7 @@ } } -@@ -587,9 +903,25 @@ +@@ -587,9 +935,25 @@ } public final void igniteForSeconds(float seconds) { @@ -575,7 +607,7 @@ public void igniteForTicks(int ticks) { if (this.remainingFireTicks < ticks) { this.setRemainingFireTicks(ticks); -@@ -610,7 +942,7 @@ +@@ -610,7 +974,7 @@ } protected void onBelowWorld() { @@ -584,7 +616,7 @@ } public boolean isFree(double offsetX, double offsetY, double offsetZ) { -@@ -672,6 +1004,7 @@ +@@ -672,6 +1036,7 @@ } public void move(MoverType type, Vec3 movement) { @@ -592,10 +624,13 @@ if (this.noPhysics) { this.setPos(this.getX() + movement.x, this.getY() + movement.y, this.getZ() + movement.z); } else { -@@ -750,6 +1083,28 @@ - } - } +@@ -747,8 +1112,30 @@ + if (movement.y != vec3d1.y) { + block.updateEntityMovementAfterFallOn(this.level(), this); ++ } ++ } ++ + // CraftBukkit start + if (this.horizontalCollision && this.getBukkitEntity() instanceof Vehicle) { + Vehicle vehicle = (Vehicle) this.getBukkitEntity(); @@ -614,14 +649,13 @@ + if (!bl.getType().isAir()) { + VehicleBlockCollisionEvent event = new VehicleBlockCollisionEvent(vehicle, bl, org.bukkit.craftbukkit.util.CraftVector.toBukkit(originalMovement)); // Paper - Expose pre-collision velocity + this.level.getCraftServer().getPluginManager().callEvent(event); -+ } -+ } + } + } + // CraftBukkit end -+ + if (!this.level().isClientSide() || this.isControlledByLocalInstance()) { Entity.MovementEmission entity_movementemission = this.getMovementEmission(); - -@@ -913,7 +1268,7 @@ +@@ -913,7 +1300,7 @@ } protected BlockPos getOnPos(float offset) { @@ -630,12 +664,10 @@ BlockPos blockposition = (BlockPos) this.mainSupportingBlockPos.get(); if (offset <= 1.0E-5F) { -@@ -1131,7 +1486,21 @@ - - protected SoundEvent getSwimHighSpeedSplashSound() { +@@ -1133,6 +1520,20 @@ return SoundEvents.GENERIC_SPLASH; -+ } -+ + } + + // CraftBukkit start - Add delegate methods + public SoundEvent getSwimSound0() { + return this.getSwimSound(); @@ -647,12 +679,13 @@ + + public SoundEvent getSwimHighSpeedSplashSound0() { + return this.getSwimHighSpeedSplashSound(); - } ++ } + // CraftBukkit end - ++ public void recordMovementThroughBlocks(Vec3 oldPos, Vec3 newPos) { this.movementThisTick.add(new Entity.Movement(oldPos, newPos)); -@@ -1599,6 +1968,7 @@ + } +@@ -1599,6 +2000,7 @@ this.setXRot(Mth.clamp(pitch, -90.0F, 90.0F) % 360.0F); this.yRotO = this.getYRot(); this.xRotO = this.getXRot(); @@ -660,7 +693,7 @@ } public void absMoveTo(double x, double y, double z) { -@@ -1609,6 +1979,7 @@ +@@ -1609,6 +2011,7 @@ this.yo = y; this.zo = d4; this.setPos(d3, y, d4); @@ -668,7 +701,7 @@ } public void moveTo(Vec3 pos) { -@@ -1628,11 +1999,19 @@ +@@ -1628,11 +2031,19 @@ } public void moveTo(double x, double y, double z, float yaw, float pitch) { @@ -688,7 +721,7 @@ } public final void setOldPosAndRot() { -@@ -1701,6 +2080,7 @@ +@@ -1701,6 +2112,7 @@ public void push(Entity entity) { if (!this.isPassengerOfSameVehicle(entity)) { if (!entity.noPhysics && !this.noPhysics) { @@ -696,7 +729,7 @@ double d0 = entity.getX() - this.getX(); double d1 = entity.getZ() - this.getZ(); double d2 = Mth.absMax(d0, d1); -@@ -1737,7 +2117,21 @@ +@@ -1737,7 +2149,21 @@ } public void push(double deltaX, double deltaY, double deltaZ) { @@ -719,7 +752,7 @@ this.hasImpulse = true; } -@@ -1858,9 +2252,21 @@ +@@ -1858,9 +2284,21 @@ } public boolean isPushable() { @@ -741,7 +774,7 @@ public void awardKillScore(Entity entityKilled, DamageSource damageSource) { if (entityKilled instanceof ServerPlayer) { CriteriaTriggers.ENTITY_KILLED_PLAYER.trigger((ServerPlayer) entityKilled, this, damageSource); -@@ -1889,74 +2295,133 @@ +@@ -1889,74 +2327,133 @@ } public boolean saveAsPassenger(CompoundTag nbt) { @@ -898,7 +931,7 @@ } ListTag nbttaglist; -@@ -1972,10 +2437,10 @@ +@@ -1972,10 +2469,10 @@ nbttaglist.add(StringTag.valueOf(s)); } @@ -911,7 +944,7 @@ if (this.isVehicle()) { nbttaglist = new ListTag(); iterator = this.getPassengers().iterator(); -@@ -1984,17 +2449,44 @@ +@@ -1984,17 +2481,44 @@ Entity entity = (Entity) iterator.next(); CompoundTag nbttagcompound1 = new CompoundTag(); @@ -959,17 +992,16 @@ } catch (Throwable throwable) { CrashReport crashreport = CrashReport.forThrowable(throwable, "Saving entity NBT"); CrashReportCategory crashreportsystemdetails = crashreport.addCategory("Entity being saved"); -@@ -2079,7 +2571,72 @@ - } +@@ -2080,6 +2604,71 @@ } else { throw new IllegalStateException("Entity has invalid position"); -+ } + } + + // CraftBukkit start + // Spigot start + if (this instanceof net.minecraft.world.entity.LivingEntity) { + this.tickCount = nbt.getInt("Spigot.ticksLived"); - } ++ } + // Spigot end + this.persist = !nbt.contains("Bukkit.persist") || nbt.getBoolean("Bukkit.persist"); + this.visibleByDefault = !nbt.contains("Bukkit.visibleByDefault") || nbt.getBoolean("Bukkit.visibleByDefault"); @@ -1032,20 +1064,21 @@ } catch (Throwable throwable) { CrashReport crashreport = CrashReport.forThrowable(throwable, "Loading entity NBT"); CrashReportCategory crashreportsystemdetails = crashreport.addCategory("Entity being loaded"); -@@ -2101,6 +2658,12 @@ - return entitytypes.canSerialize() && minecraftkey != null ? minecraftkey.toString() : null; - } +@@ -2099,7 +2688,13 @@ + ResourceLocation minecraftkey = EntityType.getKey(entitytypes); + return entitytypes.canSerialize() && minecraftkey != null ? minecraftkey.toString() : null; ++ } ++ + // CraftBukkit start - allow excluding certain data when saving + protected void addAdditionalSaveData(CompoundTag nbttagcompound, boolean includeAll) { + this.addAdditionalSaveData(nbttagcompound); -+ } + } + // CraftBukkit end -+ + protected abstract void readAdditionalSaveData(CompoundTag nbt); - protected abstract void addAdditionalSaveData(CompoundTag nbt); -@@ -2150,12 +2713,60 @@ +@@ -2150,12 +2745,60 @@ @Nullable public ItemEntity spawnAtLocation(ServerLevel world, ItemStack stack, float yOffset) { @@ -1108,7 +1141,7 @@ world.addFreshEntity(entityitem); return entityitem; } -@@ -2184,7 +2795,16 @@ +@@ -2184,7 +2827,16 @@ if (this.isAlive() && this instanceof Leashable leashable) { if (leashable.getLeashHolder() == player) { if (!this.level().isClientSide()) { @@ -1126,7 +1159,7 @@ leashable.removeLeash(); } else { leashable.dropLeash(); -@@ -2200,6 +2820,14 @@ +@@ -2200,6 +2852,14 @@ if (itemstack.is(Items.LEAD) && leashable.canHaveALeashAttachedToIt()) { if (!this.level().isClientSide()) { @@ -1141,7 +1174,7 @@ leashable.setLeashedTo(player, true); } -@@ -2265,15 +2893,15 @@ +@@ -2265,15 +2925,15 @@ } public boolean showVehicleHealth() { @@ -1160,7 +1193,7 @@ return false; } else { for (Entity entity1 = entity; entity1.vehicle != null; entity1 = entity1.vehicle) { -@@ -2285,11 +2913,32 @@ +@@ -2285,11 +2945,32 @@ if (!force && (!this.canRide(entity) || !entity.canAddPassenger(this))) { return false; } else { @@ -1194,7 +1227,7 @@ this.vehicle = entity; this.vehicle.addPassenger(this); entity.getIndirectPassengersStream().filter((entity2) -> { -@@ -2314,19 +2963,30 @@ +@@ -2314,19 +2995,30 @@ } public void removeVehicle() { @@ -1227,7 +1260,7 @@ protected void addPassenger(Entity passenger) { if (passenger.getVehicle() != this) { throw new IllegalStateException("Use x.startRiding(y), not y.addPassenger(x)"); -@@ -2349,21 +3009,53 @@ +@@ -2349,21 +3041,53 @@ } } @@ -1287,7 +1320,7 @@ } protected boolean canAddPassenger(Entity passenger) { -@@ -2464,7 +3156,7 @@ +@@ -2464,7 +3188,7 @@ if (teleporttransition != null) { ServerLevel worldserver1 = teleporttransition.newLevel(); @@ -1296,7 +1329,7 @@ this.teleport(teleporttransition); } } -@@ -2547,7 +3239,7 @@ +@@ -2547,7 +3271,7 @@ } public boolean isCrouching() { @@ -1305,7 +1338,7 @@ } public boolean isSprinting() { -@@ -2563,7 +3255,7 @@ +@@ -2563,7 +3287,7 @@ } public boolean isVisuallySwimming() { @@ -1314,7 +1347,7 @@ } public boolean isVisuallyCrawling() { -@@ -2571,6 +3263,13 @@ +@@ -2571,6 +3295,13 @@ } public void setSwimming(boolean swimming) { @@ -1328,7 +1361,7 @@ this.setSharedFlag(4, swimming); } -@@ -2609,6 +3308,7 @@ +@@ -2609,6 +3340,7 @@ @Nullable public PlayerTeam getTeam() { @@ -1336,7 +1369,7 @@ return this.level().getScoreboard().getPlayersTeam(this.getScoreboardName()); } -@@ -2624,8 +3324,12 @@ +@@ -2624,8 +3356,12 @@ return this.getTeam() != null ? this.getTeam().isAlliedTo(team) : false; } @@ -1350,7 +1383,7 @@ } public boolean getSharedFlag(int index) { -@@ -2644,7 +3348,7 @@ +@@ -2644,7 +3380,7 @@ } public int getMaxAirSupply() { @@ -1359,7 +1392,7 @@ } public int getAirSupply() { -@@ -2652,7 +3356,18 @@ +@@ -2652,7 +3388,18 @@ } public void setAirSupply(int air) { @@ -1379,7 +1412,7 @@ } public int getTicksFrozen() { -@@ -2679,11 +3394,44 @@ +@@ -2679,11 +3426,44 @@ public void thunderHit(ServerLevel world, LightningBolt lightning) { this.setRemainingFireTicks(this.remainingFireTicks + 1); @@ -1426,7 +1459,7 @@ } public void onAboveBubbleCol(boolean drag) { -@@ -2713,7 +3461,7 @@ +@@ -2713,7 +3493,7 @@ this.resetFallDistance(); } @@ -1435,7 +1468,7 @@ return true; } -@@ -2818,7 +3566,7 @@ +@@ -2818,7 +3598,7 @@ public String toString() { String s = this.level() == null ? "~NULL~" : this.level().toString(); @@ -1444,7 +1477,7 @@ } public final boolean isInvulnerableToBase(DamageSource damageSource) { -@@ -2838,6 +3586,13 @@ +@@ -2838,6 +3618,13 @@ } public void restoreFrom(Entity original) { @@ -1458,7 +1491,7 @@ CompoundTag nbttagcompound = original.saveWithoutId(new CompoundTag()); nbttagcompound.remove("Dimension"); -@@ -2850,8 +3605,57 @@ +@@ -2850,8 +3637,57 @@ public Entity teleport(TeleportTransition teleportTarget) { Level world = this.level(); @@ -1516,7 +1549,7 @@ ServerLevel worldserver1 = teleportTarget.newLevel(); boolean flag = worldserver1.dimension() != worldserver.dimension(); -@@ -2918,10 +3722,19 @@ +@@ -2918,10 +3754,19 @@ gameprofilerfiller.pop(); return null; } else { @@ -1537,7 +1570,7 @@ Iterator iterator1 = list1.iterator(); while (iterator1.hasNext()) { -@@ -2947,7 +3760,7 @@ +@@ -2947,7 +3792,7 @@ } private void sendTeleportTransitionToRidingPlayers(TeleportTransition teleportTarget) { @@ -1546,7 +1579,7 @@ Iterator iterator = this.getIndirectPassengers().iterator(); while (iterator.hasNext()) { -@@ -2995,9 +3808,17 @@ +@@ -2995,9 +3840,17 @@ } protected void removeAfterChangingDimensions() { @@ -1567,10 +1600,11 @@ } } -@@ -3006,11 +3827,34 @@ +@@ -3005,12 +3858,35 @@ + public Vec3 getRelativePortalPosition(Direction.Axis portalAxis, BlockUtil.FoundRectangle portalRect) { return PortalShape.getRelativePosition(portalRect, portalAxis, this.position(), this.getDimensions(this.getPose())); } - ++ + // CraftBukkit start + public CraftPortalEvent callPortalEvent(Entity entity, Location exit, PlayerTeleportEvent.TeleportCause cause, int searchRadius, int creationRadius) { + org.bukkit.entity.Entity bukkitEntity = entity.getBukkitEntity(); @@ -1592,7 +1626,7 @@ + return new CraftPortalEvent(event); + } + // CraftBukkit end -+ + public boolean canUsePortal(boolean allowVehicles) { return (allowVehicles || !this.isPassenger()) && this.isAlive(); } @@ -1602,7 +1636,7 @@ if (from.dimension() == Level.END && to.dimension() == Level.OVERWORLD) { Iterator iterator = this.getPassengers().iterator(); -@@ -3134,10 +3978,16 @@ +@@ -3134,10 +4010,16 @@ return (Boolean) this.entityData.get(Entity.DATA_CUSTOM_NAME_VISIBLE); } @@ -1622,7 +1656,7 @@ return entity != null; } -@@ -3187,7 +4037,7 @@ +@@ -3187,7 +4069,7 @@ /** @deprecated */ @Deprecated protected void fixupDimensions() { @@ -1631,7 +1665,7 @@ EntityDimensions entitysize = this.getDimensions(entitypose); this.dimensions = entitysize; -@@ -3196,7 +4046,7 @@ +@@ -3196,7 +4078,7 @@ public void refreshDimensions() { EntityDimensions entitysize = this.dimensions; @@ -1640,7 +1674,7 @@ EntityDimensions entitysize1 = this.getDimensions(entitypose); this.dimensions = entitysize1; -@@ -3258,10 +4108,29 @@ +@@ -3258,10 +4140,29 @@ } public final void setBoundingBox(AABB boundingBox) { @@ -1672,7 +1706,7 @@ return this.getDimensions(pose).eyeHeight(); } -@@ -3300,7 +4169,14 @@ +@@ -3300,7 +4201,14 @@ public void startSeenByPlayer(ServerPlayer player) {} @@ -1688,7 +1722,7 @@ public float rotate(Rotation rotation) { float f = Mth.wrapDegrees(this.getYRot()); -@@ -3335,7 +4211,7 @@ +@@ -3335,7 +4243,7 @@ } @Nullable @@ -1697,7 +1731,7 @@ return null; } -@@ -3373,20 +4249,34 @@ +@@ -3373,20 +4281,34 @@ } private Stream getIndirectPassengersStream() { @@ -1732,7 +1766,7 @@ return () -> { return this.getIndirectPassengersStream().iterator(); }; -@@ -3399,6 +4289,7 @@ +@@ -3399,6 +4321,7 @@ } public boolean hasExactlyOnePlayerPassenger() { @@ -1740,7 +1774,7 @@ return this.countPlayerPassengers() == 1; } -@@ -3435,7 +4326,7 @@ +@@ -3435,7 +4358,7 @@ } public boolean isControlledByLocalInstance() { @@ -1749,7 +1783,7 @@ if (entityliving instanceof Player entityhuman) { return entityhuman.isLocalPlayer(); -@@ -3445,7 +4336,7 @@ +@@ -3445,7 +4368,7 @@ } public boolean isControlledByClient() { @@ -1758,7 +1792,7 @@ return entityliving != null && entityliving.isControlledByClient(); } -@@ -3463,7 +4354,7 @@ +@@ -3463,7 +4386,7 @@ return new Vec3((double) f1 * d2 / (double) f3, 0.0D, (double) f2 * d2 / (double) f3); } @@ -1767,7 +1801,7 @@ return new Vec3(this.getX(), this.getBoundingBox().maxY, this.getZ()); } -@@ -3488,9 +4379,38 @@ +@@ -3488,9 +4411,38 @@ public int getFireImmuneTicks() { return 1; } @@ -1778,12 +1812,12 @@ + @Override + public void sendSystemMessage(Component message) { + } -+ + + @Override + public CommandSender getBukkitSender(CommandSourceStack wrapper) { + return Entity.this.getBukkitEntity(); + } - ++ + @Override + public boolean acceptsSuccess() { + return ((ServerLevel) Entity.this.level()).getGameRules().getBoolean(GameRules.RULE_SENDCOMMANDFEEDBACK); @@ -1807,7 +1841,7 @@ } public void lookAt(EntityAnchorArgument.Anchor anchorPoint, Vec3 target) { -@@ -3551,6 +4471,11 @@ +@@ -3551,6 +4503,11 @@ vec3d = vec3d.add(vec3d1); ++k1; } @@ -1819,7 +1853,7 @@ } } } -@@ -3613,7 +4538,7 @@ +@@ -3613,7 +4570,7 @@ return new ClientboundAddEntityPacket(this, entityTrackerEntry); } @@ -1828,16 +1862,17 @@ return this.type.getDimensions(); } -@@ -3714,7 +4639,39 @@ +@@ -3713,8 +4670,40 @@ + public double getRandomZ(double widthScale) { return this.getZ((2.0D * this.random.nextDouble() - 1.0D) * widthScale); } - ++ + // Paper start - Block invalid positions and bounding box + public static boolean checkPosition(Entity entity, double newX, double newY, double newZ) { + if (Double.isFinite(newX) && Double.isFinite(newY) && Double.isFinite(newZ)) { + return true; + } -+ + + String entityInfo; + try { + entityInfo = entity.toString(); @@ -1868,7 +1903,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 +4689,12 @@ +@@ -3732,6 +4721,12 @@ this.levelCallback.onMove(); } @@ -1881,7 +1916,7 @@ } public void checkDespawn() {} -@@ -3818,8 +4781,17 @@ +@@ -3818,8 +4813,17 @@ @Override public final void setRemoved(Entity.RemovalReason reason) { @@ -1900,7 +1935,7 @@ } if (this.removalReason.shouldDestroy()) { -@@ -3827,14 +4799,30 @@ +@@ -3827,14 +4831,30 @@ } this.getPassengers().forEach(Entity::stopRiding); @@ -1933,7 +1968,7 @@ @Override public void setLevelCallback(EntityInLevelCallback changeListener) { this.levelCallback = changeListener; -@@ -3887,7 +4875,7 @@ +@@ -3887,7 +4907,7 @@ } public Vec3 getKnownMovement() { @@ -1942,7 +1977,7 @@ if (entityliving instanceof Player entityhuman) { if (this.isAlive()) { -@@ -3962,4 +4950,14 @@ +@@ -3962,4 +4982,14 @@ void accept(Entity entity, double x, double y, double z); } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/LivingEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/LivingEntity.java.patch index ac25e7f770..c1e602ad26 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/LivingEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/LivingEntity.java.patch @@ -297,7 +297,7 @@ boolean flag = scoreboardteam != null && scoreboard.addPlayerToTeam(this.getStringUUID(), scoreboardteam); if (!flag) { -@@ -806,22 +909,47 @@ +@@ -806,11 +909,13 @@ if (nbt.contains("SleepingX", 99) && nbt.contains("SleepingY", 99) && nbt.contains("SleepingZ", 99)) { BlockPos blockposition = new BlockPos(nbt.getInt("SleepingX"), nbt.getInt("SleepingY"), nbt.getInt("SleepingZ")); @@ -311,11 +311,10 @@ } if (nbt.contains("Brain", 10)) { - this.brain = this.makeBrain(new Dynamic(NbtOps.INSTANCE, nbt.get("Brain"))); -+ } -+ -+ } -+ +@@ -819,9 +924,32 @@ + + } + + // CraftBukkit start + private boolean isTickingEffects = false; + private List effectsToProcess = Lists.newArrayList(); @@ -329,15 +328,15 @@ + private ProcessableEffect(MobEffectInstance effect, EntityPotionEffectEvent.Cause cause) { + this.effect = effect; + this.cause = cause; - } - ++ } ++ + private ProcessableEffect(Holder type, EntityPotionEffectEvent.Cause cause) { + this.type = type; + this.cause = cause; + } - } ++ } + // CraftBukkit end - ++ protected void tickEffects() { Iterator> iterator = this.activeEffects.keySet().iterator(); @@ -765,12 +764,10 @@ this.lastHurtByPlayer = entityhuman1; } else { -@@ -1356,14 +1658,26 @@ - - return null; +@@ -1358,12 +1660,24 @@ } -+ } -+ + } + + // Paper start - only call damage event when actuallyHurt will be called - move out amount computation logic + private float computeAmountFromEntityDamageEvent(final EntityDamageEvent event) { + // Taken from hurt()'s craftbukkit diff. @@ -780,9 +777,9 @@ + amount += (float) event.getDamage(DamageModifier.FREEZING); + amount += (float) event.getDamage(DamageModifier.HARD_HAT); + return amount; - } ++ } + // Paper end - only call damage event when actuallyHurt will be called - move out amount computation logic - ++ protected void blockUsingShield(LivingEntity attacker) { attacker.blockedByShield(this); } @@ -1157,7 +1154,7 @@ + }; + float freezingModifier = freezing.apply((double) f).floatValue(); + f += freezingModifier; -+ + + com.google.common.base.Function hardHat = new com.google.common.base.Function() { + @Override + public Double apply(Double f) { @@ -1265,7 +1262,7 @@ + if (damagesource.is(DamageTypeTags.DAMAGES_HELMET) && !this.getItemBySlot(EquipmentSlot.HEAD).isEmpty()) { + this.hurtHelmet(damagesource, f); + } - ++ + // Apply damage to armor + if (!damagesource.is(DamageTypeTags.BYPASSES_ARMOR)) { + float armorDamage = (float) (event.getDamage() + event.getDamage(DamageModifier.BLOCKING) + event.getDamage(DamageModifier.HARD_HAT)); @@ -1352,27 +1349,26 @@ } public CombatTracker getCombatTracker() { -@@ -1935,9 +2568,19 @@ +@@ -1935,8 +2568,18 @@ } public final void setArrowCount(int stuckArrowCount) { - this.entityData.set(LivingEntity.DATA_ARROW_COUNT_ID, stuckArrowCount); + // CraftBukkit start + this.setArrowCount(stuckArrowCount, false); - } - ++ } ++ + public final void setArrowCount(int i, boolean flag) { + ArrowBodyCountChangeEvent event = CraftEventFactory.callArrowBodyCountChangeEvent(this, this.getArrowCount(), i, flag); + if (event.isCancelled()) { + return; + } + this.entityData.set(LivingEntity.DATA_ARROW_COUNT_ID, event.getNewAmount()); -+ } + } + // CraftBukkit end -+ + public final int getStingerCount() { return (Integer) this.entityData.get(LivingEntity.DATA_STINGER_COUNT_ID); - } @@ -1999,7 +2642,7 @@ this.playSound(soundeffect, this.getSoundVolume(), (this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.0F); } @@ -1515,13 +1511,13 @@ - while (this.getXRot() - this.xRotO < -180.0F) { - this.xRotO -= 360.0F; - } -+ this.yHeadRotO += Math.round((this.yHeadRot - this.yHeadRotO) / 360.0F) * 360.0F; -+ // Paper end - +- - while (this.getXRot() - this.xRotO >= 180.0F) { - this.xRotO += 360.0F; - } -- ++ this.yHeadRotO += Math.round((this.yHeadRot - this.yHeadRotO) / 360.0F) * 360.0F; ++ // Paper end + - while (this.yHeadRot - this.yHeadRotO < -180.0F) { - this.yHeadRotO -= 360.0F; - } @@ -1547,10 +1543,9 @@ } - ItemStack itemstack2 = itemstack1; -- -- itemstack = this.getItemBySlot(enumitemslot); + ItemStack itemstack2 = itemstack1; final ItemStack oldEquipment = itemstack2; // Paper - PlayerArmorChangeEvent - obfhelper -+ + +- itemstack = this.getItemBySlot(enumitemslot); + itemstack = this.getItemBySlot(enumitemslot); final ItemStack newEquipment = itemstack;// Paper - PlayerArmorChangeEvent - obfhelper if (this.equipmentHasChanged(itemstack2, itemstack)) { + // Paper start - PlayerArmorChangeEvent @@ -1761,7 +1756,19 @@ } protected void internalSetAbsorptionAmount(float absorptionAmount) { -@@ -3410,9 +4110,14 @@ +@@ -3367,6 +4067,11 @@ + return ((Byte) this.entityData.get(LivingEntity.DATA_LIVING_ENTITY_FLAGS) & 2) > 0 ? InteractionHand.OFF_HAND : InteractionHand.MAIN_HAND; + } + ++ // Paper start - Properly cancel usable items ++ public void resyncUsingItem(ServerPlayer serverPlayer) { ++ this.resendPossiblyDesyncedDataValues(java.util.List.of(DATA_LIVING_ENTITY_FLAGS), serverPlayer); ++ } ++ // Paper end - Properly cancel usable items + private void updatingUsingItem() { + if (this.isUsingItem()) { + if (ItemStack.isSameItem(this.getItemInHand(this.getUsedItemHand()), this.useItem)) { +@@ -3410,9 +4115,14 @@ } public void startUsingItem(InteractionHand hand) { @@ -1777,7 +1784,7 @@ this.useItem = itemstack; this.useItemRemaining = itemstack.getUseDuration(this); if (!this.level().isClientSide) { -@@ -3483,13 +4188,50 @@ +@@ -3483,13 +4193,50 @@ this.releaseUsingItem(); } else { if (!this.useItem.isEmpty() && this.isUsingItem()) { @@ -1791,7 +1798,7 @@ + org.bukkit.inventory.EquipmentSlot hand = org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(enumhand); + event = new PlayerItemConsumeEvent((Player) this.getBukkitEntity(), craftItem, hand); // Paper + this.level().getCraftServer().getPluginManager().callEvent(event); - ++ + if (event.isCancelled()) { + // Update client + Consumable consumable = this.useItem.get(DataComponents.CONSUMABLE); @@ -1803,7 +1810,7 @@ + this.stopUsingItem(); // Paper - event is using an item, clear active item to reset its use + return; + } -+ + + itemstack = (craftItem.equals(event.getItem())) ? this.useItem.finishUsingItem(this.level(), this) : CraftItemStack.asNMSCopy(event.getItem()).finishUsingItem(this.level(), this); + } else { + itemstack = this.useItem.finishUsingItem(this.level(), this); @@ -1829,7 +1836,7 @@ } } -@@ -3512,6 +4254,7 @@ +@@ -3512,6 +4259,7 @@ public void releaseUsingItem() { if (!this.useItem.isEmpty()) { @@ -1837,7 +1844,7 @@ this.useItem.releaseUsing(this.level(), this, this.getUseItemRemainingTicks()); if (this.useItem.useOnRelease()) { this.updatingUsingItem(); -@@ -3544,12 +4287,69 @@ +@@ -3544,12 +4292,69 @@ if (this.isUsingItem() && !this.useItem.isEmpty()) { Item item = this.useItem.getItem(); @@ -1908,7 +1915,7 @@ public boolean isSuppressingSlidingDownLadder() { return this.isShiftKeyDown(); } -@@ -3568,12 +4368,18 @@ +@@ -3568,12 +4373,18 @@ } public boolean randomTeleport(double x, double y, double z, boolean particleEffects) { @@ -1929,7 +1936,7 @@ Level world = this.level(); if (world.hasChunkAt(blockposition)) { -@@ -3592,18 +4398,43 @@ +@@ -3592,18 +4403,43 @@ } if (flag2) { @@ -1977,7 +1984,7 @@ world.broadcastEntityEvent(this, (byte) 46); } -@@ -3613,7 +4444,7 @@ +@@ -3613,7 +4449,7 @@ entitycreature.getNavigation().stop(); } @@ -1986,7 +1993,7 @@ } } -@@ -3706,7 +4537,7 @@ +@@ -3706,7 +4542,7 @@ } public void stopSleeping() { @@ -1995,7 +2002,7 @@ Level world = this.level(); java.util.Objects.requireNonNull(world); -@@ -3718,9 +4549,9 @@ +@@ -3718,9 +4554,9 @@ this.level().setBlock(blockposition, (BlockState) iblockdata.setValue(BedBlock.OCCUPIED, false), 3); Vec3 vec3d = (Vec3) BedBlock.findStandUpPosition(this.getType(), this.level(), blockposition, enumdirection, this.getYRot()).orElseGet(() -> { @@ -2007,7 +2014,7 @@ }); Vec3 vec3d1 = Vec3.atBottomCenterOf(blockposition).subtract(vec3d).normalize(); float f = (float) Mth.wrapDegrees(Mth.atan2(vec3d1.z, vec3d1.x) * 57.2957763671875D - 90.0D); -@@ -3740,7 +4571,7 @@ +@@ -3740,7 +4576,7 @@ @Nullable public Direction getBedOrientation() { @@ -2016,7 +2023,7 @@ return blockposition != null ? BedBlock.getBedOrientation(this.level(), blockposition) : null; } -@@ -3905,7 +4736,7 @@ +@@ -3905,7 +4741,7 @@ public float maxUpStep() { float f = (float) this.getAttributeValue(Attributes.STEP_HEIGHT); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/animal/Bucketable.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/animal/Bucketable.java.patch index 0776d6fecd..804bc3e693 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/animal/Bucketable.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/animal/Bucketable.java.patch @@ -12,7 +12,7 @@ public interface Bucketable { -@@ -93,10 +98,22 @@ +@@ -93,10 +98,21 @@ ItemStack itemstack = player.getItemInHand(hand); if (itemstack.getItem() == Items.WATER_BUCKET && entity.isAlive()) { @@ -27,8 +27,7 @@ + itemstack1 = CraftItemStack.asNMSCopy(playerBucketFishEvent.getEntityBucket()); + if (playerBucketFishEvent.isCancelled()) { + ((ServerPlayer) player).containerMenu.sendAllDataToRemote(); // We need to update inventory to resync client's bucket -+ entity.getBukkitEntity().update((ServerPlayer) player); // We need to play out these packets as the client assumes the fish is gone -+ entity.refreshEntityData((ServerPlayer) player); // Need to send data such as the display name to client ++ entity.resendPossiblyDesyncedEntityData((ServerPlayer) player); // Paper + return Optional.of(InteractionResult.FAIL); + } + entity.playSound(((Bucketable) entity).getPickupSound(), 1.0F, 1.0F); @@ -36,7 +35,7 @@ ItemStack itemstack2 = ItemUtils.createFilledResult(itemstack, player, itemstack1, false); player.setItemInHand(hand, itemstack2); -@@ -106,7 +123,7 @@ +@@ -106,7 +122,7 @@ CriteriaTriggers.FILLED_BUCKET.trigger((ServerPlayer) player, itemstack1); } diff --git a/paper-server/patches/sources/net/minecraft/world/item/component/Consumable.java.patch b/paper-server/patches/sources/net/minecraft/world/item/component/Consumable.java.patch index 8420f1012f..a163ec68c0 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/component/Consumable.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/component/Consumable.java.patch @@ -33,16 +33,18 @@ }); } -@@ -79,6 +95,15 @@ +@@ -79,6 +95,17 @@ return stack; } + // CraftBukkit start + public void cancelUsingItem(net.minecraft.server.level.ServerPlayer entityplayer, ItemStack itemstack) { ++ final java.util.List> packets = new it.unimi.dsi.fastutil.objects.ObjectArrayList<>(); // Paper - properly resend entities - collect packets for bundle + itemstack.getAllOfType(ConsumableListener.class).forEach((consumablelistener) -> { -+ consumablelistener.cancelUsingItem(entityplayer, itemstack); ++ consumablelistener.cancelUsingItem(entityplayer, itemstack, packets); // Paper - properly resend entities - collect packets for bundle + }); -+ entityplayer.server.getPlayerList().sendActivePlayerEffects(entityplayer); ++ entityplayer.server.getPlayerList().sendActiveEffects(entityplayer, packets::add); // Paper - properly resend entities - collect packets for bundle ++ entityplayer.connection.send(new net.minecraft.network.protocol.game.ClientboundBundlePacket(packets)); + } + // CraftBukkit end + diff --git a/paper-server/patches/sources/net/minecraft/world/item/component/ConsumableListener.java.patch b/paper-server/patches/sources/net/minecraft/world/item/component/ConsumableListener.java.patch index 5d14084425..2e31354cba 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/component/ConsumableListener.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/component/ConsumableListener.java.patch @@ -5,5 +5,5 @@ void onConsume(Level world, LivingEntity user, ItemStack stack, Consumable consumable); + -+ default void cancelUsingItem(net.minecraft.server.level.ServerPlayer entityplayer, ItemStack itemstack) {} // CraftBukkit ++ default void cancelUsingItem(net.minecraft.server.level.ServerPlayer entityplayer, ItemStack itemstack, java.util.List> collectedPackets) {} // CraftBukkit // Paper - properly resend entities - collect packets for bundle } diff --git a/paper-server/patches/sources/net/minecraft/world/item/component/OminousBottleAmplifier.java.patch b/paper-server/patches/sources/net/minecraft/world/item/component/OminousBottleAmplifier.java.patch new file mode 100644 index 0000000000..57338edcbf --- /dev/null +++ b/paper-server/patches/sources/net/minecraft/world/item/component/OminousBottleAmplifier.java.patch @@ -0,0 +1,18 @@ +--- a/net/minecraft/world/item/component/OminousBottleAmplifier.java ++++ b/net/minecraft/world/item/component/OminousBottleAmplifier.java +@@ -28,8 +28,14 @@ + + @Override + public void onConsume(Level world, LivingEntity user, ItemStack stack, Consumable consumable) { +- user.addEffect(new MobEffectInstance(MobEffects.BAD_OMEN, 120000, this.value, false, false, true)); ++ user.addEffect(new MobEffectInstance(MobEffects.BAD_OMEN, 120000, this.value, false, false, true)); // Paper - properly resend entities - diff on change for below + } ++ // Paper start - properly resend entities - collect packets for bundle ++ @Override ++ public void cancelUsingItem(net.minecraft.server.level.ServerPlayer entityplayer, ItemStack itemstack, java.util.List> collectedPackets) { ++ collectedPackets.add(new net.minecraft.network.protocol.game.ClientboundRemoveMobEffectPacket(entityplayer.getId(), MobEffects.BAD_OMEN)); ++ } ++ // Paper end - properly resend entities - collect packets for bundle + + @Override + public void addToTooltip(Item.TooltipContext context, Consumer tooltip, TooltipFlag type) { diff --git a/paper-server/patches/sources/net/minecraft/world/item/component/SuspiciousStewEffects.java.patch b/paper-server/patches/sources/net/minecraft/world/item/component/SuspiciousStewEffects.java.patch index 3578ba2d3a..2836856f9c 100644 --- a/paper-server/patches/sources/net/minecraft/world/item/component/SuspiciousStewEffects.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/item/component/SuspiciousStewEffects.java.patch @@ -15,9 +15,9 @@ + // CraftBukkit start @Override -+ public void cancelUsingItem(net.minecraft.server.level.ServerPlayer entityplayer, ItemStack itemstack) { ++ public void cancelUsingItem(net.minecraft.server.level.ServerPlayer entityplayer, ItemStack itemstack, java.util.List> collectedPackets) { // Paper - properly resend entities - collect packets for bundle + for (SuspiciousStewEffects.Entry suspicioussteweffects_a : this.effects) { -+ entityplayer.connection.send(new net.minecraft.network.protocol.game.ClientboundRemoveMobEffectPacket(entityplayer.getId(), suspicioussteweffects_a.effect())); ++ collectedPackets.add(new net.minecraft.network.protocol.game.ClientboundRemoveMobEffectPacket(entityplayer.getId(), suspicioussteweffects_a.effect())); // Paper - bundlize packets + } + } + // CraftBukkit end 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 7142319f67..b25b10c24a 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 @@ -1008,7 +1008,11 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { return; } - entityTracker.broadcast(this.getHandle().getAddEntityPacket(entityTracker.serverEntity)); + // Paper start - resend possibly desynced entity instead of add entity packet + for (final ServerPlayerConnection connection : entityTracker.seenBy) { + this.getHandle().resendPossiblyDesyncedEntityData(connection.getPlayer()); + } + // Paper end - resend possibly desynced entity instead of add entity packet } public void update(ServerPlayer player) { diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java index f3a9b33802..350ad61ab3 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/entity/CraftItemFrame.java @@ -39,9 +39,11 @@ public class CraftItemFrame extends CraftHanging implements ItemFrame { protected void update() { super.update(); + // Paper start, don't mark as dirty as this is handled in super.update() // mark dirty, so that the client gets updated with item and rotation - this.getHandle().getEntityData().markDirty(net.minecraft.world.entity.decoration.ItemFrame.DATA_ITEM); - this.getHandle().getEntityData().markDirty(net.minecraft.world.entity.decoration.ItemFrame.DATA_ROTATION); + //this.getHandle().getEntityData().markDirty(net.minecraft.world.entity.decoration.ItemFrame.DATA_ITEM); + //this.getHandle().getEntityData().markDirty(net.minecraft.world.entity.decoration.ItemFrame.DATA_ROTATION); + // Paper end // update redstone if (!this.getHandle().generation) {