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.

diff --git a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
index e5be45ac86907c1f8cc154bd38fd624a2320180f..0aa3a154d68f00edcc09b947a24b2b59b1e135e6 100644
--- a/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
+++ b/src/main/java/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
@@ -54,7 +54,7 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener
     public final Connection connection;
     private ServerLoginPacketListenerImpl.State state;
     private int tick;
-    private GameProfile gameProfile;
+    private GameProfile gameProfile; private void setGameProfile(final GameProfile profile) { this.gameProfile = profile; } private GameProfile getGameProfile() { return this.gameProfile; } // Paper - OBFHELPER
     private final String serverId;
     private SecretKey secretKey;
     private ServerPlayer delayedAcceptPlayer;
@@ -318,12 +318,12 @@ public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener
                         final org.bukkit.craftbukkit.CraftServer server = ServerLoginPacketListenerImpl.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();
-                            gameProfile = CraftPlayerProfile.asAuthlibCopy(profile);
+                            profile.complete(true);
+                            setGameProfile(CraftPlayerProfile.asAuthlib(profile));
                             playerName = gameProfile.getName();
                             uniqueId = gameProfile.getId();
                             // Paper end
diff --git a/src/main/java/net/minecraft/world/entity/player/Player.java b/src/main/java/net/minecraft/world/entity/player/Player.java
index 63871a3a1981b2e8c7ad74214196e35684acb584..c4aa824d03de952fe6b306e539baa47af979add1 100644
--- a/src/main/java/net/minecraft/world/entity/player/Player.java
+++ b/src/main/java/net/minecraft/world/entity/player/Player.java
@@ -159,7 +159,7 @@ public abstract class Player extends LivingEntity {
     protected int enchantmentSeed;
     protected final float defaultFlySpeed = 0.02F;
     private int lastLevelUpTime;
-    private final GameProfile gameProfile;
+    private GameProfile gameProfile; public final void setProfile(final GameProfile profile) { this.gameProfile = profile; } // Paper - OBFHELPER
     private ItemStack lastItemInMainHand;
     private final ItemCooldowns cooldowns;
     @Nullable
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index efdcb8dac8db15c4bbaed84a7861ce98339e516a..9d853733ff9054cc48925e22c8bb3c8d9b898808 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -67,6 +67,7 @@ import net.minecraft.world.inventory.AbstractContainerMenu;
 import net.minecraft.world.item.enchantment.EnchantmentHelper;
 import net.minecraft.world.item.enchantment.Enchantments;
 import net.minecraft.world.level.GameType;
+import net.minecraft.world.level.biome.BiomeManager;
 import net.minecraft.world.level.block.entity.SignBlockEntity;
 import net.minecraft.world.level.saveddata.maps.MapDecoration;
 import net.minecraft.world.phys.Vec3;
@@ -1307,8 +1308,13 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
         hiddenPlayers.put(player.getUniqueId(), hidingPlugins);
 
         // Remove this player from the hidden player's EntityTrackerEntry
-        ChunkMap tracker = ((ServerLevel) entity.level).getChunkSource().chunkMap;
+        // Paper start
         ServerPlayer other = ((CraftPlayer) player).getHandle();
+        unregisterPlayer(other);
+    }
+    private void unregisterPlayer(ServerPlayer other) {
+        ChunkMap tracker = ((ServerLevel) entity.level).getChunkSource().chunkMap;
+        // Paper end
         ChunkMap.TrackedEntity entry = tracker.entityMap.get(other.getId());
         if (entry != null) {
             entry.removePlayer(getHandle());
@@ -1349,8 +1355,13 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
         }
         hiddenPlayers.remove(player.getUniqueId());
 
-        ChunkMap tracker = ((ServerLevel) entity.level).getChunkSource().chunkMap;
+        // Paper start
         ServerPlayer other = ((CraftPlayer) player).getHandle();
+        registerPlayer(other);
+    }
+    private void registerPlayer(ServerPlayer other) {
+        ChunkMap tracker = ((ServerLevel) entity.level).getChunkSource().chunkMap;
+        // Paper end
 
         getHandle().connection.send(new ClientboundPlayerInfoPacket(ClientboundPlayerInfoPacket.Action.ADD_PLAYER, other));
 
@@ -1359,6 +1370,50 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
             entry.updatePlayer(getHandle());
         }
     }
+    // Paper start
+    private void reregisterPlayer(ServerPlayer player) {
+        if (!hiddenPlayers.containsKey(player.getUUID())) {
+            unregisterPlayer(player);
+            registerPlayer(player);
+        }
+    }
+    public void setPlayerProfile(com.destroystokyo.paper.profile.PlayerProfile profile) {
+        ServerPlayer self = getHandle();
+        self.setProfile(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();
+    }
+
+    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.dimensionType(), worldserver.dimension(), BiomeManager.obfuscateSeed(worldserver.getSeed()), handle.gameMode.getGameModeForPlayer(), handle.gameMode.getPreviousGameModeForPlayer(), worldserver.isDebug(), worldserver.isFlat(), true));
+        handle.onUpdateAbilities();
+        connection.send(new net.minecraft.network.protocol.game.ClientboundPlayerPositionPacket(loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch(), new HashSet<>(), 0));
+        net.minecraft.server.MinecraftServer.getServer().getPlayerList().sendAllPlayerInfo(handle);
+
+        if (this.isOp()) {
+            this.setOp(false);
+            this.setOp(true);
+        }
+    }
+    // Paper end
 
     public void removeDisconnectingPlayer(Player player) {
         hiddenPlayers.remove(player.getUniqueId());