From 4cccd48a4cd0b3f898e0b3dd79a0b737284ba42e 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 | 27 +++ ...-profile-API-to-support-skin-changes.patch | 161 ++++++++++++++++++ 2 files changed, 188 insertions(+) create mode 100644 Spigot-API-Patches/Extend-player-profile-API-to-support-skin-changes.patch create mode 100644 Spigot-Server-Patches/Extend-player-profile-API-to-support-skin-changes.patch diff --git a/Spigot-API-Patches/Extend-player-profile-API-to-support-skin-changes.patch b/Spigot-API-Patches/Extend-player-profile-API-to-support-skin-changes.patch new file mode 100644 index 000000000..569b4caf8 --- /dev/null +++ b/Spigot-API-Patches/Extend-player-profile-API-to-support-skin-changes.patch @@ -0,0 +1,27 @@ +From 0000000000000000000000000000000000000000 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 +@@ -0,0 +0,0 @@ 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 +-- \ No newline at end of file diff --git a/Spigot-Server-Patches/Extend-player-profile-API-to-support-skin-changes.patch b/Spigot-Server-Patches/Extend-player-profile-API-to-support-skin-changes.patch new file mode 100644 index 000000000..3a13f606b --- /dev/null +++ b/Spigot-Server-Patches/Extend-player-profile-API-to-support-skin-changes.patch @@ -0,0 +1,161 @@ +From 0000000000000000000000000000000000000000 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 +@@ -0,0 +0,0 @@ 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 +@@ -0,0 +0,0 @@ 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 +@@ -0,0 +0,0 @@ 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 +@@ -0,0 +0,0 @@ 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; +@@ -0,0 +0,0 @@ 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) { +-- \ No newline at end of file From a7f45fb1b4bf25314328e1fb01ce234db8bfa303 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 | 27 ---- ...-profile-API-to-support-skin-changes.patch | 124 +++--------------- 2 files changed, 21 insertions(+), 130 deletions(-) delete mode 100644 Spigot-API-Patches/Extend-player-profile-API-to-support-skin-changes.patch diff --git a/Spigot-API-Patches/Extend-player-profile-API-to-support-skin-changes.patch b/Spigot-API-Patches/Extend-player-profile-API-to-support-skin-changes.patch deleted file mode 100644 index 569b4caf8..000000000 --- a/Spigot-API-Patches/Extend-player-profile-API-to-support-skin-changes.patch +++ /dev/null @@ -1,27 +0,0 @@ -From 0000000000000000000000000000000000000000 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 -@@ -0,0 +0,0 @@ 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 --- \ No newline at end of file diff --git a/Spigot-Server-Patches/Extend-player-profile-API-to-support-skin-changes.patch b/Spigot-Server-Patches/Extend-player-profile-API-to-support-skin-changes.patch index 3a13f606b..73e1bbede 100644 --- a/Spigot-Server-Patches/Extend-player-profile-API-to-support-skin-changes.patch +++ b/Spigot-Server-Patches/Extend-player-profile-API-to-support-skin-changes.patch @@ -5,51 +5,6 @@ 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 -@@ -0,0 +0,0 @@ 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 -@@ -0,0 +0,0 @@ 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 @@ -0,0 +0,0 @@ import org.bukkit.craftbukkit.CraftStatistic; @@ -80,13 +35,9 @@ index 6cbf429f..bd743be5 100644 import org.bukkit.craftbukkit.map.RenderData; import org.bukkit.craftbukkit.scoreboard.CraftScoreboard; @@ -0,0 +0,0 @@ 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); -- } + 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 8672424ca11aa40b75d9ffb26fd02434d8675a79 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 --- ...er-profile-API-to-support-skin-changes.patch | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/Spigot-Server-Patches/Extend-player-profile-API-to-support-skin-changes.patch b/Spigot-Server-Patches/Extend-player-profile-API-to-support-skin-changes.patch index 73e1bbede..157636b28 100644 --- a/Spigot-Server-Patches/Extend-player-profile-API-to-support-skin-changes.patch +++ b/Spigot-Server-Patches/Extend-player-profile-API-to-support-skin-changes.patch @@ -5,23 +5,6 @@ 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 -@@ -0,0 +0,0 @@ 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 9526c764387f57ed118f0af12d9659f8d8f36c31 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 | 23 +++++-------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/Spigot-Server-Patches/Extend-player-profile-API-to-support-skin-changes.patch b/Spigot-Server-Patches/Extend-player-profile-API-to-support-skin-changes.patch index 157636b28..5c151536a 100644 --- a/Spigot-Server-Patches/Extend-player-profile-API-to-support-skin-changes.patch +++ b/Spigot-Server-Patches/Extend-player-profile-API-to-support-skin-changes.patch @@ -6,17 +6,9 @@ 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 -@@ -0,0 +0,0 @@ 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; @@ -0,0 +0,0 @@ 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 be34f22bf90ce8c8a7923f8886f9e25b63c7fe5c 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 c5341db55..f585b44f3 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -34,4 +34,6 @@ Brokkonaut vemacs stonar96 Hugo Manrique +Andrew Steinborn +willies952002 ``` From 64077154ff5ac360301bcc885a4be3d563946dbd 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 | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/Spigot-Server-Patches/Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch b/Spigot-Server-Patches/Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch index dceaca58c..112f42f08 100644 --- a/Spigot-Server-Patches/Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch +++ b/Spigot-Server-Patches/Add-Debug-Entities-option-to-debug-dupe-uuid-issues.patch @@ -5,8 +5,27 @@ 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 +@@ -0,0 +0,0 @@ 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 @@ -0,0 +0,0 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper From 3b12d748b40ea8117d83b1f2ee8ee3008767d5aa 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 --- Spigot-Server-Patches/Duplicate-UUID-Resolve-Option.patch | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Spigot-Server-Patches/Duplicate-UUID-Resolve-Option.patch b/Spigot-Server-Patches/Duplicate-UUID-Resolve-Option.patch index 56262f7bc..00494ddb1 100644 --- a/Spigot-Server-Patches/Duplicate-UUID-Resolve-Option.patch +++ b/Spigot-Server-Patches/Duplicate-UUID-Resolve-Option.patch @@ -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 @@ -0,0 +0,0 @@ @@ -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 f41ff893ddadba4256561eb350fa6dca6c8d9f7a 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 | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 Spigot-Server-Patches/Mark-chunk-dirty-anytime-entities-change-to-guarante.patch diff --git a/Spigot-Server-Patches/Mark-chunk-dirty-anytime-entities-change-to-guarante.patch b/Spigot-Server-Patches/Mark-chunk-dirty-anytime-entities-change-to-guarante.patch new file mode 100644 index 000000000..907f33367 --- /dev/null +++ b/Spigot-Server-Patches/Mark-chunk-dirty-anytime-entities-change-to-guarante.patch @@ -0,0 +1,28 @@ +From 0000000000000000000000000000000000000000 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 +@@ -0,0 +0,0 @@ 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) { +@@ -0,0 +0,0 @@ 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) { +-- \ No newline at end of file From 782c68dad715d371eb6bcd953320132aa9b96ecc 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 | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 Spigot-Server-Patches/Add-some-Debug-to-Chunk-Entity-slices.patch diff --git a/Spigot-Server-Patches/Add-some-Debug-to-Chunk-Entity-slices.patch b/Spigot-Server-Patches/Add-some-Debug-to-Chunk-Entity-slices.patch new file mode 100644 index 000000000..c3e856e8f --- /dev/null +++ b/Spigot-Server-Patches/Add-some-Debug-to-Chunk-Entity-slices.patch @@ -0,0 +1,75 @@ +From 0000000000000000000000000000000000000000 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 +@@ -0,0 +0,0 @@ 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); +@@ -0,0 +0,0 @@ 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 +@@ -0,0 +0,0 @@ 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; +-- \ No newline at end of file