From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jedediah Smith <jedediah@silencegreys.com>
Date: Wed, 2 Mar 2016 23:13:07 -0600
Subject: [PATCH] Send absolute position the first time an entity is seen

Not needed anymore, packet spawn sends full position


diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
index 59a5f82c9f57d760ba4959a040ce8cbf0f49e4aa..d1bc927c8b429f43de2cdad98f8b329ff4c8b4db 100644
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
@@ -1301,10 +1301,14 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
         private final Entity entity;
         private final int range;
         private SectionPos lastSectionPos;
-        public final Set<ServerPlayer> seenBy = Sets.newHashSet();
+        // Paper start
+        // Replace trackedPlayers Set with a Map. The value is true until the player receives
+        // their first update (which is forced to have absolute coordinates), false afterward.
+        public java.util.Map<ServerPlayer, Boolean> trackedPlayerMap = new java.util.HashMap<>();
+        public Set<ServerPlayer> seenBy = trackedPlayerMap.keySet();
 
         public TrackedEntity(Entity entity, int i, int j, boolean flag) {
-            this.serverEntity = new ServerEntity(ChunkMap.this.level, entity, j, flag, this::broadcast, seenBy); // CraftBukkit
+            this.serverEntity = new ServerEntity(ChunkMap.this.level, entity, j, flag, this::broadcast, trackedPlayerMap); // CraftBukkit // Paper
             this.entity = entity;
             this.range = i;
             this.lastSectionPos = SectionPos.of(entity);
@@ -1386,7 +1390,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
                     player.entitiesToRemove.remove(Integer.valueOf(this.entity.getId()));
                     // CraftBukkit end
 
-                    if (flag1 && this.seenBy.add(player)) {
+                    if (flag1 && this.trackedPlayerMap.putIfAbsent(player, true) == null) { // Paper
                         this.serverEntity.addPairing(player);
                     }
                 } else if (this.seenBy.remove(player)) {
diff --git a/src/main/java/net/minecraft/server/level/ServerEntity.java b/src/main/java/net/minecraft/server/level/ServerEntity.java
index 3d386627b6d3d33da76372e4a14d0c5000eb8ffc..fa6893055fa5617742bfb4b7eff60c8139395cb6 100644
--- a/src/main/java/net/minecraft/server/level/ServerEntity.java
+++ b/src/main/java/net/minecraft/server/level/ServerEntity.java
@@ -4,6 +4,7 @@ import com.google.common.collect.Lists;
 import com.mojang.datafixers.util.Pair;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
@@ -51,7 +52,7 @@ public class ServerEntity {
     private final Entity entity;
     private final int updateInterval;
     private final boolean trackDelta;
-    private final Consumer<Packet<?>> broadcast;
+    private final Consumer<Packet<?>> broadcast; private Consumer<Packet<?>> getPacketConsumer() { return broadcast; } // Paper - OBFHELPER
     private long xp;
     private long yp;
     private long zp;
@@ -66,8 +67,23 @@ public class ServerEntity {
     private boolean wasOnGround;
     // CraftBukkit start
     private final Set<ServerPlayer> trackedPlayers;
+    // Paper start
+    private java.util.Map<ServerPlayer, Boolean> trackedPlayerMap = null;
+
+    /**
+     * Requested in https://github.com/PaperMC/Paper/issues/1537 to allow intercepting packets
+     */
+    public void sendPlayerPacket(ServerPlayer player, Packet packet) {
+        player.connection.send(packet);
+    }
+
+    public ServerEntity(ServerLevel worldserver, Entity entity, int i, boolean flag, Consumer<Packet<?>> consumer, java.util.Map<ServerPlayer, Boolean> trackedPlayers) {
+        this(worldserver, entity, i, flag, consumer, trackedPlayers.keySet());
+        trackedPlayerMap = trackedPlayers;
+    }
 
     public ServerEntity(ServerLevel worldserver, Entity entity, int i, boolean flag, Consumer<Packet<?>> consumer, Set<ServerPlayer> trackedPlayers) {
+        // Paper end
         this.trackedPlayers = trackedPlayers;
         // CraftBukkit end
         this.ap = Vec3.ZERO;
@@ -188,7 +204,25 @@ public class ServerEntity {
                 }
 
                 if (packet1 != null) {
-                    this.broadcast.accept(packet1);
+                    // paper start
+                    if (trackedPlayerMap == null || packet1 instanceof ClientboundTeleportEntityPacket) {
+                        this.broadcast.accept((packet1));
+                    } else {
+                        ClientboundTeleportEntityPacket teleportPacket = null;
+
+                        for (java.util.Map.Entry<ServerPlayer, Boolean> viewer : trackedPlayerMap.entrySet()) {
+                            if (viewer.getValue()) {
+                                viewer.setValue(false);
+                                if (teleportPacket == null) {
+                                    teleportPacket = new ClientboundTeleportEntityPacket(this.entity);
+                                }
+                                sendPlayerPacket(viewer.getKey(), teleportPacket);
+                            } else {
+                                sendPlayerPacket(viewer.getKey(), packet1);
+                            }
+                        }
+                    }
+                    // Paper end
                 }
 
                 this.sendDirtyEntityData();