From 0000000000000000000000000000000000000000 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.

See: https://github.com/PaperMC/Paper/pull/1896

== AT ==
public net.minecraft.server.level.ChunkMap$TrackedEntity serverEntity

diff --git a/src/main/java/net/minecraft/network/syncher/SynchedEntityData.java b/src/main/java/net/minecraft/network/syncher/SynchedEntityData.java
index 755989780c955592e27cae2b4b5f0e90e13543b7..37e193f57938047c8b886ed7d2816411392f94b4 100644
--- a/src/main/java/net/minecraft/network/syncher/SynchedEntityData.java
+++ b/src/main/java/net/minecraft/network/syncher/SynchedEntityData.java
@@ -244,14 +244,46 @@ public class SynchedEntityData {
     // CraftBukkit start
     public void refresh(ServerPlayer to) {
         if (!this.isEmpty()) {
-            List<SynchedEntityData.DataValue<?>> list = this.getNonDefaultValues();
+            List<SynchedEntityData.DataValue<?>> list = this.packAll(); // Paper - Update EVERYTHING not just not default
 
             if (list != null) {
+                if (to.getBukkitEntity().canSee(this.entity.getBukkitEntity())) { // Paper
                 to.connection.send(new ClientboundSetEntityDataPacket(this.entity.getId(), list));
+                } // Paper
             }
         }
     }
     // CraftBukkit end
+    // 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
+    private List<SynchedEntityData.DataValue<?>> packAll() {
+        if (this.isEmpty()) {
+            return null;
+        }
+
+        List<SynchedEntityData.DataValue<?>> list = new ArrayList<>();
+        for (DataItem<?> dataItem : this.itemsById.values()) {
+            list.add(dataItem.value());
+        }
+
+        return list;
+    }
+
+    // This method should only be used if the data of an entity could have became desynced
+    // due to interactions on the client.
+    public void resendPossiblyDesyncedEntity(ServerPlayer player) {
+        if (this.entity.tracker == null) {
+            return;
+        }
+
+        net.minecraft.server.level.ServerEntity serverEntity = this.entity.tracker.serverEntity;
+        if (player.getBukkitEntity().canSee(entity.getBukkitEntity())) {
+            serverEntity.sendPairingData(player.connection::send, player);
+        }
+    }
+    // Paper end
 
     public static class DataItem<T> {
 
diff --git a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index f0b5acdae1cc0626339aca6b16aa26e291a661fa..0c2255b6e2fb7752f85b0f83d4f84732758bd14d 100644
--- a/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -2774,7 +2774,7 @@ public class ServerGamePacketListenerImpl implements ServerPlayerConnection, Tic
 
                             // 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)) {
-                                ServerGamePacketListenerImpl.this.send(new ClientboundAddEntityPacket(entity));
+                                entity.getEntityData().resendPossiblyDesyncedEntity(player); // Paper - The entire mob gets deleted, so resend it.
                                 player.containerMenu.sendAllDataToRemote();
                             }
 
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
index 809ddbfcf873b4695257f599b882803f7fcc34fa..b348d33726b2b14ea2d12ce3430df2c0b94295f4 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -413,7 +413,7 @@ public abstract class PlayerList {
         ((ServerLevel)player.level).getChunkSource().chunkMap.addEntity(player); // Paper - track entity now
         // CraftBukkit end
 
-        player.getEntityData().refresh(player); // CraftBukkit - BungeeCord#2321, send complete data to self on spawn
+        //player.getEntityData().refresh(player); // CraftBukkit - BungeeCord#2321, send complete data to self on spawn Paper - THIS IS NOT NEEDED ANYMORE
 
         // CraftBukkit start - Only add if the player wasn't moved in the event
         if (player.level == worldserver1 && !worldserver1.players().contains(player)) {
diff --git a/src/main/java/net/minecraft/world/entity/animal/Bucketable.java b/src/main/java/net/minecraft/world/entity/animal/Bucketable.java
index 09654dd8895669109246f9bff36ab5ff5f533f65..9695a80c45b36c84f5fdef651b26a2c09d7f1f9b 100644
--- a/src/main/java/net/minecraft/world/entity/animal/Bucketable.java
+++ b/src/main/java/net/minecraft/world/entity/animal/Bucketable.java
@@ -109,8 +109,7 @@ public interface Bucketable {
             itemstack1 = CraftItemStack.asNMSCopy(playerBucketFishEvent.getEntityBucket());
             if (playerBucketFishEvent.isCancelled()) {
                 ((ServerPlayer) player).containerMenu.sendAllDataToRemote(); // We need to update inventory to resync client's bucket
-                ((ServerPlayer) player).connection.send(new ClientboundAddEntityPacket(entity)); // We need to play out these packets as the client assumes the fish is gone
-                entity.getEntityData().refresh((ServerPlayer) player); // Need to send data such as the display name to client
+                entity.getEntityData().resendPossiblyDesyncedEntity((ServerPlayer) player); // Paper
                 return Optional.of(InteractionResult.FAIL);
             }
             entity.playSound(((Bucketable) entity).getPickupSound(), 1.0F, 1.0F);
