From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sun, 18 Mar 2018 12:29:48 -0400
Subject: [PATCH] Player.setPlayerProfile API

This can be useful for changing name or skins after a player has logged in.

== AT ==
public-f net.minecraft.world.entity.player.Player gameProfile

diff --git a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
index bb5c4ecd43aa344dfe9cca8ab7c2ebf9760e94cf..203786383387765d1f5a2d2ccce79d792fd5b187 100644
--- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
@@ -330,11 +330,11 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener,
                         final org.bukkit.craftbukkit.CraftServer server = ServerLoginPacketListenerImpl.this.server.server;
 
                         // Paper start
-                        com.destroystokyo.paper.profile.PlayerProfile profile = org.bukkit.Bukkit.createProfile(uniqueId, playerName);
+                        com.destroystokyo.paper.profile.PlayerProfile profile = com.destroystokyo.paper.profile.CraftPlayerProfile.asBukkitMirror(ServerLoginPacketListenerImpl.this.gameProfile);
                         AsyncPlayerPreLoginEvent asyncEvent = new AsyncPlayerPreLoginEvent(playerName, address, uniqueId, profile);
                         server.getPluginManager().callEvent(asyncEvent);
                         profile = asyncEvent.getPlayerProfile();
-                        profile.complete();
+                        profile.complete(true); // Paper - setPlayerProfileAPI
                         gameProfile = com.destroystokyo.paper.profile.CraftPlayerProfile.asAuthlibCopy(profile);
                         playerName = gameProfile.getName();
                         uniqueId = gameProfile.getId();
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
index e7442952ef1f03969949014492a7ddc6d0796ba5..69a1852905dd4724c30ac8ab88c14251eee2c371 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java
@@ -76,8 +76,8 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa
     }
 
     @Override
-    public PlayerProfile getPlayerProfile() {
-        return new CraftPlayerProfile(this.profile);
+    public com.destroystokyo.paper.profile.PlayerProfile getPlayerProfile() { // Paper
+        return new com.destroystokyo.paper.profile.CraftPlayerProfile(this.profile); // Paper
     }
 
     public Server getServer() {
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index fb157165d46f2f2d9bbaa6f7c8199885051060a8..c9a016af654fb73bd95c1f2d5704c12dcd62241b 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -81,6 +81,7 @@ import net.minecraft.world.entity.ai.attributes.Attributes;
 import net.minecraft.world.inventory.AbstractContainerMenu;
 import net.minecraft.world.level.GameType;
 import net.minecraft.world.level.block.Blocks;
+import net.minecraft.world.level.biome.BiomeManager;
 import net.minecraft.world.level.block.entity.SignBlockEntity;
 import net.minecraft.world.level.border.BorderChangeListener;
 import net.minecraft.world.level.saveddata.maps.MapDecoration;
@@ -284,11 +285,6 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
         return server.getPlayer(getUniqueId()) != null;
     }
 
-    @Override
-    public PlayerProfile getPlayerProfile() {
-        return new CraftPlayerProfile(this.getProfile());
-    }
-
     @Override
     public InetSocketAddress getAddress() {
         if (this.getHandle().connection == null) return null;
@@ -1580,8 +1576,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
         this.hiddenEntities.put(entity.getUniqueId(), hidingPlugins);
 
         // Remove this entity from the hidden player's EntityTrackerEntry
-        ChunkMap tracker = ((ServerLevel) this.getHandle().level).getChunkSource().chunkMap;
+        // Paper start
         Entity other = ((CraftEntity) entity).getHandle();
+        unregisterEntity(other);
+
+        server.getPluginManager().callEvent(new PlayerHideEntityEvent(this, entity));
+    }
+    private void unregisterEntity(Entity other) {
+        // Paper end
+        ChunkMap tracker = ((ServerLevel) this.getHandle().level).getChunkSource().chunkMap;
         ChunkMap.TrackedEntity entry = tracker.entityMap.get(other.getId());
         if (entry != null) {
             entry.removePlayer(this.getHandle());
@@ -1594,8 +1597,6 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
                 this.getHandle().connection.send(new ClientboundPlayerInfoRemovePacket(List.of(otherPlayer.getUUID())));
             }
         }
-
-        server.getPluginManager().callEvent(new PlayerHideEntityEvent(this, entity));
     }
 
     @Override
@@ -1632,8 +1633,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
         }
         this.hiddenEntities.remove(entity.getUniqueId());
 
-        ChunkMap tracker = ((ServerLevel) this.getHandle().level).getChunkSource().chunkMap;
+        // Paper start
         Entity other = ((CraftEntity) entity).getHandle();
+        registerEntity(other);
+
+        server.getPluginManager().callEvent(new PlayerShowEntityEvent(this, entity));
+    }
+    private void registerEntity(Entity other) {
+        ChunkMap tracker = ((ServerLevel) this.getHandle().level).getChunkSource().chunkMap;
+        // Paper end
 
         if (other instanceof ServerPlayer) {
             ServerPlayer otherPlayer = (ServerPlayer) other;
@@ -1644,9 +1652,51 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
         if (entry != null && !entry.seenBy.contains(this.getHandle().connection)) {
             entry.updatePlayer(this.getHandle());
         }
+    }
+    // Paper start
+    private void reregisterPlayer(ServerPlayer player) {
+        if (!hiddenEntities.containsKey(player.getUUID())) {
+            unregisterEntity(player);
+            registerEntity(player);
+        }
+    }
+    public void setPlayerProfile(com.destroystokyo.paper.profile.PlayerProfile profile) {
+        ServerPlayer self = getHandle();
+        self.gameProfile = com.destroystokyo.paper.profile.CraftPlayerProfile.asAuthlibCopy(profile);
+        if (!self.sentListPacket) {
+            return;
+        }
+        List<ServerPlayer> players = server.getServer().getPlayerList().players;
+        for (ServerPlayer player : players) {
+            player.getBukkitEntity().reregisterPlayer(self);
+        }
+        refreshPlayer();
+    }
+    public com.destroystokyo.paper.profile.PlayerProfile getPlayerProfile() {
+        return new com.destroystokyo.paper.profile.CraftPlayerProfile(this).clone();
+    }
 
-        server.getPluginManager().callEvent(new PlayerShowEntityEvent(this, entity));
+    private void refreshPlayer() {
+        ServerPlayer handle = getHandle();
+
+        Location loc = getLocation();
+
+        ServerGamePacketListenerImpl connection = handle.connection;
+        reregisterPlayer(handle);
+
+        //Respawn the player then update their position and selected slot
+        ServerLevel worldserver = handle.getLevel();
+        connection.send(new net.minecraft.network.protocol.game.ClientboundRespawnPacket(worldserver.dimensionTypeId(), worldserver.dimension(), BiomeManager.obfuscateSeed(worldserver.getSeed()), handle.gameMode.getGameModeForPlayer(), handle.gameMode.getPreviousGameModeForPlayer(), worldserver.isDebug(), worldserver.isFlat(), net.minecraft.network.protocol.game.ClientboundRespawnPacket.KEEP_ALL_DATA, this.getHandle().getLastDeathLocation()));
+        handle.onUpdateAbilities();
+        connection.internalTeleport(loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch(), java.util.Collections.emptySet(), false);
+        net.minecraft.server.MinecraftServer.getServer().getPlayerList().sendAllPlayerInfo(handle);
+
+        if (this.isOp()) {
+            this.setOp(false);
+            this.setOp(true);
+        }
     }
+    // Paper end
 
     public void onEntityRemove(Entity entity) {
         this.hiddenEntities.remove(entity.getUUID());
diff --git a/src/main/java/org/bukkit/craftbukkit/util/Commodore.java b/src/main/java/org/bukkit/craftbukkit/util/Commodore.java
index a679133a3de51e26eb19932ece9ade292879aefd..e342ea27a8a689ea080c7881711a5dcd6322c914 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/Commodore.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/Commodore.java
@@ -306,6 +306,12 @@ public class Commodore
                             return;
                         }
 
+                        // Paper start - Rewrite plugins
+                        if ((owner.equals("org/bukkit/OfflinePlayer") || owner.equals("org/bukkit/entity/Player")) && name.equals("getPlayerProfile") && desc.equals("()Lorg/bukkit/profile/PlayerProfile;")) {
+                            super.visitMethodInsn(opcode, owner, name, "()Lcom/destroystokyo/paper/profile/PlayerProfile;", itf);
+                            return;
+                        }
+                        // Paper end
                         if ( modern )
                         {
                             if ( owner.equals( "org/bukkit/Material" ) )
