From e67d55d00d990e222edd03036743351239cb51cf Mon Sep 17 00:00:00 2001 From: NickAcPT <32451103+NickAcPT@users.noreply.github.com> Date: Sat, 21 Jul 2018 12:58:48 +0100 Subject: [PATCH 1/9] Extend player profile API to support skin changes Added code that refreshes the player's skin by sending packets with a special order, telling the client to respawn the player and re-apply the game profile --- ...-profile-API-to-support-skin-changes.patch | 29 ++++ ...-profile-API-to-support-skin-changes.patch | 163 ++++++++++++++++++ 2 files changed, 192 insertions(+) create mode 100644 Spigot-API-Patches/0126-Extend-player-profile-API-to-support-skin-changes.patch create mode 100644 Spigot-Server-Patches/0336-Extend-player-profile-API-to-support-skin-changes.patch diff --git a/Spigot-API-Patches/0126-Extend-player-profile-API-to-support-skin-changes.patch b/Spigot-API-Patches/0126-Extend-player-profile-API-to-support-skin-changes.patch new file mode 100644 index 0000000000..6e851c0fe9 --- /dev/null +++ b/Spigot-API-Patches/0126-Extend-player-profile-API-to-support-skin-changes.patch @@ -0,0 +1,29 @@ +From c54292b8620985a83368e984581d4835604512e1 Mon Sep 17 00:00:00 2001 +From: NickAcPT <32451103+NickAcPT@users.noreply.github.com> +Date: Sat, 21 Jul 2018 01:30:41 +0100 +Subject: [PATCH] Extend player profile API to support skin changes + +Added code that refreshes the player's skin by sending packets with a special order, telling the client to respawn the player and re-apply the game profile + +diff --git a/src/main/java/com/destroystokyo/paper/profile/PlayerProfile.java b/src/main/java/com/destroystokyo/paper/profile/PlayerProfile.java +index e060c38a..98eade06 100644 +--- a/src/main/java/com/destroystokyo/paper/profile/PlayerProfile.java ++++ b/src/main/java/com/destroystokyo/paper/profile/PlayerProfile.java +@@ -132,6 +132,14 @@ public interface PlayerProfile { + * @return If the profile is now complete (has UUID and Name) (if you get rate limited, this operation may fail) + */ + boolean complete(boolean textures); ++ /** ++ * If this profile is not complete, then make the API call to complete it. ++ * This is a blocking operation and should be done asynchronously. ++ * ++ * Optionally will also fill textures. ++ * @return If the profile is now complete (has UUID and Name) (if you get rate limited, this operation may fail) ++ */ ++ boolean complete(boolean textures, boolean force); + + /** + * Whether or not this Profile has textures associated to it +-- +2.16.2.windows.1 + diff --git a/Spigot-Server-Patches/0336-Extend-player-profile-API-to-support-skin-changes.patch b/Spigot-Server-Patches/0336-Extend-player-profile-API-to-support-skin-changes.patch new file mode 100644 index 0000000000..ab79ee794b --- /dev/null +++ b/Spigot-Server-Patches/0336-Extend-player-profile-API-to-support-skin-changes.patch @@ -0,0 +1,163 @@ +From e709f6448451449a488b16d4039b25ff467cc956 Mon Sep 17 00:00:00 2001 +From: NickAcPT <32451103+NickAcPT@users.noreply.github.com> +Date: Sat, 21 Jul 2018 01:30:30 +0100 +Subject: [PATCH] Extend player profile API to support skin changes + +Added code that refreshes the player's skin by sending packets with a special order, telling the client to respawn the player and re-apply the game profile + +diff --git a/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java b/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java +index 9ad5853d..dda24052 100644 +--- a/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java ++++ b/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java +@@ -170,17 +170,21 @@ public class CraftPlayerProfile implements PlayerProfile { + } + + public boolean complete(boolean textures) { ++ return complete(textures, false); ++ } ++ ++ public boolean complete(boolean textures, boolean force) { + MinecraftServer server = MinecraftServer.getServer(); + + boolean isOnlineMode = server.getOnlineMode() || (SpigotConfig.bungee && PaperConfig.bungeeOnlineMode); + boolean isCompleteFromCache = this.completeFromCache(true); +- if (isOnlineMode && (!isCompleteFromCache || textures && !hasTextures())) { ++ if ((isOnlineMode || force) && (!isCompleteFromCache || textures && !hasTextures())) { + GameProfile result = server.getSessionService().fillProfileProperties(profile, true); + if (result != null) { + this.profile = result; + } + } +- return profile.isComplete() && (!isOnlineMode || !textures || hasTextures()); ++ return profile.isComplete() && (!(isOnlineMode || force) || !textures || hasTextures()); + } + + private static void copyProfileProperties(GameProfile source, GameProfile target) { +diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java +index dd78a87b..e3f7be28 100644 +--- a/src/main/java/net/minecraft/server/WorldServer.java ++++ b/src/main/java/net/minecraft/server/WorldServer.java +@@ -135,6 +135,12 @@ public class WorldServer extends World implements IAsyncTaskHandler { + return this; + } + ++ // Paper start - Provide dimension number of world ++ public int getDimension() { ++ return dimension; ++ } ++ // Paper end ++ + // CraftBukkit start + @Override + public TileEntity getTileEntity(BlockPosition pos) { +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +index 210e3bc4..b0335684 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +@@ -101,6 +101,12 @@ public class CraftWorld implements World { + } + // Paper end + ++ // Paper start - Provide dimension number of world ++ public int getDimension() { ++ return world.dimension; ++ } ++ // Paper end ++ + private static final Random rand = new Random(); + + public CraftWorld(WorldServer world, ChunkGenerator gen, Environment env) { +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +index 6cbf429f..bd743be5 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +@@ -55,6 +55,7 @@ import org.bukkit.craftbukkit.CraftStatistic; + import org.bukkit.craftbukkit.CraftWorld; + import org.bukkit.craftbukkit.advancement.CraftAdvancement; + import org.bukkit.craftbukkit.advancement.CraftAdvancementProgress; ++import org.bukkit.craftbukkit.inventory.CraftItemStack; + import org.bukkit.craftbukkit.map.CraftMapView; + import org.bukkit.craftbukkit.map.RenderData; + import org.bukkit.craftbukkit.scoreboard.CraftScoreboard; +@@ -1172,14 +1173,102 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + public void setPlayerProfile(PlayerProfile profile) { + EntityPlayer self = getHandle(); + self.setProfile(CraftPlayerProfile.asAuthlibCopy(profile)); +- List 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() { ++ int dimension = getWorld().getEnvironment().getId(); ++ Packet respawn = new PacketPlayOutRespawn(dimension, ((WorldServer)getHandle().getWorld()).worldData.getDifficulty(), ((WorldServer)getHandle().getWorld()).worldData.getType(), EnumGamemode.getById(getHandle().playerInteractManager.getGameMode().getId())); ++ Location l = getLocation(); ++ ++ Packet pos = new PacketPlayOutPosition(l.getX(), l.getY(), l.getZ(), l.getYaw(), l.getPitch(), new HashSet<>(), 0); ++ Packet mainhand = new PacketPlayOutEntityEquipment(getEntityId(), EnumItemSlot.MAINHAND, CraftItemStack.asNMSCopy(getInventory().getItemInMainHand())); ++ Packet offhand = new PacketPlayOutEntityEquipment(getEntityId(), EnumItemSlot.OFFHAND, CraftItemStack.asNMSCopy(getInventory().getItemInOffHand())); ++ Packet helm = new PacketPlayOutEntityEquipment(getEntityId(), EnumItemSlot.HEAD, CraftItemStack.asNMSCopy(getInventory().getHelmet())); ++ Packet chest = new PacketPlayOutEntityEquipment(getEntityId(), EnumItemSlot.CHEST, CraftItemStack.asNMSCopy(getInventory().getChestplate())); ++ Packet legs = new PacketPlayOutEntityEquipment(getEntityId(), EnumItemSlot.LEGS, CraftItemStack.asNMSCopy(getInventory().getLeggings())); ++ Packet feet = new PacketPlayOutEntityEquipment(getEntityId(), EnumItemSlot.FEET, CraftItemStack.asNMSCopy(getInventory().getBoots())); ++ Packet slot = new PacketPlayOutHeldItemSlot(getInventory().getHeldItemSlot()); ++ ++ for (Player pOnline : Bukkit.getOnlinePlayers()) { ++ EntityPlayer handle = ((CraftPlayer) pOnline).getHandle(); ++ PlayerConnection playerCon = handle.playerConnection; ++ if (pOnline.equals(this)) { ++ reregisterPlayer(handle); ++ ++ //Respawn the player then update their position and selected slot ++ handle.playerConnection.sendPacket(respawn); ++ handle.updateAbilities(); ++ handle.playerConnection.sendPacket(pos); ++ handle.playerConnection.sendPacket(slot); ++ ++ ((CraftPlayer) pOnline).updateScaledHealth(); ++ pOnline.updateInventory(); ++ ++ handle.triggerHealthUpdate(); ++ ++ if (pOnline.isOp()) { ++ pOnline.setOp(false); ++ pOnline.setOp(true); ++ } ++ continue; ++ } ++ if (pOnline.getWorld().equals(getWorld()) && pOnline.canSee(this) && isOnline()) { ++ PacketPlayOutEntityDestroy removeEntity = new PacketPlayOutEntityDestroy(new int[]{getEntityId()}); ++ PacketPlayOutNamedEntitySpawn addNamed = new PacketPlayOutNamedEntitySpawn(getHandle()); ++ ++ //Remove and reregister the player ++ handle.playerConnection.sendPacket(removeEntity); ++ ((CraftPlayer) pOnline).reregisterPlayer(this.getHandle()); ++ handle.playerConnection.sendPacket(addNamed); ++ ++ //Send hand items ++ handle.playerConnection.sendPacket(mainhand); ++ handle.playerConnection.sendPacket(offhand); ++ ++ //Send armor ++ handle.playerConnection.sendPacket(helm); ++ handle.playerConnection.sendPacket(chest); ++ handle.playerConnection.sendPacket(legs); ++ handle.playerConnection.sendPacket(feet); ++ } else { ++ //Just send player update ++ ((CraftPlayer) pOnline).reregisterPlayer(this.getHandle()); ++ } ++ } ++ ++ } + // Paper end + + public void removeDisconnectingPlayer(Player player) { +-- +2.16.2.windows.1 + From e5ea4656b23a82abca04d6dc20e478ed46983bf3 Mon Sep 17 00:00:00 2001 From: NickAcPT <32451103+NickAcPT@users.noreply.github.com> Date: Sat, 21 Jul 2018 16:22:10 +0100 Subject: [PATCH 2/9] Extend player profile API to support skin changes Added code that refreshes the player's skin by sending packets with a special order, telling the client to respawn the player and re-apply the game profile --- ...-profile-API-to-support-skin-changes.patch | 29 ---- ...-profile-API-to-support-skin-changes.patch | 128 ++++-------------- 2 files changed, 23 insertions(+), 134 deletions(-) delete mode 100644 Spigot-API-Patches/0126-Extend-player-profile-API-to-support-skin-changes.patch diff --git a/Spigot-API-Patches/0126-Extend-player-profile-API-to-support-skin-changes.patch b/Spigot-API-Patches/0126-Extend-player-profile-API-to-support-skin-changes.patch deleted file mode 100644 index 6e851c0fe9..0000000000 --- a/Spigot-API-Patches/0126-Extend-player-profile-API-to-support-skin-changes.patch +++ /dev/null @@ -1,29 +0,0 @@ -From c54292b8620985a83368e984581d4835604512e1 Mon Sep 17 00:00:00 2001 -From: NickAcPT <32451103+NickAcPT@users.noreply.github.com> -Date: Sat, 21 Jul 2018 01:30:41 +0100 -Subject: [PATCH] Extend player profile API to support skin changes - -Added code that refreshes the player's skin by sending packets with a special order, telling the client to respawn the player and re-apply the game profile - -diff --git a/src/main/java/com/destroystokyo/paper/profile/PlayerProfile.java b/src/main/java/com/destroystokyo/paper/profile/PlayerProfile.java -index e060c38a..98eade06 100644 ---- a/src/main/java/com/destroystokyo/paper/profile/PlayerProfile.java -+++ b/src/main/java/com/destroystokyo/paper/profile/PlayerProfile.java -@@ -132,6 +132,14 @@ public interface PlayerProfile { - * @return If the profile is now complete (has UUID and Name) (if you get rate limited, this operation may fail) - */ - boolean complete(boolean textures); -+ /** -+ * If this profile is not complete, then make the API call to complete it. -+ * This is a blocking operation and should be done asynchronously. -+ * -+ * Optionally will also fill textures. -+ * @return If the profile is now complete (has UUID and Name) (if you get rate limited, this operation may fail) -+ */ -+ boolean complete(boolean textures, boolean force); - - /** - * Whether or not this Profile has textures associated to it --- -2.16.2.windows.1 - diff --git a/Spigot-Server-Patches/0336-Extend-player-profile-API-to-support-skin-changes.patch b/Spigot-Server-Patches/0336-Extend-player-profile-API-to-support-skin-changes.patch index ab79ee794b..64e753aa43 100644 --- a/Spigot-Server-Patches/0336-Extend-player-profile-API-to-support-skin-changes.patch +++ b/Spigot-Server-Patches/0336-Extend-player-profile-API-to-support-skin-changes.patch @@ -1,55 +1,10 @@ -From e709f6448451449a488b16d4039b25ff467cc956 Mon Sep 17 00:00:00 2001 +From ecf75579dcfbec00461841c87f96091901fe65fb Mon Sep 17 00:00:00 2001 From: NickAcPT <32451103+NickAcPT@users.noreply.github.com> Date: Sat, 21 Jul 2018 01:30:30 +0100 Subject: [PATCH] Extend player profile API to support skin changes Added code that refreshes the player's skin by sending packets with a special order, telling the client to respawn the player and re-apply the game profile -diff --git a/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java b/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java -index 9ad5853d..dda24052 100644 ---- a/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java -+++ b/src/main/java/com/destroystokyo/paper/profile/CraftPlayerProfile.java -@@ -170,17 +170,21 @@ public class CraftPlayerProfile implements PlayerProfile { - } - - public boolean complete(boolean textures) { -+ return complete(textures, false); -+ } -+ -+ public boolean complete(boolean textures, boolean force) { - MinecraftServer server = MinecraftServer.getServer(); - - boolean isOnlineMode = server.getOnlineMode() || (SpigotConfig.bungee && PaperConfig.bungeeOnlineMode); - boolean isCompleteFromCache = this.completeFromCache(true); -- if (isOnlineMode && (!isCompleteFromCache || textures && !hasTextures())) { -+ if ((isOnlineMode || force) && (!isCompleteFromCache || textures && !hasTextures())) { - GameProfile result = server.getSessionService().fillProfileProperties(profile, true); - if (result != null) { - this.profile = result; - } - } -- return profile.isComplete() && (!isOnlineMode || !textures || hasTextures()); -+ return profile.isComplete() && (!(isOnlineMode || force) || !textures || hasTextures()); - } - - private static void copyProfileProperties(GameProfile source, GameProfile target) { -diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java -index dd78a87b..e3f7be28 100644 ---- a/src/main/java/net/minecraft/server/WorldServer.java -+++ b/src/main/java/net/minecraft/server/WorldServer.java -@@ -135,6 +135,12 @@ public class WorldServer extends World implements IAsyncTaskHandler { - return this; - } - -+ // Paper start - Provide dimension number of world -+ public int getDimension() { -+ return dimension; -+ } -+ // Paper end -+ - // CraftBukkit start - @Override - public TileEntity getTileEntity(BlockPosition pos) { diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java index 210e3bc4..b0335684 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -68,7 +23,7 @@ index 210e3bc4..b0335684 100644 public CraftWorld(WorldServer world, ChunkGenerator gen, Environment env) { diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -index 6cbf429f..bd743be5 100644 +index 6cbf429f..57f17120 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java @@ -55,6 +55,7 @@ import org.bukkit.craftbukkit.CraftStatistic; @@ -79,14 +34,10 @@ index 6cbf429f..bd743be5 100644 import org.bukkit.craftbukkit.map.CraftMapView; import org.bukkit.craftbukkit.map.RenderData; import org.bukkit.craftbukkit.scoreboard.CraftScoreboard; -@@ -1172,14 +1173,102 @@ public class CraftPlayer extends CraftHumanEntity implements Player { - public void setPlayerProfile(PlayerProfile profile) { - EntityPlayer self = getHandle(); - self.setProfile(CraftPlayerProfile.asAuthlibCopy(profile)); -- List players = server.getServer().getPlayerList().players; -- for (EntityPlayer player : players) { -- player.getBukkitEntity().reregisterPlayer(self); -- } +@@ -1176,10 +1177,41 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + for (EntityPlayer player : players) { + player.getBukkitEntity().reregisterPlayer(self); + } + refreshPlayer(); } public PlayerProfile getPlayerProfile() { @@ -94,64 +45,31 @@ index 6cbf429f..bd743be5 100644 } + + private void refreshPlayer() { -+ int dimension = getWorld().getEnvironment().getId(); -+ Packet respawn = new PacketPlayOutRespawn(dimension, ((WorldServer)getHandle().getWorld()).worldData.getDifficulty(), ((WorldServer)getHandle().getWorld()).worldData.getType(), EnumGamemode.getById(getHandle().playerInteractManager.getGameMode().getId())); -+ Location l = getLocation(); ++ EntityPlayer entityplayer = getHandle(); + ++ Packet respawn = new PacketPlayOutRespawn(entityplayer.dimension, entityplayer.world.getDifficulty(), entityplayer.world.getWorldData().getType(), entityplayer.playerInteractManager.getGameMode()); ++ Location l = getLocation(); + Packet pos = new PacketPlayOutPosition(l.getX(), l.getY(), l.getZ(), l.getYaw(), l.getPitch(), new HashSet<>(), 0); -+ Packet mainhand = new PacketPlayOutEntityEquipment(getEntityId(), EnumItemSlot.MAINHAND, CraftItemStack.asNMSCopy(getInventory().getItemInMainHand())); -+ Packet offhand = new PacketPlayOutEntityEquipment(getEntityId(), EnumItemSlot.OFFHAND, CraftItemStack.asNMSCopy(getInventory().getItemInOffHand())); -+ Packet helm = new PacketPlayOutEntityEquipment(getEntityId(), EnumItemSlot.HEAD, CraftItemStack.asNMSCopy(getInventory().getHelmet())); -+ Packet chest = new PacketPlayOutEntityEquipment(getEntityId(), EnumItemSlot.CHEST, CraftItemStack.asNMSCopy(getInventory().getChestplate())); -+ Packet legs = new PacketPlayOutEntityEquipment(getEntityId(), EnumItemSlot.LEGS, CraftItemStack.asNMSCopy(getInventory().getLeggings())); -+ Packet feet = new PacketPlayOutEntityEquipment(getEntityId(), EnumItemSlot.FEET, CraftItemStack.asNMSCopy(getInventory().getBoots())); + Packet slot = new PacketPlayOutHeldItemSlot(getInventory().getHeldItemSlot()); + -+ for (Player pOnline : Bukkit.getOnlinePlayers()) { -+ EntityPlayer handle = ((CraftPlayer) pOnline).getHandle(); -+ PlayerConnection playerCon = handle.playerConnection; -+ if (pOnline.equals(this)) { -+ reregisterPlayer(handle); ++ EntityPlayer handle = getHandle(); ++ PlayerConnection playerCon = handle.playerConnection; ++ reregisterPlayer(handle); + -+ //Respawn the player then update their position and selected slot -+ handle.playerConnection.sendPacket(respawn); -+ handle.updateAbilities(); -+ handle.playerConnection.sendPacket(pos); -+ handle.playerConnection.sendPacket(slot); ++ //Respawn the player then update their position and selected slot ++ playerCon.sendPacket(respawn); ++ handle.updateAbilities(); ++ playerCon.sendPacket(pos); ++ playerCon.sendPacket(slot); + -+ ((CraftPlayer) pOnline).updateScaledHealth(); -+ pOnline.updateInventory(); ++ updateScaledHealth(); ++ this.updateInventory(); + -+ handle.triggerHealthUpdate(); ++ handle.triggerHealthUpdate(); + -+ if (pOnline.isOp()) { -+ pOnline.setOp(false); -+ pOnline.setOp(true); -+ } -+ continue; -+ } -+ if (pOnline.getWorld().equals(getWorld()) && pOnline.canSee(this) && isOnline()) { -+ PacketPlayOutEntityDestroy removeEntity = new PacketPlayOutEntityDestroy(new int[]{getEntityId()}); -+ PacketPlayOutNamedEntitySpawn addNamed = new PacketPlayOutNamedEntitySpawn(getHandle()); -+ -+ //Remove and reregister the player -+ handle.playerConnection.sendPacket(removeEntity); -+ ((CraftPlayer) pOnline).reregisterPlayer(this.getHandle()); -+ handle.playerConnection.sendPacket(addNamed); -+ -+ //Send hand items -+ handle.playerConnection.sendPacket(mainhand); -+ handle.playerConnection.sendPacket(offhand); -+ -+ //Send armor -+ handle.playerConnection.sendPacket(helm); -+ handle.playerConnection.sendPacket(chest); -+ handle.playerConnection.sendPacket(legs); -+ handle.playerConnection.sendPacket(feet); -+ } else { -+ //Just send player update -+ ((CraftPlayer) pOnline).reregisterPlayer(this.getHandle()); -+ } ++ if (this.isOp()) { ++ this.setOp(false); ++ this.setOp(true); + } + + } From 09f013532aa4a80b8322bb500c77797f0a3894e6 Mon Sep 17 00:00:00 2001 From: NickAcPT <32451103+NickAcPT@users.noreply.github.com> Date: Sat, 21 Jul 2018 16:54:52 +0100 Subject: [PATCH 3/9] Remove unsed method --- ...-profile-API-to-support-skin-changes.patch | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/Spigot-Server-Patches/0336-Extend-player-profile-API-to-support-skin-changes.patch b/Spigot-Server-Patches/0336-Extend-player-profile-API-to-support-skin-changes.patch index 64e753aa43..d26ee3fbd9 100644 --- a/Spigot-Server-Patches/0336-Extend-player-profile-API-to-support-skin-changes.patch +++ b/Spigot-Server-Patches/0336-Extend-player-profile-API-to-support-skin-changes.patch @@ -1,27 +1,10 @@ -From ecf75579dcfbec00461841c87f96091901fe65fb Mon Sep 17 00:00:00 2001 +From 95e357f7dd549b278714a9ea76bf6d11d6c2b5fd Mon Sep 17 00:00:00 2001 From: NickAcPT <32451103+NickAcPT@users.noreply.github.com> Date: Sat, 21 Jul 2018 01:30:30 +0100 Subject: [PATCH] Extend player profile API to support skin changes Added code that refreshes the player's skin by sending packets with a special order, telling the client to respawn the player and re-apply the game profile -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index 210e3bc4..b0335684 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -101,6 +101,12 @@ public class CraftWorld implements World { - } - // Paper end - -+ // Paper start - Provide dimension number of world -+ public int getDimension() { -+ return world.dimension; -+ } -+ // Paper end -+ - private static final Random rand = new Random(); - - public CraftWorld(WorldServer world, ChunkGenerator gen, Environment env) { diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java index 6cbf429f..57f17120 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java From 7dd5837dd00436339a68e4412e71f7e677fd7cb8 Mon Sep 17 00:00:00 2001 From: NickAcPT <32451103+NickAcPT@users.noreply.github.com> Date: Sat, 21 Jul 2018 18:17:54 +0100 Subject: [PATCH 4/9] Fixed more stuff --- ...-profile-API-to-support-skin-changes.patch | 27 ++++++------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/Spigot-Server-Patches/0336-Extend-player-profile-API-to-support-skin-changes.patch b/Spigot-Server-Patches/0336-Extend-player-profile-API-to-support-skin-changes.patch index d26ee3fbd9..78bf913b3a 100644 --- a/Spigot-Server-Patches/0336-Extend-player-profile-API-to-support-skin-changes.patch +++ b/Spigot-Server-Patches/0336-Extend-player-profile-API-to-support-skin-changes.patch @@ -1,4 +1,4 @@ -From 95e357f7dd549b278714a9ea76bf6d11d6c2b5fd Mon Sep 17 00:00:00 2001 +From 8a9ff94f0db13f56873de8e6f555bc7893cffc31 Mon Sep 17 00:00:00 2001 From: NickAcPT <32451103+NickAcPT@users.noreply.github.com> Date: Sat, 21 Jul 2018 01:30:30 +0100 Subject: [PATCH] Extend player profile API to support skin changes @@ -6,18 +6,10 @@ Subject: [PATCH] Extend player profile API to support skin changes Added code that refreshes the player's skin by sending packets with a special order, telling the client to respawn the player and re-apply the game profile diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -index 6cbf429f..57f17120 100644 +index 6cbf429f..01335b6b 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -@@ -55,6 +55,7 @@ import org.bukkit.craftbukkit.CraftStatistic; - import org.bukkit.craftbukkit.CraftWorld; - import org.bukkit.craftbukkit.advancement.CraftAdvancement; - import org.bukkit.craftbukkit.advancement.CraftAdvancementProgress; -+import org.bukkit.craftbukkit.inventory.CraftItemStack; - import org.bukkit.craftbukkit.map.CraftMapView; - import org.bukkit.craftbukkit.map.RenderData; - import org.bukkit.craftbukkit.scoreboard.CraftScoreboard; -@@ -1176,10 +1177,41 @@ public class CraftPlayer extends CraftHumanEntity implements Player { +@@ -1176,10 +1176,38 @@ public class CraftPlayer extends CraftHumanEntity implements Player { for (EntityPlayer player : players) { player.getBukkitEntity().reregisterPlayer(self); } @@ -30,20 +22,17 @@ index 6cbf429f..57f17120 100644 + private void refreshPlayer() { + EntityPlayer entityplayer = getHandle(); + -+ Packet respawn = new PacketPlayOutRespawn(entityplayer.dimension, entityplayer.world.getDifficulty(), entityplayer.world.getWorldData().getType(), entityplayer.playerInteractManager.getGameMode()); -+ Location l = getLocation(); -+ Packet pos = new PacketPlayOutPosition(l.getX(), l.getY(), l.getZ(), l.getYaw(), l.getPitch(), new HashSet<>(), 0); -+ Packet slot = new PacketPlayOutHeldItemSlot(getInventory().getHeldItemSlot()); ++ Location loc = getLocation(); + + EntityPlayer handle = getHandle(); -+ PlayerConnection playerCon = handle.playerConnection; ++ PlayerConnection connection = handle.playerConnection; + reregisterPlayer(handle); + + //Respawn the player then update their position and selected slot -+ playerCon.sendPacket(respawn); ++ connection.sendPacket(new PacketPlayOutRespawn(entityplayer.dimension, entityplayer.world.getDifficulty(), entityplayer.world.getWorldData().getType(), entityplayer.playerInteractManager.getGameMode())); + handle.updateAbilities(); -+ playerCon.sendPacket(pos); -+ playerCon.sendPacket(slot); ++ connection.sendPacket(new PacketPlayOutPosition(loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch(), new HashSet<>(), 0)); ++ connection.sendPacket(new PacketPlayOutHeldItemSlot(getInventory().getHeldItemSlot())); + + updateScaledHealth(); + this.updateInventory(); From 009031a21f41ff1bb31c4391ac4d2af69d8031fa Mon Sep 17 00:00:00 2001 From: Aikar Date: Mon, 23 Jul 2018 21:09:25 -0400 Subject: [PATCH 5/9] Bring some 1.13 authors to master --- LICENSE.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/LICENSE.md b/LICENSE.md index 7d4663268a..1bb8996e25 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -34,4 +34,6 @@ Brokkonaut vemacs stonar96 Hugo Manrique +Andrew Steinborn +willies952002 ``` From 5c4367f9d3e94751266c6f5640fef47a9a8a8baf Mon Sep 17 00:00:00 2001 From: Aikar Date: Mon, 23 Jul 2018 22:50:47 -0400 Subject: [PATCH 6/9] Add more entity debug info --- ...ies-option-to-debug-dupe-uuid-issues.patch | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/Spigot-Server-Patches/0336-Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch b/Spigot-Server-Patches/0336-Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch index c5f5055e2a..701004cc84 100644 --- a/Spigot-Server-Patches/0336-Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch +++ b/Spigot-Server-Patches/0336-Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch @@ -1,12 +1,31 @@ -From b061a231214e88cde43e0c3880f779723828b2b4 Mon Sep 17 00:00:00 2001 +From 46caf77f4a0b159f721e681f44228a02b3cfeee2 Mon Sep 17 00:00:00 2001 From: Aikar Date: Sat, 21 Jul 2018 08:25:40 -0400 Subject: [PATCH] Add Debug Entities option to debug dupe uuid issues Add -Ddebug.entities=true to your JVM flags to gain more information +diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java +index bcce5e8b7..f5b9aa561 100644 +--- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java ++++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java +@@ -351,6 +351,14 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver { + + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); ++ // Paper start ++ if (entity.getChunkX() != chunk.locX || entity.getChunkZ() != chunk.locZ) { ++ LogManager.getLogger().error(entity + " is not actually in this chunk! Report this to https://github.com/PaperMC/Paper/issues/1223", new Throwable()); ++ } ++ if ((int)Math.floor(entity.locX) >> 4 != chunk.locX || (int)Math.floor(entity.locZ) >> 4 != chunk.locZ) { ++ LogManager.getLogger().error(entity + " will be leaving this chunk but saved to it. Report this to https://github.com/PaperMC/Paper/issues/1223", new Throwable()); ++ } ++ // Paper end + + nbttagcompound1 = new NBTTagCompound(); + if (entity.d(nbttagcompound1)) { diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java -index f6b755863..108654d63 100644 +index 99dac412f..0d3af8cb7 100644 --- a/src/main/java/net/minecraft/server/Entity.java +++ b/src/main/java/net/minecraft/server/Entity.java @@ -73,6 +73,7 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper From 9ec7ffa358eb8193cbddab6e3536820552002e80 Mon Sep 17 00:00:00 2001 From: Aikar Date: Mon, 23 Jul 2018 22:50:56 -0400 Subject: [PATCH 7/9] Reduce and improve dupe uuid resolve message --- .../0338-Duplicate-UUID-Resolve-Option.patch | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Spigot-Server-Patches/0338-Duplicate-UUID-Resolve-Option.patch b/Spigot-Server-Patches/0338-Duplicate-UUID-Resolve-Option.patch index 5e9ec904e8..ec5afadae3 100644 --- a/Spigot-Server-Patches/0338-Duplicate-UUID-Resolve-Option.patch +++ b/Spigot-Server-Patches/0338-Duplicate-UUID-Resolve-Option.patch @@ -1,4 +1,4 @@ -From 2a6d7b5cf9db036928914e854416039d7c8a14af Mon Sep 17 00:00:00 2001 +From 60078ed225caffbeab2732eb4a64364d9d342341 Mon Sep 17 00:00:00 2001 From: Aikar Date: Sat, 21 Jul 2018 14:27:34 -0400 Subject: [PATCH] Duplicate UUID Resolve Option @@ -78,7 +78,7 @@ index 14c8edeff..e3f6557e1 100644 + } } diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java -index 04adf4e3c..ea9559583 100644 +index 04adf4e3c..b31c301ec 100644 --- a/src/main/java/net/minecraft/server/Chunk.java +++ b/src/main/java/net/minecraft/server/Chunk.java @@ -1,5 +1,10 @@ @@ -108,7 +108,7 @@ index 04adf4e3c..ea9559583 100644 } int k = MathHelper.floor(entity.locY / 16.0D); -@@ -851,6 +858,37 @@ public class Chunk { +@@ -851,6 +858,35 @@ public class Chunk { for (int j = 0; j < i; ++j) { List entityslice = aentityslice[j]; // Spigot @@ -126,13 +126,11 @@ index 04adf4e3c..ea9559583 100644 + switch (mode) { + case REGEN: { + entity.setUUID(UUID.randomUUID()); -+ logger.error("Duplicate UUID found used by " + other); -+ logger.error("Regenerated a new UUID for " + entity); ++ logger.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", regenerated UUID for " + entity + ". See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about."); + break; + } + case DELETE: { -+ logger.error("Duplicate UUID found used by " + other); -+ logger.error("Deleting duplicate entity " + entity); ++ logger.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", deleted entity " + entity + ". See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about."); + entity.die(); + iterator.remove(); + break; From ba80c7e58054944018612c415d17e7425d5d07e3 Mon Sep 17 00:00:00 2001 From: Aikar Date: Mon, 23 Jul 2018 22:54:52 -0400 Subject: [PATCH 8/9] Mark chunk dirty on entity changes This is to hopefully help avoid any chunk saving entity issues. Marks the chunk that it NEEDS to be saved, ensuring the latest state gets saved. --- ...-anytime-entities-change-to-guarante.patch | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 Spigot-Server-Patches/0339-Mark-chunk-dirty-anytime-entities-change-to-guarante.patch diff --git a/Spigot-Server-Patches/0339-Mark-chunk-dirty-anytime-entities-change-to-guarante.patch b/Spigot-Server-Patches/0339-Mark-chunk-dirty-anytime-entities-change-to-guarante.patch new file mode 100644 index 0000000000..bbf41e099b --- /dev/null +++ b/Spigot-Server-Patches/0339-Mark-chunk-dirty-anytime-entities-change-to-guarante.patch @@ -0,0 +1,30 @@ +From 37c0cfe11574838ad8c24863a4b926e0b88878d5 Mon Sep 17 00:00:00 2001 +From: Aikar +Date: Mon, 23 Jul 2018 22:18:31 -0400 +Subject: [PATCH] Mark chunk dirty anytime entities change to guarantee it + saves + + +diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java +index b31c301ec..6de053781 100644 +--- a/src/main/java/net/minecraft/server/Chunk.java ++++ b/src/main/java/net/minecraft/server/Chunk.java +@@ -683,6 +683,7 @@ public class Chunk { + entity.ad = this.locZ; + this.entitySlices[k].add(entity); + // Paper start ++ this.markDirty(); + entity.setCurrentChunk(this); + entityCounts.increment(entity.entityKeyString); + if (entity instanceof EntityItem) { +@@ -725,6 +726,7 @@ public class Chunk { + + // Paper start + if (!this.entitySlices[i].remove(entity)) { return; } ++ this.markDirty(); + entity.setCurrentChunk(null); + entityCounts.decrement(entity.entityKeyString); + if (entity instanceof EntityItem) { +-- +2.18.0 + From d155366b5420147429817f3cce515518bcbdcf58 Mon Sep 17 00:00:00 2001 From: Aikar Date: Mon, 23 Jul 2018 22:55:27 -0400 Subject: [PATCH 9/9] Add some debug for entity slices If we find any entity in an unexpected state, log it so we can discover what potentially put it in that state to relate to issue #1223 --- ...dd-some-Debug-to-Chunk-Entity-slices.patch | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 Spigot-Server-Patches/0340-Add-some-Debug-to-Chunk-Entity-slices.patch diff --git a/Spigot-Server-Patches/0340-Add-some-Debug-to-Chunk-Entity-slices.patch b/Spigot-Server-Patches/0340-Add-some-Debug-to-Chunk-Entity-slices.patch new file mode 100644 index 0000000000..e59455dc49 --- /dev/null +++ b/Spigot-Server-Patches/0340-Add-some-Debug-to-Chunk-Entity-slices.patch @@ -0,0 +1,77 @@ +From 80a062ecc95b8346f6a9161e375e41d5f5df3882 Mon Sep 17 00:00:00 2001 +From: Aikar +Date: Mon, 23 Jul 2018 22:44:23 -0400 +Subject: [PATCH] Add some Debug to Chunk Entity slices + +If we detect unexpected state, log and try to recover + +This should hopefully avoid duplicate entities ever being created +if the entity was to end up in 2 different chunk slices + +diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java +index 6de053781..be0b411e5 100644 +--- a/src/main/java/net/minecraft/server/Chunk.java ++++ b/src/main/java/net/minecraft/server/Chunk.java +@@ -681,8 +681,33 @@ public class Chunk { + entity.ab = this.locX; + entity.ac = k; + entity.ad = this.locZ; +- this.entitySlices[k].add(entity); ++ + // Paper start ++ List entitySlice = this.entitySlices[k]; ++ boolean inThis = entitySlice.contains(entity); ++ if (entity.entitySlice != null || inThis) { ++ if (entity.entitySlice == entitySlice || inThis) { ++ LogManager.getLogger().warn(entity + " was already in this chunk section! Report this to https://github.com/PaperMC/Paper/issues/1223"); ++ new Throwable().printStackTrace(); ++ return; ++ } else { ++ LogManager.getLogger().warn(entity + " is still in another ChunkSection! Report this to https://github.com/PaperMC/Paper/issues/1223"); ++ ++ Chunk chunk = entity.getCurrentChunk(); ++ if (chunk != null) { ++ if (chunk != this) { ++ LogManager.getLogger().warn(entity + " was in another chunk at that! " + chunk.locX + "," + chunk.locZ); ++ } ++ chunk.removeEntity(entity); ++ } else { ++ removeEntity(entity); ++ } ++ new Throwable().printStackTrace(); ++ } ++ } ++ entity.entitySlice = entitySlice; ++ entitySlice.add(entity); ++ + this.markDirty(); + entity.setCurrentChunk(this); + entityCounts.increment(entity.entityKeyString); +@@ -726,6 +751,12 @@ public class Chunk { + + // Paper start + if (!this.entitySlices[i].remove(entity)) { return; } ++ if (entitySlices[i] == entity.entitySlice) { ++ entity.entitySlice = null; ++ } else { ++ LogManager.getLogger().warn(entity + " was removed from a entitySlice we did not expect. Report this to https://github.com/PaperMC/Paper/issues/1223"); ++ new Throwable().printStackTrace(); ++ } + this.markDirty(); + entity.setCurrentChunk(null); + entityCounts.decrement(entity.entityKeyString); +diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java +index 7188d0c99..b3120d7cc 100644 +--- a/src/main/java/net/minecraft/server/Entity.java ++++ b/src/main/java/net/minecraft/server/Entity.java +@@ -59,6 +59,7 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper + } + } + }; ++ Object entitySlice = null; + // Paper end + static boolean isLevelAtLeast(NBTTagCompound tag, int level) { + return tag.hasKey("Bukkit.updateLevel") && tag.getInt("Bukkit.updateLevel") >= level; +-- +2.18.0 +