From 4ae489261829222b96472c09200385ca09733236 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.

diff --git a/src/main/java/net/minecraft/server/EntityHuman.java b/src/main/java/net/minecraft/server/EntityHuman.java
index 14c44a360..23a2f92f2 100644
--- a/src/main/java/net/minecraft/server/EntityHuman.java
+++ b/src/main/java/net/minecraft/server/EntityHuman.java
@@ -65,7 +65,7 @@ public abstract class EntityHuman extends EntityLiving {
     protected int bR;
     protected final float bS = 0.02F;
     private int bU;
-    private final GameProfile bV;
+    private GameProfile bV; public final void setProfile(final GameProfile profile) { this.bV = profile; } // Paper - OBFHELPER
     private ItemStack bX;
     private final ItemCooldown bY;
     @Nullable
diff --git a/src/main/java/net/minecraft/server/LoginListener.java b/src/main/java/net/minecraft/server/LoginListener.java
index 7c3b9c0b9..d4d752ddb 100644
--- a/src/main/java/net/minecraft/server/LoginListener.java
+++ b/src/main/java/net/minecraft/server/LoginListener.java
@@ -37,7 +37,7 @@ public class LoginListener implements PacketLoginInListener {
     public final NetworkManager networkManager;
     private LoginListener.EnumProtocolState g;
     private int h;
-    private GameProfile i;
+    private GameProfile i; private void setGameProfile(final GameProfile profile) { this.i = profile; } private GameProfile getGameProfile() { return this.i; } // Paper - OBFHELPER
     private final String j;
     private SecretKey loginKey;
     private EntityPlayer l;
@@ -286,12 +286,12 @@ public class LoginListener implements PacketLoginInListener {
                             final org.bukkit.craftbukkit.CraftServer server = LoginListener.this.server.server;
 
                             // Paper start
-                            PlayerProfile profile = Bukkit.createProfile(uniqueId, playerName);
+                            PlayerProfile profile = CraftPlayerProfile.asBukkitMirror(getGameProfile());
                             AsyncPlayerPreLoginEvent asyncEvent = new AsyncPlayerPreLoginEvent(playerName, address, uniqueId, profile);
                             server.getPluginManager().callEvent(asyncEvent);
                             profile = asyncEvent.getPlayerProfile();
-                            profile.complete();
-                            i = CraftPlayerProfile.asAuthlibCopy(profile);
+                            profile.complete(true);
+                            setGameProfile(CraftPlayerProfile.asAuthlib(profile));
                             playerName = i.getName();
                             uniqueId = i.getId();
                             // Paper end
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index 4fa554b29..35cacb3d2 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -1,6 +1,8 @@
 package org.bukkit.craftbukkit.entity;
 
 import com.destroystokyo.paper.Title;
+import com.destroystokyo.paper.profile.CraftPlayerProfile;
+import com.destroystokyo.paper.profile.PlayerProfile;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.io.BaseEncoding;
@@ -1193,8 +1195,13 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
         hiddenPlayers.put(player.getUniqueId(), hidingPlugins);
 
         // Remove this player from the hidden player's EntityTrackerEntry
-        PlayerChunkMap tracker = ((WorldServer) entity.world).getChunkProvider().playerChunkMap;
+        // Paper start
         EntityPlayer other = ((CraftPlayer) player).getHandle();
+        unregisterPlayer(other);
+    }
+    private void unregisterPlayer(EntityPlayer other) {
+        PlayerChunkMap tracker = ((WorldServer) entity.world).getChunkProvider().playerChunkMap;
+        // Paper end
         PlayerChunkMap.EntityTracker entry = tracker.trackedEntities.get(other.getId());
         if (entry != null) {
             entry.clear(getHandle());
@@ -1235,8 +1242,13 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
         }
         hiddenPlayers.remove(player.getUniqueId());
 
-        PlayerChunkMap tracker = ((WorldServer) entity.world).getChunkProvider().playerChunkMap;
+        // Paper start
         EntityPlayer other = ((CraftPlayer) player).getHandle();
+        registerPlayer(other);
+    }
+    private void registerPlayer(EntityPlayer other) {
+        PlayerChunkMap tracker = ((WorldServer) entity.world).getChunkProvider().playerChunkMap;
+        // Paper end
 
         getHandle().playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, other));
 
@@ -1245,6 +1257,46 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
             entry.updatePlayer(getHandle());
         }
     }
+    // Paper start
+    private void reregisterPlayer(EntityPlayer player) {
+        if (!hiddenPlayers.containsKey(player.getUniqueID())) {
+            unregisterPlayer(player);
+            registerPlayer(player);
+        }
+    }
+    public void setPlayerProfile(PlayerProfile profile) {
+        EntityPlayer self = getHandle();
+        self.setProfile(CraftPlayerProfile.asAuthlibCopy(profile));
+        List<EntityPlayer> players = server.getServer().getPlayerList().players;
+        for (EntityPlayer player : players) {
+            player.getBukkitEntity().reregisterPlayer(self);
+        }
+        refreshPlayer();
+    }
+    public PlayerProfile getPlayerProfile() {
+        return new CraftPlayerProfile(this).clone();
+    }
+
+    private void refreshPlayer() {
+        EntityPlayer handle = getHandle();
+
+        Location loc = getLocation();
+
+        PlayerConnection connection = handle.playerConnection;
+        reregisterPlayer(handle);
+
+        //Respawn the player then update their position and selected slot
+        connection.sendPacket(new net.minecraft.server.PacketPlayOutRespawn(handle.dimension, handle.world.getWorldData().getType(), handle.playerInteractManager.getGameMode()));
+        handle.updateAbilities();
+        connection.sendPacket(new net.minecraft.server.PacketPlayOutPosition(loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch(), new HashSet<>(), 0));
+        net.minecraft.server.MinecraftServer.getServer().getPlayerList().updateClient(handle);
+
+        if (this.isOp()) {
+            this.setOp(false);
+            this.setOp(true);
+        }
+    }
+    // Paper end
 
     public void removeDisconnectingPlayer(Player player) {
         hiddenPlayers.remove(player.getUniqueId());
-- 
2.21.0