diff --git a/Spigot-Server-Patches/Add-APIs-to-replace-OfflinePlayer-getLastPlayed.patch b/Spigot-Server-Patches/Add-APIs-to-replace-OfflinePlayer-getLastPlayed.patch index d229cec642..902f4f1635 100644 --- a/Spigot-Server-Patches/Add-APIs-to-replace-OfflinePlayer-getLastPlayed.patch +++ b/Spigot-Server-Patches/Add-APIs-to-replace-OfflinePlayer-getLastPlayed.patch @@ -16,7 +16,7 @@ intent to remove) and replace it with two new methods, clearly named and documented as to their purpose. diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java -index d3886a6e04..2d4021784f 100644 +index f41fd576d..1665a302a 100644 --- a/src/main/java/net/minecraft/server/EntityPlayer.java +++ b/src/main/java/net/minecraft/server/EntityPlayer.java @@ -0,0 +0,0 @@ public class EntityPlayer extends EntityHuman implements ICrafting { @@ -28,7 +28,7 @@ index d3886a6e04..2d4021784f 100644 public boolean queueHealthUpdatePacket = false; public net.minecraft.server.PacketPlayOutUpdateHealth queuedHealthUpdatePacket; diff --git a/src/main/java/net/minecraft/server/PlayerList.java b/src/main/java/net/minecraft/server/PlayerList.java -index 049d702cb9..52aa83f51f 100644 +index e46436623..659ce2181 100644 --- a/src/main/java/net/minecraft/server/PlayerList.java +++ b/src/main/java/net/minecraft/server/PlayerList.java @@ -0,0 +0,0 @@ public abstract class PlayerList { @@ -38,9 +38,9 @@ index 049d702cb9..52aa83f51f 100644 + entityplayer.loginTime = System.currentTimeMillis(); // Paper GameProfile gameprofile = entityplayer.getProfile(); UserCache usercache = this.server.getUserCache(); - GameProfile gameprofile1 = usercache.a(gameprofile.getId()); + GameProfile gameprofile1 = usercache.getProfile(gameprofile.getId()); diff --git a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java -index c1ef1c950d..3824180ee5 100644 +index c1ef1c950..3824180ee 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java @@ -0,0 +0,0 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa @@ -106,7 +106,7 @@ index c1ef1c950d..3824180ee5 100644 public Location getBedSpawnLocation() { NBTTagCompound data = getData(); diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -index 3d60e98749..ea4e569e5a 100644 +index ad70bd5ba..04fc8e233 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 @@ public class CraftPlayer extends CraftHumanEntity implements Player { diff --git a/Spigot-Server-Patches/Add-Heightmap-API.patch b/Spigot-Server-Patches/Add-Heightmap-API.patch index 4502257b91..62966a96c5 100644 --- a/Spigot-Server-Patches/Add-Heightmap-API.patch +++ b/Spigot-Server-Patches/Add-Heightmap-API.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Add Heightmap API diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java -index dd2a9c6e59..e3b4e30e65 100644 +index dfc695084..10bb06489 100644 --- a/src/main/java/net/minecraft/server/World.java +++ b/src/main/java/net/minecraft/server/World.java -@@ -0,0 +0,0 @@ public abstract class World implements IIBlockAccess, GeneratorAccess, AutoClose +@@ -0,0 +0,0 @@ public abstract class World implements GeneratorAccess, AutoCloseable { } } @@ -20,7 +20,7 @@ index dd2a9c6e59..e3b4e30e65 100644 if (i >= -30000000 && j >= -30000000 && i < 30000000 && j < 30000000) { diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index 78a2c47c5a..95fdc3bf64 100644 +index 578fe8d19..12362d560 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 { diff --git a/Spigot-Server-Patches/Add-LivingEntity-getTargetEntity.patch b/Spigot-Server-Patches/Add-LivingEntity-getTargetEntity.patch index e9fb1aad74..33c253ecfd 100644 --- a/Spigot-Server-Patches/Add-LivingEntity-getTargetEntity.patch +++ b/Spigot-Server-Patches/Add-LivingEntity-getTargetEntity.patch @@ -46,7 +46,7 @@ index 4f60b931a..c950139c0 100644 double[] adouble = new double[]{1.0D}; double d0 = vec3d1.x - vec3d.x; diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java -index 75da08596..046e7e031 100644 +index 0f2480521..2e8aedf5f 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 INamableTileEntity, ICommandListener, Ke @@ -54,19 +54,19 @@ index 75da08596..046e7e031 100644 } + public final Vec3D getEyePosition(float partialTicks) { return j(partialTicks); } // Paper - OBFHELPER - public Vec3D j(float f) { + public final Vec3D j(float f) { if (f == 1.0F) { - return new Vec3D(this.locX, this.locY + (double) this.getHeadHeight(), this.locZ); + return new Vec3D(this.locX(), this.getHeadY(), this.locZ()); @@ -0,0 +0,0 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke return this.getPassengers().size() < 1; } -+ public final float getCollisionBorderSize() { return aS(); } // Paper - OBFHELPER - public float aS() { ++ public final float getCollisionBorderSize() { return aV(); } // Paper - OBFHELPER + public float aV() { return 0.0F; } diff --git a/src/main/java/net/minecraft/server/EntityLiving.java b/src/main/java/net/minecraft/server/EntityLiving.java -index d23968a7c..31d14b19b 100644 +index 317321395..1b5acf77e 100644 --- a/src/main/java/net/minecraft/server/EntityLiving.java +++ b/src/main/java/net/minecraft/server/EntityLiving.java @@ -0,0 +0,0 @@ public abstract class EntityLiving extends Entity { @@ -108,7 +108,7 @@ index d23968a7c..31d14b19b 100644 public int getShieldBlockingDelay() { diff --git a/src/main/java/net/minecraft/server/IEntitySelector.java b/src/main/java/net/minecraft/server/IEntitySelector.java -index 9bf191945..56488b78d 100644 +index c1f462d9d..498f38109 100644 --- a/src/main/java/net/minecraft/server/IEntitySelector.java +++ b/src/main/java/net/minecraft/server/IEntitySelector.java @@ -0,0 +0,0 @@ public final class IEntitySelector { diff --git a/Spigot-Server-Patches/Add-PlayerConnectionCloseEvent.patch b/Spigot-Server-Patches/Add-PlayerConnectionCloseEvent.patch index 090f30da99..0a05726e1c 100644 --- a/Spigot-Server-Patches/Add-PlayerConnectionCloseEvent.patch +++ b/Spigot-Server-Patches/Add-PlayerConnectionCloseEvent.patch @@ -34,7 +34,7 @@ how PlayerPreLoginEvent interacts with PlayerConnectionCloseEvent is undefined. diff --git a/src/main/java/net/minecraft/server/LoginListener.java b/src/main/java/net/minecraft/server/LoginListener.java -index 41b2c90c3a..a8773037e5 100644 +index c3d57461d..a6b22254a 100644 --- a/src/main/java/net/minecraft/server/LoginListener.java +++ b/src/main/java/net/minecraft/server/LoginListener.java @@ -0,0 +0,0 @@ public class LoginListener implements PacketLoginInListener { @@ -50,7 +50,7 @@ index 41b2c90c3a..a8773037e5 100644 private SecretKey loginKey; private EntityPlayer l; diff --git a/src/main/java/net/minecraft/server/NetworkManager.java b/src/main/java/net/minecraft/server/NetworkManager.java -index d4aad8a5b7..b1dededc15 100644 +index d4aad8a5b..b1dededc1 100644 --- a/src/main/java/net/minecraft/server/NetworkManager.java +++ b/src/main/java/net/minecraft/server/NetworkManager.java @@ -0,0 +0,0 @@ public class NetworkManager extends SimpleChannelInboundHandler> { diff --git a/Spigot-Server-Patches/Add-Velocity-IP-Forwarding-Support.patch b/Spigot-Server-Patches/Add-Velocity-IP-Forwarding-Support.patch index 9b6ef2bfec..5add4eb79f 100644 --- a/Spigot-Server-Patches/Add-Velocity-IP-Forwarding-Support.patch +++ b/Spigot-Server-Patches/Add-Velocity-IP-Forwarding-Support.patch @@ -14,7 +14,7 @@ forwarding, and is integrated into the Minecraft login process by using the 1.13 login plugin message packet. diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java -index d2c039ea2f..63d8602927 100644 +index 092bff78a..7e85a0224 100644 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java @@ -0,0 +0,0 @@ import java.io.IOException; @@ -55,7 +55,7 @@ index d2c039ea2f..63d8602927 100644 } diff --git a/src/main/java/com/destroystokyo/paper/proxy/VelocityProxy.java b/src/main/java/com/destroystokyo/paper/proxy/VelocityProxy.java new file mode 100644 -index 0000000000..fdd8708f97 +index 000000000..fdd8708f9 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/proxy/VelocityProxy.java @@ -0,0 +0,0 @@ @@ -127,7 +127,7 @@ index 0000000000..fdd8708f97 + } +} diff --git a/src/main/java/net/minecraft/server/LoginListener.java b/src/main/java/net/minecraft/server/LoginListener.java -index d6befec184..41b2c90c3a 100644 +index a985ae7ba..c3d57461d 100644 --- a/src/main/java/net/minecraft/server/LoginListener.java +++ b/src/main/java/net/minecraft/server/LoginListener.java @@ -0,0 +0,0 @@ public class LoginListener implements PacketLoginInListener { @@ -203,7 +203,7 @@ index d6befec184..41b2c90c3a 100644 } diff --git a/src/main/java/net/minecraft/server/NetworkManager.java b/src/main/java/net/minecraft/server/NetworkManager.java -index 08e314af7c..046196d54d 100644 +index 08e314af7..046196d54 100644 --- a/src/main/java/net/minecraft/server/NetworkManager.java +++ b/src/main/java/net/minecraft/server/NetworkManager.java @@ -0,0 +0,0 @@ public class NetworkManager extends SimpleChannelInboundHandler> { @@ -216,7 +216,7 @@ index 08e314af7c..046196d54d 100644 public java.util.UUID spoofedUUID; public com.mojang.authlib.properties.Property[] spoofedProfile; diff --git a/src/main/java/net/minecraft/server/PacketDataSerializer.java b/src/main/java/net/minecraft/server/PacketDataSerializer.java -index fa2d3ce8cb..dac560c63f 100644 +index 93ae6dcd7..fb9186b3f 100644 --- a/src/main/java/net/minecraft/server/PacketDataSerializer.java +++ b/src/main/java/net/minecraft/server/PacketDataSerializer.java @@ -0,0 +0,0 @@ public class PacketDataSerializer extends ByteBuf { @@ -244,7 +244,7 @@ index fa2d3ce8cb..dac560c63f 100644 int j = this.i(); diff --git a/src/main/java/net/minecraft/server/PacketLoginInCustomPayload.java b/src/main/java/net/minecraft/server/PacketLoginInCustomPayload.java -index 4d1f441395..c1ca6f9501 100644 +index 4d1f44139..c1ca6f950 100644 --- a/src/main/java/net/minecraft/server/PacketLoginInCustomPayload.java +++ b/src/main/java/net/minecraft/server/PacketLoginInCustomPayload.java @@ -0,0 +0,0 @@ import java.io.IOException; @@ -259,7 +259,7 @@ index 4d1f441395..c1ca6f9501 100644 public PacketLoginInCustomPayload() {} diff --git a/src/main/java/net/minecraft/server/PacketLoginOutCustomPayload.java b/src/main/java/net/minecraft/server/PacketLoginOutCustomPayload.java -index ae74dc9e18..7eb230f1b2 100644 +index ae74dc9e1..7eb230f1b 100644 --- a/src/main/java/net/minecraft/server/PacketLoginOutCustomPayload.java +++ b/src/main/java/net/minecraft/server/PacketLoginOutCustomPayload.java @@ -0,0 +0,0 @@ public class PacketLoginOutCustomPayload implements Packet bA = DataWatcher.a(EntityWitch.class, DataWatcherRegistry.i); -- private int bB; -+ private int bB; public int getPotionUseTimeLeft() { return bB; } public void setPotionUseTimeLeft(int timeLeft) { bB = timeLeft; } // Paper - OBFHELPER - private PathfinderGoalNearestHealableRaider bC; - private PathfinderGoalNearestAttackableTargetWitch bD; +- private static final AttributeModifier bw = (new AttributeModifier(EntityWitch.b, "Drinking speed penalty", -0.25D, AttributeModifier.Operation.ADDITION)).a(false); ++ private static final AttributeModifier bw = (new AttributeModifier(EntityWitch.b, "Drinking speed penalty", -0.25D, AttributeModifier.Operation.ADDITION)).a(false); private static final AttributeModifier DRINKING_SPEED = bz; // Paper - OBFHELPER + private static final DataWatcherObject bx = DataWatcher.a(EntityWitch.class, DataWatcherRegistry.i); +- private int by; ++ private int by; public int getPotionUseTimeLeft() { return by; } public void setPotionUseTimeLeft(int timeLeft) { by = timeLeft; } // Paper - OBFHELPER + private PathfinderGoalNearestHealableRaider bz; + private PathfinderGoalNearestAttackableTargetWitch bA; @@ -0,0 +0,0 @@ public class EntityWitch extends EntityRaider implements IRangedEntity { return SoundEffects.ENTITY_WITCH_DEATH; } -+ public void setDrinkingPotion(boolean drinkingPotion) { s(drinkingPotion); } // Paper - OBFHELPER - public void s(boolean flag) { - this.getDataWatcher().set(EntityWitch.bA, flag); ++ public void setDrinkingPotion(boolean drinkingPotion) { t(drinkingPotion); } // Paper - OBFHELPER + public void t(boolean flag) { + this.getDataWatcher().set(EntityWitch.bx, flag); } + public boolean isDrinkingPotion() { return l(); } // Paper - OBFHELPER public boolean l() { - return (Boolean) this.getDataWatcher().get(EntityWitch.bA); + return (Boolean) this.getDataWatcher().get(EntityWitch.bx); } @@ -0,0 +0,0 @@ public class EntityWitch extends EntityRaider implements IRangedEntity { } @@ -67,13 +67,13 @@ index ff4e73fff3..0b849f37c5 100644 +// attributeinstance.addModifier(EntityWitch.bz); + this.setDrinkingPotion(PotionUtil.addPotionToItemStack(new ItemStack(Items.POTION), potionregistry)); // Paper end -- this.bB = this.getItemInMainHand().k(); -- this.s(true); -- this.world.playSound((EntityHuman) null, this.locX, this.locY, this.locZ, SoundEffects.ENTITY_WITCH_DRINK, this.getSoundCategory(), 1.0F, 0.8F + this.random.nextFloat() * 0.4F); +- this.by = this.getItemInMainHand().k(); +- this.t(true); +- this.world.playSound((EntityHuman) null, this.locX(), this.locY(), this.locZ(), SoundEffects.ENTITY_WITCH_DRINK, this.getSoundCategory(), 1.0F, 0.8F + this.random.nextFloat() * 0.4F); - AttributeInstance attributeinstance = this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED); - -- attributeinstance.removeModifier(EntityWitch.bz); -- attributeinstance.addModifier(EntityWitch.bz); +- attributeinstance.removeModifier(EntityWitch.bw); +- attributeinstance.addModifier(EntityWitch.bw); } } @@ -86,7 +86,7 @@ index ff4e73fff3..0b849f37c5 100644 + setSlot(EnumItemSlot.MAINHAND, CraftItemStack.asNMSCopy(WitchReadyPotionEvent.process((Witch) getBukkitEntity(), CraftItemStack.asCraftMirror(potion)))); + setPotionUseTimeLeft(getItemInMainHand().getItemUseMaxDuration()); + setDrinkingPotion(true); -+ world.sendSoundEffect(null, locX, locY, locZ, SoundEffects.ENTITY_WITCH_DRINK, getSoundCategory(), 1.0F, 0.8F + random.nextFloat() * 0.4F); ++ world.sendSoundEffect(null, locX(), locY(), locZ(), SoundEffects.ENTITY_WITCH_DRINK, getSoundCategory(), 1.0F, 0.8F + random.nextFloat() * 0.4F); + AttributeInstance attributeinstance = getAttributeInstance(GenericAttributes.MOVEMENT_SPEED); + attributeinstance.removeModifier(EntityWitch.DRINKING_SPEED); + attributeinstance.addModifier(EntityWitch.DRINKING_SPEED); @@ -94,10 +94,10 @@ index ff4e73fff3..0b849f37c5 100644 + // Paper end + @Override - public SoundEffect dV() { + public SoundEffect eq() { return SoundEffects.ENTITY_WITCH_CELEBRATE; diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftWitch.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftWitch.java -index bae107e76e..b43a2bbd5b 100644 +index bae107e76..b43a2bbd5 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftWitch.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftWitch.java @@ -0,0 +0,0 @@ diff --git a/Spigot-Server-Patches/Add-more-Zombie-API.patch b/Spigot-Server-Patches/Add-more-Zombie-API.patch index adcbafa85e..fabaf9ae7b 100644 --- a/Spigot-Server-Patches/Add-more-Zombie-API.patch +++ b/Spigot-Server-Patches/Add-more-Zombie-API.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Add more Zombie API diff --git a/src/main/java/net/minecraft/server/EntityInsentient.java b/src/main/java/net/minecraft/server/EntityInsentient.java -index 09112a8b0..d00e99cdb 100644 +index cbaed08f5..6bad1687f 100644 --- a/src/main/java/net/minecraft/server/EntityInsentient.java +++ b/src/main/java/net/minecraft/server/EntityInsentient.java @@ -0,0 +0,0 @@ public abstract class EntityInsentient extends EntityLiving { @@ -18,11 +18,11 @@ index 09112a8b0..d00e99cdb 100644 byte b0 = (Byte) this.datawatcher.get(EntityInsentient.b); diff --git a/src/main/java/net/minecraft/server/EntityZombie.java b/src/main/java/net/minecraft/server/EntityZombie.java -index 832375f27..cdaa7f636 100644 +index c8e4dcdac..1c2c05ae3 100644 --- a/src/main/java/net/minecraft/server/EntityZombie.java +++ b/src/main/java/net/minecraft/server/EntityZombie.java @@ -0,0 +0,0 @@ public class EntityZombie extends EntityMonster { - private int bF; + private int bC; public int drownedConversionTime; private int lastTick = MinecraftServer.currentTick; // CraftBukkit - add field + private boolean shouldBurnInDay = true; // Paper @@ -48,15 +48,15 @@ index 832375f27..cdaa7f636 100644 + } + // Paper end + - protected void ea() { + protected void ev() { this.b(EntityTypes.DROWNED); this.world.a((EntityHuman) null, 1040, new BlockPosition(this), 0); @@ -0,0 +0,0 @@ public class EntityZombie extends EntityMonster { } } -+ public boolean shouldBurnInDay() { return I_(); } // Paper - OBFHELPER - protected boolean I_() { ++ public boolean shouldBurnInDay() { return K_(); } // Paper - OBFHELPER + protected boolean K_() { - return true; + return shouldBurnInDay; } @@ -71,8 +71,8 @@ index 832375f27..cdaa7f636 100644 public boolean damageEntity(DamageSource damagesource, float f) { if (super.damageEntity(damagesource, f)) { @@ -0,0 +0,0 @@ public class EntityZombie extends EntityMonster { - nbttagcompound.setBoolean("CanBreakDoors", this.ed()); - nbttagcompound.setInt("InWaterTime", this.isInWater() ? this.bF : -1); + nbttagcompound.setBoolean("CanBreakDoors", this.ey()); + nbttagcompound.setInt("InWaterTime", this.isInWater() ? this.bC : -1); nbttagcompound.setInt("DrownedConversionTime", this.isDrownConverting() ? this.drownedConversionTime : -1); + nbttagcompound.setBoolean("Paper.ShouldBurnInDay", shouldBurnInDay); // Paper } diff --git a/Spigot-Server-Patches/Add-option-to-disable-pillager-patrols.patch b/Spigot-Server-Patches/Add-option-to-disable-pillager-patrols.patch index 66fe5b0d83..be7d8395a2 100644 --- a/Spigot-Server-Patches/Add-option-to-disable-pillager-patrols.patch +++ b/Spigot-Server-Patches/Add-option-to-disable-pillager-patrols.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Add option to disable pillager patrols diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index 3e5571f7d..68d36a512 100644 +index 8648d9e3d..917dc6253 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -0,0 +0,0 @@ public class PaperWorldConfig { @@ -19,7 +19,7 @@ index 3e5571f7d..68d36a512 100644 + } } diff --git a/src/main/java/net/minecraft/server/MobSpawnerPatrol.java b/src/main/java/net/minecraft/server/MobSpawnerPatrol.java -index a9d32ebdf..44c70e047 100644 +index 59161143b..f155179ad 100644 --- a/src/main/java/net/minecraft/server/MobSpawnerPatrol.java +++ b/src/main/java/net/minecraft/server/MobSpawnerPatrol.java @@ -0,0 +0,0 @@ public class MobSpawnerPatrol { diff --git a/Spigot-Server-Patches/Add-option-to-prevent-players-from-moving-into-unloa.patch b/Spigot-Server-Patches/Add-option-to-prevent-players-from-moving-into-unloa.patch index 6b30245e76..3499269758 100644 --- a/Spigot-Server-Patches/Add-option-to-prevent-players-from-moving-into-unloa.patch +++ b/Spigot-Server-Patches/Add-option-to-prevent-players-from-moving-into-unloa.patch @@ -6,12 +6,12 @@ Subject: [PATCH] Add option to prevent players from moving into unloaded diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index 3b24fbdbf..c7ff264cb 100644 +index fe3e78f36..4bcba0a2b 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -0,0 +0,0 @@ public class PaperWorldConfig { - this.armorStandTick = this.getBoolean("armor-stands-tick", this.armorStandTick); - log("ArmorStand ticking is " + (this.armorStandTick ? "enabled" : "disabled") + " by default"); + waterOverLavaFlowSpeed = getInt("water-over-lava-flow-speed", 5); + log("Water over lava flow speed: " + waterOverLavaFlowSpeed); } + + public boolean preventMovingIntoUnloadedChunks = false; @@ -20,7 +20,7 @@ index 3b24fbdbf..c7ff264cb 100644 + } } diff --git a/src/main/java/net/minecraft/server/PlayerConnection.java b/src/main/java/net/minecraft/server/PlayerConnection.java -index a814d8cae..b0fd4d800 100644 +index 77c375f71..ccad0a601 100644 --- a/src/main/java/net/minecraft/server/PlayerConnection.java +++ b/src/main/java/net/minecraft/server/PlayerConnection.java @@ -0,0 +0,0 @@ public class PlayerConnection implements PacketListenerPlayIn { @@ -38,14 +38,14 @@ index a814d8cae..b0fd4d800 100644 // CraftBukkit end PlayerConnection.LOGGER.warn("{} (vehicle of {}) moved too quickly! {},{},{}", entity.getDisplayName().getString(), this.player.getDisplayName().getString(), d6, d7, d8); @@ -0,0 +0,0 @@ public class PlayerConnection implements PacketListenerPlayIn { - double d1 = this.player.locY; - double d2 = this.player.locZ; - double d3 = this.player.locY; -- double d4 = packetplayinflying.a(this.player.locX); -+ double d4 = packetplayinflying.a(this.player.locX);double toX = d4; // Paper - OBFHELPER - double d5 = packetplayinflying.b(this.player.locY); -- double d6 = packetplayinflying.c(this.player.locZ); -+ double d6 = packetplayinflying.c(this.player.locZ);double toZ = d6; // Paper - OBFHELPER + double d1 = this.player.locY(); + double d2 = this.player.locZ(); + double d3 = this.player.locY(); +- double d4 = packetplayinflying.a(this.player.locX()); ++ double d4 = packetplayinflying.a(this.player.locX());double toX = d4; // Paper - OBFHELPER + double d5 = packetplayinflying.b(this.player.locY()); +- double d6 = packetplayinflying.c(this.player.locZ()); ++ double d6 = packetplayinflying.c(this.player.locZ());double toZ = d6; // Paper - OBFHELPER float f = packetplayinflying.a(this.player.yaw); float f1 = packetplayinflying.b(this.player.pitch); double d7 = d4 - this.l; @@ -54,8 +54,8 @@ index a814d8cae..b0fd4d800 100644 speed = player.abilities.walkSpeed * 10f; } + // Paper start - Prevent moving into unloaded chunks -+ if (player.world.paperConfig.preventMovingIntoUnloadedChunks && (this.player.locX != toX || this.player.locZ != toZ) && !worldserver.isChunkLoaded((int) Math.floor(toX) >> 4, (int) Math.floor(toZ) >> 4)) { -+ this.internalTeleport(this.player.locX, this.player.locY, this.player.locZ, this.player.yaw, this.player.pitch, Collections.emptySet()); ++ if (player.world.paperConfig.preventMovingIntoUnloadedChunks && (this.player.locX() != toX || this.player.locZ() != toZ) && !worldserver.isChunkLoaded((int) Math.floor(toX) >> 4, (int) Math.floor(toZ) >> 4)) { ++ this.internalTeleport(this.player.locX(), this.player.locY(), this.player.locZ(), this.player.yaw, this.player.pitch, Collections.emptySet()); + return; + } + // Paper end diff --git a/Spigot-Server-Patches/Add-sun-related-API.patch b/Spigot-Server-Patches/Add-sun-related-API.patch index 322ce9dbd8..7a2931352a 100644 --- a/Spigot-Server-Patches/Add-sun-related-API.patch +++ b/Spigot-Server-Patches/Add-sun-related-API.patch @@ -5,31 +5,31 @@ Subject: [PATCH] Add sun related API diff --git a/src/main/java/net/minecraft/server/EntityInsentient.java b/src/main/java/net/minecraft/server/EntityInsentient.java -index a14d490ade..09112a8b0e 100644 +index eadac07f6..cbaed08f5 100644 --- a/src/main/java/net/minecraft/server/EntityInsentient.java +++ b/src/main/java/net/minecraft/server/EntityInsentient.java @@ -0,0 +0,0 @@ public abstract class EntityInsentient extends EntityLiving { return flag; } -+ public boolean isInDaylight() { return this.dS(); } // Paper - OBFHELPER - protected boolean dS() { ++ public boolean isInDaylight() { return this.en(); } // Paper - OBFHELPER + protected boolean en() { if (this.world.J() && !this.world.isClientSide) { - float f = this.aF(); + float f = this.aI(); diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java -index c60279da8f..1c3285f5c0 100644 +index 9c23060bb..8e3367435 100644 --- a/src/main/java/net/minecraft/server/World.java +++ b/src/main/java/net/minecraft/server/World.java -@@ -0,0 +0,0 @@ public abstract class World implements IIBlockAccess, GeneratorAccess, AutoClose +@@ -0,0 +0,0 @@ public abstract class World implements GeneratorAccess, AutoCloseable { } } + public boolean isDayTime() { return J(); } // Paper - OBFHELPER public boolean J() { - return this.u < 4; + return this.worldProvider.getDimensionManager() == DimensionManager.OVERWORLD && this.c < 4; } diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index dc0a07c7cd..78a2c47c5a 100644 +index 182322752..578fe8d19 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 { @@ -47,7 +47,7 @@ index dc0a07c7cd..78a2c47c5a 100644 public boolean createExplosion(double x, double y, double z, float power) { return createExplosion(x, y, z, power, false, true); diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java -index 53c2d154ed..56c233872b 100644 +index 53c2d154e..56c233872 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java @@ -0,0 +0,0 @@ public abstract class CraftMob extends CraftLivingEntity implements Mob { diff --git a/Spigot-Server-Patches/Allow-Saving-of-Oversized-Chunks.patch b/Spigot-Server-Patches/Allow-Saving-of-Oversized-Chunks.patch deleted file mode 100644 index 65b9dfc639..0000000000 --- a/Spigot-Server-Patches/Allow-Saving-of-Oversized-Chunks.patch +++ /dev/null @@ -1,508 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Aikar -Date: Fri, 15 Feb 2019 01:08:19 -0500 -Subject: [PATCH] Allow Saving of Oversized Chunks - -The Minecraft World Region File format has a hard cap of 1MB per chunk. -This is due to the fact that the header of the file format only allocates -a single byte for sector count, meaning a maximum of 256 sectors, at 4k per sector. - -This limit can be reached fairly easily with books, resulting in the chunk being unable -to save to the world. Worse off, is that nothing printed when this occured, and silently -performed a chunk rollback on next load. - -This leads to security risk with duplication and is being actively exploited. - -This patch catches the too large scenario, falls back and moves any large Entity -or Tile Entity into a new compound, and this compound is saved into a different file. - -On Chunk Load, we check for oversized status, and if so, we load the extra file and -merge the Entities and Tile Entities from the oversized chunk back into the level to -then be loaded as normal. - -Once a chunk is returned back to normal size, the oversized flag will clear, and no -extra data file will exist. - -This fix maintains compatability with all existing Anvil Region Format tools as it -does not alter the save format. They will just not know about the extra entities. - -This fix also maintains compatability if someone switches server jars to one without -this fix, as the data will remain in the oversized file. Once the server returns -to a jar with this fix, the data will be restored. - -diff --git a/src/main/java/net/minecraft/server/NBTCompressedStreamTools.java b/src/main/java/net/minecraft/server/NBTCompressedStreamTools.java -index 9fd8a75da..d49afd622 100644 ---- a/src/main/java/net/minecraft/server/NBTCompressedStreamTools.java -+++ b/src/main/java/net/minecraft/server/NBTCompressedStreamTools.java -@@ -0,0 +0,0 @@ public class NBTCompressedStreamTools { - - } - -+ public static NBTTagCompound readNBT(DataInputStream datainputstream) throws IOException { return a(datainputstream); } // Paper - OBFHELPER - public static NBTTagCompound a(DataInputStream datainputstream) throws IOException { - return a((DataInput) datainputstream, NBTReadLimiter.a); - } -@@ -0,0 +0,0 @@ public class NBTCompressedStreamTools { - } - } - -+ public static void writeNBT(NBTTagCompound nbttagcompound, DataOutput dataoutput) throws IOException { a(nbttagcompound, dataoutput); } // Paper - OBFHELPER - public static void a(NBTTagCompound nbttagcompound, DataOutput dataoutput) throws IOException { - a((NBTBase) nbttagcompound, dataoutput); - } -diff --git a/src/main/java/net/minecraft/server/NBTTagList.java b/src/main/java/net/minecraft/server/NBTTagList.java -index b7c94fe23..80eea5dfb 100644 ---- a/src/main/java/net/minecraft/server/NBTTagList.java -+++ b/src/main/java/net/minecraft/server/NBTTagList.java -@@ -0,0 +0,0 @@ import java.util.Objects; - - public class NBTTagList extends NBTList { - -- private List list = Lists.newArrayList(); -+ List list = Lists.newArrayList(); // Paper - private -> package - private byte type = 0; - - public NBTTagList() {} -diff --git a/src/main/java/net/minecraft/server/RegionFile.java b/src/main/java/net/minecraft/server/RegionFile.java -index e90ef45ee..ccc3d6c7a 100644 ---- a/src/main/java/net/minecraft/server/RegionFile.java -+++ b/src/main/java/net/minecraft/server/RegionFile.java -@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable { - // Minecraft is limited to 256 sections per chunk. So 1MB. This can easily be overriden. - // So we extend this to use the REAL size when the count is maxed by seeking to that section and reading the length. - private static final boolean ENABLE_EXTENDED_SAVE = Boolean.parseBoolean(System.getProperty("net.minecraft.server.RegionFile.enableExtendedSave", "true")); -- private final File file; -+ final File file; // Paper - private -> package - // Spigot end - private static final byte[] a = new byte[4096]; - private final RandomAccessFile b; private RandomAccessFile getDataFile() { return this.b; } // Paper - OBFHELPER // PAIL dataFile -@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable { - - public RegionFile(File file) throws IOException { - this.b = new RandomAccessFile(file, "rw"); -+ this.file = file; // Spigot // Paper - We need this earlier - if (this.b.length() < 8192L) { // Paper - headers should be 8192 - this.b.write(RegionFile.a); - this.b.write(RegionFile.a); -@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable { - } - ((java.nio.Buffer) header).clear(); - java.nio.IntBuffer headerAsInts = header.asIntBuffer(); -+ initOversizedState(); - // Paper End - - int k; -@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable { - this.b.seek(j * 4 + 4); // Go back to where we were - } - } -- if (k > 0 && (k >> 8) > 1 && (k >> 8) + (k & 255) <= this.e.size()) { // Paper >= 1 as 0/1 are the headers, and negative isnt valid -+ if (k > 0 && (k >> 8) > 1 && (k >> 8) + (length) <= this.e.size()) { // Paper >= 1 as 0/1 are the headers, and negative isnt valid - for (int l = 0; l < (length); ++l) { - // Spigot end - this.e.set((k >> 8) + l, false); -@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable { - if (this.offsets[j] != 0) this.timestamps[j] = k; // Paper - don't set timestamp if it got 0'd above due to corruption - } - -- this.file = file; // Spigot -+ // Paper - we need this earlier - } - - @Nullable -- public synchronized DataInputStream a(ChunkCoordIntPair chunkcoordintpair) { -+ public synchronized DataInputStream getReadStream(ChunkCoordIntPair chunkcoordintpair) { return this.a(chunkcoordintpair); } public synchronized DataInputStream a(ChunkCoordIntPair chunkcoordintpair) { // Paper - OBFHELPER - try { - int i = this.getOffset(chunkcoordintpair); - -@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable { - } - } - -- public DataOutputStream c(ChunkCoordIntPair chunkcoordintpair) { -- return new DataOutputStream(new BufferedOutputStream(new DeflaterOutputStream(new RegionFile.ChunkBuffer(chunkcoordintpair)))); -+ public DataOutputStream getWriteStream(ChunkCoordIntPair chunkcoordintpair) { return this.c(chunkcoordintpair); } public DataOutputStream c(ChunkCoordIntPair chunkcoordintpair) { // Paper - OBFHELPER -+ return new DataOutputStream(new RegionFile.ChunkBuffer(chunkcoordintpair)); // Paper - remove middleware, move deflate to .close() for dynamic levels - } - - protected synchronized void a(ChunkCoordIntPair chunkcoordintpair, byte[] abyte, int i) { -@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable { - - if (i1 >= 256) { - // Spigot start -- if (!ENABLE_EXTENDED_SAVE) throw new RuntimeException(String.format("Too big to save, %d > 1048576", i)); -+ if (!USE_SPIGOT_OVERSIZED_METHOD && !RegionFileCache.isOverzealous()) throw new ChunkTooLargeException(chunkcoordintpair.x, chunkcoordintpair.z, l); // Paper - throw error instead - org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.WARNING,"Large Chunk Detected: ({0}) Size: {1} {2}", new Object[]{chunkcoordintpair, i1, this.file}); -+ if (!ENABLE_EXTENDED_SAVE) throw new RuntimeException(String.format("Too big to save, %d > 1048576", i)); // Paper - move after our check - // Spigot end - } - -@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable { - logger.error("Error backing up corrupt file" + file.getAbsolutePath(), e); - } - } -+ -+ private final byte[] oversized = new byte[1024]; -+ private int oversizedCount = 0; -+ -+ private synchronized void initOversizedState() throws IOException { -+ File metaFile = getOversizedMetaFile(); -+ if (metaFile.exists()) { -+ final byte[] read = java.nio.file.Files.readAllBytes(metaFile.toPath()); -+ System.arraycopy(read, 0, oversized, 0, oversized.length); -+ for (byte temp : oversized) { -+ oversizedCount += temp; -+ } -+ } -+ } -+ -+ private static int getChunkIndex(int x, int z) { -+ return (x & 31) + (z & 31) * 32; -+ } -+ synchronized boolean isOversized(int x, int z) { -+ return this.oversized[getChunkIndex(x, z)] == 1; -+ } -+ synchronized void setOversized(int x, int z, boolean oversized) throws IOException { -+ final int offset = getChunkIndex(x, z); -+ boolean previous = this.oversized[offset] == 1; -+ this.oversized[offset] = (byte) (oversized ? 1 : 0); -+ if (!previous && oversized) { -+ oversizedCount++; -+ } else if (!oversized && previous) { -+ oversizedCount--; -+ } -+ if (previous && !oversized) { -+ File oversizedFile = getOversizedFile(x, z); -+ if (oversizedFile.exists()) { -+ oversizedFile.delete(); -+ } -+ } -+ if (oversizedCount > 0) { -+ if (previous != oversized) { -+ writeOversizedMeta(); -+ } -+ } else if (previous) { -+ File oversizedMetaFile = getOversizedMetaFile(); -+ if (oversizedMetaFile.exists()) { -+ oversizedMetaFile.delete(); -+ } -+ } -+ } -+ -+ private void writeOversizedMeta() throws IOException { -+ java.nio.file.Files.write(getOversizedMetaFile().toPath(), oversized); -+ } -+ -+ private File getOversizedMetaFile() { -+ return new File(this.file.getParentFile(), this.file.getName().replaceAll("\\.mca$", "") + ".oversized.nbt"); -+ } -+ -+ private File getOversizedFile(int x, int z) { -+ return new File(this.file.getParentFile(), this.file.getName().replaceAll("\\.mca$", "") + "_oversized_" + x + "_" + z + ".nbt"); -+ } -+ -+ void writeOversizedData(int x, int z, NBTTagCompound oversizedData) throws IOException { -+ File file = getOversizedFile(x, z); -+ try (DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new DeflaterOutputStream(new java.io.FileOutputStream(file), new java.util.zip.Deflater(java.util.zip.Deflater.BEST_COMPRESSION), 32 * 1024), 32 * 1024))) { -+ NBTCompressedStreamTools.writeNBT(oversizedData, out); -+ } -+ this.setOversized(x, z, true); -+ -+ } -+ -+ synchronized NBTTagCompound getOversizedData(int x, int z) throws IOException { -+ File file = getOversizedFile(x, z); -+ try (DataInputStream out = new DataInputStream(new BufferedInputStream(new InflaterInputStream(new java.io.FileInputStream(file))))) { -+ return NBTCompressedStreamTools.readNBT(out); -+ } -+ -+ } -+ -+ private static final boolean USE_SPIGOT_OVERSIZED_METHOD = Boolean.getBoolean("Paper.useSpigotExtendedSaveMethod"); // Paper -+ static { -+ if (USE_SPIGOT_OVERSIZED_METHOD) { -+ org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.SEVERE, "===================================="); -+ org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.SEVERE, "Using Spigot Oversized Chunk save method. Warning this will result in extremely fragmented chunks, as well as making the entire region file unable to be to used in any other software but Forge or Spigot (not usable in Vanilla or CraftBukkit). Paper's method is highly recommended."); -+ org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.SEVERE, "===================================="); -+ } -+ } -+ public class ChunkTooLargeException extends RuntimeException { -+ public ChunkTooLargeException(int x, int z, int sectors) { -+ super("Chunk " + x + "," + z + " of " + RegionFile.this.file.toString() + " is too large (" + sectors + "/255)"); -+ } -+ } -+ private static class DirectByteArrayOutputStream extends ByteArrayOutputStream { -+ public DirectByteArrayOutputStream() { -+ super(); -+ } -+ -+ public DirectByteArrayOutputStream(int size) { -+ super(size); -+ } -+ -+ public byte[] getBuffer() { -+ return this.buf; -+ } -+ } - // Paper end - - class ChunkBuffer extends ByteArrayOutputStream { -@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable { - this.b = chunkcoordintpair; - } - -- public void close() { -- RegionFile.this.a(this.b, this.buf, this.count); -+ public void close() throws IOException { -+ // Paper start - apply dynamic compression -+ int origLength = this.count; -+ byte[] buf = this.buf; -+ DirectByteArrayOutputStream out = compressData(buf, origLength); -+ byte[] bytes = out.getBuffer(); -+ int length = out.size(); -+ -+ RegionFile.this.a(this.b, bytes, length); // Paper - change to bytes/length - } - } -+ -+ private static final byte[] compressionBuffer = new byte[1024 * 64]; // 64k fits most standard chunks input size even, ideally 1 pass through zlib -+ private static final java.util.zip.Deflater deflater = new java.util.zip.Deflater(); -+ // since file IO is single threaded, no benefit to using per-region file buffers/synchronization, we can change that later if it becomes viable. -+ private static DirectByteArrayOutputStream compressData(byte[] buf, int length) throws IOException { -+ synchronized (deflater) { -+ deflater.setInput(buf, 0, length); -+ deflater.finish(); -+ -+ DirectByteArrayOutputStream out = new DirectByteArrayOutputStream(length); -+ while (!deflater.finished()) { -+ out.write(compressionBuffer, 0, deflater.deflate(compressionBuffer)); -+ } -+ out.close(); -+ deflater.reset(); -+ return out; -+ } -+ } -+ // Paper end -+ - } -diff --git a/src/main/java/net/minecraft/server/RegionFileCache.java b/src/main/java/net/minecraft/server/RegionFileCache.java -index 871881165..c53518a47 100644 ---- a/src/main/java/net/minecraft/server/RegionFileCache.java -+++ b/src/main/java/net/minecraft/server/RegionFileCache.java -@@ -0,0 +0,0 @@ public abstract class RegionFileCache implements AutoCloseable { - // Paper start - } - -+ public RegionFile getRegionFile(ChunkCoordIntPair chunkcoordintpair, boolean existingOnly) throws IOException { return this.a(chunkcoordintpair, existingOnly); } // Paper - OBFHELPER - private RegionFile a(ChunkCoordIntPair chunkcoordintpair, boolean existingOnly) throws IOException { // CraftBukkit - long i = ChunkCoordIntPair.pair(chunkcoordintpair.getRegionX(), chunkcoordintpair.getRegionZ()); - RegionFile regionfile = (RegionFile) this.cache.getAndMoveToFirst(i); -@@ -0,0 +0,0 @@ public abstract class RegionFileCache implements AutoCloseable { - public synchronized boolean hasRegionFile(File file, int i, int j) { - return cache.containsKey(ChunkCoordIntPair.pair(i, j)); - } -+ // Paper start -+ private static void printOversizedLog(String msg, File file, int x, int z) { -+ org.apache.logging.log4j.LogManager.getLogger().fatal(msg + " (" + file.toString().replaceAll(".+[\\\\/]", "") + " - " + x + "," + z + ") Go clean it up to remove this message. /minecraft:tp " + (x<<4)+" 128 "+(z<<4) + " - DO NOT REPORT THIS TO PAPER - You may ask for help on Discord, but do not file an issue. These error messages can not be removed."); -+ } -+ -+ private static final int DEFAULT_SIZE_THRESHOLD = 1024 * 8; -+ private static final int OVERZEALOUS_TOTAL_THRESHOLD = 1024 * 64; -+ private static final int OVERZEALOUS_THRESHOLD = 1024; -+ private static int SIZE_THRESHOLD = DEFAULT_SIZE_THRESHOLD; -+ private static void resetFilterThresholds() { -+ SIZE_THRESHOLD = Math.max(1024 * 4, Integer.getInteger("Paper.FilterThreshhold", DEFAULT_SIZE_THRESHOLD)); -+ } -+ static { -+ resetFilterThresholds(); -+ } -+ -+ static boolean isOverzealous() { -+ return SIZE_THRESHOLD == OVERZEALOUS_THRESHOLD; -+ } -+ -+ private void writeRegion(ChunkCoordIntPair chunk, NBTTagCompound nbttagcompound) throws IOException { -+ RegionFile regionfile = getRegionFile(chunk, false); -+ -+ int chunkX = chunk.x; -+ int chunkZ = chunk.z; -+ -+ DataOutputStream out = regionfile.getWriteStream(chunk); -+ try { -+ NBTCompressedStreamTools.writeNBT(nbttagcompound, out); -+ out.close(); -+ regionfile.setOversized(chunkX, chunkZ, false); -+ } catch (RegionFile.ChunkTooLargeException ignored) { -+ printOversizedLog("ChunkTooLarge! Someone is trying to duplicate.", regionfile.file, chunkX, chunkZ); -+ // Clone as we are now modifying it, don't want to corrupt the pending save state -+ nbttagcompound = nbttagcompound.clone(); -+ // Filter out TileEntities and Entities -+ NBTTagCompound oversizedData = filterChunkData(nbttagcompound); -+ //noinspection SynchronizationOnLocalVariableOrMethodParameter -+ synchronized (regionfile) { -+ out = regionfile.getWriteStream(chunk); -+ NBTCompressedStreamTools.writeNBT(nbttagcompound, out); -+ try { -+ out.close(); -+ // 2048 is below the min allowed, so it means we enter overzealous mode below -+ if (SIZE_THRESHOLD == OVERZEALOUS_THRESHOLD) { -+ resetFilterThresholds(); -+ } -+ } catch (RegionFile.ChunkTooLargeException e) { -+ printOversizedLog("ChunkTooLarge even after reduction. Trying in overzealous mode.", regionfile.file, chunkX, chunkZ); -+ // Eek, major fail. We have retry logic, so reduce threshholds and fall back -+ SIZE_THRESHOLD = OVERZEALOUS_THRESHOLD; -+ throw e; -+ } -+ -+ regionfile.writeOversizedData(chunkX, chunkZ, oversizedData); -+ } -+ } -+ } -+ -+ private static NBTTagCompound filterChunkData(NBTTagCompound chunk) { -+ NBTTagCompound oversizedLevel = new NBTTagCompound(); -+ NBTTagCompound level = chunk.getCompound("Level"); -+ filterChunkList(level, oversizedLevel, "Entities"); -+ filterChunkList(level, oversizedLevel, "TileEntities"); -+ NBTTagCompound oversized = new NBTTagCompound(); -+ oversized.set("Level", oversizedLevel); -+ return oversized; -+ } -+ -+ private static void filterChunkList(NBTTagCompound level, NBTTagCompound extra, String key) { -+ NBTTagList list = level.getList(key, 10); -+ NBTTagList newList = extra.getList(key, 10); -+ int totalSize = 0; -+ for (java.util.Iterator iterator = list.list.iterator(); iterator.hasNext();) { -+ NBTBase object = iterator.next(); -+ int nbtSize = getNBTSize(object); -+ if (nbtSize > SIZE_THRESHOLD || (SIZE_THRESHOLD == OVERZEALOUS_THRESHOLD && totalSize > OVERZEALOUS_TOTAL_THRESHOLD)) { -+ newList.add(object); -+ iterator.remove(); -+ } else { -+ totalSize += nbtSize; -+ } -+ } -+ level.set(key, list); -+ extra.set(key, newList); -+ } -+ -+ -+ private static NBTTagCompound readOversizedChunk(RegionFile regionfile, ChunkCoordIntPair chunkCoordinate) throws IOException { -+ synchronized (regionfile) { -+ try (DataInputStream datainputstream = regionfile.getReadStream(chunkCoordinate)) { -+ NBTTagCompound oversizedData = regionfile.getOversizedData(chunkCoordinate.x, chunkCoordinate.z); -+ NBTTagCompound chunk = NBTCompressedStreamTools.readNBT(datainputstream); -+ if (oversizedData == null) { -+ return chunk; -+ } -+ NBTTagCompound oversizedLevel = oversizedData.getCompound("Level"); -+ NBTTagCompound level = chunk.getCompound("Level"); -+ -+ mergeChunkList(level, oversizedLevel, "Entities"); -+ mergeChunkList(level, oversizedLevel, "TileEntities"); -+ -+ chunk.set("Level", level); -+ -+ return chunk; -+ } catch (Throwable throwable) { -+ throwable.printStackTrace(); -+ throw throwable; -+ } -+ } -+ } -+ -+ private static void mergeChunkList(NBTTagCompound level, NBTTagCompound oversizedLevel, String key) { -+ NBTTagList levelList = level.getList(key, 10); -+ NBTTagList oversizedList = oversizedLevel.getList(key, 10); -+ -+ if (!oversizedList.isEmpty()) { -+ levelList.addAll(oversizedList); -+ level.set(key, levelList); -+ } -+ } -+ -+ private static int getNBTSize(NBTBase nbtBase) { -+ DataOutputStream test = new DataOutputStream(new org.apache.commons.io.output.NullOutputStream()); -+ try { -+ nbtBase.write(test); -+ return test.size(); -+ } catch (IOException e) { -+ e.printStackTrace(); -+ return 0; -+ } -+ } -+ - // Paper End - - @Nullable - public NBTTagCompound read(ChunkCoordIntPair chunkcoordintpair) throws IOException { - RegionFile regionfile = this.a(chunkcoordintpair, false); // CraftBukkit - DataInputStream datainputstream = regionfile.a(chunkcoordintpair); -+ // Paper start -+ if (regionfile.isOversized(chunkcoordintpair.x, chunkcoordintpair.z)) { -+ printOversizedLog("Loading Oversized Chunk!", regionfile.file, chunkcoordintpair.x, chunkcoordintpair.z); -+ return readOversizedChunk(regionfile, chunkcoordintpair); -+ } -+ // Paper end - Throwable throwable = null; - - NBTTagCompound nbttagcompound; -@@ -0,0 +0,0 @@ public abstract class RegionFileCache implements AutoCloseable { - - protected void write(ChunkCoordIntPair chunkcoordintpair, NBTTagCompound nbttagcompound) throws IOException { - int attempts = 0; Exception laste = null; while (attempts++ < 5) { try { // Paper -- RegionFile regionfile = this.a(chunkcoordintpair, false); // CraftBukkit -- DataOutputStream dataoutputstream = regionfile.c(chunkcoordintpair); -- Throwable throwable = null; -- -- try { -- NBTCompressedStreamTools.a(nbttagcompound, (DataOutput) dataoutputstream); -- } catch (Throwable throwable1) { -- throwable = throwable1; -- throw throwable1; -- } finally { -- if (dataoutputstream != null) { -- if (throwable != null) { -- try { -- dataoutputstream.close(); -- } catch (Throwable throwable2) { -- throwable.addSuppressed(throwable2); -- } -- } else { -- dataoutputstream.close(); -- } -- } -- -- } -+ // Paper start -+ this.writeRegion(chunkcoordintpair, nbttagcompound); -+// RegionFile regionfile = this.a(chunkcoordintpair, false); // CraftBukkit -+// DataOutputStream dataoutputstream = regionfile.c(chunkcoordintpair); -+// Throwable throwable = null; -+// -+// try { -+// NBTCompressedStreamTools.a(nbttagcompound, (DataOutput) dataoutputstream); -+// } catch (Throwable throwable1) { -+// throwable = throwable1; -+// throw throwable1; -+// } finally { -+// if (dataoutputstream != null) { -+// if (throwable != null) { -+// try { -+// dataoutputstream.close(); -+// } catch (Throwable throwable2) { -+// throwable.addSuppressed(throwable2); -+// } -+// } else { -+// dataoutputstream.close(); -+// } -+// } -+// -+// } -+ // Paper end - - // Paper start - return; --- \ No newline at end of file diff --git a/Spigot-Server-Patches/Allow-chests-to-be-placed-with-NBT-data.patch b/Spigot-Server-Patches/Allow-chests-to-be-placed-with-NBT-data.patch index 7cb6441910..38d103fed9 100644 --- a/Spigot-Server-Patches/Allow-chests-to-be-placed-with-NBT-data.patch +++ b/Spigot-Server-Patches/Allow-chests-to-be-placed-with-NBT-data.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Allow chests to be placed with NBT data diff --git a/src/main/java/net/minecraft/server/ItemStack.java b/src/main/java/net/minecraft/server/ItemStack.java -index 1e23d77e72..0e164840fd 100644 +index 627fa465c..df85f9df0 100644 --- a/src/main/java/net/minecraft/server/ItemStack.java +++ b/src/main/java/net/minecraft/server/ItemStack.java @@ -0,0 +0,0 @@ public final class ItemStack { @@ -17,7 +17,7 @@ index 1e23d77e72..0e164840fd 100644 for (BlockState blockstate : blocks) { blockstate.update(true, false); diff --git a/src/main/java/net/minecraft/server/TileEntityChest.java b/src/main/java/net/minecraft/server/TileEntityChest.java -index 387e366130..6c10f0eb89 100644 +index c4766f729..b22bd06e2 100644 --- a/src/main/java/net/minecraft/server/TileEntityChest.java +++ b/src/main/java/net/minecraft/server/TileEntityChest.java @@ -0,0 +0,0 @@ public class TileEntityChest extends TileEntityLootable { // Paper - Remove ITic diff --git a/Spigot-Server-Patches/Allow-setting-the-vex-s-summoner.patch b/Spigot-Server-Patches/Allow-setting-the-vex-s-summoner.patch index 8b04e7b662..cf5fbfc8cf 100644 --- a/Spigot-Server-Patches/Allow-setting-the-vex-s-summoner.patch +++ b/Spigot-Server-Patches/Allow-setting-the-vex-s-summoner.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Allow setting the vex's summoner diff --git a/src/main/java/net/minecraft/server/EntityVex.java b/src/main/java/net/minecraft/server/EntityVex.java -index 25aea53150..51c05376da 100644 +index a46730460..cf274666c 100644 --- a/src/main/java/net/minecraft/server/EntityVex.java +++ b/src/main/java/net/minecraft/server/EntityVex.java @@ -0,0 +0,0 @@ public class EntityVex extends EntityMonster { @@ -17,7 +17,7 @@ index 25aea53150..51c05376da 100644 this.c = entityinsentient; } diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftVex.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftVex.java -index 169c951ec3..2f7df3074f 100644 +index 169c951ec..2f7df3074 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftVex.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftVex.java @@ -0,0 +0,0 @@ public class CraftVex extends CraftMonster implements Vex { diff --git a/Spigot-Server-Patches/Anti-Xray.patch b/Spigot-Server-Patches/Anti-Xray.patch index da593c2376..5c634fb6ae 100644 --- a/Spigot-Server-Patches/Anti-Xray.patch +++ b/Spigot-Server-Patches/Anti-Xray.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Anti-Xray diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index 55b78f54c..cfc4156bc 100644 +index f5ed0a698..363676348 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -0,0 +0,0 @@ @@ -1181,7 +1181,7 @@ index 000000000..37093419c + } +} diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java -index 2003522d9..d604f96c1 100644 +index 14ec31f0a..863a2222f 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 implements IChunkAccess { @@ -1194,7 +1194,7 @@ index 2003522d9..d604f96c1 100644 } diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java -index 73f93e494..a02807411 100644 +index 6371f2f5b..17cacafe7 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 { @@ -1213,10 +1213,10 @@ index 73f93e494..a02807411 100644 - ProtoChunk protochunk = new ProtoChunk(chunkcoordintpair, chunkconverter, achunksection, protochunkticklist, protochunkticklist1); + ProtoChunk protochunk = new ProtoChunk(chunkcoordintpair, chunkconverter, achunksection, protochunkticklist, protochunkticklist1, worldserver); // Paper - Anti-Xray + protochunk.a(biomestorage); object = protochunk; - protochunk.a(abiomebase); diff --git a/src/main/java/net/minecraft/server/ChunkSection.java b/src/main/java/net/minecraft/server/ChunkSection.java -index ca2bf8b77..584b3e639 100644 +index 0d5deee36..4526527ac 100644 --- a/src/main/java/net/minecraft/server/ChunkSection.java +++ b/src/main/java/net/minecraft/server/ChunkSection.java @@ -0,0 +0,0 @@ public class ChunkSection { @@ -1227,7 +1227,7 @@ index ca2bf8b77..584b3e639 100644 + short nonEmptyBlockCount; // Paper - private -> package-private private short tickingBlockCount; private short e; - final DataPaletteBlock blockIds; // Paper - package + final DataPaletteBlock blockIds; public ChunkSection(int i) { - this(i, (short) 0, (short) 0, (short) 0); @@ -1255,7 +1255,7 @@ index ca2bf8b77..584b3e639 100644 public IBlockData getType(int i, int j, int k) { diff --git a/src/main/java/net/minecraft/server/DataPaletteBlock.java b/src/main/java/net/minecraft/server/DataPaletteBlock.java -index 1e2bca1e0..8fba1e2f3 100644 +index 2c1d1b1a5..44aed6727 100644 --- a/src/main/java/net/minecraft/server/DataPaletteBlock.java +++ b/src/main/java/net/minecraft/server/DataPaletteBlock.java @@ -0,0 +0,0 @@ package net.minecraft.server; @@ -1440,7 +1440,7 @@ index e156804f7..96a785af2 100644 public void a() { this.o(); diff --git a/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java b/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java -index ef71a1feb..483317608 100644 +index 47710067a..654474341 100644 --- a/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java +++ b/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java @@ -0,0 +0,0 @@ @@ -1451,9 +1451,9 @@ index ef71a1feb..483317608 100644 import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; @@ -0,0 +0,0 @@ public class PacketPlayOutMapChunk implements Packet { - private byte[] e; private byte[] getData() { return this.e; } // Paper - OBFHELPER - private List f; - private boolean g; + private byte[] f; private byte[] getData() { return this.f; } // Paper - OBFHELPER + private List g; + private boolean h; + private volatile boolean ready; // Paper - Async-Anti-Xray - Ready flag for the network manager - public PacketPlayOutMapChunk() {} @@ -1479,37 +1479,33 @@ index ef71a1feb..483317608 100644 @@ -0,0 +0,0 @@ public class PacketPlayOutMapChunk implements Packet { } - this.e = new byte[this.a(chunk, i)]; -- this.c = this.a(new PacketDataSerializer(this.i()), chunk, i); + this.f = new byte[this.a(chunk, i)]; +- this.c = this.a(new PacketDataSerializer(this.j()), chunk, i); + // Paper start - Anti-Xray - Add chunk packet info + if (chunkPacketInfo != null) { + chunkPacketInfo.setData(this.getData()); + } + // Paper end -+ this.c = this.writeChunk(new PacketDataSerializer(this.i()), chunk, i, chunkPacketInfo); // Paper - Anti-Xray - Add chunk packet info - this.f = Lists.newArrayList(); ++ this.c = this.writeChunk(new PacketDataSerializer(this.j()), chunk, i, chunkPacketInfo); // Paper - Anti-Xray - Add chunk packet info + this.g = Lists.newArrayList(); iterator = chunk.getTileEntities().entrySet().iterator(); int totalSigns = 0; // Paper @@ -0,0 +0,0 @@ public class PacketPlayOutMapChunk implements Packet { - this.f.add(nbttagcompound); } } -+ chunk.world.chunkPacketBlockController.modifyBlocks(this, chunkPacketInfo, forceLoad, null); // Paper - Anti-Xray - Modify blocks -+ } + // Paper start - Async-Anti-Xray - Getter and Setter for the ready flag + public boolean isReady() { + return this.ready; - } - ++ } ++ + public void setReady(boolean ready) { + this.ready = ready; -+ } + } + // Paper end -+ + @Override public void a(PacketDataSerializer packetdataserializer) throws IOException { - this.a = packetdataserializer.readInt(); @@ -0,0 +0,0 @@ public class PacketPlayOutMapChunk implements Packet { public int writeChunk(PacketDataSerializer packetDataSerializer, Chunk chunk, int chunkSectionSelector) { return this.a(packetDataSerializer, chunk, chunkSectionSelector); } // Paper - OBFHELPER @@ -1531,9 +1527,13 @@ index ef71a1feb..483317608 100644 + chunksection.getBlocks().writeDataPaletteBlock(packetdataserializer, chunkPacketInfo, k); // Paper - Anti-Xray - Add chunk packet info } } ++ chunk.world.chunkPacketBlockController.modifyBlocks(this, chunkPacketInfo, forceLoad, null); // Paper - Anti-Xray - Modify blocks ++ } + return j; + } diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java -index 98590e233..af934ef8b 100644 +index 43d9a5634..615d27863 100644 --- a/src/main/java/net/minecraft/server/PlayerChunk.java +++ b/src/main/java/net/minecraft/server/PlayerChunk.java @@ -0,0 +0,0 @@ public class PlayerChunk { @@ -1558,7 +1558,7 @@ index 98590e233..af934ef8b 100644 this.a(new PacketPlayOutMultiBlockChange(this.dirtyCount, this.dirtyBlocks, chunk), false); diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java -index b04657356..a5df9fee6 100644 +index 93729eea2..fc6436c4f 100644 --- a/src/main/java/net/minecraft/server/PlayerChunkMap.java +++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java @@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { @@ -1580,7 +1580,7 @@ index b04657356..a5df9fee6 100644 } diff --git a/src/main/java/net/minecraft/server/PlayerInteractManager.java b/src/main/java/net/minecraft/server/PlayerInteractManager.java -index a36aba015..f04fc366f 100644 +index 18ff6dd68..8720dd120 100644 --- a/src/main/java/net/minecraft/server/PlayerInteractManager.java +++ b/src/main/java/net/minecraft/server/PlayerInteractManager.java @@ -0,0 +0,0 @@ public class PlayerInteractManager { @@ -1591,9 +1591,9 @@ index a36aba015..f04fc366f 100644 + this.world.chunkPacketBlockController.onPlayerLeftClickBlock(this, blockposition, enumdirection); // Paper - Anti-Xray } - public void a(BlockPosition blockposition, PacketPlayInBlockDig.EnumPlayerDigType packetplayinblockdig_enumplayerdigtype) { + public void a(BlockPosition blockposition, PacketPlayInBlockDig.EnumPlayerDigType packetplayinblockdig_enumplayerdigtype, String s) { diff --git a/src/main/java/net/minecraft/server/ProtoChunk.java b/src/main/java/net/minecraft/server/ProtoChunk.java -index 6bdd7dda0..7bad12eb0 100644 +index 39339fa27..f376e2106 100644 --- a/src/main/java/net/minecraft/server/ProtoChunk.java +++ b/src/main/java/net/minecraft/server/ProtoChunk.java @@ -0,0 +0,0 @@ public class ProtoChunk implements IChunkAccess { @@ -1636,7 +1636,7 @@ index 6bdd7dda0..7bad12eb0 100644 return this.j[i]; diff --git a/src/main/java/net/minecraft/server/TicketType.java b/src/main/java/net/minecraft/server/TicketType.java -index 157ca6a7e..9c114d2d3 100644 +index a905a29e7..335b64435 100644 --- a/src/main/java/net/minecraft/server/TicketType.java +++ b/src/main/java/net/minecraft/server/TicketType.java @@ -0,0 +0,0 @@ public class TicketType { @@ -1648,7 +1648,7 @@ index 157ca6a7e..9c114d2d3 100644 public static TicketType a(String s, Comparator comparator) { return new TicketType<>(s, comparator, 0L); diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java -index 3f71ca9e4..e31ffdb0d 100644 +index 9ad76ab32..39725335d 100644 --- a/src/main/java/net/minecraft/server/World.java +++ b/src/main/java/net/minecraft/server/World.java @@ -0,0 +0,0 @@ package net.minecraft.server; @@ -1660,7 +1660,7 @@ index 3f71ca9e4..e31ffdb0d 100644 import com.destroystokyo.paper.event.server.ServerExceptionEvent; import com.destroystokyo.paper.exception.ServerInternalException; import com.google.common.base.MoreObjects; -@@ -0,0 +0,0 @@ public abstract class World implements IIBlockAccess, GeneratorAccess, AutoClose +@@ -0,0 +0,0 @@ public abstract class World implements GeneratorAccess, AutoCloseable { public final org.spigotmc.SpigotWorldConfig spigotConfig; // Spigot public final com.destroystokyo.paper.PaperWorldConfig paperConfig; // Paper @@ -1668,7 +1668,7 @@ index 3f71ca9e4..e31ffdb0d 100644 public final co.aikar.timings.WorldTimingsHandler timings; // Paper public static BlockPosition lastPhysicsProblem; // Spigot -@@ -0,0 +0,0 @@ public abstract class World implements IIBlockAccess, GeneratorAccess, AutoClose +@@ -0,0 +0,0 @@ public abstract class World implements GeneratorAccess, AutoCloseable { protected World(WorldData worlddata, DimensionManager dimensionmanager, BiFunction bifunction, GameProfilerFiller gameprofilerfiller, boolean flag, org.bukkit.generator.ChunkGenerator gen, org.bukkit.World.Environment env) { this.spigotConfig = new org.spigotmc.SpigotWorldConfig( worlddata.getName() ); // Spigot this.paperConfig = new com.destroystokyo.paper.PaperWorldConfig(worlddata.getName(), this.spigotConfig); // Paper @@ -1676,7 +1676,7 @@ index 3f71ca9e4..e31ffdb0d 100644 this.generator = gen; this.world = new CraftWorld((WorldServer) this, gen, env); this.ticksPerAnimalSpawns = this.getServer().getTicksPerAnimalSpawns(); // CraftBukkit -@@ -0,0 +0,0 @@ public abstract class World implements IIBlockAccess, GeneratorAccess, AutoClose +@@ -0,0 +0,0 @@ public abstract class World implements GeneratorAccess, AutoCloseable { // CraftBukkit end IBlockData iblockdata1 = chunk.setType(blockposition, iblockdata, (i & 64) != 0, (i & 1024) == 0); // CraftBukkit custom NO_PLACE flag diff --git a/Spigot-Server-Patches/Asynchronous-chunk-IO-and-loading.patch b/Spigot-Server-Patches/Asynchronous-chunk-IO-and-loading.patch deleted file mode 100644 index 8d9998da4a..0000000000 --- a/Spigot-Server-Patches/Asynchronous-chunk-IO-and-loading.patch +++ /dev/null @@ -1,3915 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Spottedleaf -Date: Sat, 13 Jul 2019 09:23:10 -0700 -Subject: [PATCH] Asynchronous chunk IO and loading - -This patch re-adds a file IO thread as well as shoving de-serializing -chunk NBT data onto worker threads. This patch also will shove -chunk data serialization onto the same worker threads when the chunk -is unloaded - this cannot be done for regular saves since that's unsafe. - -The file IO Thread - -Unlike 1.13 and below, the file IO thread is prioritized - IO tasks can -be reoredered, however they are "stuck" to a world & coordinate. - -Scheduling IO tasks works as follows, given a world & coordinate - location: - -The IO thread has been designed to ensure that reads and writes appear to -occur synchronously for a given location, however the implementation also -has the unfortunate side-effect of making every write appear as if -they occur without failure. - -The IO thread has also been designed to accomodate Mojang's decision to -store chunk data and POI data separately. It can independently schedule -tasks for each. - -However threads can wait for writes to complete and check if: - - The write was overwriten by another scheduler - - The write failed (however it does not indicate whether it was overwritten by another scheduler) - -Scheduling reads: - - - If a write task is in progress, the task is not scheduled and returns the in-progress write data - This means that readers cannot modify the NBTTagCompound returned and must clone if it they wish to write - - If a write task is not in progress but a read task is in progress, then the read task is simply chained - This means that again, readers cannot modify the NBTTagCompound returned - -Scheduling writes: - - - If a read task is in progress, ignore the read task and schedule the write - We cannot complete the read task since we assume it wants old data - not current - - If a write task is pending, overwrite the write data - The file IO thread does correctly handle cases where the data is overwritten when it - is writing data (before completing a task it will check if the data was overwritten and - will retry). - -When the file IO thread executes a task for a location, the it will -execute the read task first (if it exists), then it will execute the -write task. This ensures that, even when scheduling at different -priorities, that reads/writes for a location act synchronously. - -The downside of the file IO thread is that write failure can only be -indicated to the scheduling thread if: - -- No other thread decides to schedule another write for the location -concurrently -- The scheduling thread blocks on the write to complete (however the -current implementation can be modified to indicate success -asynchronously) - -The file io thread can be modified easily to provide indications -of write failure and write overwriting if needed. - -The upside of the file IO thread is that if a write failures, then -chunk data is not lost until server restart. This leaves more room -for spurious failure. - -Finally, the io thread will indicate to the console when reads -or writes fail - with relevant detail. - -Asynchronous chunk data serialization for unloading chunks - -When chunks unload they make a call to PlayerChunkMap#saveChunk(IChunkAccess). -Even if I make the IO asynchronous for this call, the data serialization -still hits pretty hard. And given that now the chunk system will -aggressively unload chunks more often (queued immediately at -ticket level 45 or higher), unloads occur more often, and -combined with our changes to the unload queue to make it -significantly more aggresive - chunk unloads can hit pretty hard. -Especially players running around with elytras and fireworks. - -For serializing chunk data off main, there are some tasks which cannot be -done asynchronously. Lighting data must be saved beforehand as well as -potentially some tick lists. These are completed before scheduling the -asynchronous save. - -However serializing chunk data off of the main thread is still risky. -Even though this patch schedules the save to occur after ALL references -of the chunk are removed from the world, plugins can still technically -access entities inside the chunks. For this, if the serialization task -fails for any reason, it will be re-scheduled to be serialized on the -main thread - with the hopes that the reason it failed was due to a plugin -and not an error with the save code itself. Like vanilla code - if the -serialization fails, the chunk data is lost. - -Asynchronous chunk io/loading - -Mojang's current implementation for loading chunk data off disk is -to return a CompletableFuture that will be completed by scheduling a -task to be executed on the world's chunk queue (which is only drained -on the main thread). This task will read the IO off disk and it will -apply data conversions & deserialization synchronously. Obviously -all 3 of these operations are expensive however all can be completed -asynchronously instead. - -The solution this patch uses is as follows: - -0. If an asynchronous chunk save is in progress (see above), wait -for that task to complete. It will use the serialized NBTTagCompound -created by the task. If the task fails to complete, then we would continue -with step 1. If it does not, we skip step 1. (Note: We actually load -POI data no matter what in this case). -1. Schedule an IO task to read chunk & poi data off disk. -2. The IO task will schedule a chunk load task. -3. The chunk load task executes on the async chunk loader threads -and will apply datafixers & de-serialize the chunk into a ProtoChunk -or ProtoChunkExtension. -4. The in progress chunk is then passed on to the world's chunk queue -to complete the ComletableFuture and execute any of the synchronous -tasks required to be executed by the chunk load task (i.e lighting -and some poi tasks). - -diff --git a/src/main/java/co/aikar/timings/WorldTimingsHandler.java b/src/main/java/co/aikar/timings/WorldTimingsHandler.java -index 3a79cde59..8de6c4816 100644 ---- a/src/main/java/co/aikar/timings/WorldTimingsHandler.java -+++ b/src/main/java/co/aikar/timings/WorldTimingsHandler.java -@@ -0,0 +0,0 @@ public class WorldTimingsHandler { - public final Timing chunkRangeCheckBig; - public final Timing chunkRangeCheckSmall; - -+ public final Timing poiUnload; -+ public final Timing chunkUnload; -+ public final Timing poiSaveDataSerialization; -+ public final Timing chunkSave; -+ public final Timing chunkSaveOverwriteCheck; -+ public final Timing chunkSaveDataSerialization; -+ public final Timing chunkSaveIOWait; -+ public final Timing chunkUnloadPrepareSave; -+ public final Timing chunkUnloadPOISerialization; -+ public final Timing chunkUnloadDataSave; -+ - public WorldTimingsHandler(World server) { - String name = server.worldData.getName() +" - "; - -@@ -0,0 +0,0 @@ public class WorldTimingsHandler { - miscMobSpawning = Timings.ofSafe(name + "Mob spawning - Misc"); - chunkRangeCheckBig = Timings.ofSafe(name + "Chunk Tick Range - Big"); - chunkRangeCheckSmall = Timings.ofSafe(name + "Chunk Tick Range - Small"); -+ -+ poiUnload = Timings.ofSafe(name + "Chunk unload - POI"); -+ chunkUnload = Timings.ofSafe(name + "Chunk unload - Chunk"); -+ poiSaveDataSerialization = Timings.ofSafe(name + "Chunk save - POI Data serialization"); -+ chunkSave = Timings.ofSafe(name + "Chunk save - Chunk"); -+ chunkSaveOverwriteCheck = Timings.ofSafe(name + "Chunk save - Chunk Overwrite Check"); -+ chunkSaveDataSerialization = Timings.ofSafe(name + "Chunk save - Chunk Data serialization"); -+ chunkSaveIOWait = Timings.ofSafe(name + "Chunk save - Chunk IO Wait"); -+ chunkUnloadPrepareSave = Timings.ofSafe(name + "Chunk unload - Async Save Prepare"); -+ chunkUnloadPOISerialization = Timings.ofSafe(name + "Chunk unload - POI Data Serialization"); -+ chunkUnloadDataSave = Timings.ofSafe(name + "Chunk unload - Data Serialization"); - } - - public static Timing getTickList(WorldServer worldserver, String timingsType) { -diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java -index c88b5e9dd..93c0c422d 100644 ---- a/src/main/java/com/destroystokyo/paper/PaperConfig.java -+++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java -@@ -0,0 +0,0 @@ - package com.destroystokyo.paper; - -+import com.destroystokyo.paper.io.chunk.ChunkTaskManager; - import com.google.common.base.Strings; - import com.google.common.base.Throwables; - -@@ -0,0 +0,0 @@ public class PaperConfig { - maxBookPageSize = getInt("settings.book-size.page-max", maxBookPageSize); - maxBookTotalSizeMultiplier = getDouble("settings.book-size.total-multiplier", maxBookTotalSizeMultiplier); - } -+ -+ public static boolean asyncChunks = false; -+ //public static boolean asyncChunkGeneration = true; // Leave out for now until we can control this -+ //public static boolean asyncChunkGenThreadPerWorld = true; // Leave out for now until we can control this -+ public static int asyncChunkLoadThreads = -1; -+ private static void asyncChunks() { -+ if (version < 15) { -+ boolean enabled = config.getBoolean("settings.async-chunks", true); -+ ConfigurationSection section = config.createSection("settings.async-chunks"); -+ section.set("enable", enabled); -+ section.set("load-threads", -1); -+ section.set("generation", true); -+ section.set("thread-per-world-generation", true); -+ } -+ -+ // TODO load threads now control async chunk save for unloading chunks, look into renaming this? -+ -+ asyncChunks = getBoolean("settings.async-chunks.enable", true); -+ //asyncChunkGeneration = getBoolean("settings.async-chunks.generation", true); // Leave out for now until we can control this -+ //asyncChunkGenThreadPerWorld = getBoolean("settings.async-chunks.thread-per-world-generation", true); // Leave out for now until we can control this -+ asyncChunkLoadThreads = getInt("settings.async-chunks.load-threads", -1); -+ if (asyncChunkLoadThreads <= 0) { -+ asyncChunkLoadThreads = (int) Math.min(Integer.getInteger("paper.maxChunkThreads", 8), Math.max(1, Runtime.getRuntime().availableProcessors() - 1)); -+ } -+ -+ // Let Shared Host set some limits -+ String sharedHostEnvGen = System.getenv("PAPER_ASYNC_CHUNKS_SHARED_HOST_GEN"); -+ String sharedHostEnvLoad = System.getenv("PAPER_ASYNC_CHUNKS_SHARED_HOST_LOAD"); -+ /* Ignore temporarily - we cannot control the gen threads (for now) -+ if ("1".equals(sharedHostEnvGen)) { -+ log("Async Chunks - Generation: Your host has requested to use a single thread world generation"); -+ asyncChunkGenThreadPerWorld = false; -+ } else if ("2".equals(sharedHostEnvGen)) { -+ log("Async Chunks - Generation: Your host has disabled async world generation - You will experience lag from world generation"); -+ asyncChunkGeneration = false; -+ } -+ */ -+ -+ if (sharedHostEnvLoad != null) { -+ try { -+ asyncChunkLoadThreads = Math.max(1, Math.min(asyncChunkLoadThreads, Integer.parseInt(sharedHostEnvLoad))); -+ } catch (NumberFormatException ignored) {} -+ } -+ -+ if (!asyncChunks) { -+ log("Async Chunks: Disabled - Chunks will be managed synchronosuly, and will cause tremendous lag."); -+ } else { -+ ChunkTaskManager.initGlobalLoadThreads(asyncChunkLoadThreads); -+ log("Async Chunks: Enabled - Chunks will be loaded much faster, without lag."); -+ /* Ignore temporarily - we cannot control the gen threads (for now) -+ if (!asyncChunkGeneration) { -+ log("Async Chunks - Generation: Disabled - Chunks will be generated synchronosuly, and will cause tremendous lag."); -+ } else if (asyncChunkGenThreadPerWorld) { -+ log("Async Chunks - Generation: Enabled - Chunks will be generated much faster, without lag."); -+ } else { -+ log("Async Chunks - Generation: Enabled (Single Thread) - Chunks will be generated much faster, without lag."); -+ } -+ */ -+ } -+ } - } -diff --git a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java -index 23626bef3..1edcecd2e 100644 ---- a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java -+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java -@@ -0,0 +0,0 @@ import java.util.concurrent.Executors; - import java.util.concurrent.atomic.AtomicInteger; - import java.util.function.Supplier; - -+import com.destroystokyo.paper.io.PrioritizedTaskQueue; - import net.minecraft.server.*; - import org.bukkit.Bukkit; - import org.bukkit.World.Environment; -@@ -0,0 +0,0 @@ public class ChunkPacketBlockControllerAntiXray extends ChunkPacketBlockControll - - private final AtomicInteger xrayRequests = new AtomicInteger(); - -+ // Paper start - async chunk api -+ private Integer nextTicketHold() { -+ return Integer.valueOf(this.xrayRequests.getAndIncrement()); -+ } -+ // Paper end -+ - private Integer addXrayTickets(final int x, final int z, final ChunkProviderServer chunkProvider) { - final Integer hold = Integer.valueOf(this.xrayRequests.getAndIncrement()); - -@@ -0,0 +0,0 @@ public class ChunkPacketBlockControllerAntiXray extends ChunkPacketBlockControll - chunk.world.getChunkAt(locX, locZ + 1); - } - -+ // Paper start - async chunk api -+ private void loadNeighbourAsync(ChunkProviderServer chunkProvider, WorldServer world, int chunkX, int chunkZ, int[] counter, java.util.function.Consumer onNeighourLoad, Runnable onAllNeighboursLoad) { -+ chunkProvider.getChunkAtAsynchronously(chunkX, chunkZ, true, (Chunk neighbour) -> { -+ onNeighourLoad.accept(neighbour); -+ if (++counter[0] == 4) { -+ onAllNeighboursLoad.run(); -+ } -+ }); -+ world.asyncChunkTaskManager.raisePriority(chunkX, chunkZ, PrioritizedTaskQueue.HIGHER_PRIORITY); -+ } -+ -+ private void loadNeighboursAsync(Chunk chunk, java.util.function.Consumer onNeighourLoad, Runnable onAllNeighboursLoad) { -+ int[] loaded = new int[1]; -+ -+ int locX = chunk.getPos().x; -+ int locZ = chunk.getPos().z; -+ WorldServer world = ((WorldServer)chunk.world); -+ -+ onNeighourLoad.accept(chunk); -+ -+ ChunkProviderServer chunkProvider = world.getChunkProvider(); -+ -+ this.loadNeighbourAsync(chunkProvider, world, locX - 1, locZ, loaded, onNeighourLoad, onAllNeighboursLoad); -+ this.loadNeighbourAsync(chunkProvider, world, locX + 1, locZ, loaded, onNeighourLoad, onAllNeighboursLoad); -+ this.loadNeighbourAsync(chunkProvider, world, locX, locZ - 1, loaded, onNeighourLoad, onAllNeighboursLoad); -+ this.loadNeighbourAsync(chunkProvider, world, locX, locZ + 1, loaded, onNeighourLoad, onAllNeighboursLoad); -+ } -+ // Paper end -+ - @Override - public boolean onChunkPacketCreate(Chunk chunk, int chunkSectionSelector, boolean force) { - int locX = chunk.getPos().x; -@@ -0,0 +0,0 @@ public class ChunkPacketBlockControllerAntiXray extends ChunkPacketBlockControll - - if (chunks[0] == null || chunks[1] == null || chunks[2] == null || chunks[3] == null) { - // we need to load -- MinecraftServer.getServer().scheduleOnMain(() -> { -- Integer ticketHold = this.addXrayTickets(locX, locZ, world.getChunkProvider()); -- this.loadNeighbours(chunk); -+ // Paper start - async chunk api -+ Integer ticketHold = this.nextTicketHold(); -+ this.loadNeighboursAsync(chunk, (Chunk neighbour) -> { // when a neighbour is loaded -+ ((WorldServer)neighbour.world).getChunkProvider().addTicket(TicketType.ANTIXRAY, neighbour.getPos(), 0, ticketHold); -+ }, -+ () -> { // once neighbours get loaded - this.modifyBlocks(packetPlayOutMapChunk, chunkPacketInfo, false, ticketHold); - }); -+ // Paper end - return; - } - -diff --git a/src/main/java/com/destroystokyo/paper/io/IOUtil.java b/src/main/java/com/destroystokyo/paper/io/IOUtil.java -new file mode 100644 -index 000000000..5af0ac3d9 ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/io/IOUtil.java -@@ -0,0 +0,0 @@ -+package com.destroystokyo.paper.io; -+ -+import org.bukkit.Bukkit; -+ -+public final class IOUtil { -+ -+ /* Copied from concrete or concurrentutil */ -+ -+ public static long getCoordinateKey(final int x, final int z) { -+ return ((long)z << 32) | (x & 0xFFFFFFFFL); -+ } -+ -+ public static int getCoordinateX(final long key) { -+ return (int)key; -+ } -+ -+ public static int getCoordinateZ(final long key) { -+ return (int)(key >>> 32); -+ } -+ -+ public static int getRegionCoordinate(final int chunkCoordinate) { -+ return chunkCoordinate >> 5; -+ } -+ -+ public static int getChunkInRegion(final int chunkCoordinate) { -+ return chunkCoordinate & 31; -+ } -+ -+ public static String genericToString(final Object object) { -+ return object == null ? "null" : object.getClass().getName() + ":" + object.toString(); -+ } -+ -+ public static T notNull(final T obj) { -+ if (obj == null) { -+ throw new NullPointerException(); -+ } -+ return obj; -+ } -+ -+ public static T notNull(final T obj, final String msgIfNull) { -+ if (obj == null) { -+ throw new NullPointerException(msgIfNull); -+ } -+ return obj; -+ } -+ -+ public static void arrayBounds(final int off, final int len, final int arrayLength, final String msgPrefix) { -+ if (off < 0 || len < 0 || (arrayLength - off) < len) { -+ throw new ArrayIndexOutOfBoundsException(msgPrefix + ": off: " + off + ", len: " + len + ", array length: " + arrayLength); -+ } -+ } -+ -+ public static int getPriorityForCurrentThread() { -+ return Bukkit.isPrimaryThread() ? PrioritizedTaskQueue.HIGHEST_PRIORITY : PrioritizedTaskQueue.NORMAL_PRIORITY; -+ } -+ -+ @SuppressWarnings("unchecked") -+ public static void rethrow(final Throwable throwable) throws T { -+ throw (T)throwable; -+ } -+ -+} -diff --git a/src/main/java/com/destroystokyo/paper/io/PaperFileIOThread.java b/src/main/java/com/destroystokyo/paper/io/PaperFileIOThread.java -new file mode 100644 -index 000000000..4f10a8311 ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/io/PaperFileIOThread.java -@@ -0,0 +0,0 @@ -+package com.destroystokyo.paper.io; -+ -+import net.minecraft.server.ChunkCoordIntPair; -+import net.minecraft.server.ExceptionWorldConflict; -+import net.minecraft.server.MinecraftServer; -+import net.minecraft.server.NBTTagCompound; -+import net.minecraft.server.RegionFile; -+import net.minecraft.server.WorldServer; -+import org.apache.logging.log4j.Logger; -+ -+import java.io.IOException; -+import java.util.concurrent.CompletableFuture; -+import java.util.concurrent.ConcurrentHashMap; -+import java.util.concurrent.atomic.AtomicLong; -+import java.util.function.Consumer; -+import java.util.function.Function; -+ -+/** -+ * Prioritized singleton thread responsible for all chunk IO that occurs in a minecraft server. -+ * -+ *

-+ * Singleton access: {@link Holder#INSTANCE} -+ *

-+ * -+ *

-+ * All functions provided are MT-Safe, however certain ordering constraints are (but not enforced): -+ *

  • -+ * Chunk saves may not occur for unloaded chunks. -+ *
  • -+ *
  • -+ * Tasks must be scheduled on the main thread. -+ *
  • -+ *

    -+ * -+ * @see Holder#INSTANCE -+ * @see #scheduleSave(WorldServer, int, int, NBTTagCompound, NBTTagCompound, int) -+ * @see #loadChunkDataAsync(WorldServer, int, int, int, Consumer, boolean, boolean, boolean) -+ */ -+public final class PaperFileIOThread extends QueueExecutorThread { -+ -+ public static final Logger LOGGER = MinecraftServer.LOGGER; -+ public static final NBTTagCompound FAILURE_VALUE = new NBTTagCompound(); -+ -+ public static final class Holder { -+ -+ public static final PaperFileIOThread INSTANCE = new PaperFileIOThread(); -+ -+ static { -+ INSTANCE.start(); -+ } -+ } -+ -+ private final AtomicLong writeCounter = new AtomicLong(); -+ -+ private PaperFileIOThread() { -+ super(new PrioritizedTaskQueue<>(), (int)(1.0e6)); // 1.0ms spinwait time -+ this.setName("Paper RegionFile IO Thread"); -+ this.setPriority(Thread.NORM_PRIORITY - 1); // we keep priority close to normal because threads can wait on us -+ this.setUncaughtExceptionHandler((final Thread unused, final Throwable thr) -> { -+ LOGGER.fatal("Uncaught exception thrown from IO thread, report this!", thr); -+ }); -+ } -+ -+ /* run() is implemented by superclass */ -+ -+ /* -+ * -+ * IO thread will perform reads before writes -+ * -+ * How reads/writes are scheduled: -+ * -+ * If read in progress while scheduling write, ignore read and schedule write -+ * If read in progress while scheduling read (no write in progress), chain the read task -+ * -+ * -+ * If write in progress while scheduling read, use the pending write data and ret immediately -+ * If write in progress while scheduling write (ignore read in progress), overwrite the write in progress data -+ * -+ * This allows the reads and writes to act as if they occur synchronously to the thread scheduling them, however -+ * it fails to properly propagate write failures. When writes fail the data is kept so future reads will actually -+ * read the failed write data. This should hopefully act as a way to prevent data loss for spurious fails for writing data. -+ * -+ */ -+ -+ /** -+ * Attempts to bump the priority of all IO tasks for the given chunk coordinates. This has no effect if no tasks are queued. -+ * @param world Chunk's world -+ * @param chunkX Chunk's x coordinate -+ * @param chunkZ Chunk's z coordinate -+ * @param priority Priority level to try to bump to -+ */ -+ public void bumpPriority(final WorldServer world, final int chunkX, final int chunkZ, final int priority) { -+ if (!PrioritizedTaskQueue.validPriority(priority)) { -+ throw new IllegalArgumentException("Invalid priority: " + priority); -+ } -+ -+ final Long key = Long.valueOf(IOUtil.getCoordinateKey(chunkX, chunkZ)); -+ -+ final ChunkDataTask poiTask = world.poiDataController.tasks.get(key); -+ final ChunkDataTask chunkTask = world.chunkDataController.tasks.get(key); -+ -+ if (poiTask != null) { -+ poiTask.raisePriority(priority); -+ } -+ if (chunkTask != null) { -+ chunkTask.raisePriority(priority); -+ } -+ } -+ -+ // Hack start -+ /** -+ * if {@code waitForRead} is true, then this task will wait on an available read task, else it will wait on an available -+ * write task -+ * if {@code poiTask} is true, then this task will wait on a poi task, else it will wait on chunk data task -+ * @deprecated API is garbage and will only work for main thread queueing of tasks (which is vanilla), plugins messing -+ * around asynchronously will give unexpected results -+ * @return whether the task succeeded, or {@code null} if there is no task -+ */ -+ @Deprecated -+ public Boolean waitForIOToComplete(final WorldServer world, final int chunkX, final int chunkZ, final boolean waitForRead, -+ final boolean poiTask) { -+ final ChunkDataTask task; -+ -+ final Long key = IOUtil.getCoordinateKey(chunkX, chunkZ); -+ if (poiTask) { -+ task = world.poiDataController.tasks.get(key); -+ } else { -+ task = world.chunkDataController.tasks.get(key); -+ } -+ -+ if (task == null) { -+ return null; -+ } -+ -+ if (waitForRead) { -+ ChunkDataController.InProgressRead read = task.inProgressRead; -+ if (read == null) { -+ return null; -+ } -+ return Boolean.valueOf(read.readFuture.join() != PaperFileIOThread.FAILURE_VALUE); -+ } -+ -+ // wait for write -+ ChunkDataController.InProgressWrite write = task.inProgressWrite; -+ if (write == null) { -+ return null; -+ } -+ return Boolean.valueOf(write.wrote.join() != PaperFileIOThread.FAILURE_VALUE); -+ } -+ // Hack end -+ -+ public NBTTagCompound getPendingWrite(final WorldServer world, final int chunkX, final int chunkZ, final boolean poiData) { -+ final ChunkDataController taskController = poiData ? world.poiDataController : world.chunkDataController; -+ -+ final ChunkDataTask dataTask = taskController.tasks.get(Long.valueOf(IOUtil.getCoordinateKey(chunkX, chunkZ))); -+ -+ if (dataTask == null) { -+ return null; -+ } -+ -+ final ChunkDataController.InProgressWrite write = dataTask.inProgressWrite; -+ -+ if (write == null) { -+ return null; -+ } -+ -+ return write.data; -+ } -+ -+ /** -+ * Sets the priority of all IO tasks for the given chunk coordinates. This has no effect if no tasks are queued. -+ * @param world Chunk's world -+ * @param chunkX Chunk's x coordinate -+ * @param chunkZ Chunk's z coordinate -+ * @param priority Priority level to set to -+ */ -+ public void setPriority(final WorldServer world, final int chunkX, final int chunkZ, final int priority) { -+ if (!PrioritizedTaskQueue.validPriority(priority)) { -+ throw new IllegalArgumentException("Invalid priority: " + priority); -+ } -+ -+ final Long key = Long.valueOf(IOUtil.getCoordinateKey(chunkX, chunkZ)); -+ -+ final ChunkDataTask poiTask = world.poiDataController.tasks.get(key); -+ final ChunkDataTask chunkTask = world.chunkDataController.tasks.get(key); -+ -+ if (poiTask != null) { -+ poiTask.updatePriority(priority); -+ } -+ if (chunkTask != null) { -+ chunkTask.updatePriority(priority); -+ } -+ } -+ -+ /** -+ * Schedules the chunk data to be written asynchronously. -+ *

    -+ * Impl notes: -+ *

    -+ *
  • -+ * This function presumes a chunk load for the coordinates is not called during this function (anytime after is OK). This means -+ * saves must be scheduled before a chunk is unloaded. -+ *
  • -+ *
  • -+ * Writes may be called concurrently, although only the "later" write will go through. -+ *
  • -+ * @param world Chunk's world -+ * @param chunkX Chunk's x coordinate -+ * @param chunkZ Chunk's z coordinate -+ * @param poiData Chunk point of interest data. If {@code null}, then no poi data is saved. -+ * @param chunkData Chunk data. If {@code null}, then no chunk data is saved. -+ * @param priority Priority level for this task. See {@link PrioritizedTaskQueue} -+ * @throws IllegalArgumentException If both {@code poiData} and {@code chunkData} are {@code null}. -+ * @throws IllegalStateException If the file io thread has shutdown. -+ */ -+ public void scheduleSave(final WorldServer world, final int chunkX, final int chunkZ, -+ final NBTTagCompound poiData, final NBTTagCompound chunkData, -+ final int priority) throws IllegalArgumentException { -+ if (!PrioritizedTaskQueue.validPriority(priority)) { -+ throw new IllegalArgumentException("Invalid priority: " + priority); -+ } -+ -+ final long writeCounter = this.writeCounter.getAndIncrement(); -+ -+ if (poiData != null) { -+ this.scheduleWrite(world.poiDataController, world, chunkX, chunkZ, poiData, priority, writeCounter); -+ } -+ if (chunkData != null) { -+ this.scheduleWrite(world.chunkDataController, world, chunkX, chunkZ, chunkData, priority, writeCounter); -+ } -+ } -+ -+ private void scheduleWrite(final ChunkDataController dataController, final WorldServer world, -+ final int chunkX, final int chunkZ, final NBTTagCompound data, final int priority, final long writeCounter) { -+ dataController.tasks.compute(Long.valueOf(IOUtil.getCoordinateKey(chunkX, chunkZ)), (final Long keyInMap, final ChunkDataTask taskRunning) -> { -+ if (taskRunning == null) { -+ // no task is scheduled -+ -+ // create task -+ final ChunkDataTask newTask = new ChunkDataTask(priority, world, chunkX, chunkZ, dataController); -+ newTask.inProgressWrite = new ChunkDataController.InProgressWrite(); -+ newTask.inProgressWrite.writeCounter = writeCounter; -+ newTask.inProgressWrite.data = data; -+ -+ PaperFileIOThread.this.queueTask(newTask); // schedule -+ return newTask; -+ } -+ -+ taskRunning.raisePriority(priority); -+ -+ if (taskRunning.inProgressWrite == null) { -+ taskRunning.inProgressWrite = new ChunkDataController.InProgressWrite(); -+ } -+ -+ boolean reschedule = taskRunning.inProgressWrite.writeCounter == -1L; -+ -+ // synchronize for readers -+ //noinspection SynchronizationOnLocalVariableOrMethodParameter -+ synchronized (taskRunning) { -+ taskRunning.inProgressWrite.data = data; -+ taskRunning.inProgressWrite.writeCounter = writeCounter; -+ } -+ -+ if (reschedule) { -+ // We need to reschedule this task since the previous one is not currently scheduled since it failed -+ taskRunning.reschedule(priority); -+ } -+ -+ return taskRunning; -+ }); -+ } -+ -+ /** -+ * Same as {@link #loadChunkDataAsync(WorldServer, int, int, int, Consumer, boolean, boolean, boolean)}, except this function returns -+ * a {@link CompletableFuture} which is potentially completed ASYNCHRONOUSLY ON THE FILE IO THREAD when the load task -+ * has completed. -+ *

    -+ * Note that if the chunk fails to load the returned future is completed with {@code null}. -+ *

    -+ */ -+ public CompletableFuture loadChunkDataAsyncFuture(final WorldServer world, final int chunkX, final int chunkZ, -+ final int priority, final boolean readPoiData, final boolean readChunkData, -+ final boolean intendingToBlock) { -+ final CompletableFuture future = new CompletableFuture<>(); -+ this.loadChunkDataAsync(world, chunkX, chunkZ, priority, future::complete, readPoiData, readChunkData, intendingToBlock); -+ return future; -+ } -+ -+ /** -+ * Schedules a load to be executed asynchronously. -+ *

    -+ * Impl notes: -+ *

    -+ *
  • -+ * If a chunk fails to load, the {@code onComplete} parameter is completed with {@code null}. -+ *
  • -+ *
  • -+ * It is possible for the {@code onComplete} parameter to be given {@link ChunkData} containing data -+ * this call did not request. -+ *
  • -+ *
  • -+ * The {@code onComplete} parameter may be completed during the execution of this function synchronously or it may -+ * be completed asynchronously on this file io thread. Interacting with the file IO thread in the completion of -+ * data is undefined behaviour, and can cause deadlock. -+ *
  • -+ * @param world Chunk's world -+ * @param chunkX Chunk's x coordinate -+ * @param chunkZ Chunk's z coordinate -+ * @param priority Priority level for this task. See {@link PrioritizedTaskQueue} -+ * @param onComplete Consumer to execute once this task has completed -+ * @param readPoiData Whether to read point of interest data. If {@code false}, the {@code NBTTagCompound} will be {@code null}. -+ * @param readChunkData Whether to read chunk data. If {@code false}, the {@code NBTTagCompound} will be {@code null}. -+ * @return The {@link PrioritizedTaskQueue.PrioritizedTask} associated with this task. Note that this task does not support -+ * cancellation. -+ */ -+ public void loadChunkDataAsync(final WorldServer world, final int chunkX, final int chunkZ, -+ final int priority, final Consumer onComplete, -+ final boolean readPoiData, final boolean readChunkData, -+ final boolean intendingToBlock) { -+ if (!PrioritizedTaskQueue.validPriority(priority)) { -+ throw new IllegalArgumentException("Invalid priority: " + priority); -+ } -+ -+ if (!(readPoiData | readChunkData)) { -+ throw new IllegalArgumentException("Must read chunk data or poi data"); -+ } -+ -+ final ChunkData complete = new ChunkData(); -+ final boolean[] requireCompletion = new boolean[] { readPoiData, readChunkData }; -+ -+ if (readPoiData) { -+ this.scheduleRead(world.poiDataController, world, chunkX, chunkZ, (final NBTTagCompound poiData) -> { -+ complete.poiData = poiData; -+ -+ final boolean finished; -+ -+ // avoid a race condition where the file io thread completes and we complete synchronously -+ // Note: Synchronization can be elided if both of the accesses are volatile -+ synchronized (requireCompletion) { -+ requireCompletion[0] = false; // 0 -> poi data -+ finished = !requireCompletion[1]; // 1 -> chunk data -+ } -+ -+ if (finished) { -+ onComplete.accept(complete); -+ } -+ }, priority, intendingToBlock); -+ } -+ -+ if (readChunkData) { -+ this.scheduleRead(world.chunkDataController, world, chunkX, chunkZ, (final NBTTagCompound chunkData) -> { -+ complete.chunkData = chunkData; -+ -+ final boolean finished; -+ -+ // avoid a race condition where the file io thread completes and we complete synchronously -+ // Note: Synchronization can be elided if both of the accesses are volatile -+ synchronized (requireCompletion) { -+ requireCompletion[1] = false; // 1 -> chunk data -+ finished = !requireCompletion[0]; // 0 -> poi data -+ } -+ -+ if (finished) { -+ onComplete.accept(complete); -+ } -+ }, priority, intendingToBlock); -+ } -+ -+ } -+ -+ // Note: the onComplete may be called asynchronously or synchronously here. -+ private void scheduleRead(final ChunkDataController dataController, final WorldServer world, -+ final int chunkX, final int chunkZ, final Consumer onComplete, final int priority, -+ final boolean intendingToBlock) { -+ -+ Function tryLoadFunction = (final RegionFile file) -> { -+ if (file == null) { -+ return Boolean.TRUE; -+ } -+ return Boolean.valueOf(file.chunkExists(new ChunkCoordIntPair(chunkX, chunkZ))); -+ }; -+ -+ dataController.tasks.compute(Long.valueOf(IOUtil.getCoordinateKey(chunkX, chunkZ)), (final Long keyInMap, final ChunkDataTask running) -> { -+ if (running == null) { -+ // not scheduled -+ -+ final Boolean shouldSchedule = intendingToBlock ? dataController.computeForRegionFile(chunkX, chunkZ, tryLoadFunction) : -+ dataController.computeForRegionFileIfLoaded(chunkX, chunkZ, tryLoadFunction); -+ -+ if (shouldSchedule == Boolean.FALSE) { -+ // not on disk -+ onComplete.accept(null); -+ return null; -+ } -+ -+ // set up task -+ final ChunkDataTask newTask = new ChunkDataTask(priority, world, chunkX, chunkZ, dataController); -+ newTask.inProgressRead = new ChunkDataController.InProgressRead(); -+ newTask.inProgressRead.readFuture.thenAccept(onComplete); -+ -+ PaperFileIOThread.this.queueTask(newTask); // schedule task -+ return newTask; -+ } -+ -+ running.raisePriority(priority); -+ -+ if (running.inProgressWrite == null) { -+ // chain to the read future -+ running.inProgressRead.readFuture.thenAccept(onComplete); -+ return running; -+ } -+ -+ // at this stage we have to use the in progress write's data to avoid an order issue -+ // we don't synchronize since all writes to data occur in the compute() call -+ onComplete.accept(running.inProgressWrite.data); -+ return running; -+ }); -+ } -+ -+ /** -+ * Same as {@link #loadChunkDataAsync(WorldServer, int, int, int, Consumer, boolean, boolean, boolean)}, except this function returns -+ * the {@link ChunkData} associated with the specified chunk when the task is complete. -+ * @return The chunk data, or {@code null} if the chunk failed to load. -+ */ -+ public ChunkData loadChunkData(final WorldServer world, final int chunkX, final int chunkZ, final int priority, -+ final boolean readPoiData, final boolean readChunkData) { -+ return this.loadChunkDataAsyncFuture(world, chunkX, chunkZ, priority, readPoiData, readChunkData, true).join(); -+ } -+ -+ /** -+ * Schedules the given task at the specified priority to be executed on the IO thread. -+ *

    -+ * Internal api. Do not use. -+ *

    -+ */ -+ public void runTask(final int priority, final Runnable runnable) { -+ this.queueTask(new GeneralTask(priority, runnable)); -+ } -+ -+ static final class GeneralTask extends PrioritizedTaskQueue.PrioritizedTask implements Runnable { -+ -+ private final Runnable run; -+ -+ public GeneralTask(final int priority, final Runnable run) { -+ super(priority); -+ this.run = IOUtil.notNull(run, "Task may not be null"); -+ } -+ -+ @Override -+ public void run() { -+ try { -+ this.run.run(); -+ } catch (final Throwable throwable) { -+ if (throwable instanceof ThreadDeath) { -+ throw (ThreadDeath)throwable; -+ } -+ LOGGER.fatal("Failed to execute general task on IO thread " + IOUtil.genericToString(this.run), throwable); -+ } -+ } -+ } -+ -+ public static final class ChunkData { -+ -+ public NBTTagCompound poiData; -+ public NBTTagCompound chunkData; -+ -+ public ChunkData() {} -+ -+ public ChunkData(final NBTTagCompound poiData, final NBTTagCompound chunkData) { -+ this.poiData = poiData; -+ this.chunkData = chunkData; -+ } -+ } -+ -+ public static abstract class ChunkDataController { -+ -+ // ConcurrentHashMap synchronizes per chain, so reduce the chance of task's hashes colliding. -+ public final ConcurrentHashMap tasks = new ConcurrentHashMap<>(64, 0.5f); -+ -+ public abstract void writeData(final int x, final int z, final NBTTagCompound compound) throws IOException; -+ public abstract NBTTagCompound readData(final int x, final int z) throws IOException; -+ -+ public abstract T computeForRegionFile(final int chunkX, final int chunkZ, final Function function); -+ public abstract T computeForRegionFileIfLoaded(final int chunkX, final int chunkZ, final Function function); -+ -+ public static final class InProgressWrite { -+ public long writeCounter; -+ public NBTTagCompound data; -+ -+ // Hack start -+ @Deprecated -+ public CompletableFuture wrote = new CompletableFuture<>(); -+ // Hack end -+ } -+ -+ public static final class InProgressRead { -+ public final CompletableFuture readFuture = new CompletableFuture<>(); -+ } -+ } -+ -+ public static final class ChunkDataTask extends PrioritizedTaskQueue.PrioritizedTask implements Runnable { -+ -+ public ChunkDataController.InProgressWrite inProgressWrite; -+ public ChunkDataController.InProgressRead inProgressRead; -+ -+ private final WorldServer world; -+ private final int x; -+ private final int z; -+ private final ChunkDataController taskController; -+ -+ public ChunkDataTask(final int priority, final WorldServer world, final int x, final int z, final ChunkDataController taskController) { -+ super(priority); -+ this.world = world; -+ this.x = x; -+ this.z = z; -+ this.taskController = taskController; -+ } -+ -+ @Override -+ public String toString() { -+ return "Task for world: '" + this.world.getWorld().getName() + "' at " + this.x + "," + this.z + -+ " poi: " + (this.taskController == this.world.poiDataController) + ", hash: " + this.hashCode(); -+ } -+ -+ /* -+ * -+ * IO thread will perform reads before writes -+ * -+ * How reads/writes are scheduled: -+ * -+ * If read in progress while scheduling write, ignore read and schedule write -+ * If read in progress while scheduling read (no write in progress), chain the read task -+ * -+ * -+ * If write in progress while scheduling read, use the pending write data and ret immediately -+ * If write in progress while scheduling write (ignore read in progress), overwrite the write in progress data -+ * -+ * This allows the reads and writes to act as if they occur synchronously to the thread scheduling them, however -+ * it fails to properly propagate write failures -+ * -+ */ -+ -+ void reschedule(final int priority) { -+ // priority is checked before this stage // TODO what -+ this.queue.lazySet(null); -+ this.inProgressWrite.wrote = new CompletableFuture<>(); // Hack -+ this.priority.lazySet(priority); -+ PaperFileIOThread.Holder.INSTANCE.queueTask(this); -+ } -+ -+ @Override -+ public void run() { -+ ChunkDataController.InProgressRead read = this.inProgressRead; -+ if (read != null) { -+ NBTTagCompound compound = PaperFileIOThread.FAILURE_VALUE; -+ try { -+ compound = this.taskController.readData(this.x, this.z); -+ } catch (final Throwable thr) { -+ if (thr instanceof ThreadDeath) { -+ throw (ThreadDeath)thr; -+ } -+ LOGGER.fatal("Failed to read chunk data for task: " + this.toString(), thr); -+ // fall through to complete with null data -+ } -+ read.readFuture.complete(compound); -+ } -+ -+ final Long chunkKey = Long.valueOf(IOUtil.getCoordinateKey(this.x, this.z)); -+ -+ ChunkDataController.InProgressWrite write = this.inProgressWrite; -+ -+ if (write == null) { -+ // IntelliJ warns this is invalid, however it does not consider that writes to the task map & the inProgress field can occur concurrently. -+ ChunkDataTask inMap = this.taskController.tasks.compute(chunkKey, (final Long keyInMap, final ChunkDataTask valueInMap) -> { -+ if (valueInMap == null) { -+ throw new IllegalStateException("Write completed concurrently, expected this task: " + ChunkDataTask.this.toString() + ", report this!"); -+ } -+ if (valueInMap != ChunkDataTask.this) { -+ throw new IllegalStateException("Chunk task mismatch, expected this task: " + ChunkDataTask.this.toString() + ", got: " + valueInMap.toString() + ", report this!"); -+ } -+ return valueInMap.inProgressWrite == null ? null : valueInMap; -+ }); -+ -+ if (inMap == null) { -+ return; // set the task value to null, indicating we're done -+ } -+ -+ // not null, which means there was a concurrent write -+ write = this.inProgressWrite; -+ } -+ -+ // check if another process is writing -+ try { -+ this.world.checkSession(); -+ } catch (final ExceptionWorldConflict ex) { -+ LOGGER.fatal("Couldn't save chunk; already in use by another instance of Minecraft?", ex); -+ // we don't need to set the write counter to -1 as we know at this stage there's no point in re-scheduling -+ // writes since they'll fail anyways. -+ write.wrote.complete(PaperFileIOThread.FAILURE_VALUE); // Hack - However we need to fail the write -+ return; -+ } -+ -+ for (;;) { -+ final long writeCounter; -+ final NBTTagCompound data; -+ -+ //noinspection SynchronizationOnLocalVariableOrMethodParameter -+ synchronized (write) { -+ writeCounter = write.writeCounter; -+ data = write.data; -+ } -+ -+ boolean failedWrite = false; -+ -+ try { -+ this.taskController.writeData(this.x, this.z, data); -+ } catch (final Throwable thr) { -+ if (thr instanceof ThreadDeath) { -+ throw (ThreadDeath)thr; -+ } -+ LOGGER.fatal("Failed to write chunk data for task: " + this.toString(), thr); -+ failedWrite = true; -+ } -+ -+ boolean finalFailWrite = failedWrite; -+ -+ ChunkDataTask inMap = this.taskController.tasks.compute(chunkKey, (final Long keyInMap, final ChunkDataTask valueInMap) -> { -+ if (valueInMap == null) { -+ ChunkDataTask.this.inProgressWrite.wrote.complete(PaperFileIOThread.FAILURE_VALUE); // Hack -+ throw new IllegalStateException("Write completed concurrently, expected this task: " + ChunkDataTask.this.toString() + ", report this!"); -+ } -+ if (valueInMap != ChunkDataTask.this) { -+ ChunkDataTask.this.inProgressWrite.wrote.complete(PaperFileIOThread.FAILURE_VALUE); // Hack -+ throw new IllegalStateException("Chunk task mismatch, expected this task: " + ChunkDataTask.this.toString() + ", got: " + valueInMap.toString() + ", report this!"); -+ } -+ if (valueInMap.inProgressWrite.writeCounter == writeCounter) { -+ if (finalFailWrite) { -+ valueInMap.inProgressWrite.writeCounter = -1L; -+ valueInMap.inProgressWrite.wrote.complete(PaperFileIOThread.FAILURE_VALUE); -+ } else { -+ valueInMap.inProgressWrite.wrote.complete(data); -+ } -+ -+ return null; -+ } -+ return valueInMap; -+ // Hack end -+ }); -+ -+ if (inMap == null) { -+ // write counter matched, so we wrote the most up-to-date pending data, we're done here -+ // or we failed to write and successfully set the write counter to -1 -+ return; // we're done here -+ } -+ -+ // fetch & write new data -+ continue; -+ } -+ } -+ } -+} -diff --git a/src/main/java/com/destroystokyo/paper/io/PrioritizedTaskQueue.java b/src/main/java/com/destroystokyo/paper/io/PrioritizedTaskQueue.java -new file mode 100644 -index 000000000..78bd238f4 ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/io/PrioritizedTaskQueue.java -@@ -0,0 +0,0 @@ -+package com.destroystokyo.paper.io; -+ -+import java.util.concurrent.ConcurrentLinkedQueue; -+import java.util.concurrent.atomic.AtomicBoolean; -+import java.util.concurrent.atomic.AtomicInteger; -+import java.util.concurrent.atomic.AtomicReference; -+ -+public class PrioritizedTaskQueue { -+ -+ // lower numbers are a higher priority (except < 0) -+ // higher priorities are always executed before lower priorities -+ -+ /** -+ * Priority value indicating the task has completed or is being completed. -+ */ -+ public static final int COMPLETING_PRIORITY = -1; -+ -+ /** -+ * Highest priority, should only be used for main thread tasks or tasks that are blocking the main thread. -+ */ -+ public static final int HIGHEST_PRIORITY = 0; -+ -+ /** -+ * Should be only used in an IO task so that chunk loads do not wait on other IO tasks. -+ * This only exists because IO tasks are scheduled before chunk load tasks to decrease IO waiting times. -+ */ -+ public static final int HIGHER_PRIORITY = 1; -+ -+ /** -+ * Should be used for scheduling chunk loads/generation that would increase response times to users. -+ */ -+ public static final int HIGH_PRIORITY = 2; -+ -+ /** -+ * Default priority. -+ */ -+ public static final int NORMAL_PRIORITY = 3; -+ -+ /** -+ * Use for tasks not at all critical and can potentially be delayed. -+ */ -+ public static final int LOW_PRIORITY = 4; -+ -+ /** -+ * Use for tasks that should "eventually" execute. -+ */ -+ public static final int LOWEST_PRIORITY = 5; -+ -+ private static final int TOTAL_PRIORITIES = 6; -+ -+ final ConcurrentLinkedQueue[] queues = (ConcurrentLinkedQueue[])new ConcurrentLinkedQueue[TOTAL_PRIORITIES]; -+ -+ private final AtomicBoolean shutdown = new AtomicBoolean(); -+ -+ { -+ for (int i = 0; i < TOTAL_PRIORITIES; ++i) { -+ this.queues[i] = new ConcurrentLinkedQueue<>(); -+ } -+ } -+ -+ /** -+ * Returns whether the specified priority is valid -+ */ -+ public static boolean validPriority(final int priority) { -+ return priority >= 0 && priority < TOTAL_PRIORITIES; -+ } -+ -+ /** -+ * Queues a task. -+ * @throws IllegalStateException If the task has already been queued. Use {@link PrioritizedTask#raisePriority(int)} to -+ * raise a task's priority. -+ * This can also be thrown if the queue has shutdown. -+ */ -+ public void add(final T task) throws IllegalStateException { -+ task.onQueue(this); -+ this.queues[task.getPriority()].add(task); -+ if (this.shutdown.get()) { -+ // note: we're not actually sure at this point if our task will go through -+ throw new IllegalStateException("Queue has shutdown, refusing to execute task " + IOUtil.genericToString(task)); -+ } -+ } -+ -+ /** -+ * Polls the highest priority task currently available. {@code null} if none. -+ */ -+ public T poll() { -+ T task; -+ for (int i = 0; i < TOTAL_PRIORITIES; ++i) { -+ final ConcurrentLinkedQueue queue = this.queues[i]; -+ -+ while ((task = queue.poll()) != null) { -+ final int prevPriority = task.tryComplete(i); -+ if (prevPriority != COMPLETING_PRIORITY && prevPriority <= i) { -+ // if the prev priority was greater-than or equal to our current priority -+ return task; -+ } -+ } -+ } -+ -+ return null; -+ } -+ -+ /** -+ * Returns whether this queue may have tasks queued. -+ *

    -+ * This operation is not atomic, but is MT-Safe. -+ *

    -+ * @return {@code true} if tasks may be queued, {@code false} otherwise -+ */ -+ public boolean hasTasks() { -+ for (int i = 0; i < TOTAL_PRIORITIES; ++i) { -+ final ConcurrentLinkedQueue queue = this.queues[i]; -+ -+ if (queue.peek() != null) { -+ return true; -+ } -+ } -+ return false; -+ } -+ -+ /** -+ * Prevent further additions to this queue. Attempts to add after this call has completed (potentially during) will -+ * result in {@link IllegalStateException} being thrown. -+ *

    -+ * This operation is atomic with respect to other shutdown calls -+ *

    -+ *

    -+ * After this call has completed, regardless of return value, this queue will be shutdown. -+ *

    -+ * @return {@code true} if the queue was shutdown, {@code false} if it has shut down already -+ */ -+ public boolean shutdown() { -+ return this.shutdown.getAndSet(false); -+ } -+ -+ public abstract static class PrioritizedTask { -+ -+ protected final AtomicReference queue = new AtomicReference<>(); -+ -+ protected final AtomicInteger priority; -+ -+ protected PrioritizedTask() { -+ this(PrioritizedTaskQueue.NORMAL_PRIORITY); -+ } -+ -+ protected PrioritizedTask(final int priority) { -+ if (!PrioritizedTaskQueue.validPriority(priority)) { -+ throw new IllegalArgumentException("Invalid priority " + priority); -+ } -+ this.priority = new AtomicInteger(priority); -+ } -+ -+ /** -+ * Returns the current priority. Note that {@link PrioritizedTaskQueue#COMPLETING_PRIORITY} will be returned -+ * if this task is completing or has completed. -+ */ -+ public final int getPriority() { -+ return this.priority.get(); -+ } -+ -+ /** -+ * Returns whether this task is scheduled to execute, or has been already executed. -+ */ -+ public boolean isScheduled() { -+ return this.queue.get() != null; -+ } -+ -+ final int tryComplete(final int minPriority) { -+ for (int curr = this.getPriorityVolatile();;) { -+ if (curr == COMPLETING_PRIORITY) { -+ return COMPLETING_PRIORITY; -+ } -+ if (curr > minPriority) { -+ // curr is lower priority -+ return curr; -+ } -+ -+ if (curr == (curr = this.compareAndExchangePriorityVolatile(curr, COMPLETING_PRIORITY))) { -+ return curr; -+ } -+ continue; -+ } -+ } -+ -+ /** -+ * Forces this task to be completed. -+ * @return {@code true} if the task was cancelled, {@code false} if the task has already completed or is being completed. -+ */ -+ public boolean cancel() { -+ return this.exchangePriorityVolatile(PrioritizedTaskQueue.COMPLETING_PRIORITY) != PrioritizedTaskQueue.COMPLETING_PRIORITY; -+ } -+ -+ /** -+ * Attempts to raise the priority to the priority level specified. -+ * @param priority Priority specified -+ * @return {@code true} if successful, {@code false} otherwise. -+ */ -+ public boolean raisePriority(final int priority) { -+ if (!PrioritizedTaskQueue.validPriority(priority)) { -+ throw new IllegalArgumentException("Invalid priority"); -+ } -+ -+ for (int curr = this.getPriorityVolatile();;) { -+ if (curr == COMPLETING_PRIORITY) { -+ return false; -+ } -+ if (priority >= curr) { -+ return true; -+ } -+ -+ if (curr == (curr = this.compareAndExchangePriorityVolatile(curr, priority))) { -+ PrioritizedTaskQueue queue = this.queue.get(); -+ if (queue != null) { -+ //noinspection unchecked -+ queue.queues[priority].add(this); // silently fail on shutdown -+ } -+ return true; -+ } -+ continue; -+ } -+ } -+ -+ /** -+ * Attempts to set this task's priority level to the level specified. -+ * @param priority Specified priority level. -+ * @return {@code true} if successful, {@code false} if this task is completing or has completed. -+ */ -+ public boolean updatePriority(final int priority) { -+ if (!PrioritizedTaskQueue.validPriority(priority)) { -+ throw new IllegalArgumentException("Invalid priority"); -+ } -+ -+ for (int curr = this.getPriorityVolatile();;) { -+ if (curr == COMPLETING_PRIORITY) { -+ return false; -+ } -+ if (curr == priority) { -+ return true; -+ } -+ -+ if (curr == (curr = this.compareAndExchangePriorityVolatile(curr, priority))) { -+ PrioritizedTaskQueue queue = this.queue.get(); -+ if (queue != null) { -+ //noinspection unchecked -+ queue.queues[priority].add(this); // silently fail on shutdown -+ } -+ return true; -+ } -+ continue; -+ } -+ } -+ -+ void onQueue(final PrioritizedTaskQueue queue) { -+ if (this.queue.getAndSet(queue) != null) { -+ throw new IllegalStateException("Already queued!"); -+ } -+ } -+ -+ /* priority */ -+ -+ protected final int getPriorityVolatile() { -+ return this.priority.get(); -+ } -+ -+ protected final int compareAndExchangePriorityVolatile(final int expect, final int update) { -+ if (this.priority.compareAndSet(expect, update)) { -+ return expect; -+ } -+ return this.priority.get(); -+ } -+ -+ protected final int exchangePriorityVolatile(final int value) { -+ return this.priority.getAndSet(value); -+ } -+ } -+} -diff --git a/src/main/java/com/destroystokyo/paper/io/QueueExecutorThread.java b/src/main/java/com/destroystokyo/paper/io/QueueExecutorThread.java -new file mode 100644 -index 000000000..ee906b594 ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/io/QueueExecutorThread.java -@@ -0,0 +0,0 @@ -+package com.destroystokyo.paper.io; -+ -+import net.minecraft.server.MinecraftServer; -+import org.apache.logging.log4j.Logger; -+ -+import java.util.concurrent.ConcurrentLinkedQueue; -+import java.util.concurrent.atomic.AtomicBoolean; -+import java.util.concurrent.locks.LockSupport; -+ -+public class QueueExecutorThread extends Thread { -+ -+ private static final Logger LOGGER = MinecraftServer.LOGGER; -+ -+ protected final PrioritizedTaskQueue queue; -+ protected final long spinWaitTime; -+ -+ protected volatile boolean closed; -+ -+ protected final AtomicBoolean parked = new AtomicBoolean(); -+ -+ protected volatile ConcurrentLinkedQueue flushQueue = new ConcurrentLinkedQueue<>(); -+ protected volatile long flushCycles; -+ -+ public QueueExecutorThread(final PrioritizedTaskQueue queue) { -+ this(queue, (int)(1.e6)); // 1.0ms -+ } -+ -+ public QueueExecutorThread(final PrioritizedTaskQueue queue, final long spinWaitTime) { // in ms -+ this.queue = queue; -+ this.spinWaitTime = spinWaitTime; -+ } -+ -+ @Override -+ public void run() { -+ final long spinWaitTime = this.spinWaitTime; -+ main_loop: -+ for (;;) { -+ this.pollTasks(true); -+ -+ // spinwait -+ -+ final long start = System.nanoTime(); -+ -+ for (;;) { -+ // If we are interrpted for any reason, park() will always return immediately. Clear so that we don't needlessly use cpu in such an event. -+ Thread.interrupted(); -+ LockSupport.parkNanos("Spinwaiting on tasks", 1000L); // 1us -+ -+ if (this.pollTasks(true)) { -+ // restart loop, found tasks -+ continue main_loop; -+ } -+ -+ if (this.handleClose()) { -+ return; // we're done -+ } -+ -+ if ((System.nanoTime() - start) >= spinWaitTime) { -+ break; -+ } -+ } -+ -+ if (this.handleClose()) { -+ return; -+ } -+ -+ this.parked.set(true); -+ -+ // We need to parse here to avoid a race condition where a thread queues a task before we set parked to true -+ // (i.e it will not notify us) -+ if (this.pollTasks(true)) { -+ this.parked.set(false); -+ continue; -+ } -+ -+ if (this.handleClose()) { -+ return; -+ } -+ -+ // we don't need to check parked before sleeping, but we do need to check parked in a do-while loop -+ // LockSupport.park() can fail for any reason -+ do { -+ Thread.interrupted(); -+ LockSupport.park("Waiting on tasks"); -+ } while (this.parked.get()); -+ } -+ } -+ -+ protected boolean handleClose() { -+ if (this.closed) { -+ this.pollTasks(true); // this ensures we've emptied the queue -+ this.handleFlushThreads(true); -+ return true; -+ } -+ return false; -+ } -+ -+ protected boolean pollTasks(boolean flushTasks) { -+ Runnable task; -+ boolean ret = false; -+ -+ while ((task = this.queue.poll()) != null) { -+ ret = true; -+ try { -+ task.run(); -+ } catch (final Throwable throwable) { -+ if (throwable instanceof ThreadDeath) { -+ throw (ThreadDeath)throwable; -+ } -+ LOGGER.fatal("Exception thrown from prioritized runnable task in thread '" + this.getName() + "': " + IOUtil.genericToString(task), throwable); -+ } -+ } -+ -+ if (flushTasks) { -+ this.handleFlushThreads(false); -+ } -+ -+ return ret; -+ } -+ -+ protected void handleFlushThreads(final boolean shutdown) { -+ Thread parking; -+ ConcurrentLinkedQueue flushQueue = this.flushQueue; -+ do { -+ ++flushCycles; // may be plain read opaque write -+ while ((parking = flushQueue.poll()) != null) { -+ LockSupport.unpark(parking); -+ } -+ } while (this.pollTasks(false)); -+ -+ if (shutdown) { -+ this.flushQueue = null; -+ -+ // defend against a race condition where a flush thread double-checks right before we set to null -+ while ((parking = flushQueue.poll()) != null) { -+ LockSupport.unpark(parking); -+ } -+ } -+ } -+ -+ /** -+ * Notify's this thread that a task has been added to its queue -+ * @return {@code true} if this thread was waiting for tasks, {@code false} if it is executing tasks -+ */ -+ public boolean notifyTasks() { -+ if (this.parked.get() && this.parked.getAndSet(false)) { -+ LockSupport.unpark(this); -+ return true; -+ } -+ return false; -+ } -+ -+ protected void queueTask(final T task) { -+ this.queue.add(task); -+ this.notifyTasks(); -+ } -+ -+ /** -+ * Waits until this thread's queue is empty. -+ * -+ * @throws IllegalStateException If the current thread is {@code this} thread. -+ */ -+ public void flush() { -+ final Thread currentThread = Thread.currentThread(); -+ -+ if (currentThread == this) { -+ // avoid deadlock -+ throw new IllegalStateException("Cannot flush the queue executor thread while on the queue executor thread"); -+ } -+ -+ // order is important -+ -+ int successes = 0; -+ long lastCycle = -1L; -+ -+ do { -+ final ConcurrentLinkedQueue flushQueue = this.flushQueue; -+ if (flushQueue == null) { -+ return; -+ } -+ -+ flushQueue.add(currentThread); -+ -+ // double check flush queue -+ if (this.flushQueue == null) { -+ return; -+ } -+ -+ final long currentCycle = this.flushCycles; // may be opaque read -+ -+ if (currentCycle == lastCycle) { -+ Thread.yield(); -+ continue; -+ } -+ -+ // force response -+ this.parked.set(false); -+ LockSupport.unpark(this); -+ -+ LockSupport.park("flushing queue executor thread"); -+ -+ // returns whether there are tasks queued, does not return whether there are tasks executing -+ // this is why we cycle twice twice through flush (we know a pollTask call is made after a flush cycle) -+ // we really only need to guarantee that the tasks this thread has queued has gone through, and can leave -+ // tasks queued concurrently that are unsychronized with this thread as undefined behavior -+ if (this.queue.hasTasks()) { -+ successes = 0; -+ } else { -+ ++successes; -+ } -+ -+ } while (successes != 2); -+ -+ } -+ -+ /** -+ * Closes this queue executor's queue and optionally waits for it to empty. -+ *

    -+ * If wait is {@code true}, then the queue will be empty by the time this call completes. -+ *

    -+ *

    -+ * This function is MT-Safe. -+ *

    -+ * @param wait If this call is to wait until the queue is empty -+ * @param killQueue Whether to shutdown this thread's queue -+ * @return whether this thread shut down the queue -+ */ -+ public boolean close(final boolean wait, final boolean killQueue) { -+ boolean ret = !killQueue ? false : this.queue.shutdown(); -+ this.closed = true; -+ -+ // force thread to respond to the shutdown -+ this.parked.set(false); -+ LockSupport.unpark(this); -+ -+ if (wait) { -+ this.flush(); -+ } -+ return ret; -+ } -+} -diff --git a/src/main/java/com/destroystokyo/paper/io/chunk/ChunkLoadTask.java b/src/main/java/com/destroystokyo/paper/io/chunk/ChunkLoadTask.java -new file mode 100644 -index 000000000..305da4786 ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/io/chunk/ChunkLoadTask.java -@@ -0,0 +0,0 @@ -+package com.destroystokyo.paper.io.chunk; -+ -+import co.aikar.timings.Timing; -+import com.destroystokyo.paper.io.PaperFileIOThread; -+import com.destroystokyo.paper.io.IOUtil; -+import net.minecraft.server.ChunkCoordIntPair; -+import net.minecraft.server.ChunkRegionLoader; -+import net.minecraft.server.PlayerChunkMap; -+import net.minecraft.server.WorldServer; -+ -+import java.util.ArrayDeque; -+import java.util.function.Consumer; -+ -+public final class ChunkLoadTask extends ChunkTask { -+ -+ public boolean cancelled; -+ -+ Consumer onComplete; -+ public PaperFileIOThread.ChunkData chunkData; -+ -+ private boolean hasCompleted; -+ -+ public ChunkLoadTask(final WorldServer world, final int chunkX, final int chunkZ, final int priority, -+ final ChunkTaskManager taskManager, -+ final Consumer onComplete) { -+ super(world, chunkX, chunkZ, priority, taskManager); -+ this.onComplete = onComplete; -+ } -+ -+ private static final ArrayDeque EMPTY_QUEUE = new ArrayDeque<>(); -+ -+ private static ChunkRegionLoader.InProgressChunkHolder createEmptyHolder() { -+ return new ChunkRegionLoader.InProgressChunkHolder(null, EMPTY_QUEUE); -+ } -+ -+ @Override -+ public void run() { -+ try { -+ this.executeTask(); -+ } catch (final Throwable ex) { -+ PaperFileIOThread.LOGGER.error("Failed to execute chunk load task: " + this.toString(), ex); -+ if (!this.hasCompleted) { -+ this.complete(ChunkLoadTask.createEmptyHolder()); -+ } -+ } -+ } -+ -+ private boolean checkCancelled() { -+ if (this.cancelled) { -+ // IntelliJ does not understand writes may occur to cancelled concurrently. -+ return this.taskManager.chunkLoadTasks.compute(Long.valueOf(IOUtil.getCoordinateKey(this.chunkX, this.chunkZ)), (final Long keyInMap, final ChunkLoadTask valueInMap) -> { -+ if (valueInMap != ChunkLoadTask.this) { -+ throw new IllegalStateException("Expected this task to be scheduled, but another was! Other: " + valueInMap + ", current: " + ChunkLoadTask.this); -+ } -+ -+ if (valueInMap.cancelled) { -+ return null; -+ } -+ return valueInMap; -+ }) == null; -+ } -+ return false; -+ } -+ -+ public void executeTask() { -+ if (this.checkCancelled()) { -+ return; -+ } -+ -+ // either executed synchronously or asynchronously -+ final PaperFileIOThread.ChunkData chunkData = this.chunkData; -+ -+ if (chunkData.poiData == PaperFileIOThread.FAILURE_VALUE || chunkData.chunkData == PaperFileIOThread.FAILURE_VALUE) { -+ PaperFileIOThread.LOGGER.error("Could not load chunk for task: " + this.toString() + ", file IO thread has dumped the relevant exception above"); -+ this.complete(ChunkLoadTask.createEmptyHolder()); -+ return; -+ } -+ -+ if (chunkData.chunkData == null) { -+ // not on disk -+ this.complete(ChunkLoadTask.createEmptyHolder()); -+ return; -+ } -+ -+ final ChunkCoordIntPair chunkPos = new ChunkCoordIntPair(this.chunkX, this.chunkZ); -+ -+ final PlayerChunkMap chunkManager = this.world.getChunkProvider().playerChunkMap; -+ -+ try (Timing ignored = this.world.timings.chunkIOStage1.startTimingIfSync()) { -+ final ChunkRegionLoader.InProgressChunkHolder chunkHolder; -+ -+ // apply fixes -+ -+ try { -+ if (chunkData.poiData != null) { -+ chunkData.poiData = chunkData.poiData.clone(); // clone data for safety, file IO thread does not clone -+ } -+ chunkData.chunkData = chunkManager.getChunkData(this.world.getWorldProvider().getDimensionManager(), -+ chunkManager.getWorldPersistentDataSupplier(), chunkData.chunkData.clone(), chunkPos, this.world); // clone data for safety, file IO thread does not clone -+ } catch (final Throwable ex) { -+ PaperFileIOThread.LOGGER.error("Could not apply datafixers for chunk task: " + this.toString(), ex); -+ this.complete(ChunkLoadTask.createEmptyHolder()); -+ } -+ -+ if (this.checkCancelled()) { -+ return; -+ } -+ -+ try { -+ this.world.getChunkProvider().playerChunkMap.updateChunkStatusOnDisk(chunkPos, chunkData.chunkData); -+ } catch (final Throwable ex) { -+ PaperFileIOThread.LOGGER.warn("Failed to update chunk status cache for task: " + this.toString(), ex); -+ // non-fatal, continue -+ } -+ -+ try { -+ chunkHolder = ChunkRegionLoader.loadChunk(this.world, -+ chunkManager.definedStructureManager, chunkManager.getVillagePlace(), chunkPos, -+ chunkData.chunkData, true); -+ } catch (final Throwable ex) { -+ PaperFileIOThread.LOGGER.error("Could not de-serialize chunk data for task: " + this.toString(), ex); -+ this.complete(ChunkLoadTask.createEmptyHolder()); -+ return; -+ } -+ -+ this.complete(chunkHolder); -+ } -+ } -+ -+ private void complete(final ChunkRegionLoader.InProgressChunkHolder holder) { -+ this.hasCompleted = true; -+ holder.poiData = this.chunkData == null ? null : this.chunkData.poiData; -+ -+ this.taskManager.chunkLoadTasks.compute(Long.valueOf(IOUtil.getCoordinateKey(this.chunkX, this.chunkZ)), (final Long keyInMap, final ChunkLoadTask valueInMap) -> { -+ if (valueInMap != ChunkLoadTask.this) { -+ throw new IllegalStateException("Expected this task to be scheduled, but another was! Other: " + valueInMap + ", current: " + ChunkLoadTask.this); -+ } -+ if (valueInMap.cancelled) { -+ return null; -+ } -+ try { -+ ChunkLoadTask.this.onComplete.accept(holder); -+ } catch (final Throwable thr) { -+ PaperFileIOThread.LOGGER.error("Failed to complete chunk data for task: " + this.toString(), thr); -+ } -+ return null; -+ }); -+ } -+} -diff --git a/src/main/java/com/destroystokyo/paper/io/chunk/ChunkSaveTask.java b/src/main/java/com/destroystokyo/paper/io/chunk/ChunkSaveTask.java -new file mode 100644 -index 000000000..60312b85f ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/io/chunk/ChunkSaveTask.java -@@ -0,0 +0,0 @@ -+package com.destroystokyo.paper.io.chunk; -+ -+import co.aikar.timings.Timing; -+import com.destroystokyo.paper.io.PaperFileIOThread; -+import com.destroystokyo.paper.io.IOUtil; -+import com.destroystokyo.paper.io.PrioritizedTaskQueue; -+import net.minecraft.server.ChunkRegionLoader; -+import net.minecraft.server.IAsyncTaskHandler; -+import net.minecraft.server.IChunkAccess; -+import net.minecraft.server.NBTTagCompound; -+import net.minecraft.server.WorldServer; -+ -+import java.util.concurrent.CompletableFuture; -+import java.util.concurrent.atomic.AtomicInteger; -+ -+public final class ChunkSaveTask extends ChunkTask { -+ -+ public final ChunkRegionLoader.AsyncSaveData asyncSaveData; -+ public final IChunkAccess chunk; -+ public final CompletableFuture onComplete = new CompletableFuture<>(); -+ -+ private final AtomicInteger attemptedPriority; -+ -+ public ChunkSaveTask(final WorldServer world, final int chunkX, final int chunkZ, final int priority, -+ final ChunkTaskManager taskManager, final ChunkRegionLoader.AsyncSaveData asyncSaveData, -+ final IChunkAccess chunk) { -+ super(world, chunkX, chunkZ, priority, taskManager); -+ this.chunk = chunk; -+ this.asyncSaveData = asyncSaveData; -+ this.attemptedPriority = new AtomicInteger(priority); -+ } -+ -+ @Override -+ public void run() { -+ // can be executed asynchronously or synchronously -+ final NBTTagCompound compound; -+ -+ try (Timing ignored = this.world.timings.chunkUnloadDataSave.startTimingIfSync()) { -+ compound = ChunkRegionLoader.saveChunk(this.world, this.chunk, this.asyncSaveData); -+ } catch (final Throwable ex) { -+ // has a plugin modified something it should not have and made us CME? -+ PaperFileIOThread.LOGGER.error("Failed to serialize unloading chunk data for task: " + this.toString() + ", falling back to a synchronous execution", ex); -+ -+ // Note: We add to the server thread queue here since this is what the server will drain tasks from -+ // when waiting for chunks -+ ChunkTaskManager.queueChunkWaitTask(() -> { -+ try (Timing ignored = this.world.timings.chunkUnloadDataSave.startTiming()) { -+ NBTTagCompound data = PaperFileIOThread.FAILURE_VALUE; -+ -+ try { -+ data = ChunkRegionLoader.saveChunk(this.world, this.chunk, this.asyncSaveData); -+ PaperFileIOThread.LOGGER.info("Successfully serialized chunk data for task: " + this.toString() + " synchronously"); -+ } catch (final Throwable ex1) { -+ PaperFileIOThread.LOGGER.fatal("Failed to synchronously serialize unloading chunk data for task: " + this.toString() + "! Chunk data will be lost", ex1); -+ } -+ -+ ChunkSaveTask.this.complete(data); -+ } -+ }); -+ -+ return; // the main thread will now complete the data -+ } -+ -+ this.complete(compound); -+ } -+ -+ @Override -+ public boolean raisePriority(final int priority) { -+ if (!PrioritizedTaskQueue.validPriority(priority)) { -+ throw new IllegalStateException("Invalid priority: " + priority); -+ } -+ -+ // we know priority is valid here -+ for (int curr = this.attemptedPriority.get();;) { -+ if (curr <= priority) { -+ break; // curr is higher/same priority -+ } -+ if (this.attemptedPriority.compareAndSet(curr, priority)) { -+ break; -+ } -+ curr = this.attemptedPriority.get(); -+ } -+ -+ return super.raisePriority(priority); -+ } -+ -+ @Override -+ public boolean updatePriority(final int priority) { -+ if (!PrioritizedTaskQueue.validPriority(priority)) { -+ throw new IllegalStateException("Invalid priority: " + priority); -+ } -+ this.attemptedPriority.set(priority); -+ return super.updatePriority(priority); -+ } -+ -+ private void complete(final NBTTagCompound compound) { -+ try { -+ this.onComplete.complete(compound); -+ } catch (final Throwable thr) { -+ PaperFileIOThread.LOGGER.error("Failed to complete chunk data for task: " + this.toString(), thr); -+ } -+ if (compound != PaperFileIOThread.FAILURE_VALUE) { -+ PaperFileIOThread.Holder.INSTANCE.scheduleSave(this.world, this.chunkX, this.chunkZ, null, compound, this.attemptedPriority.get()); -+ } -+ this.taskManager.chunkSaveTasks.compute(Long.valueOf(IOUtil.getCoordinateKey(this.chunkX, this.chunkZ)), (final Long keyInMap, final ChunkSaveTask valueInMap) -> { -+ if (valueInMap != ChunkSaveTask.this) { -+ throw new IllegalStateException("Expected this task to be scheduled, but another was! Other: " + valueInMap + ", this: " + ChunkSaveTask.this); -+ } -+ return null; -+ }); -+ } -+} -diff --git a/src/main/java/com/destroystokyo/paper/io/chunk/ChunkTask.java b/src/main/java/com/destroystokyo/paper/io/chunk/ChunkTask.java -new file mode 100644 -index 000000000..1dfa8abfd ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/io/chunk/ChunkTask.java -@@ -0,0 +0,0 @@ -+package com.destroystokyo.paper.io.chunk; -+ -+import com.destroystokyo.paper.io.PaperFileIOThread; -+import com.destroystokyo.paper.io.PrioritizedTaskQueue; -+import net.minecraft.server.WorldServer; -+ -+abstract class ChunkTask extends PrioritizedTaskQueue.PrioritizedTask implements Runnable { -+ -+ public final WorldServer world; -+ public final int chunkX; -+ public final int chunkZ; -+ public final ChunkTaskManager taskManager; -+ -+ public ChunkTask(final WorldServer world, final int chunkX, final int chunkZ, final int priority, -+ final ChunkTaskManager taskManager) { -+ super(priority); -+ this.world = world; -+ this.chunkX = chunkX; -+ this.chunkZ = chunkZ; -+ this.taskManager = taskManager; -+ } -+ -+ @Override -+ public String toString() { -+ return "Chunk task: class:" + this.getClass().getName() + ", for world '" + this.world.getWorld().getName() + -+ "', (" + this.chunkX + "," + this.chunkZ + "), hashcode:" + this.hashCode() + ", priority: " + this.getPriority(); -+ } -+ -+ @Override -+ public boolean raisePriority(final int priority) { -+ PaperFileIOThread.Holder.INSTANCE.bumpPriority(this.world, this.chunkX, this.chunkZ, priority); -+ return super.raisePriority(priority); -+ } -+ -+ @Override -+ public boolean updatePriority(final int priority) { -+ PaperFileIOThread.Holder.INSTANCE.setPriority(this.world, this.chunkX, this.chunkZ, priority); -+ return super.updatePriority(priority); -+ } -+} -diff --git a/src/main/java/com/destroystokyo/paper/io/chunk/ChunkTaskManager.java b/src/main/java/com/destroystokyo/paper/io/chunk/ChunkTaskManager.java -new file mode 100644 -index 000000000..59d73bfad ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/io/chunk/ChunkTaskManager.java -@@ -0,0 +0,0 @@ -+package com.destroystokyo.paper.io.chunk; -+ -+import com.destroystokyo.paper.io.PaperFileIOThread; -+import com.destroystokyo.paper.io.IOUtil; -+import com.destroystokyo.paper.io.PrioritizedTaskQueue; -+import com.destroystokyo.paper.io.QueueExecutorThread; -+import net.minecraft.server.ChunkRegionLoader; -+import net.minecraft.server.IAsyncTaskHandler; -+import net.minecraft.server.IChunkAccess; -+import net.minecraft.server.MinecraftServer; -+import net.minecraft.server.NBTTagCompound; -+import net.minecraft.server.WorldServer; -+import org.apache.logging.log4j.Level; -+import org.bukkit.Bukkit; -+import org.spigotmc.AsyncCatcher; -+ -+import java.util.ArrayDeque; -+import java.util.concurrent.CompletableFuture; -+import java.util.concurrent.ConcurrentHashMap; -+import java.util.concurrent.ConcurrentLinkedQueue; -+import java.util.function.Consumer; -+ -+public final class ChunkTaskManager { -+ -+ private final QueueExecutorThread[] workers; -+ private final WorldServer world; -+ -+ private final PrioritizedTaskQueue queue; -+ private final boolean perWorldQueue; -+ -+ final ConcurrentHashMap chunkLoadTasks = new ConcurrentHashMap<>(64, 0.5f); -+ final ConcurrentHashMap chunkSaveTasks = new ConcurrentHashMap<>(64, 0.5f); -+ -+ private final PrioritizedTaskQueue chunkTasks = new PrioritizedTaskQueue<>(); // used if async chunks are disabled in config -+ -+ protected static QueueExecutorThread[] globalWorkers; -+ protected static PrioritizedTaskQueue globalQueue; -+ -+ protected static final ConcurrentLinkedQueue CHUNK_WAIT_QUEUE = new ConcurrentLinkedQueue<>(); -+ -+ public static final ArrayDeque WAITING_CHUNKS = new ArrayDeque<>(); // stack -+ -+ private static final class ChunkInfo { -+ -+ public final int chunkX; -+ public final int chunkZ; -+ public final WorldServer world; -+ -+ public ChunkInfo(final int chunkX, final int chunkZ, final WorldServer world) { -+ this.chunkX = chunkX; -+ this.chunkZ = chunkZ; -+ this.world = world; -+ } -+ -+ @Override -+ public String toString() { -+ return "[( " + this.chunkX + "," + this.chunkZ + ") in '" + this.world.getWorld().getName() + "']"; -+ } -+ } -+ -+ public static void pushChunkWait(final WorldServer world, final int chunkX, final int chunkZ) { -+ synchronized (WAITING_CHUNKS) { -+ WAITING_CHUNKS.push(new ChunkInfo(chunkX, chunkZ, world)); -+ } -+ } -+ -+ public static void popChunkWait() { -+ synchronized (WAITING_CHUNKS) { -+ WAITING_CHUNKS.pop(); -+ } -+ } -+ -+ public static String getChunkWaitInfo() { -+ synchronized (WAITING_CHUNKS) { -+ return WAITING_CHUNKS.toString(); -+ } -+ } -+ -+ public static void dumpAllChunkLoadInfo() { -+ synchronized (WAITING_CHUNKS) { -+ if (WAITING_CHUNKS.isEmpty()) { -+ return; -+ } -+ -+ PaperFileIOThread.LOGGER.log(Level.ERROR, "Chunk wait task info below: "); -+ -+ for (final ChunkInfo chunkInfo : WAITING_CHUNKS) { -+ final long key = IOUtil.getCoordinateKey(chunkInfo.chunkX, chunkInfo.chunkZ); -+ final ChunkLoadTask loadTask = chunkInfo.world.asyncChunkTaskManager.chunkLoadTasks.get(key); -+ final ChunkSaveTask saveTask = chunkInfo.world.asyncChunkTaskManager.chunkSaveTasks.get(key); -+ -+ PaperFileIOThread.LOGGER.log(Level.ERROR, chunkInfo.chunkX + "," + chunkInfo.chunkZ + " in '" + chunkInfo.world.getWorld().getName() + ":"); -+ PaperFileIOThread.LOGGER.log(Level.ERROR, "Load Task - " + (loadTask == null ? "none" : loadTask.toString())); -+ PaperFileIOThread.LOGGER.log(Level.ERROR, "Save Task - " + (saveTask == null ? "none" : saveTask.toString())); -+ // log current status of chunk to indicate whether we're waiting on generation or loading -+ net.minecraft.server.PlayerChunk chunkHolder = chunkInfo.world.getChunkProvider().playerChunkMap.getVisibleChunk(key); -+ -+ if (chunkHolder == null) { -+ PaperFileIOThread.LOGGER.log(Level.ERROR, "Chunk Holder - null"); -+ } else { -+ IChunkAccess chunk = chunkHolder.getAvailableChunkNow(); -+ PaperFileIOThread.LOGGER.log(Level.ERROR, "Chunk Holder - non-null"); -+ PaperFileIOThread.LOGGER.log(Level.ERROR, "Chunk Status - " + ((chunk == null) ? "null chunk" : chunk.getChunkStatus().toString())); -+ } -+ -+ } -+ } -+ } -+ -+ public static void initGlobalLoadThreads(int threads) { -+ if (threads <= 0 || globalWorkers != null) { -+ return; -+ } -+ -+ globalWorkers = new QueueExecutorThread[threads]; -+ globalQueue = new PrioritizedTaskQueue<>(); -+ -+ for (int i = 0; i < threads; ++i) { -+ globalWorkers[i] = new QueueExecutorThread<>(globalQueue, (long)0.10e6); //0.1ms -+ globalWorkers[i].setName("Paper Async Chunk Task Thread #" + i); -+ globalWorkers[i].setPriority(Thread.NORM_PRIORITY - 1); -+ globalWorkers[i].setUncaughtExceptionHandler((final Thread thread, final Throwable throwable) -> { -+ PaperFileIOThread.LOGGER.fatal("Thread '" + thread.getName() + "' threw an uncaught exception!", throwable); -+ }); -+ -+ globalWorkers[i].start(); -+ } -+ } -+ -+ /** -+ * Creates this chunk task manager to operate off the specified number of threads. If the specified number of threads is -+ * less-than or equal to 0, then this chunk task manager will operate off of the world's chunk task queue. -+ * @param world Specified world. -+ * @param threads Specified number of threads. -+ * @see net.minecraft.server.ChunkProviderServer#serverThreadQueue -+ */ -+ public ChunkTaskManager(final WorldServer world, final int threads) { -+ this.world = world; -+ this.workers = threads <= 0 ? null : new QueueExecutorThread[threads]; -+ this.queue = new PrioritizedTaskQueue<>(); -+ this.perWorldQueue = true; -+ -+ for (int i = 0; i < threads; ++i) { -+ this.workers[i] = new QueueExecutorThread<>(this.queue, (long)0.10e6); //0.1ms -+ this.workers[i].setName("Async chunk loader thread #" + i + " for world: " + world.getWorldData().getName()); -+ this.workers[i].setPriority(Thread.NORM_PRIORITY - 1); -+ this.workers[i].setUncaughtExceptionHandler((final Thread thread, final Throwable throwable) -> { -+ PaperFileIOThread.LOGGER.fatal("Thread '" + thread.getName() + "' threw an uncaught exception!", throwable); -+ }); -+ -+ this.workers[i].start(); -+ } -+ } -+ -+ /** -+ * Creates the chunk task manager to work from the global workers. When {@link #close(boolean)} is invoked, -+ * the global queue is not shutdown. If the global workers is configured to be disabled or use 0 threads, then -+ * this chunk task manager will operate off of the world's chunk task queue. -+ * @param world The world that this task manager is responsible for -+ * @see net.minecraft.server.ChunkProviderServer#serverThreadQueue -+ */ -+ public ChunkTaskManager(final WorldServer world) { -+ this.world = world; -+ this.workers = globalWorkers; -+ this.queue = globalQueue; -+ this.perWorldQueue = false; -+ } -+ -+ public boolean pollNextChunkTask() { -+ final ChunkTask task = this.chunkTasks.poll(); -+ -+ if (task != null) { -+ task.run(); -+ return true; -+ } -+ return false; -+ } -+ -+ /** -+ * Polls and runs the next available chunk wait queue task. This is to be used when the server is waiting on a chunk queue. -+ * (per-world can cause issues if all the worker threads are blocked waiting for a response from the main thread) -+ */ -+ public static boolean pollChunkWaitQueue() { -+ final Runnable run = CHUNK_WAIT_QUEUE.poll(); -+ if (run != null) { -+ run.run(); -+ return true; -+ } -+ return false; -+ } -+ -+ /** -+ * Queues a chunk wait task. Note that this will execute out of order with respect to tasks scheduled on a world's -+ * chunk task queue, since this is the global chunk wait queue. -+ */ -+ public static void queueChunkWaitTask(final Runnable runnable) { -+ CHUNK_WAIT_QUEUE.add(runnable); -+ } -+ -+ private static void drainChunkWaitQueue() { -+ Runnable run; -+ while ((run = CHUNK_WAIT_QUEUE.poll()) != null) { -+ run.run(); -+ } -+ } -+ -+ /** -+ * The exact same as {@link #scheduleChunkLoad(int, int, int, Consumer, boolean)}, except that the chunk data is provided as -+ * the {@code data} parameter. -+ */ -+ public ChunkLoadTask scheduleChunkLoad(final int chunkX, final int chunkZ, final int priority, -+ final Consumer onComplete, -+ final boolean intendingToBlock, final CompletableFuture dataFuture) { -+ final WorldServer world = this.world; -+ -+ return this.chunkLoadTasks.compute(Long.valueOf(IOUtil.getCoordinateKey(chunkX, chunkZ)), (final Long keyInMap, final ChunkLoadTask valueInMap) -> { -+ if (valueInMap != null) { -+ if (!valueInMap.cancelled) { -+ throw new IllegalStateException("Double scheduling chunk load for task: " + valueInMap.toString()); -+ } -+ valueInMap.cancelled = false; -+ valueInMap.onComplete = onComplete; -+ return valueInMap; -+ } -+ -+ final ChunkLoadTask ret = new ChunkLoadTask(world, chunkX, chunkZ, priority, ChunkTaskManager.this, onComplete); -+ -+ dataFuture.thenAccept((final NBTTagCompound data) -> { -+ final boolean failed = data == PaperFileIOThread.FAILURE_VALUE; -+ PaperFileIOThread.Holder.INSTANCE.loadChunkDataAsync(world, chunkX, chunkZ, priority, (final PaperFileIOThread.ChunkData chunkData) -> { -+ ret.chunkData = chunkData; -+ if (!failed) { -+ chunkData.chunkData = data; -+ } -+ ChunkTaskManager.this.internalSchedule(ret); // only schedule to the worker threads here -+ }, true, failed, intendingToBlock); // read data off disk if the future fails -+ }); -+ -+ return ret; -+ }); -+ } -+ -+ public void cancelChunkLoad(final int chunkX, final int chunkZ) { -+ this.chunkLoadTasks.compute(IOUtil.getCoordinateKey(chunkX, chunkZ), (final Long keyInMap, final ChunkLoadTask valueInMap) -> { -+ if (valueInMap == null) { -+ return null; -+ } -+ -+ if (valueInMap.cancelled) { -+ PaperFileIOThread.LOGGER.warn("Task " + valueInMap.toString() + " is already cancelled!"); -+ } -+ valueInMap.cancelled = true; -+ if (valueInMap.cancel()) { -+ return null; -+ } -+ -+ return valueInMap; -+ }); -+ } -+ -+ /** -+ * Schedules an asynchronous chunk load for the specified coordinates. The onComplete parameter may be invoked asynchronously -+ * on a worker thread or on the world's chunk executor queue. As such the code that is executed for the parameter should be -+ * carefully chosen. -+ * @param chunkX Chunk's x coordinate -+ * @param chunkZ Chunk's z coordinate -+ * @param priority Priority for this task -+ * @param onComplete The consumer to invoke with the {@link net.minecraft.server.ChunkRegionLoader.InProgressChunkHolder} object once this task is complete -+ * @param intendingToBlock Whether the caller is intending to block on this task completing (this is a performance tune, and has no adverse side-effects) -+ * @return The {@link ChunkLoadTask} associated with -+ */ -+ public ChunkLoadTask scheduleChunkLoad(final int chunkX, final int chunkZ, final int priority, -+ final Consumer onComplete, -+ final boolean intendingToBlock) { -+ final WorldServer world = this.world; -+ -+ return this.chunkLoadTasks.compute(Long.valueOf(IOUtil.getCoordinateKey(chunkX, chunkZ)), (final Long keyInMap, final ChunkLoadTask valueInMap) -> { -+ if (valueInMap != null) { -+ if (!valueInMap.cancelled) { -+ throw new IllegalStateException("Double scheduling chunk load for task: " + valueInMap.toString()); -+ } -+ valueInMap.cancelled = false; -+ valueInMap.onComplete = onComplete; -+ return valueInMap; -+ } -+ -+ final ChunkLoadTask ret = new ChunkLoadTask(world, chunkX, chunkZ, priority, ChunkTaskManager.this, onComplete); -+ -+ PaperFileIOThread.Holder.INSTANCE.loadChunkDataAsync(world, chunkX, chunkZ, priority, (final PaperFileIOThread.ChunkData chunkData) -> { -+ ret.chunkData = chunkData; -+ ChunkTaskManager.this.internalSchedule(ret); // only schedule to the worker threads here -+ }, true, true, intendingToBlock); -+ -+ return ret; -+ }); -+ } -+ -+ /** -+ * Schedules an async save for the specified chunk. The chunk, at the beginning of this call, must be completely unloaded -+ * from the world. -+ * @param chunkX Chunk's x coordinate -+ * @param chunkZ Chunk's z coordinate -+ * @param priority Priority for this task -+ * @param asyncSaveData Async save data. See {@link ChunkRegionLoader#getAsyncSaveData(WorldServer, IChunkAccess)} -+ * @param chunk Chunk to save -+ * @return The {@link ChunkSaveTask} associated with the save task. -+ */ -+ public ChunkSaveTask scheduleChunkSave(final int chunkX, final int chunkZ, final int priority, -+ final ChunkRegionLoader.AsyncSaveData asyncSaveData, -+ final IChunkAccess chunk) { -+ AsyncCatcher.catchOp("chunk save schedule"); -+ -+ final WorldServer world = this.world; -+ -+ return this.chunkSaveTasks.compute(Long.valueOf(IOUtil.getCoordinateKey(chunkX, chunkZ)), (final Long keyInMap, final ChunkSaveTask valueInMap) -> { -+ if (valueInMap != null) { -+ throw new IllegalStateException("Double scheduling chunk save for task: " + valueInMap.toString()); -+ } -+ -+ final ChunkSaveTask ret = new ChunkSaveTask(world, chunkX, chunkZ, priority, ChunkTaskManager.this, asyncSaveData, chunk); -+ -+ ChunkTaskManager.this.internalSchedule(ret); -+ -+ return ret; -+ }); -+ } -+ -+ /** -+ * Returns a completable future which will be completed with the un-copied chunk data for an in progress async save. -+ * Returns {@code null} if no save is in progress. -+ * @param chunkX Chunk's x coordinate -+ * @param chunkZ Chunk's z coordinate -+ */ -+ public CompletableFuture getChunkSaveFuture(final int chunkX, final int chunkZ) { -+ final ChunkSaveTask chunkSaveTask = this.chunkSaveTasks.get(Long.valueOf(IOUtil.getCoordinateKey(chunkX, chunkZ))); -+ if (chunkSaveTask == null) { -+ return null; -+ } -+ return chunkSaveTask.onComplete; -+ } -+ -+ /** -+ * Returns the chunk object being used to serialize data async for an unloaded chunk. Note that modifying this chunk -+ * is not safe to do as another thread is handling its save. The chunk is also not loaded into the world. -+ * @param chunkX Chunk's x coordinate -+ * @param chunkZ Chunk's z coordinate -+ * @return Chunk object for an in-progress async save, or {@code null} if no save is in progress -+ */ -+ public IChunkAccess getChunkInSaveProgress(final int chunkX, final int chunkZ) { -+ final ChunkSaveTask chunkSaveTask = this.chunkSaveTasks.get(Long.valueOf(IOUtil.getCoordinateKey(chunkX, chunkZ))); -+ if (chunkSaveTask == null) { -+ return null; -+ } -+ return chunkSaveTask.chunk; -+ } -+ -+ public void flush() { -+ // flush here since we schedule tasks on the IO thread that can schedule tasks here -+ drainChunkWaitQueue(); -+ PaperFileIOThread.Holder.INSTANCE.flush(); -+ drainChunkWaitQueue(); -+ -+ if (this.workers == null) { -+ if (Bukkit.isPrimaryThread()) { -+ ((IAsyncTaskHandler)this.world.getChunkProvider().serverThreadQueue).executeAll(); -+ } else { -+ CompletableFuture wait = new CompletableFuture<>(); -+ MinecraftServer.getServer().scheduleOnMain(() -> { -+ ((IAsyncTaskHandler)this.world.getChunkProvider().serverThreadQueue).executeAll(); -+ }); -+ wait.join(); -+ } -+ } else { -+ for (final QueueExecutorThread worker : this.workers) { -+ worker.flush(); -+ } -+ } -+ -+ // flush again since tasks we execute async saves -+ drainChunkWaitQueue(); -+ PaperFileIOThread.Holder.INSTANCE.flush(); -+ } -+ -+ public void close(final boolean wait) { -+ // flush here since we schedule tasks on the IO thread that can schedule tasks to this task manager -+ // we do this regardless of the wait param since after we invoke close no tasks can be queued -+ PaperFileIOThread.Holder.INSTANCE.flush(); -+ -+ if (this.workers == null) { -+ if (wait) { -+ this.flush(); -+ } -+ return; -+ } -+ -+ if (this.workers != globalWorkers) { -+ for (final QueueExecutorThread worker : this.workers) { -+ worker.close(false, this.perWorldQueue); -+ } -+ } -+ -+ if (wait) { -+ this.flush(); -+ } -+ } -+ -+ public void raisePriority(final int chunkX, final int chunkZ, final int priority) { -+ final Long chunkKey = Long.valueOf(IOUtil.getCoordinateKey(chunkX, chunkZ)); -+ -+ ChunkSaveTask chunkSaveTask = this.chunkSaveTasks.get(chunkKey); -+ if (chunkSaveTask != null) { -+ final boolean raised = chunkSaveTask.raisePriority(priority); -+ if (chunkSaveTask.isScheduled() && raised) { -+ // only notify if we're in queue to be executed -+ this.internalScheduleNotify(); -+ } -+ } -+ -+ ChunkLoadTask chunkLoadTask = this.chunkLoadTasks.get(chunkKey); -+ if (chunkLoadTask != null) { -+ final boolean raised = chunkLoadTask.raisePriority(priority); -+ if (chunkLoadTask.isScheduled() && raised) { -+ // only notify if we're in queue to be executed -+ this.internalScheduleNotify(); -+ } -+ } -+ } -+ -+ protected void internalSchedule(final ChunkTask task) { -+ if (this.workers == null) { -+ this.chunkTasks.add(task); -+ return; -+ } -+ -+ // It's important we order the task to be executed before notifying. Avoid a race condition where the worker thread -+ // wakes up and goes to sleep before we actually schedule (or it's just about to sleep) -+ this.queue.add(task); -+ this.internalScheduleNotify(); -+ } -+ -+ protected void internalScheduleNotify() { -+ if (this.workers == null) { -+ return; -+ } -+ for (final QueueExecutorThread worker : this.workers) { -+ if (worker.notifyTasks()) { -+ // break here since we only want to wake up one worker for scheduling one task -+ break; -+ } -+ } -+ } -+ -+} -diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java -index 56761afdf..277c2245d 100644 ---- a/src/main/java/net/minecraft/server/ChunkProviderServer.java -+++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java -@@ -0,0 +0,0 @@ public class ChunkProviderServer extends IChunkProvider { - return playerChunk.getAvailableChunkNow(); - - } -+ -+ private long asyncLoadSeqCounter; -+ -+ public void getChunkAtAsynchronously(int x, int z, boolean gen, java.util.function.Consumer onComplete) { -+ if (Thread.currentThread() != this.serverThread) { -+ this.serverThreadQueue.execute(() -> { -+ this.getChunkAtAsynchronously(x, z, gen, onComplete); -+ }); -+ return; -+ } -+ -+ long k = ChunkCoordIntPair.pair(x, z); -+ ChunkCoordIntPair chunkPos = new ChunkCoordIntPair(x, z); -+ -+ IChunkAccess ichunkaccess; -+ -+ // try cache -+ for (int l = 0; l < 4; ++l) { -+ if (k == this.cachePos[l] && ChunkStatus.FULL == this.cacheStatus[l]) { -+ ichunkaccess = this.cacheChunk[l]; -+ if (ichunkaccess != null) { // CraftBukkit - the chunk can become accessible in the meantime TODO for non-null chunks it might also make sense to check that the chunk's state hasn't changed in the meantime -+ -+ // move to first in cache -+ -+ for (int i1 = 3; i1 > 0; --i1) { -+ this.cachePos[i1] = this.cachePos[i1 - 1]; -+ this.cacheStatus[i1] = this.cacheStatus[i1 - 1]; -+ this.cacheChunk[i1] = this.cacheChunk[i1 - 1]; -+ } -+ -+ this.cachePos[0] = k; -+ this.cacheStatus[0] = ChunkStatus.FULL; -+ this.cacheChunk[0] = ichunkaccess; -+ -+ onComplete.accept((Chunk)ichunkaccess); -+ -+ return; -+ } -+ } -+ } -+ -+ if (gen) { -+ this.bringToFullStatusAsync(x, z, chunkPos, onComplete); -+ return; -+ } -+ -+ IChunkAccess current = this.getChunkAtImmediately(x, z); // we want to bypass ticket restrictions -+ if (current != null) { -+ if (!(current instanceof ProtoChunkExtension) && !(current instanceof net.minecraft.server.Chunk)) { -+ onComplete.accept(null); // the chunk is not gen'd -+ return; -+ } -+ // we know the chunk is at full status here (either in read-only mode or the real thing) -+ this.bringToFullStatusAsync(x, z, chunkPos, onComplete); -+ return; -+ } -+ -+ ChunkStatus status = world.getChunkProvider().playerChunkMap.getStatusOnDiskNoLoad(x, z); -+ -+ if (status != null && status != ChunkStatus.FULL) { -+ // does not exist on disk -+ onComplete.accept(null); -+ return; -+ } -+ -+ if (status == ChunkStatus.FULL) { -+ this.bringToFullStatusAsync(x, z, chunkPos, onComplete); -+ return; -+ } -+ -+ // status is null here -+ -+ // here we don't know what status it is and we're not supposed to generate -+ // so we asynchronously load empty status -+ -+ this.bringToStatusAsync(x, z, chunkPos, ChunkStatus.EMPTY, (IChunkAccess chunk) -> { -+ if (!(chunk instanceof ProtoChunkExtension) && !(chunk instanceof net.minecraft.server.Chunk)) { -+ // the chunk on disk was not a full status chunk -+ onComplete.accept(null); -+ return; -+ } -+ this.bringToFullStatusAsync(x, z, chunkPos, onComplete); // bring to full status if required -+ }); -+ } -+ -+ private void bringToFullStatusAsync(int x, int z, ChunkCoordIntPair chunkPos, java.util.function.Consumer onComplete) { -+ this.bringToStatusAsync(x, z, chunkPos, ChunkStatus.FULL, (java.util.function.Consumer)onComplete); -+ } -+ -+ private void bringToStatusAsync(int x, int z, ChunkCoordIntPair chunkPos, ChunkStatus status, java.util.function.Consumer onComplete) { -+ CompletableFuture> future = this.getChunkFutureMainThread(x, z, status, true); -+ Long identifier = Long.valueOf(this.asyncLoadSeqCounter++); -+ int ticketLevel = MCUtil.getTicketLevelFor(status); -+ this.addTicketAtLevel(TicketType.ASYNC_LOAD, chunkPos, ticketLevel, identifier); -+ -+ future.whenCompleteAsync((Either either, Throwable throwable) -> { -+ // either left -> success -+ // either right -> failure -+ -+ if (throwable != null) { -+ throw new RuntimeException(throwable); -+ } -+ -+ this.removeTicketAtLevel(TicketType.ASYNC_LOAD, chunkPos, ticketLevel, identifier); -+ this.addTicketAtLevel(TicketType.UNKNOWN, chunkPos, ticketLevel, chunkPos); // allow unloading -+ -+ Optional failure = either.right(); -+ -+ if (failure.isPresent()) { -+ // failure -+ throw new IllegalStateException("Chunk failed to load: " + failure.get().toString()); -+ } -+ -+ onComplete.accept(either.left().get()); -+ -+ }, this.serverThreadQueue); -+ } -+ -+ public void addTicketAtLevel(TicketType ticketType, ChunkCoordIntPair chunkPos, int ticketLevel, T identifier) { -+ this.chunkMapDistance.addTicketAtLevel(ticketType, chunkPos, ticketLevel, identifier); -+ } -+ -+ public void removeTicketAtLevel(TicketType ticketType, ChunkCoordIntPair chunkPos, int ticketLevel, T identifier) { -+ this.chunkMapDistance.removeTicketAtLevel(ticketType, chunkPos, ticketLevel, identifier); -+ } - // Paper end - - @Nullable - @Override - public IChunkAccess getChunkAt(int i, int j, ChunkStatus chunkstatus, boolean flag) { -+ final int x = i; final int z = j; // Paper - conflict on variable change - if (Thread.currentThread() != this.serverThread) { - return (IChunkAccess) CompletableFuture.supplyAsync(() -> { - return this.getChunkAt(i, j, chunkstatus, flag); -@@ -0,0 +0,0 @@ public class ChunkProviderServer extends IChunkProvider { - CompletableFuture> completablefuture = this.getChunkFutureMainThread(i, j, chunkstatus, flag); - - if (!completablefuture.isDone()) { // Paper -+ // Paper start - async chunk io/loading -+ this.world.asyncChunkTaskManager.raisePriority(x, z, com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGHEST_PRIORITY); -+ com.destroystokyo.paper.io.chunk.ChunkTaskManager.pushChunkWait(this.world, x, z); -+ // Paper end - this.world.timings.chunkAwait.startTiming(); // Paper - this.serverThreadQueue.awaitTasks(completablefuture::isDone); -+ com.destroystokyo.paper.io.chunk.ChunkTaskManager.popChunkWait(); // Paper - async chunk debug - this.world.timings.chunkAwait.stopTiming(); // Paper - } // Paper - ichunkaccess = (IChunkAccess) ((Either) completablefuture.join()).map((ichunkaccess1) -> { -@@ -0,0 +0,0 @@ public class ChunkProviderServer extends IChunkProvider { - protected boolean executeNext() { - // CraftBukkit start - process pending Chunk loadCallback() and unloadCallback() after each run task - try { -+ boolean execChunkTask = com.destroystokyo.paper.io.chunk.ChunkTaskManager.pollChunkWaitQueue() || ChunkProviderServer.this.world.asyncChunkTaskManager.pollNextChunkTask(); // Paper - if (ChunkProviderServer.this.tickDistanceManager()) { - return true; - } else { - ChunkProviderServer.this.lightEngine.queueUpdate(); -- return super.executeNext(); -+ return super.executeNext() || execChunkTask; // Paper - } - } finally { - playerChunkMap.callbackExecutor.run(); -diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java -index a02807411..98cc4efcf 100644 ---- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java -+++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java -@@ -0,0 +0,0 @@ import it.unimi.dsi.fastutil.longs.LongOpenHashSet; - import it.unimi.dsi.fastutil.longs.LongSet; - import it.unimi.dsi.fastutil.shorts.ShortList; - import it.unimi.dsi.fastutil.shorts.ShortListIterator; -+import java.util.ArrayDeque; // Paper - import java.util.Arrays; - import java.util.BitSet; - import java.util.EnumSet; -@@ -0,0 +0,0 @@ public class ChunkRegionLoader { - - private static final Logger LOGGER = LogManager.getLogger(); - -+ // Paper start -+ public static final class InProgressChunkHolder { -+ -+ public final ProtoChunk protoChunk; -+ public final ArrayDeque tasks; -+ -+ public NBTTagCompound poiData; -+ -+ public InProgressChunkHolder(final ProtoChunk protoChunk, final ArrayDeque tasks) { -+ this.protoChunk = protoChunk; -+ this.tasks = tasks; -+ } -+ } -+ - public static ProtoChunk loadChunk(WorldServer worldserver, DefinedStructureManager definedstructuremanager, VillagePlace villageplace, ChunkCoordIntPair chunkcoordintpair, NBTTagCompound nbttagcompound) { -+ InProgressChunkHolder holder = loadChunk(worldserver, definedstructuremanager, villageplace, chunkcoordintpair, nbttagcompound, true); -+ holder.tasks.forEach(Runnable::run); -+ return holder.protoChunk; -+ } -+ -+ public static InProgressChunkHolder loadChunk(WorldServer worldserver, DefinedStructureManager definedstructuremanager, VillagePlace villageplace, ChunkCoordIntPair chunkcoordintpair, NBTTagCompound nbttagcompound, boolean distinguish) { -+ ArrayDeque tasksToExecuteOnMain = new ArrayDeque<>(); -+ // Paper end - ChunkGenerator chunkgenerator = worldserver.getChunkProvider().getChunkGenerator(); - WorldChunkManager worldchunkmanager = chunkgenerator.getWorldChunkManager(); - NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("Level"); -@@ -0,0 +0,0 @@ public class ChunkRegionLoader { - LightEngine lightengine = chunkproviderserver.getLightEngine(); - - if (flag) { -- lightengine.b(chunkcoordintpair, true); -+ tasksToExecuteOnMain.add(() -> { // Paper - delay this task since we're executing off-main -+ lightengine.b(chunkcoordintpair, true); -+ }); // Paper - delay this task since we're executing off-main - } - - for (int k = 0; k < nbttaglist.size(); ++k) { -@@ -0,0 +0,0 @@ public class ChunkRegionLoader { - achunksection[b0] = chunksection; - } - -- villageplace.a(chunkcoordintpair, chunksection); -+ tasksToExecuteOnMain.add(() -> { // Paper - delay this task since we're executing off-main -+ villageplace.a(chunkcoordintpair, chunksection); -+ }); // Paper - delay this task since we're executing off-main - } - - if (flag) { - if (nbttagcompound2.hasKeyOfType("BlockLight", 7)) { -- lightengine.a(EnumSkyBlock.BLOCK, SectionPosition.a(chunkcoordintpair, b0), new NibbleArray(nbttagcompound2.getByteArray("BlockLight"))); -+ // Paper start - delay this task since we're executing off-main -+ NibbleArray blockLight = new NibbleArray(nbttagcompound2.getByteArray("BlockLight")); -+ // Note: We move the block light nibble array creation here for perf & in case the compound is modified -+ tasksToExecuteOnMain.add(() -> { -+ lightengine.a(EnumSkyBlock.BLOCK, SectionPosition.a(chunkcoordintpair, b0), blockLight); -+ }); -+ // Paper end - } - - if (flag2 && nbttagcompound2.hasKeyOfType("SkyLight", 7)) { -- lightengine.a(EnumSkyBlock.SKY, SectionPosition.a(chunkcoordintpair, b0), new NibbleArray(nbttagcompound2.getByteArray("SkyLight"))); -+ // Paper start - delay this task since we're executing off-main -+ NibbleArray skyLight = new NibbleArray(nbttagcompound2.getByteArray("SkyLight")); -+ // Note: We move the block light nibble array creation here for perf & in case the compound is modified -+ tasksToExecuteOnMain.add(() -> { -+ lightengine.a(EnumSkyBlock.SKY, SectionPosition.a(chunkcoordintpair, b0), skyLight); -+ }); -+ // Paper end - } - } - } -@@ -0,0 +0,0 @@ public class ChunkRegionLoader { - } - - if (chunkstatus_type == ChunkStatus.Type.LEVELCHUNK) { -- return new ProtoChunkExtension((Chunk) object); -+ return new InProgressChunkHolder(new ProtoChunkExtension((Chunk) object), tasksToExecuteOnMain); // Paper - Async chunk loading - } else { - ProtoChunk protochunk1 = (ProtoChunk) object; - -@@ -0,0 +0,0 @@ public class ChunkRegionLoader { - protochunk1.a(worldgenstage_features, BitSet.valueOf(nbttagcompound5.getByteArray(s1))); - } - -- return protochunk1; -+ return new InProgressChunkHolder(protochunk1, tasksToExecuteOnMain); // Paper - Async chunk loading - } - } - -+ // Paper start - async chunk save for unload -+ public static final class AsyncSaveData { -+ public final NibbleArray[] blockLight; // null or size of 17 (for indices -1 through 15) -+ public final NibbleArray[] skyLight; -+ -+ public final NBTTagList blockTickList; // non-null if we had to go to the server's tick list -+ public final NBTTagList fluidTickList; // non-null if we had to go to the server's tick list -+ -+ public final long worldTime; -+ -+ public AsyncSaveData(NibbleArray[] blockLight, NibbleArray[] skyLight, NBTTagList blockTickList, NBTTagList fluidTickList, -+ long worldTime) { -+ this.blockLight = blockLight; -+ this.skyLight = skyLight; -+ this.blockTickList = blockTickList; -+ this.fluidTickList = fluidTickList; -+ this.worldTime = worldTime; -+ } -+ } -+ -+ // must be called sync -+ public static AsyncSaveData getAsyncSaveData(WorldServer world, IChunkAccess chunk) { -+ org.spigotmc.AsyncCatcher.catchOp("preparation of chunk data for async save"); -+ ChunkCoordIntPair chunkPos = chunk.getPos(); -+ -+ LightEngineThreaded lightenginethreaded = world.getChunkProvider().getLightEngine(); -+ -+ NibbleArray[] blockLight = new NibbleArray[17 - (-1)]; -+ NibbleArray[] skyLight = new NibbleArray[17 - (-1)]; -+ -+ for (int i = -1; i < 17; ++i) { -+ NibbleArray blockArray = lightenginethreaded.a(EnumSkyBlock.BLOCK).a(SectionPosition.a(chunkPos, i)); -+ NibbleArray skyArray = lightenginethreaded.a(EnumSkyBlock.SKY).a(SectionPosition.a(chunkPos, i)); -+ -+ // copy data for safety -+ if (blockArray != null) { -+ blockArray = blockArray.copy(); -+ } -+ if (skyArray != null) { -+ skyArray = skyArray.copy(); -+ } -+ -+ // apply offset of 1 for -1 starting index -+ blockLight[i + 1] = blockArray; -+ skyLight[i + 1] = skyArray; -+ } -+ -+ TickList blockTickList = chunk.n(); -+ -+ NBTTagList blockTickListSerialized; -+ if (blockTickList instanceof ProtoChunkTickList || blockTickList instanceof TickListChunk) { -+ blockTickListSerialized = null; -+ } else { -+ blockTickListSerialized = world.getBlockTickList().a(chunkPos); -+ } -+ -+ TickList fluidTickList = chunk.o(); -+ -+ NBTTagList fluidTickListSerialized; -+ if (fluidTickList instanceof ProtoChunkTickList || fluidTickList instanceof TickListChunk) { -+ fluidTickListSerialized = null; -+ } else { -+ fluidTickListSerialized = world.getFluidTickList().a(chunkPos); -+ } -+ -+ return new AsyncSaveData(blockLight, skyLight, blockTickListSerialized, fluidTickListSerialized, world.getTime()); -+ } -+ - public static NBTTagCompound saveChunk(WorldServer worldserver, IChunkAccess ichunkaccess) { -+ return saveChunk(worldserver, ichunkaccess, null); -+ } -+ public static NBTTagCompound saveChunk(WorldServer worldserver, IChunkAccess ichunkaccess, AsyncSaveData asyncsavedata) { -+ // Paper end - ChunkCoordIntPair chunkcoordintpair = ichunkaccess.getPos(); - NBTTagCompound nbttagcompound = new NBTTagCompound(); - NBTTagCompound nbttagcompound1 = new NBTTagCompound(); -@@ -0,0 +0,0 @@ public class ChunkRegionLoader { - nbttagcompound.set("Level", nbttagcompound1); - nbttagcompound1.setInt("xPos", chunkcoordintpair.x); - nbttagcompound1.setInt("zPos", chunkcoordintpair.z); -- nbttagcompound1.setLong("LastUpdate", worldserver.getTime()); -+ nbttagcompound1.setLong("LastUpdate", asyncsavedata != null ? asyncsavedata.worldTime : worldserver.getTime()); // Paper - async chunk unloading - nbttagcompound1.setLong("InhabitedTime", ichunkaccess.q()); - nbttagcompound1.setString("Status", ichunkaccess.getChunkStatus().d()); - ChunkConverter chunkconverter = ichunkaccess.p(); -@@ -0,0 +0,0 @@ public class ChunkRegionLoader { - - NBTTagCompound nbttagcompound2; - -- for (int i = -1; i < 17; ++i) { -+ for (int i = -1; i < 17; ++i) { // Paper - conflict on loop parameter change - int finalI = i; - ChunkSection chunksection = (ChunkSection) Arrays.stream(achunksection).filter((chunksection1) -> { - return chunksection1 != null && chunksection1.getYPosition() >> 4 == finalI; - }).findFirst().orElse(Chunk.a); -- NibbleArray nibblearray = lightenginethreaded.a(EnumSkyBlock.BLOCK).a(SectionPosition.a(chunkcoordintpair, i)); -- NibbleArray nibblearray1 = lightenginethreaded.a(EnumSkyBlock.SKY).a(SectionPosition.a(chunkcoordintpair, i)); -- -+ // Paper start - async chunk save for unload -+ NibbleArray nibblearray; // block light -+ NibbleArray nibblearray1; // sky light -+ if (asyncsavedata == null) { -+ nibblearray = lightenginethreaded.a(EnumSkyBlock.BLOCK).a(SectionPosition.a(chunkcoordintpair, i)); /// Paper - diff on method change (see getAsyncSaveData) -+ nibblearray1 = lightenginethreaded.a(EnumSkyBlock.SKY).a(SectionPosition.a(chunkcoordintpair, i)); // Paper - diff on method change (see getAsyncSaveData) -+ } else { -+ nibblearray = asyncsavedata.blockLight[i + 1]; // +1 to offset the -1 starting index -+ nibblearray1 = asyncsavedata.skyLight[i + 1]; // +1 to offset the -1 starting index -+ } -+ // Paper end - if (chunksection != Chunk.a || nibblearray != null || nibblearray1 != null) { - nbttagcompound2 = new NBTTagCompound(); - nbttagcompound2.setByte("Y", (byte) (i & 255)); -@@ -0,0 +0,0 @@ public class ChunkRegionLoader { - // Paper start - if ((int)Math.floor(entity.locX) >> 4 != chunk.getPos().x || (int)Math.floor(entity.locZ) >> 4 != chunk.getPos().z) { - LogManager.getLogger().warn(entity + " is not in this chunk, skipping save. This a bug fix to a vanilla bug. Do not report this to PaperMC please."); -- toUpdate.add(entity); -+ if (asyncsavedata == null) toUpdate.add(entity); // todo fix this broken code, entityJoinedWorld wont work in this case! - continue; - } -- if (entity.dead) { -+ if (asyncsavedata == null && entity.dead) { // todo - continue; - } - // Paper end -@@ -0,0 +0,0 @@ public class ChunkRegionLoader { - } - - nbttagcompound1.set("Entities", nbttaglist2); -- TickList ticklist = ichunkaccess.n(); -+ TickList ticklist = ichunkaccess.n(); // Paper - diff on method change (see getAsyncSaveData) - - if (ticklist instanceof ProtoChunkTickList) { - nbttagcompound1.set("ToBeTicked", ((ProtoChunkTickList) ticklist).b()); - } else if (ticklist instanceof TickListChunk) { -- nbttagcompound1.set("TileTicks", ((TickListChunk) ticklist).a(worldserver.getTime())); -+ nbttagcompound1.set("TileTicks", ((TickListChunk) ticklist).a(asyncsavedata != null ? asyncsavedata.worldTime : worldserver.getTime())); // Paper - async chunk unloading -+ // Paper start - async chunk save for unload -+ } else if (asyncsavedata != null) { -+ nbttagcompound1.set("TileTicks", asyncsavedata.blockTickList); -+ // Paper end - } else { -- nbttagcompound1.set("TileTicks", worldserver.getBlockTickList().a(chunkcoordintpair)); -+ nbttagcompound1.set("TileTicks", worldserver.getBlockTickList().a(chunkcoordintpair)); // Paper - diff on method change (see getAsyncSaveData) - } - -- TickList ticklist1 = ichunkaccess.o(); -+ TickList ticklist1 = ichunkaccess.o(); // Paper - diff on method change (see getAsyncSaveData) - - if (ticklist1 instanceof ProtoChunkTickList) { - nbttagcompound1.set("LiquidsToBeTicked", ((ProtoChunkTickList) ticklist1).b()); - } else if (ticklist1 instanceof TickListChunk) { -- nbttagcompound1.set("LiquidTicks", ((TickListChunk) ticklist1).a(worldserver.getTime())); -+ nbttagcompound1.set("LiquidTicks", ((TickListChunk) ticklist1).a(asyncsavedata != null ? asyncsavedata.worldTime : worldserver.getTime())); // Paper - async chunk unloading -+ // Paper start - async chunk save for unload -+ } else if (asyncsavedata != null) { -+ nbttagcompound1.set("LiquidTicks", asyncsavedata.fluidTickList); -+ // Paper end - } else { -- nbttagcompound1.set("LiquidTicks", worldserver.getFluidTickList().a(chunkcoordintpair)); -+ nbttagcompound1.set("LiquidTicks", worldserver.getFluidTickList().a(chunkcoordintpair)); // Paper - diff on method change (see getAsyncSaveData) - } - - nbttagcompound1.set("PostProcessing", a(ichunkaccess.l())); -diff --git a/src/main/java/net/minecraft/server/ChunkStatus.java b/src/main/java/net/minecraft/server/ChunkStatus.java -index e324989b4..abb0d69d2 100644 ---- a/src/main/java/net/minecraft/server/ChunkStatus.java -+++ b/src/main/java/net/minecraft/server/ChunkStatus.java -@@ -0,0 +0,0 @@ public class ChunkStatus { - return ChunkStatus.q.size(); - } - -+ public static int getTicketLevelOffset(ChunkStatus status) { return ChunkStatus.a(status); } // Paper - OBFHELPER - public static int a(ChunkStatus chunkstatus) { - return ChunkStatus.r.getInt(chunkstatus.c()); - } -diff --git a/src/main/java/net/minecraft/server/IAsyncTaskHandler.java b/src/main/java/net/minecraft/server/IAsyncTaskHandler.java -index d521d25cf..84024e6ba 100644 ---- a/src/main/java/net/minecraft/server/IAsyncTaskHandler.java -+++ b/src/main/java/net/minecraft/server/IAsyncTaskHandler.java -@@ -0,0 +0,0 @@ public abstract class IAsyncTaskHandler implements Mailbox public - while (this.executeNext()) { - ; - } -diff --git a/src/main/java/net/minecraft/server/IChunkLoader.java b/src/main/java/net/minecraft/server/IChunkLoader.java -index 3f14392e6..39f6ddb1d 100644 ---- a/src/main/java/net/minecraft/server/IChunkLoader.java -+++ b/src/main/java/net/minecraft/server/IChunkLoader.java -@@ -0,0 +0,0 @@ package net.minecraft.server; - import com.mojang.datafixers.DataFixer; - import java.io.File; - import java.io.IOException; -+// Paper start -+import java.util.concurrent.CompletableFuture; -+import java.util.concurrent.CompletionException; -+// Paper end - import java.util.function.Supplier; - import javax.annotation.Nullable; - -@@ -0,0 +0,0 @@ public class IChunkLoader extends RegionFileCache { - - protected final DataFixer b; - @Nullable -- private PersistentStructureLegacy a; -+ private volatile PersistentStructureLegacy a; // Paper - async chunk loading -+ -+ private final Object persistentDataLock = new Object(); // Paper - - public IChunkLoader(File file, DataFixer datafixer) { - super(file); -@@ -0,0 +0,0 @@ public class IChunkLoader extends RegionFileCache { - private boolean check(ChunkProviderServer cps, int x, int z) throws IOException { - ChunkCoordIntPair pos = new ChunkCoordIntPair(x, z); - if (cps != null) { -- com.google.common.base.Preconditions.checkState(org.bukkit.Bukkit.isPrimaryThread(), "primary thread"); -- if (cps.isLoaded(x, z)) { -+ //com.google.common.base.Preconditions.checkState(org.bukkit.Bukkit.isPrimaryThread(), "primary thread"); // Paper - this function is now MT-Safe -+ if (cps.getChunkAtIfCachedImmediately(x, z) != null) { // Paper - isLoaded is a ticket level check, not a chunk loaded check! - return true; - } - } - - if (this.chunkExists(pos)) { -- NBTTagCompound nbt = read(pos); -+ // Paper start - prioritize -+ NBTTagCompound nbt = cps == null ? read(pos) : -+ com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE.loadChunkData((WorldServer)cps.getWorld(), x, z, -+ com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGHER_PRIORITY, false, true).chunkData; -+ // Paper end - if (nbt != null) { - NBTTagCompound level = nbt.getCompound("Level"); - if (level.getBoolean("TerrainPopulated")) { -@@ -0,0 +0,0 @@ public class IChunkLoader extends RegionFileCache { - if (i < 1493) { - nbttagcompound = GameProfileSerializer.a(this.b, DataFixTypes.CHUNK, nbttagcompound, i, 1493); - if (nbttagcompound.getCompound("Level").getBoolean("hasLegacyStructureData")) { -+ synchronized (this.persistentDataLock) { // Paper - Async chunk loading - if (this.a == null) { - this.a = PersistentStructureLegacy.a(dimensionmanager.getType(), (WorldPersistentData) supplier.get()); // CraftBukkit - getType - } - - nbttagcompound = this.a.a(nbttagcompound); -+ } // Paper - Async chunk loading - } - } - -@@ -0,0 +0,0 @@ public class IChunkLoader extends RegionFileCache { - public void write(ChunkCoordIntPair chunkcoordintpair, NBTTagCompound nbttagcompound) throws IOException { - super.write(chunkcoordintpair, nbttagcompound); - if (this.a != null) { -+ synchronized (this.persistentDataLock) { // Paper - Async chunk loading - this.a.a(chunkcoordintpair.pair()); -+ } // Paper - Async chunk loading - } - - } -diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java -index 23d1935dd..14f8b6104 100644 ---- a/src/main/java/net/minecraft/server/MCUtil.java -+++ b/src/main/java/net/minecraft/server/MCUtil.java -@@ -0,0 +0,0 @@ public final class MCUtil { - out.print(fileData); - } - } -+ -+ public static int getTicketLevelFor(ChunkStatus status) { -+ // TODO make sure the constant `33` is correct on future updates. See getChunkAt(int, int, ChunkStatus, boolean) -+ return 33 + ChunkStatus.getTicketLevelOffset(status); -+ } - } -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 5238a1a7c..0b0058138 100644 ---- a/src/main/java/net/minecraft/server/MinecraftServer.java -+++ b/src/main/java/net/minecraft/server/MinecraftServer.java -@@ -0,0 +0,0 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant { - -- private static long d; -+ private static final java.util.concurrent.atomic.AtomicLong COUNTER = new java.util.concurrent.atomic.AtomicLong(); // Paper - async chunk loading - private final T e; - public final BlockPosition a; - public final long b; -@@ -0,0 +0,0 @@ public class NextTickListEntry { - } - - public NextTickListEntry(BlockPosition blockposition, T t0, long i, TickListPriority ticklistpriority) { -- this.f = (long) (NextTickListEntry.d++); -+ this.f = (long) (NextTickListEntry.COUNTER.getAndIncrement()); // Paper - async chunk loading - this.a = blockposition.immutableCopy(); - this.e = t0; - this.b = i; -diff --git a/src/main/java/net/minecraft/server/NibbleArray.java b/src/main/java/net/minecraft/server/NibbleArray.java -index 90c096876..eb2c06155 100644 ---- a/src/main/java/net/minecraft/server/NibbleArray.java -+++ b/src/main/java/net/minecraft/server/NibbleArray.java -@@ -0,0 +0,0 @@ public class NibbleArray { - return this.a; - } - -+ public NibbleArray copy() { return this.b(); } // Paper - OBFHELPER - public NibbleArray b() { - return this.a == null ? new NibbleArray() : new NibbleArray((byte[]) this.a.clone()); - } -diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java -index af934ef8b..34d0ab0d5 100644 ---- a/src/main/java/net/minecraft/server/PlayerChunk.java -+++ b/src/main/java/net/minecraft/server/PlayerChunk.java -@@ -0,0 +0,0 @@ public class PlayerChunk { - ChunkStatus chunkstatus = getChunkStatus(this.oldTicketLevel); - ChunkStatus chunkstatus1 = getChunkStatus(this.ticketLevel); - boolean flag = this.oldTicketLevel <= PlayerChunkMap.GOLDEN_TICKET; -- boolean flag1 = this.ticketLevel <= PlayerChunkMap.GOLDEN_TICKET; -+ boolean flag1 = this.ticketLevel <= PlayerChunkMap.GOLDEN_TICKET; // Paper - diff on change: (flag1 = new ticket level is in loadable range) - PlayerChunk.State playerchunk_state = getChunkState(this.oldTicketLevel); - PlayerChunk.State playerchunk_state1 = getChunkState(this.ticketLevel); - // CraftBukkit start -@@ -0,0 +0,0 @@ public class PlayerChunk { - } - }); - -+ // Paper start -+ if (!flag1) { -+ playerchunkmap.world.asyncChunkTaskManager.cancelChunkLoad(this.location.x, this.location.z); -+ } -+ // Paper end -+ - for (int i = flag1 ? chunkstatus1.c() + 1 : 0; i <= chunkstatus.c(); ++i) { - completablefuture = (CompletableFuture) this.statusFutures.get(i); - if (completablefuture != null) { -diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java -index a5df9fee6..6b36bbe87 100644 ---- a/src/main/java/net/minecraft/server/PlayerChunkMap.java -+++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java -@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { - private final LightEngineThreaded lightEngine; - private final IAsyncTaskHandler executor; - public final ChunkGenerator chunkGenerator; -- private final Supplier m; -+ private final Supplier m; public final Supplier getWorldPersistentDataSupplier() { return this.m; } // Paper - OBFHELPER - private final VillagePlace n; - public final LongSet unloadQueue; - private boolean updatingChunksModified; -@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { - public final WorldLoadListener worldLoadListener; - public final PlayerChunkMap.a chunkDistanceManager; public final PlayerChunkMap.a getChunkMapDistanceManager() { return this.chunkDistanceManager; } // Paper - OBFHELPER - private final AtomicInteger v; -- private final DefinedStructureManager definedStructureManager; -+ public final DefinedStructureManager definedStructureManager; // Paper - private -> public - private final File x; - private final PlayerMap playerMap; - public final Int2ObjectMap trackedEntities; -@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { - this.lightEngine = new LightEngineThreaded(ilightaccess, this, this.world.getWorldProvider().g(), threadedmailbox1, this.q.a(threadedmailbox1, false)); - this.chunkDistanceManager = new PlayerChunkMap.a(executor, iasynctaskhandler); - this.m = supplier; -- this.n = new VillagePlace(new File(this.x, "poi"), datafixer); -+ this.n = new VillagePlace(new File(this.x, "poi"), datafixer, this.world); // Paper - this.setViewDistance(i); - } - -@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { - } - - @Nullable -- protected PlayerChunk getVisibleChunk(long i) { -+ public PlayerChunk getVisibleChunk(long i) { // Paper - protected -> public - return (PlayerChunk) this.visibleChunks.get(i); - } - -@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { - @Override - public void close() throws IOException { - this.q.close(); -+ this.world.asyncChunkTaskManager.close(true); // Paper - Required since we're closing regionfiles in the next line - this.n.close(); - super.close(); - } -@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { - shouldSave = ((Chunk) ichunkaccess).lastSaved + world.paperConfig.autoSavePeriod <= world.getTime(); - } - -- if (shouldSave && this.saveChunk(ichunkaccess)) { -+ if (shouldSave && this.saveChunk(ichunkaccess, true)) { // Paper - async chunk io - ++savedThisTick; - playerchunk.m(); - } -@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { - return (IChunkAccess) completablefuture.join(); - }).filter((ichunkaccess) -> { - return ichunkaccess instanceof ProtoChunkExtension || ichunkaccess instanceof Chunk; -- }).filter(this::saveChunk).forEach((ichunkaccess) -> { -+ }).filter((chunk) -> this.saveChunk(chunk, true)).forEach((ichunkaccess) -> { // Paper - async io for chunk save - mutableboolean.setTrue(); - }); - } while (mutableboolean.isTrue()); -@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { - this.b(() -> { - return true; - }); -+ this.world.asyncChunkTaskManager.flush(); // Paper - flush to preserve behavior compat with pre-async behaviour - PlayerChunkMap.LOGGER.info("ThreadedAnvilChunkStorage ({}): All chunks are saved", this.x.getName()); - } else { - this.visibleChunks.values().stream().filter(PlayerChunk::hasBeenLoaded).forEach((playerchunk) -> { - IChunkAccess ichunkaccess = (IChunkAccess) playerchunk.getChunkSave().getNow(null); // CraftBukkit - decompile error - - if (ichunkaccess instanceof ProtoChunkExtension || ichunkaccess instanceof Chunk) { -- this.saveChunk(ichunkaccess); -+ this.saveChunk(ichunkaccess, true); // Paper - playerchunk.m(); - } - - }); -+ com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE.flush(); // Paper - flush to preserve behavior compat with pre-async behaviour - } - - } -@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { - protected void unloadChunks(BooleanSupplier booleansupplier) { - GameProfilerFiller gameprofilerfiller = this.world.getMethodProfiler(); - -+ try (Timing ignored = this.world.timings.poiUnload.startTiming()) { // Paper - gameprofilerfiller.enter("poi"); - this.n.a(booleansupplier); -+ } // Paper - gameprofilerfiller.exitEnter("chunk_unload"); - if (!this.world.isSavingDisabled()) { -+ try (Timing ignored = this.world.timings.chunkUnload.startTiming()) { // Paper - this.b(booleansupplier); -+ }// Paper - } - - gameprofilerfiller.exit(); -@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { - - } - -+ // Paper start - async chunk save for unload -+ // Note: This is very unsafe to call if the chunk is still in use. -+ // This is also modeled after PlayerChunkMap#saveChunk(IChunkAccess, boolean), with the intentional difference being -+ // serializing the chunk is left to a worker thread. -+ private void asyncSave(IChunkAccess chunk) { -+ ChunkCoordIntPair chunkPos = chunk.getPos(); -+ NBTTagCompound poiData; -+ try (Timing ignored = this.world.timings.chunkUnloadPOISerialization.startTiming()) { -+ poiData = this.getVillagePlace().getData(chunk.getPos()); -+ } -+ -+ com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE.scheduleSave(this.world, chunkPos.x, chunkPos.z, -+ poiData, null, com.destroystokyo.paper.io.PrioritizedTaskQueue.LOW_PRIORITY); -+ -+ if (!chunk.isNeedsSaving()) { -+ return; -+ } -+ -+ ChunkStatus chunkstatus = chunk.getChunkStatus(); -+ -+ // Copied from PlayerChunkMap#saveChunk(IChunkAccess, boolean) -+ if (chunkstatus.getType() != ChunkStatus.Type.LEVELCHUNK) { -+ try (co.aikar.timings.Timing ignored1 = this.world.timings.chunkSaveOverwriteCheck.startTiming()) { // Paper -+ // Paper start - Optimize save by using status cache -+ try { -+ ChunkStatus statusOnDisk = this.getChunkStatusOnDisk(chunkPos); -+ if (statusOnDisk != null && statusOnDisk.getType() == ChunkStatus.Type.LEVELCHUNK) { -+ // Paper end -+ return; -+ } -+ -+ if (chunkstatus == ChunkStatus.EMPTY && chunk.h().values().stream().noneMatch(StructureStart::e)) { -+ return; -+ } -+ } catch (IOException ex) { -+ ex.printStackTrace(); -+ return; -+ } -+ } -+ } -+ -+ ChunkRegionLoader.AsyncSaveData asyncSaveData; -+ try (Timing ignored = this.world.timings.chunkUnloadPrepareSave.startTiming()) { -+ asyncSaveData = ChunkRegionLoader.getAsyncSaveData(this.world, chunk); -+ } -+ -+ this.world.asyncChunkTaskManager.scheduleChunkSave(chunkPos.x, chunkPos.z, com.destroystokyo.paper.io.PrioritizedTaskQueue.LOW_PRIORITY, -+ asyncSaveData, chunk); -+ -+ chunk.setLastSaved(this.world.getTime()); -+ chunk.setNeedsSaving(false); -+ } -+ // Paper end -+ - private void a(long i, PlayerChunk playerchunk) { - CompletableFuture completablefuture = playerchunk.getChunkSave(); - Consumer consumer = (ichunkaccess) -> { // CraftBukkit - decompile error -@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { - ((Chunk) ichunkaccess).setLoaded(false); - } - -- this.saveChunk(ichunkaccess); -+ //this.saveChunk(ichunkaccess);// Paper - delay - if (this.loadedChunks.remove(i) && ichunkaccess instanceof Chunk) { - Chunk chunk = (Chunk) ichunkaccess; - - this.world.unloadChunk(chunk); - } - -+ try { -+ this.asyncSave(ichunkaccess); // Paper - async chunk saving -+ } catch (Throwable ex) { -+ LOGGER.fatal("Failed to prepare async save, attempting synchronous save", ex); -+ this.saveChunk(ichunkaccess, true); -+ } -+ - this.lightEngine.a(ichunkaccess.getPos()); - this.lightEngine.queueUpdate(); - this.worldLoadListener.a(ichunkaccess.getPos(), (ChunkStatus) null); -@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { - } - } - -+ // Paper start - Async chunk io -+ public NBTTagCompound completeChunkData(NBTTagCompound compound, ChunkCoordIntPair chunkcoordintpair) throws IOException { -+ return compound == null ? null : this.getChunkData(this.world.getWorldProvider().getDimensionManager(), this.getWorldPersistentDataSupplier(), compound, chunkcoordintpair, this.world); -+ } -+ // Paper end -+ - private CompletableFuture> f(ChunkCoordIntPair chunkcoordintpair) { -- return CompletableFuture.supplyAsync(() -> { -+ // Paper start - Async chunk io -+ final java.util.function.BiFunction> syncLoadComplete = (chunkHolder, ioThrowable) -> { - try (Timing ignored = this.world.timings.syncChunkLoadTimer.startTimingIfSync()) { // Paper -- NBTTagCompound nbttagcompound; // Paper -- try (Timing ignored2 = this.world.timings.chunkIOStage1.startTimingIfSync()) { // Paper -- nbttagcompound = this.readChunkData(chunkcoordintpair); -+ if (ioThrowable != null) { -+ com.destroystokyo.paper.io.IOUtil.rethrow(ioThrowable); - } -- -- if (nbttagcompound != null) { -- boolean flag = nbttagcompound.hasKeyOfType("Level", 10) && nbttagcompound.getCompound("Level").hasKeyOfType("Status", 8); -- -- if (flag) { -- ProtoChunk protochunk = ChunkRegionLoader.loadChunk(this.world, this.definedStructureManager, this.n, chunkcoordintpair, nbttagcompound); -- -- protochunk.setLastSaved(this.world.getTime()); -- return Either.left(protochunk); -- } -- -- PlayerChunkMap.LOGGER.error("Chunk file at {} is missing level data, skipping", chunkcoordintpair); -+ this.getVillagePlace().loadInData(chunkcoordintpair, chunkHolder.poiData); -+ chunkHolder.tasks.forEach(Runnable::run); -+ // Paper - async load completes this -+ // Paper end -+ -+ // Paper start - This is done async -+ if (chunkHolder.protoChunk != null) { -+ chunkHolder.protoChunk.setLastSaved(this.world.getTime()); -+ return Either.left(chunkHolder.protoChunk); - } -+ // Paper end - } catch (ReportedException reportedexception) { - Throwable throwable = reportedexception.getCause(); - -@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { - } - - return Either.left(new ProtoChunk(chunkcoordintpair, ChunkConverter.a, this.world)); // Paper - Anti-Xray -- }, this.executor); -+ // Paper start - Async chunk io -+ }; -+ CompletableFuture> ret = new CompletableFuture<>(); -+ -+ Consumer chunkHolderConsumer = (ChunkRegionLoader.InProgressChunkHolder holder) -> { -+ PlayerChunkMap.this.executor.addTask(() -> { -+ ret.complete(syncLoadComplete.apply(holder, null)); -+ }); -+ }; -+ -+ CompletableFuture chunkSaveFuture = this.world.asyncChunkTaskManager.getChunkSaveFuture(chunkcoordintpair.x, chunkcoordintpair.z); -+ if (chunkSaveFuture != null) { -+ this.world.asyncChunkTaskManager.scheduleChunkLoad(chunkcoordintpair.x, chunkcoordintpair.z, -+ com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGH_PRIORITY, chunkHolderConsumer, false, chunkSaveFuture); -+ this.world.asyncChunkTaskManager.raisePriority(chunkcoordintpair.x, chunkcoordintpair.z, com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGH_PRIORITY); -+ } else { -+ this.world.asyncChunkTaskManager.scheduleChunkLoad(chunkcoordintpair.x, chunkcoordintpair.z, -+ com.destroystokyo.paper.io.PrioritizedTaskQueue.NORMAL_PRIORITY, chunkHolderConsumer, false); -+ } -+ return ret; -+ // Paper end - } - - private CompletableFuture> b(PlayerChunk playerchunk, ChunkStatus chunkstatus) { -@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { - return this.v.get(); - } - -+ // Paper start - async chunk io -+ private boolean writeDataAsync(ChunkCoordIntPair chunkPos, NBTTagCompound poiData, NBTTagCompound chunkData, boolean async) { -+ com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE.scheduleSave(this.world, chunkPos.x, chunkPos.z, -+ poiData, chunkData, !async ? com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGHEST_PRIORITY : com.destroystokyo.paper.io.PrioritizedTaskQueue.LOW_PRIORITY); -+ -+ if (async) { -+ return true; -+ } -+ -+ try (co.aikar.timings.Timing ignored = this.world.timings.chunkSaveIOWait.startTiming()) { // Paper -+ Boolean successPoi = com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE.waitForIOToComplete(this.world, chunkPos.x, chunkPos.z, true, true); -+ Boolean successChunk = com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE.waitForIOToComplete(this.world, chunkPos.x, chunkPos.z, true, false); -+ -+ if (successPoi == Boolean.FALSE || successChunk == Boolean.FALSE) { -+ return false; -+ } -+ -+ // null indicates no task existed, which means our write completed before we waited on it -+ -+ return true; -+ } // Paper -+ } -+ // Paper end -+ - public boolean saveChunk(IChunkAccess ichunkaccess) { -- this.n.a(ichunkaccess.getPos()); -+ // Paper start - async param -+ return this.saveChunk(ichunkaccess, false); -+ } -+ public boolean saveChunk(IChunkAccess ichunkaccess, boolean async) { -+ try (co.aikar.timings.Timing ignored = this.world.timings.chunkSave.startTiming()) { -+ NBTTagCompound poiData = this.getVillagePlace().getData(ichunkaccess.getPos()); // Paper -+ //this.n.a(ichunkaccess.getPos()); // Delay -+ // Paper end - if (!ichunkaccess.isNeedsSaving()) { - return false; - } else { -- try { -- this.world.checkSession(); -- } catch (ExceptionWorldConflict exceptionworldconflict) { -- PlayerChunkMap.LOGGER.error("Couldn't save chunk; already in use by another instance of Minecraft?", exceptionworldconflict); -- com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(exceptionworldconflict); // Paper -- return false; -- } -+ // Paper - The save session check is performed on the IO thread - - ichunkaccess.setLastSaved(this.world.getTime()); - ichunkaccess.setNeedsSaving(false); -@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { - NBTTagCompound nbttagcompound; - - if (chunkstatus.getType() != ChunkStatus.Type.LEVELCHUNK) { -+ try (co.aikar.timings.Timing ignored1 = this.world.timings.chunkSaveOverwriteCheck.startTiming()) { // Paper - // Paper start - Optimize save by using status cache - ChunkStatus statusOnDisk = this.getChunkStatusOnDisk(chunkcoordintpair); - if (statusOnDisk != null && statusOnDisk.getType() == ChunkStatus.Type.LEVELCHUNK) { - // Paper end -+ this.writeDataAsync(ichunkaccess.getPos(), poiData, null, async); // Paper - Async chunk io - return false; - } - - if (chunkstatus == ChunkStatus.EMPTY && ichunkaccess.h().values().stream().noneMatch(StructureStart::e)) { -+ this.writeDataAsync(ichunkaccess.getPos(), poiData, null, async); // Paper - Async chunk io - return false; - } - } -- -+ } // Paper -+ try (co.aikar.timings.Timing ignored1 = this.world.timings.chunkSaveDataSerialization.startTiming()) { // Paper - nbttagcompound = ChunkRegionLoader.saveChunk(this.world, ichunkaccess); -- this.write(chunkcoordintpair, nbttagcompound); -- return true; -+ } // Paper -+ return this.writeDataAsync(ichunkaccess.getPos(), poiData, nbttagcompound, async); // Paper - Async chunk io -+ //return true; // Paper - } catch (Exception exception) { - PlayerChunkMap.LOGGER.error("Failed to save chunk {},{}", chunkcoordintpair.x, chunkcoordintpair.z, exception); - com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(exception); // Paper - return false; - } - } -+ } // Paper - } - - protected void setViewDistance(int i) { -@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { - } - } - -+ // Paper start - Asynchronous chunk io -+ @Nullable -+ @Override -+ public NBTTagCompound read(ChunkCoordIntPair chunkcoordintpair) throws IOException { -+ if (Thread.currentThread() != com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE) { -+ NBTTagCompound ret = com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE -+ .loadChunkDataAsyncFuture(this.world, chunkcoordintpair.x, chunkcoordintpair.z, com.destroystokyo.paper.io.IOUtil.getPriorityForCurrentThread(), -+ false, true, true).join().chunkData; -+ -+ if (ret == com.destroystokyo.paper.io.PaperFileIOThread.FAILURE_VALUE) { -+ throw new IOException("See logs for further detail"); -+ } -+ return ret; -+ } -+ return super.read(chunkcoordintpair); -+ } -+ -+ @Override -+ public void write(ChunkCoordIntPair chunkcoordintpair, NBTTagCompound nbttagcompound) throws IOException { -+ if (Thread.currentThread() != com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE) { -+ com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE.scheduleSave( -+ this.world, chunkcoordintpair.x, chunkcoordintpair.z, null, nbttagcompound, -+ com.destroystokyo.paper.io.IOUtil.getPriorityForCurrentThread()); -+ -+ Boolean ret = com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE.waitForIOToComplete(this.world, -+ chunkcoordintpair.x, chunkcoordintpair.z, true, false); -+ -+ if (ret == Boolean.FALSE) { -+ throw new IOException("See logs for further detail"); -+ } -+ return; -+ } -+ super.write(chunkcoordintpair, nbttagcompound); -+ } -+ // Paper end -+ - @Nullable - public NBTTagCompound readChunkData(ChunkCoordIntPair chunkcoordintpair) throws IOException { // Paper - private -> public - NBTTagCompound nbttagcompound = this.read(chunkcoordintpair); -@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { - - // Paper start - chunk status cache "api" - public ChunkStatus getChunkStatusOnDiskIfCached(ChunkCoordIntPair chunkPos) { -+ // Paper start - async chunk save for unload -+ IChunkAccess unloadingChunk = this.world.asyncChunkTaskManager.getChunkInSaveProgress(chunkPos.x, chunkPos.z); -+ if (unloadingChunk != null) { -+ return unloadingChunk.getChunkStatus(); -+ } -+ // Paper end -+ // Paper start - async io -+ NBTTagCompound inProgressWrite = com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE -+ .getPendingWrite(this.world, chunkPos.x, chunkPos.z, false); -+ -+ if (inProgressWrite != null) { -+ return ChunkRegionLoader.getStatus(inProgressWrite); -+ } -+ // Paper end -+ - RegionFile regionFile = this.getRegionFileIfLoaded(chunkPos); - - return regionFile == null ? null : regionFile.getStatusIfCached(chunkPos.x, chunkPos.z); - } - - public ChunkStatus getChunkStatusOnDisk(ChunkCoordIntPair chunkPos) throws IOException { -+ // Paper start - async chunk save for unload -+ IChunkAccess unloadingChunk = this.world.asyncChunkTaskManager.getChunkInSaveProgress(chunkPos.x, chunkPos.z); -+ if (unloadingChunk != null) { -+ return unloadingChunk.getChunkStatus(); -+ } -+ // Paper end -+ // Paper start - async io -+ NBTTagCompound inProgressWrite = com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE -+ .getPendingWrite(this.world, chunkPos.x, chunkPos.z, false); -+ -+ if (inProgressWrite != null) { -+ return ChunkRegionLoader.getStatus(inProgressWrite); -+ } -+ // Paper end -+ synchronized (this) { // Paper - async io - RegionFile regionFile = this.getRegionFile(chunkPos, false); - - if (!regionFile.chunkExists(chunkPos)) { -@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { - if (status != null) { - return status; - } -+ // Paper start - async io -+ } - -- this.readChunkData(chunkPos); -+ NBTTagCompound compound = this.readChunkData(chunkPos); - -- return regionFile.getStatusIfCached(chunkPos.x, chunkPos.z); -+ return ChunkRegionLoader.getStatus(compound); -+ // Paper end - } - - public void updateChunkStatusOnDisk(ChunkCoordIntPair chunkPos, @Nullable NBTTagCompound compound) throws IOException { -+ synchronized (this) { // Paper - async io - RegionFile regionFile = this.getRegionFile(chunkPos, false); - - regionFile.setStatus(chunkPos.x, chunkPos.z, ChunkRegionLoader.getStatus(compound)); -+ } // Paper - async io - } - -+ // Paper start - async io -+ // this function will not load chunk data off disk to check for status -+ // ret null for unknown, empty for empty status on disk or absent from disk -+ public ChunkStatus getStatusOnDiskNoLoad(int x, int z) { -+ // Paper start - async chunk save for unload -+ IChunkAccess unloadingChunk = this.world.asyncChunkTaskManager.getChunkInSaveProgress(x, z); -+ if (unloadingChunk != null) { -+ return unloadingChunk.getChunkStatus(); -+ } -+ // Paper end -+ // Paper start - async io -+ net.minecraft.server.NBTTagCompound inProgressWrite = com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE -+ .getPendingWrite(this.world, x, z, false); -+ -+ if (inProgressWrite != null) { -+ return net.minecraft.server.ChunkRegionLoader.getStatus(inProgressWrite); -+ } -+ // Paper end -+ // variant of PlayerChunkMap#getChunkStatusOnDisk that does not load data off disk, but loads the region file -+ ChunkCoordIntPair chunkPos = new ChunkCoordIntPair(x, z); -+ synchronized (world.getChunkProvider().playerChunkMap) { -+ net.minecraft.server.RegionFile file; -+ try { -+ file = world.getChunkProvider().playerChunkMap.getRegionFile(chunkPos, false); -+ } catch (IOException ex) { -+ throw new RuntimeException(ex); -+ } -+ -+ return !file.chunkExists(chunkPos) ? ChunkStatus.EMPTY : file.getStatusIfCached(x, z); -+ } -+ } -+ // Paper end -+ - public IChunkAccess getUnloadingChunk(int chunkX, int chunkZ) { - PlayerChunk chunkHolder = this.pendingUnload.get(ChunkCoordIntPair.pair(chunkX, chunkZ)); - return chunkHolder == null ? null : chunkHolder.getAvailableChunkNow(); -@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { - - } - -+ public VillagePlace getVillagePlace() { return this.h(); } // Paper - OBFHELPER - protected VillagePlace h() { - return this.n; - } -diff --git a/src/main/java/net/minecraft/server/RegionFile.java b/src/main/java/net/minecraft/server/RegionFile.java -index a8c8ace46..22144eb00 100644 ---- a/src/main/java/net/minecraft/server/RegionFile.java -+++ b/src/main/java/net/minecraft/server/RegionFile.java -@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable { - this.d[j] = i; // Spigot - move this to after the write - } - -- public void close() throws IOException { -+ public synchronized void close() throws IOException { // Paper - synchronize - this.closed = true; // Paper - this.b.close(); - } -diff --git a/src/main/java/net/minecraft/server/RegionFileCache.java b/src/main/java/net/minecraft/server/RegionFileCache.java -index d2b328945..d3d610742 100644 ---- a/src/main/java/net/minecraft/server/RegionFileCache.java -+++ b/src/main/java/net/minecraft/server/RegionFileCache.java -@@ -0,0 +0,0 @@ public abstract class RegionFileCache implements AutoCloseable { - } - - // Paper start -- public RegionFile getRegionFileIfLoaded(ChunkCoordIntPair chunkcoordintpair) { -+ public synchronized RegionFile getRegionFileIfLoaded(ChunkCoordIntPair chunkcoordintpair) { // Paper - synchronize for async io - return this.cache.getAndMoveToFirst(ChunkCoordIntPair.pair(chunkcoordintpair.getRegionX(), chunkcoordintpair.getRegionZ())); - } - // Paper end - - public RegionFile getRegionFile(ChunkCoordIntPair chunkcoordintpair, boolean existingOnly) throws IOException { return this.a(chunkcoordintpair, existingOnly); } // Paper - OBFHELPER -- private RegionFile a(ChunkCoordIntPair chunkcoordintpair, boolean existingOnly) throws IOException { // CraftBukkit -+ private synchronized RegionFile a(ChunkCoordIntPair chunkcoordintpair, boolean existingOnly) throws IOException { // CraftBukkit // Paper - synchronize for async io - long i = ChunkCoordIntPair.pair(chunkcoordintpair.getRegionX(), chunkcoordintpair.getRegionZ()); - RegionFile regionfile = (RegionFile) this.cache.getAndMoveToFirst(i); - -@@ -0,0 +0,0 @@ public abstract class RegionFileCache implements AutoCloseable { - } - - // CraftBukkit start -- public boolean chunkExists(ChunkCoordIntPair pos) throws IOException { -+ public synchronized boolean chunkExists(ChunkCoordIntPair pos) throws IOException { // Paper - synchronize - copyIfNeeded(pos.x, pos.z); // Paper - RegionFile regionfile = a(pos, true); - -diff --git a/src/main/java/net/minecraft/server/RegionFileSection.java b/src/main/java/net/minecraft/server/RegionFileSection.java -index 4b3e0c0f0..04b7dab64 100644 ---- a/src/main/java/net/minecraft/server/RegionFileSection.java -+++ b/src/main/java/net/minecraft/server/RegionFileSection.java -@@ -0,0 +0,0 @@ public class RegionFileSection extends RegionFi - - private static final Logger LOGGER = LogManager.getLogger(); - private final Long2ObjectMap> b = new Long2ObjectOpenHashMap(); -- private final LongLinkedOpenHashSet d = new LongLinkedOpenHashSet(); -+ protected final LongLinkedOpenHashSet d = new LongLinkedOpenHashSet(); // Paper - private -> protected - private final BiFunction, R> e; - private final Function f; - private final DataFixer g; -@@ -0,0 +0,0 @@ public class RegionFileSection extends RegionFi - } - - protected void a(BooleanSupplier booleansupplier) { -- while (!this.d.isEmpty() && booleansupplier.getAsBoolean()) { -- ChunkCoordIntPair chunkcoordintpair = SectionPosition.a(this.d.firstLong()).u(); -+ while (!this.d.isEmpty() && booleansupplier.getAsBoolean()) { // Paper - conflict here to avoid obfhelpers -+ ChunkCoordIntPair chunkcoordintpair = SectionPosition.a(this.d.firstLong()).u(); // Paper - conflict here to avoid obfhelpers - - this.d(chunkcoordintpair); - } -@@ -0,0 +0,0 @@ public class RegionFileSection extends RegionFi - } - - private void b(ChunkCoordIntPair chunkcoordintpair) { -- this.a(chunkcoordintpair, DynamicOpsNBT.a, this.c(chunkcoordintpair)); -+ // Paper start - load data in function -+ this.loadInData(chunkcoordintpair, this.c(chunkcoordintpair)); -+ } -+ public void loadInData(ChunkCoordIntPair chunkPos, NBTTagCompound compound) { -+ this.a(chunkPos, DynamicOpsNBT.a, compound); -+ // Paper end - } - - @Nullable -@@ -0,0 +0,0 @@ public class RegionFileSection extends RegionFi - } - - private void d(ChunkCoordIntPair chunkcoordintpair) { -- Dynamic dynamic = this.a(chunkcoordintpair, DynamicOpsNBT.a); -+ Dynamic dynamic = this.a(chunkcoordintpair, DynamicOpsNBT.a); // Paper - conflict here to avoid adding obfhelpers :) - NBTBase nbtbase = (NBTBase) dynamic.getValue(); - - if (nbtbase instanceof NBTTagCompound) { -@@ -0,0 +0,0 @@ public class RegionFileSection extends RegionFi - - } - -+ // Paper start - internal get data function, copied from above -+ private NBTTagCompound getDataInternal(ChunkCoordIntPair chunkcoordintpair) { -+ Dynamic dynamic = this.a(chunkcoordintpair, DynamicOpsNBT.a); -+ NBTBase nbtbase = (NBTBase) dynamic.getValue(); -+ -+ if (nbtbase instanceof NBTTagCompound) { -+ return (NBTTagCompound)nbtbase; -+ } else { -+ RegionFileSection.LOGGER.error("Expected compound tag, got {}", nbtbase); -+ } -+ return null; -+ } -+ // Paper end -+ - private Dynamic a(ChunkCoordIntPair chunkcoordintpair, DynamicOps dynamicops) { - Map map = Maps.newHashMap(); - -@@ -0,0 +0,0 @@ public class RegionFileSection extends RegionFi - public void a(ChunkCoordIntPair chunkcoordintpair) { - if (!this.d.isEmpty()) { - for (int i = 0; i < 16; ++i) { -- long j = SectionPosition.a(chunkcoordintpair, i).v(); -+ long j = SectionPosition.a(chunkcoordintpair, i).v(); // Paper - conflict here to avoid obfhelpers - -- if (this.d.contains(j)) { -+ if (this.d.contains(j)) { // Paper - conflict here to avoid obfhelpers - this.d(chunkcoordintpair); - return; - } -@@ -0,0 +0,0 @@ public class RegionFileSection extends RegionFi - } - - } -+ -+ // Paper start - get data function -+ public NBTTagCompound getData(ChunkCoordIntPair chunkcoordintpair) { -+ // Note: Copied from above -+ // This is checking if the data exists, then it builds it later in getDataInternal(ChunkCoordIntPair) -+ if (!this.d.isEmpty()) { -+ for (int i = 0; i < 16; ++i) { -+ long j = SectionPosition.a(chunkcoordintpair, i).v(); -+ -+ if (this.d.contains(j)) { -+ return this.getDataInternal(chunkcoordintpair); -+ } -+ } -+ } -+ return null; -+ } -+ // Paper end - } -diff --git a/src/main/java/net/minecraft/server/TicketType.java b/src/main/java/net/minecraft/server/TicketType.java -index 9c114d2d3..e3150f85a 100644 ---- a/src/main/java/net/minecraft/server/TicketType.java -+++ b/src/main/java/net/minecraft/server/TicketType.java -@@ -0,0 +0,0 @@ public class TicketType { - public static final TicketType PLUGIN = a("plugin", (a, b) -> 0); // CraftBukkit - public static final TicketType PLUGIN_TICKET = a("plugin_ticket", (plugin1, plugin2) -> plugin1.getClass().getName().compareTo(plugin2.getClass().getName())); // Craftbukkit - public static final TicketType ANTIXRAY = a("antixray", Integer::compareTo); // Paper - Anti-Xray -+ public static final TicketType ASYNC_LOAD = a("async_load", Long::compareTo); // Paper - - public static TicketType a(String s, Comparator comparator) { - return new TicketType<>(s, comparator, 0L); -diff --git a/src/main/java/net/minecraft/server/VillagePlace.java b/src/main/java/net/minecraft/server/VillagePlace.java -index 316959064..0e98b7803 100644 ---- a/src/main/java/net/minecraft/server/VillagePlace.java -+++ b/src/main/java/net/minecraft/server/VillagePlace.java -@@ -0,0 +0,0 @@ public class VillagePlace extends RegionFileSection { - - private final VillagePlace.a a = new VillagePlace.a(); - -+ private final WorldServer world; // Paper -+ - public VillagePlace(File file, DataFixer datafixer) { -+ // Paper start -+ this(file, datafixer, null); -+ } -+ public VillagePlace(File file, DataFixer datafixer, WorldServer world) { -+ // Paper end - super(file, VillagePlaceSection::new, VillagePlaceSection::new, datafixer, DataFixTypes.POI_CHUNK); -+ this.world = world; // Paper - } - - public void a(BlockPosition blockposition, VillagePlaceType villageplacetype) { -@@ -0,0 +0,0 @@ public class VillagePlace extends RegionFileSection { - - @Override - public void a(BooleanSupplier booleansupplier) { -- super.a(booleansupplier); -+ // Paper start - async chunk io -+ if (this.world == null) { -+ super.a(booleansupplier); -+ } else { -+ //super.a(booleansupplier); // re-implement below -+ while (!((RegionFileSection)this).d.isEmpty() && booleansupplier.getAsBoolean()) { -+ ChunkCoordIntPair chunkcoordintpair = SectionPosition.a(((RegionFileSection)this).d.firstLong()).u(); -+ -+ NBTTagCompound data; -+ try (co.aikar.timings.Timing ignored1 = this.world.timings.poiSaveDataSerialization.startTiming()) { -+ data = this.getData(chunkcoordintpair); -+ } -+ com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE.scheduleSave(this.world, -+ chunkcoordintpair.x, chunkcoordintpair.z, data, null, com.destroystokyo.paper.io.PrioritizedTaskQueue.LOW_PRIORITY); -+ } -+ } -+ // Paper end - this.a.a(); - } - -@@ -0,0 +0,0 @@ public class VillagePlace extends RegionFileSection { - } - } - -+ // Paper start - Asynchronous chunk io -+ @javax.annotation.Nullable -+ @Override -+ public NBTTagCompound read(ChunkCoordIntPair chunkcoordintpair) throws java.io.IOException { -+ if (this.world != null && Thread.currentThread() != com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE) { -+ NBTTagCompound ret = com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE -+ .loadChunkDataAsyncFuture(this.world, chunkcoordintpair.x, chunkcoordintpair.z, com.destroystokyo.paper.io.IOUtil.getPriorityForCurrentThread(), -+ true, false, true).join().poiData; -+ -+ if (ret == com.destroystokyo.paper.io.PaperFileIOThread.FAILURE_VALUE) { -+ throw new java.io.IOException("See logs for further detail"); -+ } -+ return ret; -+ } -+ return super.read(chunkcoordintpair); -+ } -+ -+ @Override -+ public void write(ChunkCoordIntPair chunkcoordintpair, NBTTagCompound nbttagcompound) throws java.io.IOException { -+ if (this.world != null && Thread.currentThread() != com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE) { -+ com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE.scheduleSave( -+ this.world, chunkcoordintpair.x, chunkcoordintpair.z, nbttagcompound, null, -+ com.destroystokyo.paper.io.IOUtil.getPriorityForCurrentThread()); -+ -+ Boolean ret = com.destroystokyo.paper.io.PaperFileIOThread.Holder.INSTANCE.waitForIOToComplete(this.world, -+ chunkcoordintpair.x, chunkcoordintpair.z, true, true); -+ -+ if (ret == Boolean.FALSE) { -+ throw new java.io.IOException("See logs for further detail"); -+ } -+ return; -+ } -+ super.write(chunkcoordintpair, nbttagcompound); -+ } -+ // Paper end -+ - public static enum Occupancy { - - HAS_SPACE(VillagePlaceRecord::d), IS_OCCUPIED(VillagePlaceRecord::e), ANY((villageplacerecord) -> { -diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java -index 133095665..5e0f6a105 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 { - return new Throwable(entity + " Added to world at " + new java.util.Date()); - } - -+ // Paper start - Asynchronous IO -+ public final com.destroystokyo.paper.io.PaperFileIOThread.ChunkDataController poiDataController = new com.destroystokyo.paper.io.PaperFileIOThread.ChunkDataController() { -+ @Override -+ public void writeData(int x, int z, NBTTagCompound compound) throws java.io.IOException { -+ WorldServer.this.getChunkProvider().playerChunkMap.getVillagePlace().write(new ChunkCoordIntPair(x, z), compound); -+ } -+ -+ @Override -+ public NBTTagCompound readData(int x, int z) throws java.io.IOException { -+ return WorldServer.this.getChunkProvider().playerChunkMap.getVillagePlace().read(new ChunkCoordIntPair(x, z)); -+ } -+ -+ @Override -+ public T computeForRegionFile(int chunkX, int chunkZ, java.util.function.Function function) { -+ synchronized (WorldServer.this.getChunkProvider().playerChunkMap.getVillagePlace()) { -+ RegionFile file; -+ -+ try { -+ file = WorldServer.this.getChunkProvider().playerChunkMap.getVillagePlace().getRegionFile(new ChunkCoordIntPair(chunkX, chunkZ), false); -+ } catch (java.io.IOException ex) { -+ throw new RuntimeException(ex); -+ } -+ -+ return function.apply(file); -+ } -+ } -+ -+ @Override -+ public T computeForRegionFileIfLoaded(int chunkX, int chunkZ, java.util.function.Function function) { -+ synchronized (WorldServer.this.getChunkProvider().playerChunkMap.getVillagePlace()) { -+ RegionFile file = WorldServer.this.getChunkProvider().playerChunkMap.getVillagePlace().getRegionFileIfLoaded(new ChunkCoordIntPair(chunkX, chunkZ)); -+ return function.apply(file); -+ } -+ } -+ }; -+ -+ public final com.destroystokyo.paper.io.PaperFileIOThread.ChunkDataController chunkDataController = new com.destroystokyo.paper.io.PaperFileIOThread.ChunkDataController() { -+ @Override -+ public void writeData(int x, int z, NBTTagCompound compound) throws java.io.IOException { -+ WorldServer.this.getChunkProvider().playerChunkMap.write(new ChunkCoordIntPair(x, z), compound); -+ } -+ -+ @Override -+ public NBTTagCompound readData(int x, int z) throws java.io.IOException { -+ return WorldServer.this.getChunkProvider().playerChunkMap.read(new ChunkCoordIntPair(x, z)); -+ } -+ -+ @Override -+ public T computeForRegionFile(int chunkX, int chunkZ, java.util.function.Function function) { -+ synchronized (WorldServer.this.getChunkProvider().playerChunkMap) { -+ RegionFile file; -+ -+ try { -+ file = WorldServer.this.getChunkProvider().playerChunkMap.getRegionFile(new ChunkCoordIntPair(chunkX, chunkZ), false); -+ } catch (java.io.IOException ex) { -+ throw new RuntimeException(ex); -+ } -+ -+ return function.apply(file); -+ } -+ } -+ -+ @Override -+ public T computeForRegionFileIfLoaded(int chunkX, int chunkZ, java.util.function.Function function) { -+ synchronized (WorldServer.this.getChunkProvider().playerChunkMap) { -+ RegionFile file = WorldServer.this.getChunkProvider().playerChunkMap.getRegionFileIfLoaded(new ChunkCoordIntPair(chunkX, chunkZ)); -+ return function.apply(file); -+ } -+ } -+ }; -+ public final com.destroystokyo.paper.io.chunk.ChunkTaskManager asyncChunkTaskManager; -+ // Paper end -+ - // Add env and gen to constructor - public WorldServer(MinecraftServer minecraftserver, Executor executor, WorldNBTStorage worldnbtstorage, WorldData worlddata, DimensionManager dimensionmanager, GameProfilerFiller gameprofilerfiller, WorldLoadListener worldloadlistener, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen) { - super(worlddata, dimensionmanager, (world, worldprovider) -> { -@@ -0,0 +0,0 @@ public class WorldServer extends World { - - this.mobSpawnerTrader = this.worldProvider.getDimensionManager().getType() == DimensionManager.OVERWORLD ? new MobSpawnerTrader(this) : null; // CraftBukkit - getType() - this.getServer().addWorld(this.getWorld()); // CraftBukkit -+ -+ this.asyncChunkTaskManager = new com.destroystokyo.paper.io.chunk.ChunkTaskManager(this); // Paper - } - - // CraftBukkit start -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index 2227de3bf..243722b67 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 { - return true; - } - -- net.minecraft.server.RegionFile file; -- try { -- file = world.getChunkProvider().playerChunkMap.getRegionFile(chunkPos, false); -- } catch (IOException ex) { -- throw new RuntimeException(ex); -- } -+ ChunkStatus status = world.getChunkProvider().playerChunkMap.getStatusOnDiskNoLoad(x, z); // Paper - async io - move to own method - -- ChunkStatus status = file.getStatusIfCached(x, z); -- if (!file.chunkExists(chunkPos) || (status != null && status != ChunkStatus.FULL)) { -+ // Paper start - async io -+ if (status == ChunkStatus.EMPTY) { -+ // does not exist on disk - return false; - } - -+ if (status == null) { // at this stage we don't know what it is on disk - IChunkAccess chunk = world.getChunkProvider().getChunkAt(x, z, ChunkStatus.EMPTY, true); - if (!(chunk instanceof ProtoChunkExtension) && !(chunk instanceof net.minecraft.server.Chunk)) { - return false; - } -+ } else if (status != ChunkStatus.FULL) { -+ return false; // not full status on disk -+ } -+ // Paper end - - // fall through to load - // we do this so we do not re-read the chunk data on disk -@@ -0,0 +0,0 @@ public class CraftWorld implements World { - return persistentRaid.a.values().stream().map(CraftRaid::new).collect(Collectors.toList()); - } - -+ // Paper start -+ @Override -+ public CompletableFuture getChunkAtAsync(int x, int z, boolean gen) { -+ if (Bukkit.isPrimaryThread()) { -+ net.minecraft.server.Chunk immediate = this.world.getChunkProvider().getChunkAtIfLoadedImmediately(x, z); -+ if (immediate != null) { -+ return CompletableFuture.completedFuture(immediate.bukkitChunk); -+ } -+ } -+ -+ CompletableFuture ret = new CompletableFuture<>(); -+ this.world.getChunkProvider().getChunkAtAsynchronously(x, z, gen, (net.minecraft.server.Chunk chunk) -> { -+ ret.complete(chunk == null ? null : chunk.bukkitChunk); -+ }); -+ -+ return ret; -+ } -+ // Paper end -+ - // Spigot start - @Override - public int getViewDistance() { -diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java -index a1d93200e..6ca0ebfde 100644 ---- a/src/main/java/org/spigotmc/WatchdogThread.java -+++ b/src/main/java/org/spigotmc/WatchdogThread.java -@@ -0,0 +0,0 @@ import java.lang.management.ThreadInfo; - import java.util.logging.Level; - import java.util.logging.Logger; - import com.destroystokyo.paper.PaperConfig; -+import com.destroystokyo.paper.io.chunk.ChunkTaskManager; // Paper - import net.minecraft.server.MinecraftServer; - import org.bukkit.Bukkit; - -@@ -0,0 +0,0 @@ public class WatchdogThread extends Thread - log.log( Level.SEVERE, "If you are unsure or still think this is a Paper bug, please report this to https://github.com/PaperMC/Paper/issues" ); - log.log( Level.SEVERE, "Be sure to include ALL relevant console errors and Minecraft crash reports" ); - log.log( Level.SEVERE, "Paper version: " + Bukkit.getServer().getVersion() ); -+ ChunkTaskManager.dumpAllChunkLoadInfo(); // Paper - // - if ( net.minecraft.server.World.lastPhysicsProblem != null ) - { -@@ -0,0 +0,0 @@ public class WatchdogThread extends Thread - // Paper end - Different message for short timeout - log.log( Level.SEVERE, "------------------------------" ); - log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Paper!):" ); // Paper -+ log.log( Level.SEVERE, "The server is waiting on these chunks: " + ChunkTaskManager.getChunkWaitInfo() ); // Paper - async chunk debug - dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( MinecraftServer.getServer().serverThread.getId(), Integer.MAX_VALUE ), log ); - log.log( Level.SEVERE, "------------------------------" ); - // --- \ No newline at end of file diff --git a/Spigot-Server-Patches/Avoid-dimension-id-collisions.patch b/Spigot-Server-Patches/Avoid-dimension-id-collisions.patch index 949b4c0332..6cff4cd5b8 100644 --- a/Spigot-Server-Patches/Avoid-dimension-id-collisions.patch +++ b/Spigot-Server-Patches/Avoid-dimension-id-collisions.patch @@ -8,7 +8,7 @@ we would reuse an existing dimension id, if some other dimension was unloaded before. diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index b0f4ef1b48..d0834ecaab 100644 +index 1ca9a7724..e1c9a9e12 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -0,0 +0,0 @@ public final class CraftServer implements Server { diff --git a/Spigot-Server-Patches/Avoid-hopper-searches-if-there-are-no-items.patch b/Spigot-Server-Patches/Avoid-hopper-searches-if-there-are-no-items.patch index 864c312232..b551fa7672 100644 --- a/Spigot-Server-Patches/Avoid-hopper-searches-if-there-are-no-items.patch +++ b/Spigot-Server-Patches/Avoid-hopper-searches-if-there-are-no-items.patch @@ -14,7 +14,7 @@ And since minecart hoppers are used _very_ rarely near we can avoid alot of sear Combined, this adds up a lot. diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java -index d604f96c1..67dc837f4 100644 +index 863a2222f..f2a04cb6a 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 implements IChunkAccess { @@ -27,7 +27,7 @@ index d604f96c1..67dc837f4 100644 + private final int[] inventoryEntityCounts = new int[16]; // Paper end - public Chunk(World world, ChunkCoordIntPair chunkcoordintpair, BiomeBase[] abiomebase, ChunkConverter chunkconverter, TickList ticklist, TickList ticklist1, long i, @Nullable ChunkSection[] achunksection, @Nullable Consumer consumer) { + public Chunk(World world, ChunkCoordIntPair chunkcoordintpair, BiomeStorage biomestorage, ChunkConverter chunkconverter, TickList ticklist, TickList ticklist1, long i, @Nullable ChunkSection[] achunksection, @Nullable Consumer consumer) { @@ -0,0 +0,0 @@ public class Chunk implements IChunkAccess { entity.chunkY = k; entity.chunkZ = this.loc.z; @@ -54,22 +54,6 @@ index d604f96c1..67dc837f4 100644 entityCounts.decrement(entity.getMinecraftKeyString()); this.markDirty(); // Paper // Paper end -@@ -0,0 +0,0 @@ public class Chunk implements IChunkAccess { - for (int k = i; k <= j; ++k) { - Iterator iterator = this.entitySlices[k].iterator(); // Spigot - -+ // Paper start - Don't search for inventories if we have none, and that is all we want -+ /* -+ * We check if they want inventories by seeing if it is the static `IEntitySelector.c` -+ * -+ * Make sure the inventory selector stays in sync. -+ * It should be the one that checks `var1 instanceof IInventory && var1.isAlive()` -+ */ -+ if (predicate == IEntitySelector.isInventory() && inventoryEntityCounts[k] <= 0) continue; -+ // Paper end - while (iterator.hasNext()) { - Entity entity = (Entity) iterator.next(); - if (entity.shouldBeRemoved) continue; // Paper @@ -0,0 +0,0 @@ public class Chunk implements IChunkAccess { i = MathHelper.clamp(i, 0, this.entitySlices.length - 1); j = MathHelper.clamp(j, 0, this.entitySlices.length - 1); @@ -88,9 +72,20 @@ index d604f96c1..67dc837f4 100644 + if (counts != null && counts[k] <= 0) continue; // Paper - Don't check a chunk if it doesn't have the type we are looking for Iterator iterator = this.entitySlices[k].iterator(); // Spigot ++ // Paper start - Don't search for inventories if we have none, and that is all we want ++ /* ++ * We check if they want inventories by seeing if it is the static `IEntitySelector.c` ++ * ++ * Make sure the inventory selector stays in sync. ++ * It should be the one that checks `var1 instanceof IInventory && var1.isAlive()` ++ */ ++ if (predicate == IEntitySelector.isInventory() && inventoryEntityCounts[k] <= 0) continue; ++ // Paper end while (iterator.hasNext()) { + T t0 = (T) iterator.next(); // CraftBukkit - decompile error + if (t0.shouldBeRemoved) continue; // Paper diff --git a/src/main/java/net/minecraft/server/IEntitySelector.java b/src/main/java/net/minecraft/server/IEntitySelector.java -index 56488b78d..56739e6ed 100644 +index 498f38109..a2d1ef360 100644 --- a/src/main/java/net/minecraft/server/IEntitySelector.java +++ b/src/main/java/net/minecraft/server/IEntitySelector.java @@ -0,0 +0,0 @@ public final class IEntitySelector { diff --git a/Spigot-Server-Patches/Backport-MC-160177-fix-from-1.15.patch b/Spigot-Server-Patches/Backport-MC-160177-fix-from-1.15.patch deleted file mode 100644 index 52e6dd4ca9..0000000000 --- a/Spigot-Server-Patches/Backport-MC-160177-fix-from-1.15.patch +++ /dev/null @@ -1,32 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Gerrygames -Date: Thu, 21 Nov 2019 14:56:51 +0100 -Subject: [PATCH] Backport MC-160177 fix from 1.15 - - -diff --git a/src/main/java/net/minecraft/server/PlayerInteractManager.java b/src/main/java/net/minecraft/server/PlayerInteractManager.java -index bc03a82b..c77e812b 100644 ---- a/src/main/java/net/minecraft/server/PlayerInteractManager.java -+++ b/src/main/java/net/minecraft/server/PlayerInteractManager.java -@@ -0,0 +0,0 @@ public class PlayerInteractManager { - this.l = -1; - this.e = false; - } else { -- this.a(iblockdata, this.g); -+ this.a(iblockdata, this.g, this.lastDigTick); // Paper - pass lastDigTick as an argument - } - } - - } - -- private float a(IBlockData iblockdata, BlockPosition blockposition) { -- int i = this.currentTick - this.k; -+ // Paper start - Add startTick parameter and use it -+ private float a(IBlockData iblockdata, BlockPosition blockposition) { return this.a(iblockdata, blockposition, this.k); } -+ private float a(IBlockData iblockdata, BlockPosition blockposition, int startTick) { -+ int i = this.currentTick - startTick; -+ // Paper end - float f = iblockdata.getDamage(this.player, this.player.world, blockposition) * (float) (i + 1); - int j = (int) (f * 10.0F); - --- \ No newline at end of file diff --git a/Spigot-Server-Patches/Block-Entity-remove-from-being-called-on-Players.patch b/Spigot-Server-Patches/Block-Entity-remove-from-being-called-on-Players.patch index 835e6ad775..5bb7511947 100644 --- a/Spigot-Server-Patches/Block-Entity-remove-from-being-called-on-Players.patch +++ b/Spigot-Server-Patches/Block-Entity-remove-from-being-called-on-Players.patch @@ -12,7 +12,7 @@ Player we will look at limiting the scope of this change. It appears to be unintentional in the few cases we've seen so far. diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -index ea4e569e5a..15c874fc82 100644 +index 04fc8e233..4b847ddd8 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 @@ public class CraftPlayer extends CraftHumanEntity implements Player { @@ -28,7 +28,7 @@ index ea4e569e5a..15c874fc82 100644 + super.remove(); + } + } - //Paper end + // Paper end // Spigot start -- \ No newline at end of file diff --git a/Spigot-Server-Patches/BlockDestroyEvent.patch b/Spigot-Server-Patches/BlockDestroyEvent.patch index 3be551e6f9..b95eca5580 100644 --- a/Spigot-Server-Patches/BlockDestroyEvent.patch +++ b/Spigot-Server-Patches/BlockDestroyEvent.patch @@ -11,10 +11,10 @@ floating in the air. This can replace many uses of BlockPhysicsEvent diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java -index ef561cd95b..dad0c893fd 100644 +index e8f83627c..61a3ddc4d 100644 --- a/src/main/java/net/minecraft/server/World.java +++ b/src/main/java/net/minecraft/server/World.java -@@ -0,0 +0,0 @@ public abstract class World implements IIBlockAccess, GeneratorAccess, AutoClose +@@ -0,0 +0,0 @@ public abstract class World implements GeneratorAccess, AutoCloseable { return false; } else { Fluid fluid = this.getFluid(blockposition); diff --git a/Spigot-Server-Patches/Book-Size-Limits.patch b/Spigot-Server-Patches/Book-Size-Limits.patch index 41372fd806..874cb34b89 100644 --- a/Spigot-Server-Patches/Book-Size-Limits.patch +++ b/Spigot-Server-Patches/Book-Size-Limits.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Book Size Limits Puts some limits on the size of books. diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java -index 63d8602927..8feb0efdcd 100644 +index 7e85a0224..30f35304a 100644 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java @@ -0,0 +0,0 @@ public class PaperConfig { @@ -22,7 +22,7 @@ index 63d8602927..8feb0efdcd 100644 + } } diff --git a/src/main/java/net/minecraft/server/PlayerConnection.java b/src/main/java/net/minecraft/server/PlayerConnection.java -index 45c2777552..04b3791f36 100644 +index d263897da..e5db2de26 100644 --- a/src/main/java/net/minecraft/server/PlayerConnection.java +++ b/src/main/java/net/minecraft/server/PlayerConnection.java @@ -0,0 +0,0 @@ import java.util.Iterator; @@ -73,7 +73,7 @@ index 45c2777552..04b3791f36 100644 + } + } + // Paper end - // CraftBukkit start PlayerConnectionUtils.ensureMainThread(packetplayinbedit, this, this.player.getWorldServer()); + // CraftBukkit start if (this.lastBookTick + 20 > MinecraftServer.currentTick) { -- \ No newline at end of file diff --git a/Spigot-Server-Patches/Call-WhitelistToggleEvent-when-whitelist-is-toggled.patch b/Spigot-Server-Patches/Call-WhitelistToggleEvent-when-whitelist-is-toggled.patch index c432056b12..60543f2c99 100644 --- a/Spigot-Server-Patches/Call-WhitelistToggleEvent-when-whitelist-is-toggled.patch +++ b/Spigot-Server-Patches/Call-WhitelistToggleEvent-when-whitelist-is-toggled.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Call WhitelistToggleEvent when whitelist is toggled diff --git a/src/main/java/net/minecraft/server/PlayerList.java b/src/main/java/net/minecraft/server/PlayerList.java -index 0da3f95f89..65df0e7c8c 100644 +index 62d807597..9d715d891 100644 --- a/src/main/java/net/minecraft/server/PlayerList.java +++ b/src/main/java/net/minecraft/server/PlayerList.java @@ -0,0 +0,0 @@ public abstract class PlayerList { diff --git a/Spigot-Server-Patches/Call-player-spectator-target-events.patch b/Spigot-Server-Patches/Call-player-spectator-target-events.patch index 2eaa6c0625..3f5e9772d5 100644 --- a/Spigot-Server-Patches/Call-player-spectator-target-events.patch +++ b/Spigot-Server-Patches/Call-player-spectator-target-events.patch @@ -5,17 +5,17 @@ Subject: [PATCH] Call player spectator target events diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java -index 898089983..ad5dcab78 100644 +index 26096afbe..bdb73b68a 100644 --- a/src/main/java/net/minecraft/server/EntityPlayer.java +++ b/src/main/java/net/minecraft/server/EntityPlayer.java @@ -0,0 +0,0 @@ public class EntityPlayer extends EntityHuman implements ICrafting { - private EnumChatVisibility ck; - private boolean cl = true; - private long cm = SystemUtils.getMonotonicMillis(); + private EnumChatVisibility ch; + private boolean ci = true; + private long cj = SystemUtils.getMonotonicMillis(); - private Entity spectatedEntity; + private Entity spectatedEntity; private void setSpectatorTargetField(Entity e) { this.spectatedEntity = e; } // Paper - OBFHELPER public boolean worldChangeInvuln; - private boolean cp; private void setHasSeenCredits(boolean has) { this.cp = has; } // Paper - OBFHELPER + private boolean cm; private void setHasSeenCredits(boolean has) { this.cm = has; } // Paper - OBFHELPER private final RecipeBookServer recipeBook; @@ -0,0 +0,0 @@ public class EntityPlayer extends EntityHuman implements ICrafting { return (Entity) (this.spectatedEntity == null ? this : this.spectatedEntity); @@ -29,7 +29,7 @@ index 898089983..ad5dcab78 100644 - this.spectatedEntity = (Entity) (entity == null ? this : entity); - if (entity1 != this.spectatedEntity) { - this.playerConnection.sendPacket(new PacketPlayOutCamera(this.spectatedEntity)); -- this.playerConnection.a(this.spectatedEntity.locX, this.spectatedEntity.locY, this.spectatedEntity.locZ, this.yaw, this.pitch, TeleportCause.SPECTATE); // CraftBukkit +- this.playerConnection.a(this.spectatedEntity.locX(), this.spectatedEntity.locY(), this.spectatedEntity.locZ(), this.yaw, this.pitch, TeleportCause.SPECTATE); // CraftBukkit + if (newSpectatorTarget == null) { + newSpectatorTarget = this; } @@ -53,9 +53,17 @@ index 898089983..ad5dcab78 100644 + setSpectatorTargetField(newSpectatorTarget); + + this.playerConnection.sendPacket(new PacketPlayOutCamera(newSpectatorTarget)); -+ this.playerConnection.a(this.spectatedEntity.locX, this.spectatedEntity.locY, this.spectatedEntity.locZ, this.yaw, this.pitch, TeleportCause.SPECTATE); // CraftBukkit ++ this.playerConnection.a(this.spectatedEntity.locY(), this.spectatedEntity.locY(), this.spectatedEntity.locZ(), this.yaw, this.pitch, TeleportCause.SPECTATE); // CraftBukkit + // Paper end } + @Override +@@ -0,0 +0,0 @@ public class EntityPlayer extends EntityHuman implements ICrafting { + if (this.portalCooldown > 0 && !this.worldChangeInvuln) { + --this.portalCooldown; + } +- + } + @Override -- \ No newline at end of file diff --git a/Spigot-Server-Patches/Catch-JsonParseException-in-Entity-and-TE-names.patch b/Spigot-Server-Patches/Catch-JsonParseException-in-Entity-and-TE-names.patch index 23d3202e8a..6bbd27cf26 100644 --- a/Spigot-Server-Patches/Catch-JsonParseException-in-Entity-and-TE-names.patch +++ b/Spigot-Server-Patches/Catch-JsonParseException-in-Entity-and-TE-names.patch @@ -13,24 +13,24 @@ Shulkers) may need to be changed in order for it to re-save properly No more crashing though. diff --git a/src/main/java/net/minecraft/server/CommandBlockListenerAbstract.java b/src/main/java/net/minecraft/server/CommandBlockListenerAbstract.java -index 6d85cd508e..23a69ba571 100644 +index e6bf34ab0..c447cf1ae 100644 --- a/src/main/java/net/minecraft/server/CommandBlockListenerAbstract.java +++ b/src/main/java/net/minecraft/server/CommandBlockListenerAbstract.java @@ -0,0 +0,0 @@ public abstract class CommandBlockListenerAbstract implements ICommandListener { this.command = nbttagcompound.getString("Command"); this.successCount = nbttagcompound.getInt("SuccessCount"); if (nbttagcompound.hasKeyOfType("CustomName", 8)) { -- this.customName = IChatBaseComponent.ChatSerializer.a(nbttagcompound.getString("CustomName")); -+ this.customName = MCUtil.getBaseComponentFromNbt("CustomName", nbttagcompound); // Paper - Catch ParseException +- this.setName(IChatBaseComponent.ChatSerializer.a(nbttagcompound.getString("CustomName"))); ++ this.setName(MCUtil.getBaseComponentFromNbt("CustomName", nbttagcompound)); // Paper - Catch ParseException } if (nbttagcompound.hasKeyOfType("TrackOutput", 1)) { diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java -index f9b097fd6f..4833a9652a 100644 +index b03316bc8..5d31068d7 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 INamableTileEntity, ICommandListener, Ke - this.setPosition(this.locX, this.locY, this.locZ); + this.Z(); this.setYawPitch(this.yaw, this.pitch); if (nbttagcompound.hasKeyOfType("CustomName", 8)) { - this.setCustomName(IChatBaseComponent.ChatSerializer.a(nbttagcompound.getString("CustomName"))); @@ -39,7 +39,7 @@ index f9b097fd6f..4833a9652a 100644 this.setCustomNameVisible(nbttagcompound.getBoolean("CustomNameVisible")); diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java -index 6d278a0da5..ec3732193f 100644 +index 6d278a0da..ec3732193 100644 --- a/src/main/java/net/minecraft/server/MCUtil.java +++ b/src/main/java/net/minecraft/server/MCUtil.java @@ -0,0 +0,0 @@ public final class MCUtil { @@ -63,7 +63,7 @@ index 6d278a0da5..ec3732193f 100644 + } } diff --git a/src/main/java/net/minecraft/server/TileEntityBanner.java b/src/main/java/net/minecraft/server/TileEntityBanner.java -index 560330ba05..bfc58a7754 100644 +index 21a2415fb..7a911c5a5 100644 --- a/src/main/java/net/minecraft/server/TileEntityBanner.java +++ b/src/main/java/net/minecraft/server/TileEntityBanner.java @@ -0,0 +0,0 @@ public class TileEntityBanner extends TileEntity implements INamableTileEntity { @@ -76,7 +76,7 @@ index 560330ba05..bfc58a7754 100644 if (this.hasWorld()) { diff --git a/src/main/java/net/minecraft/server/TileEntityContainer.java b/src/main/java/net/minecraft/server/TileEntityContainer.java -index 473ec2cbde..ab6b86e4e9 100644 +index 473ec2cbd..ab6b86e4e 100644 --- a/src/main/java/net/minecraft/server/TileEntityContainer.java +++ b/src/main/java/net/minecraft/server/TileEntityContainer.java @@ -0,0 +0,0 @@ public abstract class TileEntityContainer extends TileEntity implements IInvento diff --git a/Spigot-Server-Patches/Catch-exceptions-from-dispenser-entity-spawns.patch b/Spigot-Server-Patches/Catch-exceptions-from-dispenser-entity-spawns.patch index c0d76ec572..06dc7e6afe 100644 --- a/Spigot-Server-Patches/Catch-exceptions-from-dispenser-entity-spawns.patch +++ b/Spigot-Server-Patches/Catch-exceptions-from-dispenser-entity-spawns.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Catch exceptions from dispenser entity spawns diff --git a/src/main/java/net/minecraft/server/IDispenseBehavior.java b/src/main/java/net/minecraft/server/IDispenseBehavior.java -index 976c72208..fe3d9d5fa 100644 +index 8c4a8b4ba..83877d672 100644 --- a/src/main/java/net/minecraft/server/IDispenseBehavior.java +++ b/src/main/java/net/minecraft/server/IDispenseBehavior.java @@ -0,0 +0,0 @@ public interface IDispenseBehavior { diff --git a/Spigot-Server-Patches/Check-Drowned-for-Villager-Aggression-Config.patch b/Spigot-Server-Patches/Check-Drowned-for-Villager-Aggression-Config.patch index be40219a01..9d28dd9d8d 100644 --- a/Spigot-Server-Patches/Check-Drowned-for-Villager-Aggression-Config.patch +++ b/Spigot-Server-Patches/Check-Drowned-for-Villager-Aggression-Config.patch @@ -5,16 +5,16 @@ Subject: [PATCH] Check Drowned for Villager Aggression Config diff --git a/src/main/java/net/minecraft/server/EntityDrowned.java b/src/main/java/net/minecraft/server/EntityDrowned.java -index fd10cf318a..6f0094e6dc 100644 +index 255b3d0cd..3a1928b22 100644 --- a/src/main/java/net/minecraft/server/EntityDrowned.java +++ b/src/main/java/net/minecraft/server/EntityDrowned.java @@ -0,0 +0,0 @@ public class EntityDrowned extends EntityZombie implements IRangedEntity { this.goalSelector.a(7, new PathfinderGoalRandomStroll(this, 1.0D)); this.targetSelector.a(1, (new PathfinderGoalHurtByTarget(this, new Class[]{EntityDrowned.class})).a(EntityPigZombie.class)); - this.targetSelector.a(2, new PathfinderGoalNearestAttackableTarget<>(this, EntityHuman.class, 10, true, false, this::h)); + this.targetSelector.a(2, new PathfinderGoalNearestAttackableTarget<>(this, EntityHuman.class, 10, true, false, this::i)); - this.targetSelector.a(3, new PathfinderGoalNearestAttackableTarget<>(this, EntityVillagerAbstract.class, false)); + if ( world.spigotConfig.zombieAggressiveTowardsVillager ) this.targetSelector.a(3, new PathfinderGoalNearestAttackableTarget<>(this, EntityVillagerAbstract.class, false)); // Paper this.targetSelector.a(3, new PathfinderGoalNearestAttackableTarget<>(this, EntityIronGolem.class, true)); - this.targetSelector.a(5, new PathfinderGoalNearestAttackableTarget<>(this, EntityTurtle.class, 10, true, false, EntityTurtle.bz)); + this.targetSelector.a(5, new PathfinderGoalNearestAttackableTarget<>(this, EntityTurtle.class, 10, true, false, EntityTurtle.bw)); } -- \ No newline at end of file diff --git a/Spigot-Server-Patches/Chunk-debug-command.patch b/Spigot-Server-Patches/Chunk-debug-command.patch index 51b69855b4..205d260acf 100644 --- a/Spigot-Server-Patches/Chunk-debug-command.patch +++ b/Spigot-Server-Patches/Chunk-debug-command.patch @@ -185,7 +185,7 @@ index d704fc79c..09efbf725 100644 * Ported from MinecraftForge - author: LexManos - License: LGPLv2.1 */ diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java -index 0ce2d6d0f..162700313 100644 +index 038b7b68d..9b2bafdbd 100644 --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java @@ -0,0 +0,0 @@ import org.apache.logging.log4j.Logger; @@ -399,7 +399,7 @@ index ec3732193..23d1935dd 100644 + } } diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java -index 7407c1a56..14a176d61 100644 +index 3c6fe0596..43d9a5634 100644 --- a/src/main/java/net/minecraft/server/PlayerChunk.java +++ b/src/main/java/net/minecraft/server/PlayerChunk.java @@ -0,0 +0,0 @@ public class PlayerChunk { @@ -412,7 +412,7 @@ index 7407c1a56..14a176d61 100644 private int dirtyCount; private int r; diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java -index 67d011745..493770bf6 100644 +index 5fd001170..827831aab 100644 --- a/src/main/java/net/minecraft/server/PlayerChunkMap.java +++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java @@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { @@ -430,11 +430,11 @@ index 67d011745..493770bf6 100644 public final WorldLoadListener worldLoadListener; - public final PlayerChunkMap.a chunkDistanceManager; + public final PlayerChunkMap.a chunkDistanceManager; public final PlayerChunkMap.a getChunkMapDistanceManager() { return this.chunkDistanceManager; } // Paper - OBFHELPER - private final AtomicInteger v; + private final AtomicInteger u; private final DefinedStructureManager definedStructureManager; - private final File x; + private final File w; diff --git a/src/main/java/net/minecraft/server/Ticket.java b/src/main/java/net/minecraft/server/Ticket.java -index 0430ca535..badbe6c19 100644 +index 77bb6b092..7a8397815 100644 --- a/src/main/java/net/minecraft/server/Ticket.java +++ b/src/main/java/net/minecraft/server/Ticket.java @@ -0,0 +0,0 @@ public final class Ticket implements Comparable> { @@ -442,11 +442,11 @@ index 0430ca535..badbe6c19 100644 private final TicketType a; private final int b; - public final T identifier; -- private final long d; +- private long d; + public final T identifier; public final T getObjectReason() { return this.identifier; } // Paper - OBFHELPER -+ private final long d; public final long getCreationTick() { return this.d; } // Paper - OBFHELPER ++ private long d; public final long getCreationTick() { return this.d; } // Paper - OBFHELPER - protected Ticket(TicketType tickettype, int i, T t0, long j) { + protected Ticket(TicketType tickettype, int i, T t0) { this.a = tickettype; @@ -0,0 +0,0 @@ public final class Ticket implements Comparable> { return this.a; diff --git a/Spigot-Server-Patches/ChunkMapDistance-CME.patch b/Spigot-Server-Patches/ChunkMapDistance-CME.patch index fd1fc97605..f5a217fb57 100644 --- a/Spigot-Server-Patches/ChunkMapDistance-CME.patch +++ b/Spigot-Server-Patches/ChunkMapDistance-CME.patch @@ -5,7 +5,7 @@ Subject: [PATCH] ChunkMapDistance CME diff --git a/src/main/java/net/minecraft/server/ChunkMapDistance.java b/src/main/java/net/minecraft/server/ChunkMapDistance.java -index 101eb58ac..63a688725 100644 +index a35f0e18b..8c1945687 100644 --- a/src/main/java/net/minecraft/server/ChunkMapDistance.java +++ b/src/main/java/net/minecraft/server/ChunkMapDistance.java @@ -0,0 +0,0 @@ public abstract class ChunkMapDistance { diff --git a/Spigot-Server-Patches/Configurable-Keep-Spawn-Loaded-range-per-world.patch b/Spigot-Server-Patches/Configurable-Keep-Spawn-Loaded-range-per-world.patch index 7ac1f88a1c..032f9fb8db 100644 --- a/Spigot-Server-Patches/Configurable-Keep-Spawn-Loaded-range-per-world.patch +++ b/Spigot-Server-Patches/Configurable-Keep-Spawn-Loaded-range-per-world.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Configurable Keep Spawn Loaded range per world This lets you disable it for some worlds and lower it for others. diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index acc84eec1..9ff115294 100644 +index 14fa5fdb7..332f20ce8 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -0,0 +0,0 @@ public class PaperWorldConfig { @@ -21,7 +21,7 @@ index acc84eec1..9ff115294 100644 + } } diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index ee0200170..a6f112bd0 100644 +index 443e727dc..0a7648381 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -0,0 +0,0 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant list = worldchunkmanager.a(); - Random random = new Random(this.getSeed()); -- BlockPosition blockposition = worldchunkmanager.a(0, 0, 256, list, random); +- BlockPosition blockposition = worldchunkmanager.a(0, this.getSeaLevel(), 0, 256, list, random); - ChunkCoordIntPair chunkcoordintpair = blockposition == null ? new ChunkCoordIntPair(0, 0) : new ChunkCoordIntPair(blockposition); - ++// Paper start - moved down ++// WorldChunkManager worldchunkmanager = this.getChunkProvider().getChunkGenerator().getWorldChunkManager(); ++// List list = worldchunkmanager.a(); ++// Random random = new Random(this.getSeed()); ++// BlockPosition blockposition = worldchunkmanager.a(0, this.getSeaLevel(), 0, 256, list, random); ++// ChunkCoordIntPair chunkcoordintpair = blockposition == null ? new ChunkCoordIntPair(0, 0) : new ChunkCoordIntPair(blockposition); ++// Paper end // CraftBukkit start if (this.generator != null) { Random rand = new Random(this.getSeed()); @@ -31,10 +38,9 @@ index 5df9b0ffda..1330956655 100644 + WorldChunkManager worldchunkmanager = this.chunkProvider.getChunkGenerator().getWorldChunkManager(); + List list = worldchunkmanager.a(); + Random random = new Random(this.getSeed()); -+ BlockPosition blockposition = worldchunkmanager.a(0, 0, 256, list, random); ++ BlockPosition blockposition = worldchunkmanager.a(0, this.getSeaLevel(), 0, 256, list, random); + ChunkCoordIntPair chunkcoordintpair = blockposition == null ? new ChunkCoordIntPair(0, 0) : new ChunkCoordIntPair(blockposition); + // Paper end -+ if (blockposition == null) { WorldServer.LOGGER.warn("Unable to find spawn biome"); } diff --git a/Spigot-Server-Patches/Don-t-allow-digging-into-unloaded-chunks.patch b/Spigot-Server-Patches/Don-t-allow-digging-into-unloaded-chunks.patch index ca37da2182..03e4dbbcb9 100644 --- a/Spigot-Server-Patches/Don-t-allow-digging-into-unloaded-chunks.patch +++ b/Spigot-Server-Patches/Don-t-allow-digging-into-unloaded-chunks.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Don't allow digging into unloaded chunks diff --git a/src/main/java/net/minecraft/server/PlayerConnection.java b/src/main/java/net/minecraft/server/PlayerConnection.java -index 9eb6982508..45c2777552 100644 +index ccad0a601..d263897da 100644 --- a/src/main/java/net/minecraft/server/PlayerConnection.java +++ b/src/main/java/net/minecraft/server/PlayerConnection.java @@ -0,0 +0,0 @@ public class PlayerConnection implements PacketListenerPlayIn { diff --git a/Spigot-Server-Patches/Don-t-check-ConvertSigns-boolean-every-sign-save.patch b/Spigot-Server-Patches/Don-t-check-ConvertSigns-boolean-every-sign-save.patch index 19d625ce52..ec98657c9a 100644 --- a/Spigot-Server-Patches/Don-t-check-ConvertSigns-boolean-every-sign-save.patch +++ b/Spigot-Server-Patches/Don-t-check-ConvertSigns-boolean-every-sign-save.patch @@ -7,7 +7,7 @@ property lookups arent super cheap. they synchronize, validate and check security managers. diff --git a/src/main/java/net/minecraft/server/TileEntitySign.java b/src/main/java/net/minecraft/server/TileEntitySign.java -index c7aa3b350a..0a8d9b52dd 100644 +index f051f2d3c..5eb86c434 100644 --- a/src/main/java/net/minecraft/server/TileEntitySign.java +++ b/src/main/java/net/minecraft/server/TileEntitySign.java @@ -0,0 +0,0 @@ public class TileEntitySign extends TileEntity implements ICommandListener { // diff --git a/Spigot-Server-Patches/Don-t-sleep-after-profile-lookups-if-not-needed.patch b/Spigot-Server-Patches/Don-t-sleep-after-profile-lookups-if-not-needed.patch index 3c32659693..83275ab228 100644 --- a/Spigot-Server-Patches/Don-t-sleep-after-profile-lookups-if-not-needed.patch +++ b/Spigot-Server-Patches/Don-t-sleep-after-profile-lookups-if-not-needed.patch @@ -7,7 +7,7 @@ Mojang was sleeping even if we had no more requests to go after the current one finished, resulting in 100ms lost per profile lookup diff --git a/src/main/java/com/mojang/authlib/yggdrasil/YggdrasilGameProfileRepository.java b/src/main/java/com/mojang/authlib/yggdrasil/YggdrasilGameProfileRepository.java -index 71e48e87b4..23f1447cfc 100644 +index 71e48e87b..23f1447cf 100644 --- a/src/main/java/com/mojang/authlib/yggdrasil/YggdrasilGameProfileRepository.java +++ b/src/main/java/com/mojang/authlib/yggdrasil/YggdrasilGameProfileRepository.java @@ -0,0 +0,0 @@ public class YggdrasilGameProfileRepository implements GameProfileRepository { diff --git a/Spigot-Server-Patches/Duplicate-UUID-Resolve-Option.patch b/Spigot-Server-Patches/Duplicate-UUID-Resolve-Option.patch index 13a3dca812..00676d89dc 100644 --- a/Spigot-Server-Patches/Duplicate-UUID-Resolve-Option.patch +++ b/Spigot-Server-Patches/Duplicate-UUID-Resolve-Option.patch @@ -33,7 +33,7 @@ But for those who are ok with leaving this inconsistent behavior, you may use WA It is recommended you regenerate the entities, as these were legit entities, and deserve your love. diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index cf9470057..2cf5ef8b7 100644 +index 3b1289099..14fa5fdb7 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -0,0 +0,0 @@ public class PaperWorldConfig { @@ -81,7 +81,7 @@ index cf9470057..2cf5ef8b7 100644 + } } diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java -index 09e010e67..ee8f80174 100644 +index c7c600b80..64c327669 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 implements IChunkAccess { @@ -91,9 +91,9 @@ index 09e010e67..ee8f80174 100644 + return; // Paper } - int k = MathHelper.floor(entity.locY / 16.0D); + int k = MathHelper.floor(entity.locY() / 16.0D); diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java -index f87514a20..55c73ffca 100644 +index 96b0917a3..f0d1a5b14 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 INamableTileEntity, ICommandListener, Ke @@ -103,9 +103,9 @@ index f87514a20..55c73ffca 100644 + public void setUUID(UUID uuid) { a(uuid); } // Paper - OBFHELPER public void a(UUID uuid) { this.uniqueID = uuid; - this.ap = this.uniqueID.toString(); + this.am = this.uniqueID.toString(); diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java -index db938da25..82b7d328a 100644 +index a2b09597f..5fd001170 100644 --- a/src/main/java/net/minecraft/server/PlayerChunkMap.java +++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java @@ -0,0 +0,0 @@ @@ -121,6 +121,7 @@ index db938da25..82b7d328a 100644 import java.io.Writer; import java.util.ArrayList; +import java.util.HashMap; // Paper + import java.util.Collection; import java.util.Iterator; import java.util.List; +import java.util.Map; // Paper @@ -209,15 +210,15 @@ index db938da25..82b7d328a 100644 if (list != null) { diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java -index cc2c13990..29bb795f7 100644 +index 0138738b3..d59eee27a 100644 --- a/src/main/java/net/minecraft/server/WorldServer.java +++ b/src/main/java/net/minecraft/server/WorldServer.java @@ -0,0 +0,0 @@ package net.minecraft.server; - - import co.aikar.timings.TimingHistory; - import co.aikar.timings.Timings; + import com.google.common.annotations.VisibleForTesting; + import co.aikar.timings.TimingHistory; // Paper + import co.aikar.timings.Timings; // Paper + -+import com.destroystokyo.paper.PaperWorldConfig; ++import com.destroystokyo.paper.PaperWorldConfig; // Paper import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Queues; diff --git a/Spigot-Server-Patches/Entity-getEntitySpawnReason.patch b/Spigot-Server-Patches/Entity-getEntitySpawnReason.patch index 33f8a35ba8..ec9f30a87c 100644 --- a/Spigot-Server-Patches/Entity-getEntitySpawnReason.patch +++ b/Spigot-Server-Patches/Entity-getEntitySpawnReason.patch @@ -10,7 +10,7 @@ persistenting Living Entity, SPAWNER for spawners, or DEFAULT since data was not stored. diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java -index 046e7e031c..f87514a200 100644 +index 2e8aedf5f..96b0917a3 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 INamableTileEntity, ICommandListener, Ke @@ -19,8 +19,8 @@ index 046e7e031c..f87514a200 100644 List entitySlice = null; + public org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason; // Paper end - static boolean isLevelAtLeast(NBTTagCompound tag, int level) { - return tag.hasKey("Bukkit.updateLevel") && tag.getInt("Bukkit.updateLevel") >= level; + + public com.destroystokyo.paper.loottable.PaperLootableInventoryData lootableData; // Paper @@ -0,0 +0,0 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke if (this.origin != null) { nbttagcompound.set("Paper.Origin", this.createList(origin.getX(), origin.getY(), origin.getZ())); @@ -59,7 +59,7 @@ index 046e7e031c..f87514a200 100644 } catch (Throwable throwable) { diff --git a/src/main/java/net/minecraft/server/PlayerList.java b/src/main/java/net/minecraft/server/PlayerList.java -index 65df0e7c8c..abddc8895e 100644 +index 9d715d891..49437f212 100644 --- a/src/main/java/net/minecraft/server/PlayerList.java +++ b/src/main/java/net/minecraft/server/PlayerList.java @@ -0,0 +0,0 @@ public abstract class PlayerList { @@ -72,7 +72,7 @@ index 65df0e7c8c..abddc8895e 100644 }); diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java -index dbd357f1a3..628ad8b839 100644 +index ecfcecfdc..63e7f2bce 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 { @@ -84,7 +84,7 @@ index dbd357f1a3..628ad8b839 100644 if (entity.valid) { MinecraftServer.LOGGER.error("Attempted Double World add on " + entity, new Throwable()); diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java -index 517e422180..31db42e9fb 100644 +index 915ccb180..dfa15372b 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java @@ -0,0 +0,0 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity { diff --git a/Spigot-Server-Patches/Expose-the-internal-current-tick.patch b/Spigot-Server-Patches/Expose-the-internal-current-tick.patch index 40b63f62d9..dfcefbf718 100644 --- a/Spigot-Server-Patches/Expose-the-internal-current-tick.patch +++ b/Spigot-Server-Patches/Expose-the-internal-current-tick.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Expose the internal current tick diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index 7a8ab7d40..105d31906 100644 +index 18b76ed0f..f3da3a82d 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -0,0 +0,0 @@ public final class CraftServer implements Server { diff --git a/Spigot-Server-Patches/Fire-BlockPistonRetractEvent-for-all-empty-pistons.patch b/Spigot-Server-Patches/Fire-BlockPistonRetractEvent-for-all-empty-pistons.patch index 1902f655f8..446c61ff47 100644 --- a/Spigot-Server-Patches/Fire-BlockPistonRetractEvent-for-all-empty-pistons.patch +++ b/Spigot-Server-Patches/Fire-BlockPistonRetractEvent-for-all-empty-pistons.patch @@ -24,7 +24,7 @@ Instead we opt to remove the check entirely so that the event fires for all piston types. diff --git a/src/main/java/net/minecraft/server/BlockPiston.java b/src/main/java/net/minecraft/server/BlockPiston.java -index e883c7ac98..de804348f3 100644 +index 1170a2810..b29525c40 100644 --- a/src/main/java/net/minecraft/server/BlockPiston.java +++ b/src/main/java/net/minecraft/server/BlockPiston.java @@ -0,0 +0,0 @@ public class BlockPiston extends BlockDirectional { @@ -44,5 +44,5 @@ index e883c7ac98..de804348f3 100644 + //} // Paper // PAIL: checkME - what happened to setTypeAndData? // CraftBukkit end - world.playBlockAction(blockposition, this, b0, enumdirection.a()); + world.playBlockAction(blockposition, this, b0, enumdirection.b()); -- \ No newline at end of file diff --git a/Spigot-Server-Patches/Fire-event-on-GS4-query.patch b/Spigot-Server-Patches/Fire-event-on-GS4-query.patch index abf286d298..b63d751c3d 100644 --- a/Spigot-Server-Patches/Fire-event-on-GS4-query.patch +++ b/Spigot-Server-Patches/Fire-event-on-GS4-query.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Fire event on GS4 query diff --git a/src/main/java/net/minecraft/server/RemoteConnectionThread.java b/src/main/java/net/minecraft/server/RemoteConnectionThread.java -index 66bfbcf02b..d821ef9a75 100644 +index 66bfbcf02..d821ef9a7 100644 --- a/src/main/java/net/minecraft/server/RemoteConnectionThread.java +++ b/src/main/java/net/minecraft/server/RemoteConnectionThread.java @@ -0,0 +0,0 @@ public abstract class RemoteConnectionThread implements Runnable { @@ -26,7 +26,7 @@ index 66bfbcf02b..d821ef9a75 100644 return this.b.getPlayerCount(); } diff --git a/src/main/java/net/minecraft/server/RemoteStatusListener.java b/src/main/java/net/minecraft/server/RemoteStatusListener.java -index a556a71334..67e94a6f07 100644 +index 16bb89af0..1b084f211 100644 --- a/src/main/java/net/minecraft/server/RemoteStatusListener.java +++ b/src/main/java/net/minecraft/server/RemoteStatusListener.java @@ -0,0 +0,0 @@ public class RemoteStatusListener extends RemoteConnectionThread { @@ -184,7 +184,7 @@ index a556a71334..67e94a6f07 100644 } } diff --git a/src/main/java/net/minecraft/server/RemoteStatusReply.java b/src/main/java/net/minecraft/server/RemoteStatusReply.java -index 848b5c3f0e..73efea7e13 100644 +index 848b5c3f0..73efea7e1 100644 --- a/src/main/java/net/minecraft/server/RemoteStatusReply.java +++ b/src/main/java/net/minecraft/server/RemoteStatusReply.java @@ -0,0 +0,0 @@ public class RemoteStatusReply { diff --git a/Spigot-Server-Patches/Fix-AssertionError-when-player-hand-set-to-empty-typ.patch b/Spigot-Server-Patches/Fix-AssertionError-when-player-hand-set-to-empty-typ.patch index e64b01e265..29495c97cc 100644 --- a/Spigot-Server-Patches/Fix-AssertionError-when-player-hand-set-to-empty-typ.patch +++ b/Spigot-Server-Patches/Fix-AssertionError-when-player-hand-set-to-empty-typ.patch @@ -7,7 +7,7 @@ Fixes an AssertionError when setting the player's item in hand to null or a new Fixes GH-2718 diff --git a/src/main/java/net/minecraft/server/EntityLiving.java b/src/main/java/net/minecraft/server/EntityLiving.java -index f675ad2f5..addbafe66 100644 +index d04ea03bc..5431f8a8d 100644 --- a/src/main/java/net/minecraft/server/EntityLiving.java +++ b/src/main/java/net/minecraft/server/EntityLiving.java @@ -0,0 +0,0 @@ public abstract class EntityLiving extends Entity { @@ -19,7 +19,7 @@ index f675ad2f5..addbafe66 100644 if (enumhand == EnumHand.MAIN_HAND) { return this.getEquipment(EnumItemSlot.MAINHAND); diff --git a/src/main/java/net/minecraft/server/PlayerConnection.java b/src/main/java/net/minecraft/server/PlayerConnection.java -index ae17cdf23..c9872cbd0 100644 +index 8c54022e0..c559a7b95 100644 --- a/src/main/java/net/minecraft/server/PlayerConnection.java +++ b/src/main/java/net/minecraft/server/PlayerConnection.java @@ -0,0 +0,0 @@ public class PlayerConnection implements PacketListenerPlayIn { diff --git a/Spigot-Server-Patches/Fix-CB-call-to-changed-postToMainThread-method.patch b/Spigot-Server-Patches/Fix-CB-call-to-changed-postToMainThread-method.patch index eee46ba32e..feb7ff824f 100644 --- a/Spigot-Server-Patches/Fix-CB-call-to-changed-postToMainThread-method.patch +++ b/Spigot-Server-Patches/Fix-CB-call-to-changed-postToMainThread-method.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Fix CB call to changed postToMainThread method diff --git a/src/main/java/net/minecraft/server/PlayerConnection.java b/src/main/java/net/minecraft/server/PlayerConnection.java -index 37b97438c..4a16d6c14 100644 +index 17eeecaf4..8c54022e0 100644 --- a/src/main/java/net/minecraft/server/PlayerConnection.java +++ b/src/main/java/net/minecraft/server/PlayerConnection.java @@ -0,0 +0,0 @@ public class PlayerConnection implements PacketListenerPlayIn { diff --git a/Spigot-Server-Patches/Fix-CraftServer-isPrimaryThread-and-MinecraftServer-.patch b/Spigot-Server-Patches/Fix-CraftServer-isPrimaryThread-and-MinecraftServer-.patch index c2c234bdb6..075a0b6d1a 100644 --- a/Spigot-Server-Patches/Fix-CraftServer-isPrimaryThread-and-MinecraftServer-.patch +++ b/Spigot-Server-Patches/Fix-CraftServer-isPrimaryThread-and-MinecraftServer-.patch @@ -16,7 +16,7 @@ handling that should have been handled synchronously will be handled synchronously when the server gets shut down. diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 24dccfb7a1..ee02001700 100644 +index c023848c7..443e727dc 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -0,0 +0,0 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant -Date: Sat, 15 Jun 2019 08:54:33 -0700 -Subject: [PATCH] Fix World#isChunkGenerated calls - -Optimize World#loadChunk() too -This patch also adds a chunk status cache on region files (note that -its only purpose is to cache the status on DISK) - -diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java -index 8689e0f9f..56761afdf 100644 ---- a/src/main/java/net/minecraft/server/ChunkProviderServer.java -+++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java -@@ -0,0 +0,0 @@ public class ChunkProviderServer extends IChunkProvider { - private final WorldServer world; - private final Thread serverThread; - private final LightEngineThreaded lightEngine; -- private final ChunkProviderServer.a serverThreadQueue; -+ public final ChunkProviderServer.a serverThreadQueue; // Paper private -> public - public final PlayerChunkMap playerChunkMap; - private final WorldPersistentData worldPersistentData; - private long lastTickTime; -@@ -0,0 +0,0 @@ public class ChunkProviderServer extends IChunkProvider { - - return playerChunk.getFullChunk(); - } -+ -+ @Nullable -+ public IChunkAccess getChunkAtImmediately(int x, int z) { -+ long k = ChunkCoordIntPair.pair(x, z); -+ -+ // Note: Bypass cache to make this MT-Safe -+ -+ PlayerChunk playerChunk = this.getChunk(k); -+ if (playerChunk == null) { -+ return null; -+ } -+ -+ return playerChunk.getAvailableChunkNow(); -+ -+ } - // Paper end - - @Nullable -diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java -index e778c2e85..73f93e494 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 { - return nbttagcompound; - } - -+ // Paper start -+ public static ChunkStatus getStatus(NBTTagCompound compound) { -+ if (compound == null) { -+ return null; -+ } -+ -+ // Note: Copied from below -+ return ChunkStatus.getStatus(compound.getCompound("Level").getString("Status")); -+ } -+ // Paper end -+ - public static ChunkStatus.Type a(@Nullable NBTTagCompound nbttagcompound) { - if (nbttagcompound != null) { - ChunkStatus chunkstatus = ChunkStatus.a(nbttagcompound.getCompound("Level").getString("Status")); -diff --git a/src/main/java/net/minecraft/server/ChunkStatus.java b/src/main/java/net/minecraft/server/ChunkStatus.java -index dd1822d6f..e324989b4 100644 ---- a/src/main/java/net/minecraft/server/ChunkStatus.java -+++ b/src/main/java/net/minecraft/server/ChunkStatus.java -@@ -0,0 +0,0 @@ public class ChunkStatus { - return this.s; - } - -+ public ChunkStatus getPreviousStatus() { return this.e(); } // Paper - OBFHELPER - public ChunkStatus e() { - return this.u; - } -@@ -0,0 +0,0 @@ public class ChunkStatus { - return this.y; - } - -+ // Paper start -+ public static ChunkStatus getStatus(String name) { -+ try { -+ // We need this otherwise we return EMPTY for invalid names -+ MinecraftKey key = new MinecraftKey(name); -+ return IRegistry.CHUNK_STATUS.getOptional(key).orElse(null); -+ } catch (Exception ex) { -+ return null; // invalid name -+ } -+ } -+ // Paper end - public static ChunkStatus a(String s) { - return (ChunkStatus) IRegistry.CHUNK_STATUS.get(MinecraftKey.a(s)); - } -diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java -index 14a176d61..98590e233 100644 ---- a/src/main/java/net/minecraft/server/PlayerChunk.java -+++ b/src/main/java/net/minecraft/server/PlayerChunk.java -@@ -0,0 +0,0 @@ public class PlayerChunk { - Either either = (Either) statusFuture.getNow(null); - return either == null ? null : (Chunk) either.left().orElse(null); - } -+ -+ public IChunkAccess getAvailableChunkNow() { -+ // TODO can we just getStatusFuture(EMPTY)? -+ for (ChunkStatus curr = ChunkStatus.FULL, next = curr.getPreviousStatus(); curr != next; curr = next, next = next.getPreviousStatus()) { -+ CompletableFuture> future = this.getStatusFutureUnchecked(curr); -+ Either either = future.getNow(null); -+ if (either == null || !either.left().isPresent()) { -+ continue; -+ } -+ return either.left().get(); -+ } -+ return null; -+ } - // Paper end - - public CompletableFuture> getStatusFutureUnchecked(ChunkStatus chunkstatus) { -diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java -index 6f1e48ba4..eb49e9021 100644 ---- a/src/main/java/net/minecraft/server/PlayerChunkMap.java -+++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java -@@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { - } - - @Nullable -- private NBTTagCompound readChunkData(ChunkCoordIntPair chunkcoordintpair) throws IOException { -+ public NBTTagCompound readChunkData(ChunkCoordIntPair chunkcoordintpair) throws IOException { // Paper - private -> public - NBTTagCompound nbttagcompound = this.read(chunkcoordintpair); - -- return nbttagcompound == null ? null : this.getChunkData(this.world.getWorldProvider().getDimensionManager(), this.m, nbttagcompound, chunkcoordintpair, world); // CraftBukkit -+ // Paper start - Cache chunk status on disk -+ if (nbttagcompound == null) { -+ return null; -+ } -+ -+ nbttagcompound = this.getChunkData(this.world.getWorldProvider().getDimensionManager(), this.m, nbttagcompound, chunkcoordintpair, world); // CraftBukkit -+ if (nbttagcompound == null) { -+ return null; -+ } -+ -+ this.updateChunkStatusOnDisk(chunkcoordintpair, nbttagcompound); -+ -+ return nbttagcompound; -+ // Paper end -+ } -+ -+ // Paper start - chunk status cache "api" -+ public ChunkStatus getChunkStatusOnDiskIfCached(ChunkCoordIntPair chunkPos) { -+ RegionFile regionFile = this.getRegionFileIfLoaded(chunkPos); -+ -+ return regionFile == null ? null : regionFile.getStatusIfCached(chunkPos.x, chunkPos.z); -+ } -+ -+ public ChunkStatus getChunkStatusOnDisk(ChunkCoordIntPair chunkPos) throws IOException { -+ RegionFile regionFile = this.getRegionFile(chunkPos, false); -+ -+ if (!regionFile.chunkExists(chunkPos)) { -+ return null; -+ } -+ -+ ChunkStatus status = regionFile.getStatusIfCached(chunkPos.x, chunkPos.z); -+ -+ if (status != null) { -+ return status; -+ } -+ -+ this.readChunkData(chunkPos); -+ -+ return regionFile.getStatusIfCached(chunkPos.x, chunkPos.z); -+ } -+ -+ public void updateChunkStatusOnDisk(ChunkCoordIntPair chunkPos, @Nullable NBTTagCompound compound) throws IOException { -+ RegionFile regionFile = this.getRegionFile(chunkPos, false); -+ -+ regionFile.setStatus(chunkPos.x, chunkPos.z, ChunkRegionLoader.getStatus(compound)); -+ } -+ -+ public IChunkAccess getUnloadingChunk(int chunkX, int chunkZ) { -+ PlayerChunk chunkHolder = this.pendingUnload.get(ChunkCoordIntPair.pair(chunkX, chunkZ)); -+ return chunkHolder == null ? null : chunkHolder.getAvailableChunkNow(); - } -+ // Paper end - - boolean isOutsideOfRange(ChunkCoordIntPair chunkcoordintpair) { - // Spigot start -diff --git a/src/main/java/net/minecraft/server/RegionFile.java b/src/main/java/net/minecraft/server/RegionFile.java -index ccc3d6c7a..b487e8060 100644 ---- a/src/main/java/net/minecraft/server/RegionFile.java -+++ b/src/main/java/net/minecraft/server/RegionFile.java -@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable { - private final int[] d = new int[1024]; private final int[] timestamps = d; // Paper - OBFHELPER - private final List e; // PAIL freeSectors - -+ // Paper start - Cache chunk status -+ private final ChunkStatus[] statuses = new ChunkStatus[32 * 32]; -+ -+ private boolean closed; -+ -+ // invoked on write/read -+ public void setStatus(int x, int z, ChunkStatus status) { -+ if (this.closed) { -+ // We've used an invalid region file. -+ throw new IllegalStateException("RegionFile is closed"); -+ } -+ this.statuses[this.getChunkLocation(new ChunkCoordIntPair(x, z))] = status; -+ } -+ -+ public ChunkStatus getStatusIfCached(int x, int z) { -+ if (this.closed) { -+ // We've used an invalid region file. -+ throw new IllegalStateException("RegionFile is closed"); -+ } -+ final int location = this.getChunkLocation(new ChunkCoordIntPair(x, z)); -+ return this.statuses[location]; -+ } -+ // Paper end -+ - public RegionFile(File file) throws IOException { - this.b = new RandomAccessFile(file, "rw"); - this.file = file; // Spigot // Paper - We need this earlier -@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable { - return this.c[this.f(chunkcoordintpair)]; - } - -+ public final boolean chunkExists(ChunkCoordIntPair chunkPos) { return this.d(chunkPos); } // Paper - OBFHELPER - public boolean d(ChunkCoordIntPair chunkcoordintpair) { - return this.getOffset(chunkcoordintpair) != 0; - } -@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable { - this.c[j] = i; // Spigot - move this to after the write - } - -+ private final int getChunkLocation(ChunkCoordIntPair chunkcoordintpair) { return this.f(chunkcoordintpair); } // Paper - OBFHELPER - private int f(ChunkCoordIntPair chunkcoordintpair) { - return chunkcoordintpair.j() + chunkcoordintpair.k() * 32; - } -@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable { - } - - public void close() throws IOException { -+ this.closed = true; // Paper - this.b.close(); - } - -diff --git a/src/main/java/net/minecraft/server/RegionFileCache.java b/src/main/java/net/minecraft/server/RegionFileCache.java -index 6f34d8aea..d2b328945 100644 ---- a/src/main/java/net/minecraft/server/RegionFileCache.java -+++ b/src/main/java/net/minecraft/server/RegionFileCache.java -@@ -0,0 +0,0 @@ public abstract class RegionFileCache implements AutoCloseable { - // Paper start - } - -+ // Paper start -+ public RegionFile getRegionFileIfLoaded(ChunkCoordIntPair chunkcoordintpair) { -+ return this.cache.getAndMoveToFirst(ChunkCoordIntPair.pair(chunkcoordintpair.getRegionX(), chunkcoordintpair.getRegionZ())); -+ } -+ // Paper end -+ - public RegionFile getRegionFile(ChunkCoordIntPair chunkcoordintpair, boolean existingOnly) throws IOException { return this.a(chunkcoordintpair, existingOnly); } // Paper - OBFHELPER - private RegionFile a(ChunkCoordIntPair chunkcoordintpair, boolean existingOnly) throws IOException { // CraftBukkit - long i = ChunkCoordIntPair.pair(chunkcoordintpair.getRegionX(), chunkcoordintpair.getRegionZ()); -@@ -0,0 +0,0 @@ public abstract class RegionFileCache implements AutoCloseable { - try { - NBTCompressedStreamTools.writeNBT(nbttagcompound, out); - out.close(); -+ regionfile.setStatus(chunk.x, chunk.z, ChunkRegionLoader.getStatus(nbttagcompound)); // Paper - cache status on disk - regionfile.setOversized(chunkX, chunkZ, false); - } catch (RegionFile.ChunkTooLargeException ignored) { - printOversizedLog("ChunkTooLarge! Someone is trying to duplicate.", regionfile.file, chunkX, chunkZ); -@@ -0,0 +0,0 @@ public abstract class RegionFileCache implements AutoCloseable { - if (SIZE_THRESHOLD == OVERZEALOUS_THRESHOLD) { - resetFilterThresholds(); - } -+ regionfile.setStatus(chunk.x, chunk.z, ChunkRegionLoader.getStatus(nbttagcompound)); // Paper - cache status on disk - } catch (RegionFile.ChunkTooLargeException e) { - printOversizedLog("ChunkTooLarge even after reduction. Trying in overzealous mode.", regionfile.file, chunkX, chunkZ); - // Eek, major fail. We have retry logic, so reduce threshholds and fall back -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index e42bd2638..2227de3bf 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -0,0 +0,0 @@ import java.util.Objects; - import java.util.Random; - import java.util.Set; - import java.util.UUID; -+import java.util.concurrent.CompletableFuture; - import java.util.function.Predicate; - import java.util.stream.Collectors; - import it.unimi.dsi.fastutil.longs.Long2ObjectMap; -@@ -0,0 +0,0 @@ public class CraftWorld implements World { - - @Override - public boolean isChunkGenerated(int x, int z) { -+ // Paper start - Fix this method -+ if (!Bukkit.isPrimaryThread()) { -+ return CompletableFuture.supplyAsync(() -> { -+ return CraftWorld.this.isChunkGenerated(x, z); -+ }, world.getChunkProvider().serverThreadQueue).join(); -+ } -+ IChunkAccess chunk = world.getChunkProvider().getChunkAtImmediately(x, z); -+ if (chunk == null) { -+ chunk = world.getChunkProvider().playerChunkMap.getUnloadingChunk(x, z); -+ } -+ if (chunk != null) { -+ return chunk instanceof ProtoChunkExtension || chunk instanceof net.minecraft.server.Chunk; -+ } - try { -- return world.getChunkProvider().getChunkAtIfCachedImmediately(x, z) != null || world.getChunkProvider().playerChunkMap.chunkExists(new ChunkCoordIntPair(x, z)); // Paper -+ return world.getChunkProvider().playerChunkMap.getChunkStatusOnDisk(new ChunkCoordIntPair(x, z)) == ChunkStatus.FULL; -+ // Paper end - } catch (IOException ex) { - throw new RuntimeException(ex); - } -@@ -0,0 +0,0 @@ public class CraftWorld implements World { - @Override - public boolean loadChunk(int x, int z, boolean generate) { - org.spigotmc.AsyncCatcher.catchOp("chunk load"); // Spigot -- IChunkAccess chunk = world.getChunkProvider().getChunkAt(x, z, generate || isChunkGenerated(x, z) ? ChunkStatus.FULL : ChunkStatus.EMPTY, true); // Paper -+ // Paper start - Optimize this method -+ ChunkCoordIntPair chunkPos = new ChunkCoordIntPair(x, z); - -- // If generate = false, but the chunk already exists, we will get this back. -- if (chunk instanceof ProtoChunkExtension) { -- // We then cycle through again to get the full chunk immediately, rather than after the ticket addition -- chunk = world.getChunkProvider().getChunkAt(x, z, ChunkStatus.FULL, true); -- } -+ if (!generate) { - -- if (chunk instanceof net.minecraft.server.Chunk) { -- world.getChunkProvider().addTicket(TicketType.PLUGIN, new ChunkCoordIntPair(x, z), 1, Unit.INSTANCE); -- return true; -+ IChunkAccess immediate = world.getChunkProvider().getChunkAtImmediately(x, z); -+ if (immediate == null) { -+ immediate = world.getChunkProvider().playerChunkMap.getUnloadingChunk(x, z); -+ } -+ if (immediate != null) { -+ if (!(immediate instanceof ProtoChunkExtension) && !(immediate instanceof net.minecraft.server.Chunk)) { -+ return false; // not full status -+ } -+ world.getChunkProvider().addTicket(TicketType.PLUGIN, chunkPos, 1, Unit.INSTANCE); -+ world.getChunkAt(x, z); // make sure we're at ticket level 32 or lower -+ return true; -+ } -+ -+ net.minecraft.server.RegionFile file; -+ try { -+ file = world.getChunkProvider().playerChunkMap.getRegionFile(chunkPos, false); -+ } catch (IOException ex) { -+ throw new RuntimeException(ex); -+ } -+ -+ ChunkStatus status = file.getStatusIfCached(x, z); -+ if (!file.chunkExists(chunkPos) || (status != null && status != ChunkStatus.FULL)) { -+ return false; -+ } -+ -+ IChunkAccess chunk = world.getChunkProvider().getChunkAt(x, z, ChunkStatus.EMPTY, true); -+ if (!(chunk instanceof ProtoChunkExtension) && !(chunk instanceof net.minecraft.server.Chunk)) { -+ return false; -+ } -+ -+ // fall through to load -+ // we do this so we do not re-read the chunk data on disk - } - -- return false; -+ world.getChunkProvider().addTicket(TicketType.PLUGIN, chunkPos, 1, Unit.INSTANCE); -+ world.getChunkProvider().getChunkAt(x, z, ChunkStatus.FULL, true); -+ return true; -+ // Paper end - } - - @Override --- \ No newline at end of file diff --git a/Spigot-Server-Patches/Fix-issues-with-entity-loss-due-to-unloaded-chunks.patch b/Spigot-Server-Patches/Fix-issues-with-entity-loss-due-to-unloaded-chunks.patch index 82c1591e08..312dbe6f0f 100644 --- a/Spigot-Server-Patches/Fix-issues-with-entity-loss-due-to-unloaded-chunks.patch +++ b/Spigot-Server-Patches/Fix-issues-with-entity-loss-due-to-unloaded-chunks.patch @@ -19,15 +19,15 @@ This change ensures the chunks are always loaded when entities are added to the world, or a valid entity moves between chunks. diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java -index 628ad8b839..cc2c139904 100644 +index 63e7f2bce..0138738b3 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 { this.getChunkAt(entity.chunkX, entity.chunkZ).a(entity, entity.chunkY); } -- if (!entity.bU() && !this.isChunkLoaded(i, k)) { -+ if (!entity.valid && !entity.bU() && !this.isChunkLoaded(i, k)) { // Paper - always load chunks to register valid entities location +- if (!entity.cc() && !this.isChunkLoaded(i, k)) { ++ if (!entity.valid && !entity.cc() && !this.isChunkLoaded(i, k)) { // Paper - always load chunks to register valid entities location entity.inChunk = false; } else { this.getChunkAt(i, k).a(entity); @@ -35,8 +35,8 @@ index 628ad8b839..cc2c139904 100644 return false; } // CraftBukkit end -- IChunkAccess ichunkaccess = this.getChunkAt(MathHelper.floor(entity.locX / 16.0D), MathHelper.floor(entity.locZ / 16.0D), ChunkStatus.FULL, entity.attachedToPlayer); -+ IChunkAccess ichunkaccess = this.getChunkAt(MathHelper.floor(entity.locX / 16.0D), MathHelper.floor(entity.locZ / 16.0D), ChunkStatus.FULL, true); // Paper - always load chunks for entity adds +- IChunkAccess ichunkaccess = this.getChunkAt(MathHelper.floor(entity.locX() / 16.0D), MathHelper.floor(entity.locZ() / 16.0D), ChunkStatus.FULL, entity.attachedToPlayer); ++ IChunkAccess ichunkaccess = this.getChunkAt(MathHelper.floor(entity.locX() / 16.0D), MathHelper.floor(entity.locZ() / 16.0D), ChunkStatus.FULL, true); // Paper - always load chunks for entity adds if (!(ichunkaccess instanceof Chunk)) { return false; diff --git a/Spigot-Server-Patches/Fix-nether-portal-creation.patch b/Spigot-Server-Patches/Fix-nether-portal-creation.patch index 4ed1a93015..ea4c643d03 100644 --- a/Spigot-Server-Patches/Fix-nether-portal-creation.patch +++ b/Spigot-Server-Patches/Fix-nether-portal-creation.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Fix nether portal creation diff --git a/src/main/java/org/bukkit/craftbukkit/util/BlockStateListPopulator.java b/src/main/java/org/bukkit/craftbukkit/util/BlockStateListPopulator.java -index 88fbfc01b..f66f8b323 100644 +index 60330f753..c22dbbfbe 100644 --- a/src/main/java/org/bukkit/craftbukkit/util/BlockStateListPopulator.java +++ b/src/main/java/org/bukkit/craftbukkit/util/BlockStateListPopulator.java @@ -0,0 +0,0 @@ public class BlockStateListPopulator extends DummyGeneratorAccess { diff --git a/Spigot-Server-Patches/Fix-sign-edit-memory-leak.patch b/Spigot-Server-Patches/Fix-sign-edit-memory-leak.patch index 688bface28..132a3abfbd 100644 --- a/Spigot-Server-Patches/Fix-sign-edit-memory-leak.patch +++ b/Spigot-Server-Patches/Fix-sign-edit-memory-leak.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Fix sign edit memory leak when a player edits a sign, a reference to their Entity is never cleand up. diff --git a/src/main/java/net/minecraft/server/PlayerConnection.java b/src/main/java/net/minecraft/server/PlayerConnection.java -index 4a1565f4f7..6030766099 100644 +index c3feccbd6..c54e1c9a0 100644 --- a/src/main/java/net/minecraft/server/PlayerConnection.java +++ b/src/main/java/net/minecraft/server/PlayerConnection.java @@ -0,0 +0,0 @@ public class PlayerConnection implements PacketListenerPlayIn { @@ -19,7 +19,7 @@ index 4a1565f4f7..6030766099 100644 this.sendPacket(tileentity.getUpdatePacket()); // CraftBukkit return; diff --git a/src/main/java/net/minecraft/server/TileEntitySign.java b/src/main/java/net/minecraft/server/TileEntitySign.java -index 1b01792730..c7aa3b350a 100644 +index 4c2273497..f051f2d3c 100644 --- a/src/main/java/net/minecraft/server/TileEntitySign.java +++ b/src/main/java/net/minecraft/server/TileEntitySign.java @@ -0,0 +0,0 @@ public class TileEntitySign extends TileEntity implements ICommandListener { // @@ -34,9 +34,9 @@ index 1b01792730..c7aa3b350a 100644 } public void a(EntityHuman entityhuman) { -- this.j = entityhuman; +- this.c = entityhuman; + // Paper start -+ //this.g = entityhuman; ++ //this.c = entityhuman; + signEditor = entityhuman != null ? entityhuman.getUniqueID() : null; + // Paper end } diff --git a/Spigot-Server-Patches/Fix-some-generation-concurrency-issues.patch b/Spigot-Server-Patches/Fix-some-generation-concurrency-issues.patch index e30aced2ee..2445694835 100644 --- a/Spigot-Server-Patches/Fix-some-generation-concurrency-issues.patch +++ b/Spigot-Server-Patches/Fix-some-generation-concurrency-issues.patch @@ -5,10 +5,10 @@ Subject: [PATCH] Fix some generation concurrency issues diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java -index e3b4e30e6..10c149fae 100644 +index 10bb06489..9ad76ab32 100644 --- a/src/main/java/net/minecraft/server/World.java +++ b/src/main/java/net/minecraft/server/World.java -@@ -0,0 +0,0 @@ public abstract class World implements IIBlockAccess, GeneratorAccess, AutoClose +@@ -0,0 +0,0 @@ public abstract class World implements GeneratorAccess, AutoCloseable { private int tileTickPosition; public final Map explosionDensityCache = new HashMap<>(); // Paper - Optimize explosions public java.util.ArrayDeque redstoneUpdateInfos; // Paper - Move from Map in BlockRedstoneTorch to here @@ -33,7 +33,7 @@ index e3b4e30e6..10c149fae 100644 public CraftWorld getWorld() { return this.world; diff --git a/src/main/java/net/minecraft/server/WorldGenStronghold.java b/src/main/java/net/minecraft/server/WorldGenStronghold.java -index ddf726867..c2188ceef 100644 +index b34a0683a..e002ed53f 100644 --- a/src/main/java/net/minecraft/server/WorldGenStronghold.java +++ b/src/main/java/net/minecraft/server/WorldGenStronghold.java @@ -0,0 +0,0 @@ import javax.annotation.Nullable; @@ -42,20 +42,20 @@ index ddf726867..c2188ceef 100644 + /* // Paper start - no shared state private boolean a; - private ChunkCoordIntPair[] aS; - private final List aT = Lists.newArrayList(); - private long aU; -+ */ + private ChunkCoordIntPair[] aq; + private final List ar = Lists.newArrayList(); + private long as; ++ */ public WorldGenStronghold(Function, ? extends WorldGenFeatureEmptyConfiguration> function) { super(function); @@ -0,0 +0,0 @@ public class WorldGenStronghold extends StructureGenerator chunkgenerator, Random random, int i, int j) { + public boolean a(BiomeManager biomemanager, ChunkGenerator chunkgenerator, Random random, int i, int j, BiomeBase biomebase) { + // Paper start + /* - if (this.aU != chunkgenerator.getSeed()) { + if (this.as != chunkgenerator.getSeed()) { this.d(); } + */ @@ -71,7 +71,7 @@ index ddf726867..c2188ceef 100644 + }} + // Paper end -- ChunkCoordIntPair[] achunkcoordintpair = this.aS; +- ChunkCoordIntPair[] achunkcoordintpair = this.aq; + ChunkCoordIntPair[] achunkcoordintpair = world.strongholdCoords; // Paper int k = achunkcoordintpair.length; @@ -82,9 +82,9 @@ index ddf726867..c2188ceef 100644 private void d() { + /* // Paper this.a = false; - this.aS = null; - this.aT.clear(); -+ */ // Paper + this.aq = null; + this.ar.clear(); ++ */ // Paper } @Override @@ -101,7 +101,7 @@ index ddf726867..c2188ceef 100644 } else { + // Paper start - no shared state + /* - if (this.aU != world.getSeed()) { + if (this.as != world.getSeed()) { this.d(); } + */ @@ -120,7 +120,7 @@ index ddf726867..c2188ceef 100644 BlockPosition blockposition1 = null; BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(); double d0 = Double.MAX_VALUE; -- ChunkCoordIntPair[] achunkcoordintpair = this.aS; +- ChunkCoordIntPair[] achunkcoordintpair = this.aq; + ChunkCoordIntPair[] achunkcoordintpair = world.strongholdCoords; // Paper int j = achunkcoordintpair.length; @@ -129,8 +129,8 @@ index ddf726867..c2188ceef 100644 } private void a(ChunkGenerator chunkgenerator) { -- this.aU = chunkgenerator.getSeed(); -+ //this.aU = chunkgenerator.getSeed(); // Paper +- this.as = chunkgenerator.getSeed(); ++ //this.as = chunkgenerator.getSeed(); // Paper List list = Lists.newArrayList(); Iterator iterator = IRegistry.BIOME.iterator(); @@ -138,17 +138,17 @@ index ddf726867..c2188ceef 100644 int j = chunkgenerator.getSettings().f(); int k = chunkgenerator.getSettings().g(); -- this.aS = new ChunkCoordIntPair[j]; +- this.aq = new ChunkCoordIntPair[j]; + ChunkCoordIntPair[] strongholdCoords = chunkgenerator.getWorld().strongholdCoords = new ChunkCoordIntPair[j]; // Paper int l = 0; -- Iterator iterator1 = this.aT.iterator(); +- Iterator iterator1 = this.ar.iterator(); + Iterator iterator1 = chunkgenerator.getWorld().strongholdStuctures.iterator(); // Paper while (iterator1.hasNext()) { StructureStart structurestart = (StructureStart) iterator1.next(); -- if (l < this.aS.length) { -- this.aS[l++] = new ChunkCoordIntPair(structurestart.f(), structurestart.g()); +- if (l < this.aq.length) { +- this.aq[l++] = new ChunkCoordIntPair(structurestart.f(), structurestart.g()); + if (l < strongholdCoords.length) { // Paper + strongholdCoords[l++] = new ChunkCoordIntPair(structurestart.f(), structurestart.g()); // Paper } @@ -158,12 +158,12 @@ index ddf726867..c2188ceef 100644 double d0 = random.nextDouble() * 3.141592653589793D * 2.0D; int i1 = l; -- if (l < this.aS.length) { +- if (l < this.aq.length) { + if (l < strongholdCoords.length) { // Paper int j1 = 0; int k1 = 0; -- for (int l1 = 0; l1 < this.aS.length; ++l1) { +- for (int l1 = 0; l1 < this.aq.length; ++l1) { + for (int l1 = 0; l1 < strongholdCoords.length; ++l1) { // Paper double d1 = (double) (4 * i + i * k1 * 6) + (random.nextDouble() - 0.5D) * (double) i * 2.5D; int i2 = (int) Math.round(Math.cos(d0) * d1); @@ -172,7 +172,7 @@ index ddf726867..c2188ceef 100644 } if (l1 >= i1) { -- this.aS[l1] = new ChunkCoordIntPair(i2, j2); +- this.aq[l1] = new ChunkCoordIntPair(i2, j2); + strongholdCoords[l1] = new ChunkCoordIntPair(i2, j2); // Paper } @@ -181,8 +181,8 @@ index ddf726867..c2188ceef 100644 ++k1; j1 = 0; k += 2 * k / (k1 + 1); -- k = Math.min(k, this.aS.length - l1); -+ k = Math.min(k, strongholdCoords.length - l1); +- k = Math.min(k, this.aq.length - l1); ++ k = Math.min(k, strongholdCoords.length - l1); // Paper d0 += random.nextDouble() * 3.141592653589793D * 2.0D; } } @@ -190,7 +190,7 @@ index ddf726867..c2188ceef 100644 this.a(chunkgenerator.getSeaLevel(), this.d, 10); } while (this.b.isEmpty() || worldgenstrongholdpieces_worldgenstrongholdstart.b == null); -- ((WorldGenStronghold) this.k()).aT.add(this); +- ((WorldGenStronghold) this.l()).ar.add(this); + chunkgenerator.getWorld().strongholdStuctures.add(this); // Paper - this worries me, this is never cleared, even in vanilla (world seed never changes "world", and that appears to be the only relevant world) } } diff --git a/Spigot-Server-Patches/Fix-sounds-when-item-frames-are-modified-MC-123450.patch b/Spigot-Server-Patches/Fix-sounds-when-item-frames-are-modified-MC-123450.patch index f66a7c2397..10b699cff1 100644 --- a/Spigot-Server-Patches/Fix-sounds-when-item-frames-are-modified-MC-123450.patch +++ b/Spigot-Server-Patches/Fix-sounds-when-item-frames-are-modified-MC-123450.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Fix sounds when item frames are modified (MC-123450) This also fixes the adding sound playing when the item frame direction is changed. diff --git a/src/main/java/net/minecraft/server/EntityItemFrame.java b/src/main/java/net/minecraft/server/EntityItemFrame.java -index 6f6837ec4..b078435c6 100644 +index 9b1e07452..f8a2f32f1 100644 --- a/src/main/java/net/minecraft/server/EntityItemFrame.java +++ b/src/main/java/net/minecraft/server/EntityItemFrame.java @@ -0,0 +0,0 @@ public class EntityItemFrame extends EntityHanging { diff --git a/Spigot-Server-Patches/Fix-spawning-of-hanging-entities-that-are-not-ItemFr.patch b/Spigot-Server-Patches/Fix-spawning-of-hanging-entities-that-are-not-ItemFr.patch index fa51e0c579..2f3f099478 100644 --- a/Spigot-Server-Patches/Fix-spawning-of-hanging-entities-that-are-not-ItemFr.patch +++ b/Spigot-Server-Patches/Fix-spawning-of-hanging-entities-that-are-not-ItemFr.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Fix spawning of hanging entities that are not ItemFrames and diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index 243722b6..f33d9c8b 100644 +index b1ae19be7..cd519a546 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 { diff --git a/Spigot-Server-Patches/Fix-stuck-in-sneak-when-changing-worlds-MC-10657.patch b/Spigot-Server-Patches/Fix-stuck-in-sneak-when-changing-worlds-MC-10657.patch index 8bd513578d..4336caaafc 100644 --- a/Spigot-Server-Patches/Fix-stuck-in-sneak-when-changing-worlds-MC-10657.patch +++ b/Spigot-Server-Patches/Fix-stuck-in-sneak-when-changing-worlds-MC-10657.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Fix stuck in sneak when changing worlds (MC-10657) diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java -index 7801879c8..8541dbe8d 100644 +index 19ba79c65..453f02561 100644 --- a/src/main/java/net/minecraft/server/EntityPlayer.java +++ b/src/main/java/net/minecraft/server/EntityPlayer.java @@ -0,0 +0,0 @@ public class EntityPlayer extends EntityHuman implements ICrafting { @@ -18,7 +18,7 @@ index 7801879c8..8541dbe8d 100644 PlayerChangedWorldEvent changeEvent = new PlayerChangedWorldEvent(this.getBukkitEntity(), worldserver.getWorld()); this.world.getServer().getPluginManager().callEvent(changeEvent); diff --git a/src/main/java/net/minecraft/server/PlayerList.java b/src/main/java/net/minecraft/server/PlayerList.java -index 3cb443c4f..4ba349e1a 100644 +index f702619aa..7b79ee4fe 100644 --- a/src/main/java/net/minecraft/server/PlayerList.java +++ b/src/main/java/net/minecraft/server/PlayerList.java @@ -0,0 +0,0 @@ public abstract class PlayerList { @@ -28,8 +28,6 @@ index 3cb443c4f..4ba349e1a 100644 + entityplayer.setSneaking(false); // Paper - fix MC-10657 + // Fire advancement trigger - entityplayer.b(((CraftWorld) fromWorld).getHandle()); + entityplayer.triggerDimensionAdvancements(((CraftWorld) fromWorld).getHandle()); --- -2.23.0.rc1 - +-- \ No newline at end of file diff --git a/Spigot-Server-Patches/Fix-zero-tick-instant-grow-farms-MC-113809.patch b/Spigot-Server-Patches/Fix-zero-tick-instant-grow-farms-MC-113809.patch index df36a18f1d..6ad366138b 100644 --- a/Spigot-Server-Patches/Fix-zero-tick-instant-grow-farms-MC-113809.patch +++ b/Spigot-Server-Patches/Fix-zero-tick-instant-grow-farms-MC-113809.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Fix zero-tick instant grow farms MC-113809 diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index 7723b1c15..af07f9a16 100644 +index fd9bedf5a..8648d9e3d 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -0,0 +0,0 @@ public class PaperWorldConfig { @@ -21,77 +21,77 @@ index 7723b1c15..af07f9a16 100644 public Map altItemDespawnRateMap; private void altItemDespawnRate() { diff --git a/src/main/java/net/minecraft/server/Block.java b/src/main/java/net/minecraft/server/Block.java -index e077359b0..619237d68 100644 +index 46490a943..54a61283e 100644 --- a/src/main/java/net/minecraft/server/Block.java +++ b/src/main/java/net/minecraft/server/Block.java @@ -0,0 +0,0 @@ public class Block implements IMaterial { - private final float frictionFactor; + private final float g; protected final BlockStateList blockStateList; private IBlockData blockData; + public boolean randomTick = false; // Paper - fix MC-113809 protected final boolean v; - private final boolean g; - @Nullable + private final boolean i; + private final boolean j; diff --git a/src/main/java/net/minecraft/server/BlockBamboo.java b/src/main/java/net/minecraft/server/BlockBamboo.java -index ffb65776c..3a9c3b54a 100644 +index c482aad3e..02c548dd9 100644 --- a/src/main/java/net/minecraft/server/BlockBamboo.java +++ b/src/main/java/net/minecraft/server/BlockBamboo.java @@ -0,0 +0,0 @@ public class BlockBamboo extends Block implements IBlockFragilePlantElement { - if (!iblockdata.canPlace(world, blockposition)) { - world.b(blockposition, true); + if (!iblockdata.canPlace(worldserver, blockposition)) { + worldserver.b(blockposition, true); } else if ((Integer) iblockdata.get(BlockBamboo.f) == 0) { -+ if (world.paperConfig.fixZeroTickInstantGrowFarms && !randomTick) return; // Paper - fix MC-113809 - if (world.random.nextInt(Math.max(1, (int) (100.0F / world.spigotConfig.bambooModifier) * 3)) == 0 && world.isEmpty(blockposition.up()) && world.getLightLevel(blockposition.up(), 0) >= 9) { // Spigot - int i = this.b((IBlockAccess) world, blockposition) + 1; ++ if (worldserver.paperConfig.fixZeroTickInstantGrowFarms && !randomTick) return; // Paper - fix MC-113809 + if (random.nextInt(Math.max(1, (int) (100.0F / worldserver.spigotConfig.bambooModifier) * 3)) == 0 && worldserver.isEmpty(blockposition.up()) && worldserver.getLightLevel(blockposition.up(), 0) >= 9) { // Spigot + int i = this.b(worldserver, blockposition) + 1; diff --git a/src/main/java/net/minecraft/server/BlockCactus.java b/src/main/java/net/minecraft/server/BlockCactus.java -index 29f9ff6c1..124b4b4b0 100644 +index 4c82fe335..728491dd9 100644 --- a/src/main/java/net/minecraft/server/BlockCactus.java +++ b/src/main/java/net/minecraft/server/BlockCactus.java @@ -0,0 +0,0 @@ public class BlockCactus extends Block { - if (!iblockdata.canPlace(world, blockposition)) { - world.b(blockposition, true); + if (!iblockdata.canPlace(worldserver, blockposition)) { + worldserver.b(blockposition, true); } else { + if (world.paperConfig.fixZeroTickInstantGrowFarms && !randomTick) return; // Paper - fix MC-113809 BlockPosition blockposition1 = blockposition.up(); - if (world.isEmpty(blockposition1)) { + if (worldserver.isEmpty(blockposition1)) { diff --git a/src/main/java/net/minecraft/server/BlockChorusFlower.java b/src/main/java/net/minecraft/server/BlockChorusFlower.java -index 74fa4889f..cde2524ca 100644 +index d70b52cad..ff0ba15f8 100644 --- a/src/main/java/net/minecraft/server/BlockChorusFlower.java +++ b/src/main/java/net/minecraft/server/BlockChorusFlower.java @@ -0,0 +0,0 @@ public class BlockChorusFlower extends Block { - if (!iblockdata.canPlace(world, blockposition)) { - world.b(blockposition, true); + if (!iblockdata.canPlace(worldserver, blockposition)) { + worldserver.b(blockposition, true); } else { + if (world.paperConfig.fixZeroTickInstantGrowFarms && !randomTick) return; // Paper - fix MC-113809 BlockPosition blockposition1 = blockposition.up(); - if (world.isEmpty(blockposition1) && blockposition1.getY() < 256) { + if (worldserver.isEmpty(blockposition1) && blockposition1.getY() < 256) { diff --git a/src/main/java/net/minecraft/server/BlockReed.java b/src/main/java/net/minecraft/server/BlockReed.java -index ff674a9d5..a4850c070 100644 +index baa2ed2b6..083b7ee59 100644 --- a/src/main/java/net/minecraft/server/BlockReed.java +++ b/src/main/java/net/minecraft/server/BlockReed.java @@ -0,0 +0,0 @@ public class BlockReed extends Block { - if (!iblockdata.canPlace(world, blockposition)) { - world.b(blockposition, true); - } else if (world.isEmpty(blockposition.up())) { -+ if (world.paperConfig.fixZeroTickInstantGrowFarms && !randomTick) return; // Paper - fix MC-113809 + if (!iblockdata.canPlace(worldserver, blockposition)) { + worldserver.b(blockposition, true); + } else if (worldserver.isEmpty(blockposition.up())) { ++ if (worldserver.paperConfig.fixZeroTickInstantGrowFarms && !randomTick) return; // Paper - fix MC-113809 int i; - for (i = 1; world.getType(blockposition.down(i)).getBlock() == this; ++i) { + for (i = 1; worldserver.getType(blockposition.down(i)).getBlock() == this; ++i) { diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java -index bd1e4dbae..5b18f4bd5 100644 +index fd42390d4..4c53f8063 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 { - IBlockData iblockdata = chunksection.getType(blockposition2.getX() - j, blockposition2.getY() - j1, blockposition2.getZ() - k); + IBlockData iblockdata = this.getType(nextticklistentry.a); - if (iblockdata.q()) { -+ iblockdata.getBlock().randomTick = true; // Paper - fix MC-113809 - iblockdata.b((World) this, blockposition2, this.random); -+ iblockdata.getBlock().randomTick = false; // Paper - fix MC-113809 - } + if (iblockdata.getBlock() == nextticklistentry.b()) { ++ iblockdata.getBlock().randomTick = true; // Paper - fix MC-113809 + iblockdata.a(this, nextticklistentry.a, this.random); ++ iblockdata.getBlock().randomTick = false; // Paper - fix MC-113809 + } - Fluid fluid = iblockdata.p(); + } -- \ No newline at end of file diff --git a/Spigot-Server-Patches/Fixed-MC-156852.patch b/Spigot-Server-Patches/Fixed-MC-156852.patch index 0e43e64dba..bcdbbdaf25 100644 --- a/Spigot-Server-Patches/Fixed-MC-156852.patch +++ b/Spigot-Server-Patches/Fixed-MC-156852.patch @@ -12,15 +12,16 @@ issue in 1.8-1.12. Originally solved by Gnembon on MC-5694 at bugs.mojang.com diff --git a/src/main/java/net/minecraft/server/PlayerInteractManager.java b/src/main/java/net/minecraft/server/PlayerInteractManager.java -index e5e9de542..c96564a59 100644 +index 8720dd120..cc77dd8e5 100644 --- a/src/main/java/net/minecraft/server/PlayerInteractManager.java +++ b/src/main/java/net/minecraft/server/PlayerInteractManager.java @@ -0,0 +0,0 @@ public class PlayerInteractManager { int j = (int) (f * 10.0F); this.world.a(this.player.getId(), blockposition, j); +- this.player.playerConnection.sendPacket(new PacketPlayOutBlockBreak(blockposition, this.world.getType(blockposition), packetplayinblockdig_enumplayerdigtype, true, "actual start of destroying")); + this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition)); // Paper - fixes MC-156852 - this.player.playerConnection.sendPacket(new PacketPlayOutBlockBreak(blockposition, this.world.getType(blockposition), packetplayinblockdig_enumplayerdigtype, true)); this.l = j; } + } else if (packetplayinblockdig_enumplayerdigtype == PacketPlayInBlockDig.EnumPlayerDigType.STOP_DESTROY_BLOCK) { -- \ No newline at end of file diff --git a/Spigot-Server-Patches/Generator-Settings.patch b/Spigot-Server-Patches/Generator-Settings.patch index 5c7b7fdf00..7ce5c8c55a 100644 --- a/Spigot-Server-Patches/Generator-Settings.patch +++ b/Spigot-Server-Patches/Generator-Settings.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Generator Settings diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index 09c8ea2ed..7723b1c15 100644 +index f63525d67..fd9bedf5a 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -0,0 +0,0 @@ public class PaperWorldConfig { @@ -19,7 +19,7 @@ index 09c8ea2ed..7723b1c15 100644 + } } diff --git a/src/main/java/net/minecraft/server/ChunkGeneratorAbstract.java b/src/main/java/net/minecraft/server/ChunkGeneratorAbstract.java -index 096866b2b..338b67c3c 100644 +index af81a8414..2268fbdd8 100644 --- a/src/main/java/net/minecraft/server/ChunkGeneratorAbstract.java +++ b/src/main/java/net/minecraft/server/ChunkGeneratorAbstract.java @@ -0,0 +0,0 @@ public abstract class ChunkGeneratorAbstract diff --git a/Spigot-Server-Patches/Handle-Excessive-Signs-in-Chunks-creating-too-large-.patch b/Spigot-Server-Patches/Handle-Excessive-Signs-in-Chunks-creating-too-large-.patch index 1c4b1baaf7..8c20d1f2bf 100644 --- a/Spigot-Server-Patches/Handle-Excessive-Signs-in-Chunks-creating-too-large-.patch +++ b/Spigot-Server-Patches/Handle-Excessive-Signs-in-Chunks-creating-too-large-.patch @@ -11,7 +11,7 @@ Use -DPaper.excessiveSignsLimit=500 to configure that limit, or -1 to disable the limit and let your players be abused. diff --git a/src/main/java/net/minecraft/server/NetworkManager.java b/src/main/java/net/minecraft/server/NetworkManager.java -index b1dededc15..e156804f7a 100644 +index b1dededc1..e156804f7 100644 --- a/src/main/java/net/minecraft/server/NetworkManager.java +++ b/src/main/java/net/minecraft/server/NetworkManager.java @@ -0,0 +0,0 @@ public class NetworkManager extends SimpleChannelInboundHandler> { @@ -31,7 +31,7 @@ index b1dededc15..e156804f7a 100644 private void sendPacketQueue() { this.o(); } // Paper - OBFHELPER diff --git a/src/main/java/net/minecraft/server/Packet.java b/src/main/java/net/minecraft/server/Packet.java -index 2d8e6a2f4a..8d0965a053 100644 +index 2d8e6a2f4..8d0965a05 100644 --- a/src/main/java/net/minecraft/server/Packet.java +++ b/src/main/java/net/minecraft/server/Packet.java @@ -0,0 +0,0 @@ public interface Packet { @@ -43,7 +43,7 @@ index 2d8e6a2f4a..8d0965a053 100644 return false; } diff --git a/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java b/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java -index 58eccd9c63..ef71a1feb3 100644 +index a0b87f89d..47710067a 100644 --- a/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java +++ b/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java @@ -0,0 +0,0 @@ public class PacketPlayOutMapChunk implements Packet { @@ -63,8 +63,8 @@ index 58eccd9c63..ef71a1feb3 100644 ChunkCoordIntPair chunkcoordintpair = chunk.getPos(); @@ -0,0 +0,0 @@ public class PacketPlayOutMapChunk implements Packet { - this.c = this.a(new PacketDataSerializer(this.i()), chunk, i); - this.f = Lists.newArrayList(); + this.c = this.a(new PacketDataSerializer(this.j()), chunk, i); + this.g = Lists.newArrayList(); iterator = chunk.getTileEntities().entrySet().iterator(); + int totalSigns = 0; // Paper diff --git a/Spigot-Server-Patches/Handle-Large-Packets-disconnecting-client.patch b/Spigot-Server-Patches/Handle-Large-Packets-disconnecting-client.patch index 492a92f807..1850aa6e4f 100644 --- a/Spigot-Server-Patches/Handle-Large-Packets-disconnecting-client.patch +++ b/Spigot-Server-Patches/Handle-Large-Packets-disconnecting-client.patch @@ -7,7 +7,7 @@ If a players inventory is too big to send in a single packet, split the inventory set into multiple packets instead. diff --git a/src/main/java/net/minecraft/server/NetworkManager.java b/src/main/java/net/minecraft/server/NetworkManager.java -index 046196d54d..d4aad8a5b7 100644 +index 046196d54..d4aad8a5b 100644 --- a/src/main/java/net/minecraft/server/NetworkManager.java +++ b/src/main/java/net/minecraft/server/NetworkManager.java @@ -0,0 +0,0 @@ public class NetworkManager extends SimpleChannelInboundHandler> { @@ -27,7 +27,7 @@ index 046196d54d..d4aad8a5b7 100644 NetworkManager.LOGGER.debug("Skipping packet due to errors", throwable.getCause()); } else { diff --git a/src/main/java/net/minecraft/server/Packet.java b/src/main/java/net/minecraft/server/Packet.java -index 601d4d0fa2..2d8e6a2f4a 100644 +index 601d4d0fa..2d8e6a2f4 100644 --- a/src/main/java/net/minecraft/server/Packet.java +++ b/src/main/java/net/minecraft/server/Packet.java @@ -0,0 +0,0 @@ public interface Packet { @@ -44,7 +44,7 @@ index 601d4d0fa2..2d8e6a2f4a 100644 return false; } diff --git a/src/main/java/net/minecraft/server/PacketEncoder.java b/src/main/java/net/minecraft/server/PacketEncoder.java -index 63c4dbd327..b0cfef52cb 100644 +index 63c4dbd32..b0cfef52c 100644 --- a/src/main/java/net/minecraft/server/PacketEncoder.java +++ b/src/main/java/net/minecraft/server/PacketEncoder.java @@ -0,0 +0,0 @@ public class PacketEncoder extends MessageToByteEncoder> { @@ -80,20 +80,20 @@ index 63c4dbd327..b0cfef52cb 100644 + // Paper end } diff --git a/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java b/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java -index d19a30ad87..58eccd9c63 100644 +index 1fcbbd698..a0b87f89d 100644 --- a/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java +++ b/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java @@ -0,0 +0,0 @@ public class PacketPlayOutMapChunk implements Packet { - this.d = packetdataserializer.l(); + int i = packetdataserializer.i(); - if (i > 2097152) { + if (i > 2097152) { // Paper - if this changes, update PacketEncoder throw new RuntimeException("Chunk Packet trying to allocate too much memory on read."); } else { - this.e = new byte[i]; + this.f = new byte[i]; diff --git a/src/main/java/net/minecraft/server/PacketPlayOutWindowItems.java b/src/main/java/net/minecraft/server/PacketPlayOutWindowItems.java -index f7c3655671..631234324d 100644 +index f7c365567..631234324 100644 --- a/src/main/java/net/minecraft/server/PacketPlayOutWindowItems.java +++ b/src/main/java/net/minecraft/server/PacketPlayOutWindowItems.java @@ -0,0 +0,0 @@ public class PacketPlayOutWindowItems implements Packet { diff --git a/Spigot-Server-Patches/Handle-bad-chunks-more-gracefully.patch b/Spigot-Server-Patches/Handle-bad-chunks-more-gracefully.patch deleted file mode 100644 index 1610c34507..0000000000 --- a/Spigot-Server-Patches/Handle-bad-chunks-more-gracefully.patch +++ /dev/null @@ -1,61 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Shane Freeder -Date: Mon, 15 Apr 2019 02:24:52 +0100 -Subject: [PATCH] Handle bad chunks more gracefully - -Prior to this change the server would crash when attempting to load a -chunk from a region with bad data. - -After this change the server will defer back to vanilla behavior. At -this time, that means attempting to generate a chunk in its place -(and occasionally just not generating anything and leaving small -holes in the world (This statement might not be accurate as of 1.13.x)). - -Should Mojang choose to alter this behavior in the future, this change -will simply defer to whatever that new behavior is. - -diff --git a/src/main/java/net/minecraft/server/RegionFileCache.java b/src/main/java/net/minecraft/server/RegionFileCache.java -index c53518a47..6f34d8aea 100644 ---- a/src/main/java/net/minecraft/server/RegionFileCache.java -+++ b/src/main/java/net/minecraft/server/RegionFileCache.java -@@ -0,0 +0,0 @@ public abstract class RegionFileCache implements AutoCloseable { - private static NBTTagCompound readOversizedChunk(RegionFile regionfile, ChunkCoordIntPair chunkCoordinate) throws IOException { - synchronized (regionfile) { - try (DataInputStream datainputstream = regionfile.getReadStream(chunkCoordinate)) { -- NBTTagCompound oversizedData = regionfile.getOversizedData(chunkCoordinate.x, chunkCoordinate.z); -- NBTTagCompound chunk = NBTCompressedStreamTools.readNBT(datainputstream); -+ // Paper start - Handle bad chunks more gracefully - also handle similarly with oversized data -+ NBTTagCompound oversizedData = null; -+ -+ try { -+ oversizedData = regionfile.getOversizedData(chunkCoordinate.x, chunkCoordinate.z); -+ } catch (Exception ex) {} -+ -+ NBTTagCompound chunk; -+ -+ try { -+ chunk = NBTCompressedStreamTools.readNBT(datainputstream); -+ } catch (final Exception ex) { -+ return null; -+ } -+ // Paper end - if (oversizedData == null) { - return chunk; - } -@@ -0,0 +0,0 @@ public abstract class RegionFileCache implements AutoCloseable { - - try { - if (datainputstream != null) { -- nbttagcompound = NBTCompressedStreamTools.a(datainputstream); -- return nbttagcompound; -+ // Paper start - Handle bad chunks more gracefully -+ try { -+ return NBTCompressedStreamTools.a(datainputstream); -+ } catch (Exception ex) { -+ return null; -+ } -+ // Paper end - } - - nbttagcompound = null; --- \ No newline at end of file diff --git a/Spigot-Server-Patches/Here-s-Johnny.patch b/Spigot-Server-Patches/Here-s-Johnny.patch index 411616b824..cac0301090 100644 --- a/Spigot-Server-Patches/Here-s-Johnny.patch +++ b/Spigot-Server-Patches/Here-s-Johnny.patch @@ -5,20 +5,20 @@ Subject: [PATCH] Here's Johnny! diff --git a/src/main/java/net/minecraft/server/EntityVindicator.java b/src/main/java/net/minecraft/server/EntityVindicator.java -index 06918b8c91..ffbd6e9269 100644 +index 73ecdd22e..c974c02e9 100644 --- a/src/main/java/net/minecraft/server/EntityVindicator.java +++ b/src/main/java/net/minecraft/server/EntityVindicator.java @@ -0,0 +0,0 @@ public class EntityVindicator extends EntityIllagerAbstract { private static final Predicate b = (enumdifficulty) -> { return enumdifficulty == EnumDifficulty.NORMAL || enumdifficulty == EnumDifficulty.HARD; }; -- private boolean bz; -+ private boolean bz; public boolean isJohnny() { return bz; } public void setJohnny(boolean johnny) { bz = johnny; } // Paper - OBFHELPER +- private boolean bw; ++ private boolean bw; public boolean isJohnny() { return bw; } public void setJohnny(boolean johnny) { bw = johnny; } // Paper - OBFHELPER public EntityVindicator(EntityTypes entitytypes, World world) { super(entitytypes, world); diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftVindicator.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftVindicator.java -index 951d479298..5ff957ced7 100644 +index 951d47929..5ff957ced 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftVindicator.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftVindicator.java @@ -0,0 +0,0 @@ public class CraftVindicator extends CraftIllager implements Vindicator { diff --git a/Spigot-Server-Patches/Honor-EntityAgeable.ageLock.patch b/Spigot-Server-Patches/Honor-EntityAgeable.ageLock.patch index fbec4ff2f1..3ec786ed99 100644 --- a/Spigot-Server-Patches/Honor-EntityAgeable.ageLock.patch +++ b/Spigot-Server-Patches/Honor-EntityAgeable.ageLock.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Honor EntityAgeable.ageLock diff --git a/src/main/java/net/minecraft/server/EntityAgeable.java b/src/main/java/net/minecraft/server/EntityAgeable.java -index da9740a99e..e87754ef33 100644 +index cec938436..3d27f0964 100644 --- a/src/main/java/net/minecraft/server/EntityAgeable.java +++ b/src/main/java/net/minecraft/server/EntityAgeable.java @@ -0,0 +0,0 @@ public abstract class EntityAgeable extends EntityCreature { diff --git a/Spigot-Server-Patches/Hook-into-CB-plugin-rewrites.patch b/Spigot-Server-Patches/Hook-into-CB-plugin-rewrites.patch index e6fa8982f5..25a6de58f3 100644 --- a/Spigot-Server-Patches/Hook-into-CB-plugin-rewrites.patch +++ b/Spigot-Server-Patches/Hook-into-CB-plugin-rewrites.patch @@ -8,7 +8,7 @@ our own relocation. Also lets us rewrite NMS calls for when we're debugging in an IDE pre-relocate. diff --git a/src/main/java/org/bukkit/craftbukkit/util/Commodore.java b/src/main/java/org/bukkit/craftbukkit/util/Commodore.java -index 467b2d9385..61f1023557 100644 +index 467b2d938..61f102355 100644 --- a/src/main/java/org/bukkit/craftbukkit/util/Commodore.java +++ b/src/main/java/org/bukkit/craftbukkit/util/Commodore.java @@ -0,0 +0,0 @@ import java.io.FileOutputStream; diff --git a/Spigot-Server-Patches/Implement-CraftBlockSoundGroup.patch b/Spigot-Server-Patches/Implement-CraftBlockSoundGroup.patch index 24f4041c83..ae86ae8d09 100644 --- a/Spigot-Server-Patches/Implement-CraftBlockSoundGroup.patch +++ b/Spigot-Server-Patches/Implement-CraftBlockSoundGroup.patch @@ -49,7 +49,7 @@ index 000000000..99f99330d + } +} diff --git a/src/main/java/net/minecraft/server/IBlockData.java b/src/main/java/net/minecraft/server/IBlockData.java -index 9fd1b4915..21f734a73 100644 +index 8fb0b5af0..276f207a8 100644 --- a/src/main/java/net/minecraft/server/IBlockData.java +++ b/src/main/java/net/minecraft/server/IBlockData.java @@ -0,0 +0,0 @@ public class IBlockData extends BlockDataAbstract implements @@ -61,42 +61,42 @@ index 9fd1b4915..21f734a73 100644 return this.getBlock().getStepSound(this); } diff --git a/src/main/java/net/minecraft/server/SoundEffectType.java b/src/main/java/net/minecraft/server/SoundEffectType.java -index 5460d409b..ccd5b0529 100644 +index b774d2d8d..0184bf3fc 100644 --- a/src/main/java/net/minecraft/server/SoundEffectType.java +++ b/src/main/java/net/minecraft/server/SoundEffectType.java @@ -0,0 +0,0 @@ public class SoundEffectType { - public static final SoundEffectType v = new SoundEffectType(1.0F, 1.0F, SoundEffects.BLOCK_LANTERN_BREAK, SoundEffects.BLOCK_LANTERN_STEP, SoundEffects.BLOCK_LANTERN_PLACE, SoundEffects.BLOCK_LANTERN_HIT, SoundEffects.BLOCK_LANTERN_FALL); - public final float w; + public static final SoundEffectType w = new SoundEffectType(1.0F, 1.0F, SoundEffects.BLOCK_LANTERN_BREAK, SoundEffects.BLOCK_LANTERN_STEP, SoundEffects.BLOCK_LANTERN_PLACE, SoundEffects.BLOCK_LANTERN_HIT, SoundEffects.BLOCK_LANTERN_FALL); public final float x; -- private final SoundEffect y; -+ private final SoundEffect y; public final SoundEffect getBreakSound() { return this.y; } // Paper - OBFHELPER - private final SoundEffect z; + public final float y; +- private final SoundEffect z; ++ private final SoundEffect z; public final SoundEffect getBreakSound() { return this.z; } // Paper - OBFHELPER private final SoundEffect A; -- private final SoundEffect B; -+ private final SoundEffect B; public final SoundEffect getHitSound() { return this.B; } // Paper - OBFHELPER - private final SoundEffect C; + private final SoundEffect B; +- private final SoundEffect C; ++ private final SoundEffect C; public final SoundEffect getHitSound() { return this.B; } // Paper - OBFHELPER + private final SoundEffect D; public SoundEffectType(float f, float f1, SoundEffect soundeffect, SoundEffect soundeffect1, SoundEffect soundeffect2, SoundEffect soundeffect3, SoundEffect soundeffect4) { @@ -0,0 +0,0 @@ public class SoundEffectType { - return this.x; + return this.y; } + public final SoundEffect getStepSound() { return this.d(); } // Paper - OBFHELPER public SoundEffect d() { - return this.z; + return this.A; } + public final SoundEffect getPlaceSound() { return this.e(); } // Paper - OBFHELPER public SoundEffect e() { - return this.A; + return this.B; } + public final SoundEffect getFallSound() { return this.g(); } // Paper - OBFHELPER public SoundEffect g() { - return this.C; + return this.D; } diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java -index da3c50c70..d9749adb7 100644 +index 887ade5e8..aef8dd169 100644 --- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlock.java @@ -0,0 +0,0 @@ public class CraftBlock implements Block { diff --git a/Spigot-Server-Patches/Implement-PlayerPostRespawnEvent.patch b/Spigot-Server-Patches/Implement-PlayerPostRespawnEvent.patch index 891f66532c..0802e24f40 100644 --- a/Spigot-Server-Patches/Implement-PlayerPostRespawnEvent.patch +++ b/Spigot-Server-Patches/Implement-PlayerPostRespawnEvent.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Implement PlayerPostRespawnEvent diff --git a/src/main/java/net/minecraft/server/PlayerList.java b/src/main/java/net/minecraft/server/PlayerList.java -index abddc8895e..a183bb450d 100644 +index 49437f212..68e089a4b 100644 --- a/src/main/java/net/minecraft/server/PlayerList.java +++ b/src/main/java/net/minecraft/server/PlayerList.java @@ -0,0 +0,0 @@ public abstract class PlayerList { diff --git a/Spigot-Server-Patches/Implement-alternative-item-despawn-rate.patch b/Spigot-Server-Patches/Implement-alternative-item-despawn-rate.patch index d4ddd347a0..a853e6ba6e 100644 --- a/Spigot-Server-Patches/Implement-alternative-item-despawn-rate.patch +++ b/Spigot-Server-Patches/Implement-alternative-item-despawn-rate.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Implement alternative item-despawn-rate diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index 56ca65819..b1ad22c9f 100644 +index 097b623fd..d0f5b2ab7 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -0,0 +0,0 @@ @@ -80,10 +80,10 @@ index 56ca65819..b1ad22c9f 100644 + } } diff --git a/src/main/java/net/minecraft/server/EntityItem.java b/src/main/java/net/minecraft/server/EntityItem.java -index bc7e706d1..df26cef6a 100644 +index a0d1e7fd2..e61af3f5e 100644 --- a/src/main/java/net/minecraft/server/EntityItem.java +++ b/src/main/java/net/minecraft/server/EntityItem.java -@@ -0,0 +0,0 @@ import java.util.List; +@@ -0,0 +0,0 @@ import java.util.Objects; import java.util.UUID; import javax.annotation.Nullable; // CraftBukkit start @@ -111,8 +111,8 @@ index bc7e706d1..df26cef6a 100644 this.age = 0; @@ -0,0 +0,0 @@ public class EntityItem extends Entity { - public void u() { - this.p(); + public void s() { + this.o(); - this.age = world.spigotConfig.itemDespawnRate - 1; // Spigot + this.age = this.getDespawnRate() - 1; // Spigot // Paper } @@ -125,6 +125,6 @@ index bc7e706d1..df26cef6a 100644 + // Paper end + @Override - public Packet N() { + public Packet L() { return new PacketPlayOutSpawnEntity(this); -- \ No newline at end of file diff --git a/Spigot-Server-Patches/Implement-an-API-for-CanPlaceOn-and-CanDestroy-NBT-v.patch b/Spigot-Server-Patches/Implement-an-API-for-CanPlaceOn-and-CanDestroy-NBT-v.patch index 8b8560dd10..3d90815cdf 100644 --- a/Spigot-Server-Patches/Implement-an-API-for-CanPlaceOn-and-CanDestroy-NBT-v.patch +++ b/Spigot-Server-Patches/Implement-an-API-for-CanPlaceOn-and-CanDestroy-NBT-v.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Implement an API for CanPlaceOn and CanDestroy NBT values diff --git a/src/main/java/net/minecraft/server/ArgumentBlock.java b/src/main/java/net/minecraft/server/ArgumentBlock.java -index 005ebec266..97d85f8451 100644 +index 005ebec26..97d85f845 100644 --- a/src/main/java/net/minecraft/server/ArgumentBlock.java +++ b/src/main/java/net/minecraft/server/ArgumentBlock.java @@ -0,0 +0,0 @@ public class ArgumentBlock { @@ -32,7 +32,7 @@ index 005ebec266..97d85f8451 100644 this.s = this::l; if (this.i.canRead() && this.i.peek() == '#') { diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java -index 6a6b80b5bd..dbb4e5258c 100644 +index 1eede4bcc..8a9a48bb8 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftMetaItem.java @@ -0,0 +0,0 @@ import org.bukkit.persistence.PersistentDataContainer; @@ -284,9 +284,9 @@ index 6a6b80b5bd..dbb4e5258c 100644 )); } @@ -0,0 +0,0 @@ class CraftMetaItem implements ItemMeta, Damageable, Repairable, BlockDataMeta { - return spigot; } - // Spigot end + // Paper end + + // Paper start - Implement an API for CanPlaceOn and CanDestroy NBT values + @Override + @SuppressWarnings("deprecation") diff --git a/Spigot-Server-Patches/Implement-furnace-cook-speed-multiplier-API.patch b/Spigot-Server-Patches/Implement-furnace-cook-speed-multiplier-API.patch index 12f9bcd604..a31bdbaa6e 100644 --- a/Spigot-Server-Patches/Implement-furnace-cook-speed-multiplier-API.patch +++ b/Spigot-Server-Patches/Implement-furnace-cook-speed-multiplier-API.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Implement furnace cook speed multiplier API Signed-off-by: Tassu diff --git a/src/main/java/net/minecraft/server/TileEntityFurnace.java b/src/main/java/net/minecraft/server/TileEntityFurnace.java -index 2bbd3663a7..55b5646103 100644 +index 0419aea92..04849c3da 100644 --- a/src/main/java/net/minecraft/server/TileEntityFurnace.java +++ b/src/main/java/net/minecraft/server/TileEntityFurnace.java @@ -0,0 +0,0 @@ import java.util.Map; @@ -57,7 +57,7 @@ index 2bbd3663a7..55b5646103 100644 this.cookTimeTotal = this.getRecipeCookingTime(); this.burn(irecipe); diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftFurnace.java b/src/main/java/org/bukkit/craftbukkit/block/CraftFurnace.java -index 9cc67915ca..1ce10ea049 100644 +index 9cc67915c..1ce10ea04 100644 --- a/src/main/java/org/bukkit/craftbukkit/block/CraftFurnace.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftFurnace.java @@ -0,0 +0,0 @@ public class CraftFurnace extends CraftContainer diff --git a/Spigot-Server-Patches/Implement-getters-and-setters-for-EntityItem-owner-a.patch b/Spigot-Server-Patches/Implement-getters-and-setters-for-EntityItem-owner-a.patch index 463c540b3d..ba6d016849 100644 --- a/Spigot-Server-Patches/Implement-getters-and-setters-for-EntityItem-owner-a.patch +++ b/Spigot-Server-Patches/Implement-getters-and-setters-for-EntityItem-owner-a.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Implement getters and setters for EntityItem owner and diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java -index 3f552b5905..cb756b1ba0 100644 +index 3f552b590..cb756b1ba 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java @@ -0,0 +0,0 @@ import org.bukkit.entity.EntityType; diff --git a/Spigot-Server-Patches/Improve-POI-data-saving-logic.patch b/Spigot-Server-Patches/Improve-POI-data-saving-logic.patch deleted file mode 100644 index 1fd47718c4..0000000000 --- a/Spigot-Server-Patches/Improve-POI-data-saving-logic.patch +++ /dev/null @@ -1,28 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Spottedleaf -Date: Mon, 19 Aug 2019 06:33:17 -0700 -Subject: [PATCH] Improve POI data saving logic - -- Do not unload data if world saving is disabled -- Aggressively target unloading - -diff --git a/src/main/java/net/minecraft/server/VillagePlace.java b/src/main/java/net/minecraft/server/VillagePlace.java -index 0e98b7803..fb99b4306 100644 ---- a/src/main/java/net/minecraft/server/VillagePlace.java -+++ b/src/main/java/net/minecraft/server/VillagePlace.java -@@ -0,0 +0,0 @@ public class VillagePlace extends RegionFileSection { - // Paper start - async chunk io - if (this.world == null) { - super.a(booleansupplier); -- } else { -+ } else if (!this.world.isSavingDisabled()) { // Paper - only save if saving is enabled - //super.a(booleansupplier); // re-implement below -- while (!((RegionFileSection)this).d.isEmpty() && booleansupplier.getAsBoolean()) { -+ // Paper start - target unloading aggressively -+ int queueTarget = Math.min(this.d.size() - 100, (int)(this.d.size() * 0.96)); -+ while (!((RegionFileSection)this).d.isEmpty() && (this.d.size() > queueTarget || booleansupplier.getAsBoolean())) { -+ // Paper end - ChunkCoordIntPair chunkcoordintpair = SectionPosition.a(((RegionFileSection)this).d.firstLong()).u(); - - NBTTagCompound data; --- \ No newline at end of file diff --git a/Spigot-Server-Patches/Improve-death-events.patch b/Spigot-Server-Patches/Improve-death-events.patch index 34a1ef19c6..94db654b38 100644 --- a/Spigot-Server-Patches/Improve-death-events.patch +++ b/Spigot-Server-Patches/Improve-death-events.patch @@ -15,7 +15,7 @@ items and experience which is otherwise only properly possible by using internal code. diff --git a/src/main/java/net/minecraft/server/CombatTracker.java b/src/main/java/net/minecraft/server/CombatTracker.java -index 84c3ea9d00..f563a7b630 100644 +index 6daa400d2..38fe29f8a 100644 --- a/src/main/java/net/minecraft/server/CombatTracker.java +++ b/src/main/java/net/minecraft/server/CombatTracker.java @@ -0,0 +0,0 @@ public class CombatTracker { @@ -27,7 +27,7 @@ index 84c3ea9d00..f563a7b630 100644 int i = this.f ? 300 : 100; diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java -index 7811beb807..f9b097fd6f 100644 +index 75343a438..b03316bc8 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 INamableTileEntity, ICommandListener, Ke @@ -45,9 +45,9 @@ index 7811beb807..f9b097fd6f 100644 + public void onKill(EntityLiving entityLiving) { this.b(entityLiving); } // Paper - OBFHELPER public void b(EntityLiving entityliving) {} - protected void i(double d0, double d1, double d2) { + protected void k(double d0, double d1, double d2) { diff --git a/src/main/java/net/minecraft/server/EntityArmorStand.java b/src/main/java/net/minecraft/server/EntityArmorStand.java -index 85fc48371d..02595cfccd 100644 +index b51662ee9..e6032e3ed 100644 --- a/src/main/java/net/minecraft/server/EntityArmorStand.java +++ b/src/main/java/net/minecraft/server/EntityArmorStand.java @@ -0,0 +0,0 @@ public class EntityArmorStand extends EntityLiving { @@ -61,7 +61,7 @@ index 85fc48371d..02595cfccd 100644 } diff --git a/src/main/java/net/minecraft/server/EntityFox.java b/src/main/java/net/minecraft/server/EntityFox.java -index e22e99df38..ca38ccf768 100644 +index ac8eaffb9..7495af8e3 100644 --- a/src/main/java/net/minecraft/server/EntityFox.java +++ b/src/main/java/net/minecraft/server/EntityFox.java @@ -0,0 +0,0 @@ public class EntityFox extends EntityAnimal { @@ -94,7 +94,7 @@ index e22e99df38..ca38ccf768 100644 public static boolean a(EntityFox entityfox, EntityLiving entityliving) { diff --git a/src/main/java/net/minecraft/server/EntityHorseChestedAbstract.java b/src/main/java/net/minecraft/server/EntityHorseChestedAbstract.java -index 2483cfd28a..2a988366cd 100644 +index 80717ad9a..53aac5bcc 100644 --- a/src/main/java/net/minecraft/server/EntityHorseChestedAbstract.java +++ b/src/main/java/net/minecraft/server/EntityHorseChestedAbstract.java @@ -0,0 +0,0 @@ public abstract class EntityHorseChestedAbstract extends EntityHorseAbstract { @@ -119,18 +119,18 @@ index 2483cfd28a..2a988366cd 100644 public void b(NBTTagCompound nbttagcompound) { super.b(nbttagcompound); diff --git a/src/main/java/net/minecraft/server/EntityLiving.java b/src/main/java/net/minecraft/server/EntityLiving.java -index b09712ca91..1e53af2cce 100644 +index ac85a5d23..c6bd68919 100644 --- a/src/main/java/net/minecraft/server/EntityLiving.java +++ b/src/main/java/net/minecraft/server/EntityLiving.java @@ -0,0 +0,0 @@ public abstract class EntityLiving extends Entity { + protected float aT; + protected float aU; protected float aV; - protected float aW; - protected float aX; -- protected int aY; -+ protected int aY; protected int getKillCount() { return this.aY; } // Paper - OBFHELPER +- protected int aW; ++ protected int aW; protected int getKillCount() { return this.aW; } // Paper - OBFHELPER public float lastDamage; protected boolean jumping; - public float bb; + public float aZ; @@ -0,0 +0,0 @@ public abstract class EntityLiving extends Entity { public boolean collides = true; public boolean canPickUpLoot; @@ -148,11 +148,11 @@ index b09712ca91..1e53af2cce 100644 + //SoundEffect soundeffect = this.getSoundDeath(); - if (flag1 && soundeffect != null) { -- this.a(soundeffect, this.getSoundVolume(), this.cV()); +- this.a(soundeffect, this.getSoundVolume(), this.dn()); - } -+ //if (flag1 && soundeffect != null) { -+ // this.a(soundeffect, this.getSoundVolume(), this.cV()); -+ //} ++// if (flag1 && soundeffect != null) { ++// this.a(soundeffect, this.getSoundVolume(), this.dn()); ++// } + this.silentDeath = !flag1; // mark entity as dying silently + // Paper end @@ -166,12 +166,12 @@ index b09712ca91..1e53af2cce 100644 EntityLiving entityliving = this.getKillingEntity(); + /* // Paper - move down to make death event cancellable - if (this.aY >= 0 && entityliving != null) { - entityliving.a(this, this.aY, damagesource); + if (this.aW >= 0 && entityliving != null) { + entityliving.a(this, this.aW, damagesource); } @@ -0,0 +0,0 @@ public abstract class EntityLiving extends Entity { if (this.isSleeping()) { - this.dy(); + this.entityWakeup(); } + */ // Paper @@ -198,15 +198,7 @@ index b09712ca91..1e53af2cce 100644 + } + // Paper end + - boolean flag = false; - -- if (entityliving instanceof EntityWither) { -+ if (this.killed && entityliving instanceof EntityWither) { - if (this.world.getGameRules().getBoolean(GameRules.MOB_GRIEFING)) { - BlockPosition blockposition = new BlockPosition(this.locX, this.locY, this.locZ); - IBlockData iblockdata = Blocks.WITHER_ROSE.getBlockData(); -@@ -0,0 +0,0 @@ public abstract class EntityLiving extends Entity { - } + this.f(entityliving); } + if (this.killed) { // Paper @@ -216,6 +208,19 @@ index b09712ca91..1e53af2cce 100644 } } +@@ -0,0 +0,0 @@ public abstract class EntityLiving extends Entity { + if (!this.world.isClientSide) { + boolean flag = false; + +- if (entityliving instanceof EntityWither) { ++ if (this.killed && entityliving instanceof EntityWither) { // Paper + if (this.world.getGameRules().getBoolean(GameRules.MOB_GRIEFING)) { + BlockPosition blockposition = new BlockPosition(this); + IBlockData iblockdata = Blocks.WITHER_ROSE.getBlockData(); +@@ -0,0 +0,0 @@ public abstract class EntityLiving extends Entity { + } + } + - protected void d(DamageSource damagesource) { + protected org.bukkit.event.entity.EntityDeathEvent processDeath(DamageSource damagesource) { return d(damagesource); } // Paper - OBFHELPER + protected org.bukkit.event.entity.EntityDeathEvent d(DamageSource damagesource) { // Paper @@ -225,7 +230,7 @@ index b09712ca91..1e53af2cce 100644 @@ -0,0 +0,0 @@ public abstract class EntityLiving extends Entity { boolean flag = this.lastDamageByPlayerTime > 0; - this.cF(); // CraftBukkit - from below // PAIL + this.dropInventory(); // CraftBukkit - from below + org.bukkit.event.entity.EntityDeathEvent deathEvent; // Paper if (this.isDropExperience() && this.world.getGameRules().getBoolean(GameRules.DO_MOB_LOOT)) { this.a(damagesource, flag); @@ -242,15 +247,16 @@ index b09712ca91..1e53af2cce 100644 + this.postDeathDropItems(deathEvent); // Paper + this.drops = new ArrayList<>(); // Paper -- // this.cF();// CraftBukkit - moved up + // this.dropInventory();// CraftBukkit - moved up +- this.dropExperience(); + return deathEvent; // Paper } - protected void cF() {} + protected void dropInventory() {} + protected void postDeathDropItems(org.bukkit.event.entity.EntityDeathEvent event) {} // Paper - method for post death logic that cannot be ran before the event is potentially cancelled - protected void dropDeathLoot(DamageSource damagesource, int i, boolean flag) {} - + // CraftBukkit start + public int getExpReward() { @@ -0,0 +0,0 @@ public abstract class EntityLiving extends Entity { return SoundEffects.ENTITY_GENERIC_HURT; } @@ -268,12 +274,12 @@ index b09712ca91..1e53af2cce 100644 return 1.0F; } -+ public float getSoundPitch() { return cV();} // Paper - OBFHELPER - protected float cV() { ++ public float getSoundPitch() { return dn();} // Paper - OBFHELPER + protected float dn() { return this.isBaby() ? (this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.5F : (this.random.nextFloat() - this.random.nextFloat()) * 0.2F + 1.0F; } diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java -index 7e37164c0e..1b35e6c478 100644 +index bb2a0b7e6..26096afbe 100644 --- a/src/main/java/net/minecraft/server/EntityPlayer.java +++ b/src/main/java/net/minecraft/server/EntityPlayer.java @@ -0,0 +0,0 @@ public class EntityPlayer extends EntityHuman implements ICrafting { @@ -324,7 +330,7 @@ index 7e37164c0e..1b35e6c478 100644 } } diff --git a/src/main/java/org/bukkit/craftbukkit/CraftSound.java b/src/main/java/org/bukkit/craftbukkit/CraftSound.java -index 73cb64e09d..9f317ff2e8 100644 +index 90fdf89c8..b761a41dc 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftSound.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftSound.java @@ -0,0 +0,0 @@ public enum CraftSound { @@ -351,7 +357,7 @@ index 73cb64e09d..9f317ff2e8 100644 this.minecraftKey = minecraftKey; } diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -index 2c99a1e9df..e9458dc65f 100644 +index 9e0be10d7..ad70bd5ba 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 @@ public class CraftPlayer extends CraftHumanEntity implements Player { @@ -372,7 +378,7 @@ index 2c99a1e9df..e9458dc65f 100644 public void injectScaledMaxHealth(Collection collection, boolean force) { diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java -index 84c292168e..dc15703fd4 100644 +index 844781f4e..4fb1092ce 100644 --- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java +++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java @@ -0,0 +0,0 @@ public class CraftEventFactory { diff --git a/Spigot-Server-Patches/Lazy-init-world-storage-in-CraftOfflinePlayer.patch b/Spigot-Server-Patches/Lazy-init-world-storage-in-CraftOfflinePlayer.patch index 6132a51cf4..aa9b7f66b5 100644 --- a/Spigot-Server-Patches/Lazy-init-world-storage-in-CraftOfflinePlayer.patch +++ b/Spigot-Server-Patches/Lazy-init-world-storage-in-CraftOfflinePlayer.patch @@ -8,7 +8,7 @@ worlds loaded. This is typically a rare occurrence but probably one that should be covered as best we can. diff --git a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java -index 6a448c02ec..c1ef1c950d 100644 +index 6a448c02e..c1ef1c950 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java @@ -0,0 +0,0 @@ import org.bukkit.plugin.Plugin; diff --git a/Spigot-Server-Patches/Limit-Client-Sign-length-more.patch b/Spigot-Server-Patches/Limit-Client-Sign-length-more.patch index 7d06b5a49d..f3757812ca 100644 --- a/Spigot-Server-Patches/Limit-Client-Sign-length-more.patch +++ b/Spigot-Server-Patches/Limit-Client-Sign-length-more.patch @@ -22,7 +22,7 @@ it only impacts data sent from the client. Set -DPaper.maxSignLength=XX to change limit or -1 to disable diff --git a/src/main/java/net/minecraft/server/PlayerConnection.java b/src/main/java/net/minecraft/server/PlayerConnection.java -index 6030766099..b030269f0a 100644 +index c54e1c9a0..bd2ee459f 100644 --- a/src/main/java/net/minecraft/server/PlayerConnection.java +++ b/src/main/java/net/minecraft/server/PlayerConnection.java @@ -0,0 +0,0 @@ public class PlayerConnection implements PacketListenerPlayIn { diff --git a/Spigot-Server-Patches/Limit-lightning-strike-effect-distance.patch b/Spigot-Server-Patches/Limit-lightning-strike-effect-distance.patch index 727e5927bf..f7ec498d69 100644 --- a/Spigot-Server-Patches/Limit-lightning-strike-effect-distance.patch +++ b/Spigot-Server-Patches/Limit-lightning-strike-effect-distance.patch @@ -5,14 +5,13 @@ Subject: [PATCH] Limit lightning strike effect distance diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index 4a4e9d715..3b24fbdbf 100644 +index e49318a19..fe3e78f36 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -0,0 +0,0 @@ public class PaperWorldConfig { - skeleHorseSpawnChance = 0.01D; // Vanilla value } } -+ + + public double sqrMaxThunderDistance; + public double sqrMaxLightningImpactSoundDistance; + public double maxLightningFlashDistance; @@ -34,16 +33,17 @@ index 4a4e9d715..3b24fbdbf 100644 + maxLightningFlashDistance = 512; // Vanilla value + } + } - ++ public int fixedInhabitedTime; private void fixedInhabitedTime() { + if (PaperConfig.version < 16) { diff --git a/src/main/java/net/minecraft/server/EntityLightning.java b/src/main/java/net/minecraft/server/EntityLightning.java -index 2ceee79cf..27bf271bb 100644 +index 7c518983a..0bd67395e 100644 --- a/src/main/java/net/minecraft/server/EntityLightning.java +++ b/src/main/java/net/minecraft/server/EntityLightning.java @@ -0,0 +0,0 @@ public class EntityLightning extends Entity { - double deltaX = this.locX - player.locX; - double deltaZ = this.locZ - player.locZ; + double deltaX = this.locX() - player.locX(); + double deltaZ = this.locZ() - player.locZ(); double distanceSquared = deltaX * deltaX + deltaZ * deltaZ; + // Paper start - Limit lightning strike effect distance + if (distanceSquared <= this.world.paperConfig.sqrMaxLightningImpactSoundDistance) { @@ -58,26 +58,26 @@ index 2ceee79cf..27bf271bb 100644 + // Paper end if (distanceSquared > viewDistance * viewDistance) { double deltaLength = Math.sqrt(distanceSquared); - double relativeX = player.locX + (deltaX / deltaLength) * viewDistance; + double relativeX = player.locX() + (deltaX / deltaLength) * viewDistance; @@ -0,0 +0,0 @@ public class EntityLightning extends Entity { } } // CraftBukkit end -- this.world.playSound((EntityHuman) null, this.locX, this.locY, this.locZ, SoundEffects.ENTITY_LIGHTNING_BOLT_IMPACT, SoundCategory.WEATHER, 2.0F, 0.5F + this.random.nextFloat() * 0.2F); -+ //this.world.playSound((EntityHuman) null, this.locX, this.locY, this.locZ, SoundEffects.ENTITY_LIGHTNING_BOLT_IMPACT, SoundCategory.WEATHER, 2.0F, 0.5F + this.random.nextFloat() * 0.2F); // Paper - Limit lightning strike effect distance (the packet is now sent from inside the loop) +- this.world.playSound((EntityHuman) null, this.locX(), this.locY(), this.locZ(), SoundEffects.ENTITY_LIGHTNING_BOLT_IMPACT, SoundCategory.WEATHER, 2.0F, 0.5F + this.random.nextFloat() * 0.2F); ++ //this.world.playSound((EntityHuman) null, this.locX(), this.locY(), this.locZ(), SoundEffects.ENTITY_LIGHTNING_BOLT_IMPACT, SoundCategory.WEATHER, 2.0F, 0.5F + this.random.nextFloat() * 0.2F); // Paper - Limit lightning strike effect distance (the packet is now sent from inside the loop) } --this.lifeTicks; diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java -index 8b55879c6..dbd357f1a 100644 +index a0c714129..ecfcecfdc 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 { } // CraftBukkit end this.globalEntityList.add(entitylightning); -- this.server.getPlayerList().sendPacketNearby((EntityHuman) null, entitylightning.locX, entitylightning.locY, entitylightning.locZ, 512.0D, this, new PacketPlayOutSpawnEntityWeather(entitylightning)); // Paper - use world instead of dimension -+ this.server.getPlayerList().sendPacketNearby((EntityHuman) null, entitylightning.locX, entitylightning.locY, entitylightning.locZ, paperConfig.maxLightningFlashDistance, this, new PacketPlayOutSpawnEntityWeather(entitylightning)); // Paper - use world instead of dimension, limit lightning strike effect distance +- this.server.getPlayerList().sendPacketNearby((EntityHuman) null, entitylightning.locX(), entitylightning.locY(), entitylightning.locZ(), 512.0D, this, new PacketPlayOutSpawnEntityWeather(entitylightning)); // Paper - use world instead of dimension ++ this.server.getPlayerList().sendPacketNearby((EntityHuman) null, entitylightning.locX(), entitylightning.locY(), entitylightning.locZ(), paperConfig.maxLightningFlashDistance, this, new PacketPlayOutSpawnEntityWeather(entitylightning)); // Paper - use world instead of dimension, limit lightning strike effect distance } @Override diff --git a/Spigot-Server-Patches/Log-other-thread-in-DataPaletteBlock-lock-failure.patch b/Spigot-Server-Patches/Log-other-thread-in-DataPaletteBlock-lock-failure.patch index a653cfca13..f29f7cc15f 100644 --- a/Spigot-Server-Patches/Log-other-thread-in-DataPaletteBlock-lock-failure.patch +++ b/Spigot-Server-Patches/Log-other-thread-in-DataPaletteBlock-lock-failure.patch @@ -22,7 +22,7 @@ index 000000000..a3b174618 + } +} diff --git a/src/main/java/net/minecraft/server/DataPaletteBlock.java b/src/main/java/net/minecraft/server/DataPaletteBlock.java -index a3bb2e877..1e2bca1e0 100644 +index d5f5a5187..2c1d1b1a5 100644 --- a/src/main/java/net/minecraft/server/DataPaletteBlock.java +++ b/src/main/java/net/minecraft/server/DataPaletteBlock.java @@ -0,0 +0,0 @@ public class DataPaletteBlock implements DataPaletteExpandable { diff --git a/Spigot-Server-Patches/MC-114618-Fix-EntityAreaEffectCloud-from-going-negat.patch b/Spigot-Server-Patches/MC-114618-Fix-EntityAreaEffectCloud-from-going-negat.patch index c8972b2f53..940880a46f 100644 --- a/Spigot-Server-Patches/MC-114618-Fix-EntityAreaEffectCloud-from-going-negat.patch +++ b/Spigot-Server-Patches/MC-114618-Fix-EntityAreaEffectCloud-from-going-negat.patch @@ -6,7 +6,7 @@ Subject: [PATCH] MC-114618 - Fix EntityAreaEffectCloud from going negative diff --git a/src/main/java/net/minecraft/server/EntityAreaEffectCloud.java b/src/main/java/net/minecraft/server/EntityAreaEffectCloud.java -index 3a8e10533..fe527aba5 100644 +index e8f3e55fd..44289c230 100644 --- a/src/main/java/net/minecraft/server/EntityAreaEffectCloud.java +++ b/src/main/java/net/minecraft/server/EntityAreaEffectCloud.java @@ -0,0 +0,0 @@ public class EntityAreaEffectCloud extends Entity { diff --git a/Spigot-Server-Patches/MC-136865-Use-valid-item-for-enchantment-checks-on-b.patch b/Spigot-Server-Patches/MC-136865-Use-valid-item-for-enchantment-checks-on-b.patch deleted file mode 100644 index e610fb4d97..0000000000 --- a/Spigot-Server-Patches/MC-136865-Use-valid-item-for-enchantment-checks-on-b.patch +++ /dev/null @@ -1,31 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: MisterVector -Date: Thu, 1 Nov 2018 14:50:05 -0700 -Subject: [PATCH] MC-136865: Use valid item for enchantment checks on block - break - -When an itemstack runs out of durability, the amount is reduced to -0 which then marks the item as invalid. This causes the last unit -of durability to not apply enchantments as the enchantment level -check sees the item as a dud. - -keep the clone of the item used to a non empty value so it represents -the item used. - -diff --git a/src/main/java/net/minecraft/server/PlayerInteractManager.java b/src/main/java/net/minecraft/server/PlayerInteractManager.java -index a898535d1b..f953bd7ceb 100644 ---- a/src/main/java/net/minecraft/server/PlayerInteractManager.java -+++ b/src/main/java/net/minecraft/server/PlayerInteractManager.java -@@ -0,0 +0,0 @@ public class PlayerInteractManager { - ItemStack itemstack = this.player.getItemInMainHand(); - boolean flag1 = this.player.hasBlock(iblockdata); - -+ ItemStack itemstack1 = flag && flag1 && event.isDropItems() && !itemstack.isEmpty() ? itemstack.cloneItemStack() : ItemStack.a; // Paper - MC-136865 - clone before use - itemstack.a(this.world, iblockdata, blockposition, this.player); - if (flag && flag1 && event.isDropItems()) { // CraftBukkit - Check if block should drop items -- ItemStack itemstack1 = itemstack.isEmpty() ? ItemStack.a : itemstack.cloneItemStack(); -+ //ItemStack itemstack1 = itemstack.isEmpty() ? ItemStack.a : itemstack.cloneItemStack(); // Paper - MC-136865 - move up - - block.a(this.world, this.player, blockposition, iblockdata, tileentity, itemstack1); - } --- \ No newline at end of file diff --git a/Spigot-Server-Patches/MC-145260-Fix-Whitelist-On-Off-inconsistency.patch b/Spigot-Server-Patches/MC-145260-Fix-Whitelist-On-Off-inconsistency.patch index d620284727..b1f0a7abf1 100644 --- a/Spigot-Server-Patches/MC-145260-Fix-Whitelist-On-Off-inconsistency.patch +++ b/Spigot-Server-Patches/MC-145260-Fix-Whitelist-On-Off-inconsistency.patch @@ -11,7 +11,7 @@ everything to the Whitelist object. https://github.com/PaperMC/Paper/issues/1880 diff --git a/src/main/java/net/minecraft/server/JsonList.java b/src/main/java/net/minecraft/server/JsonList.java -index 8570e38f42..c97be42dd7 100644 +index 8570e38f4..c97be42dd 100644 --- a/src/main/java/net/minecraft/server/JsonList.java +++ b/src/main/java/net/minecraft/server/JsonList.java @@ -0,0 +0,0 @@ public class JsonList> { @@ -23,7 +23,7 @@ index 8570e38f42..c97be42dd7 100644 this.e = flag; } diff --git a/src/main/java/net/minecraft/server/PlayerList.java b/src/main/java/net/minecraft/server/PlayerList.java -index 52aa83f51f..0da3f95f89 100644 +index 659ce2181..62d807597 100644 --- a/src/main/java/net/minecraft/server/PlayerList.java +++ b/src/main/java/net/minecraft/server/PlayerList.java @@ -0,0 +0,0 @@ public abstract class PlayerList { diff --git a/Spigot-Server-Patches/MC-50319-Check-other-worlds-for-shooter-of-projectil.patch b/Spigot-Server-Patches/MC-50319-Check-other-worlds-for-shooter-of-projectil.patch index d91f4f595e..08059d93ea 100644 --- a/Spigot-Server-Patches/MC-50319-Check-other-worlds-for-shooter-of-projectil.patch +++ b/Spigot-Server-Patches/MC-50319-Check-other-worlds-for-shooter-of-projectil.patch @@ -11,12 +11,12 @@ If the projectile fails to find the shooter in the current world, check other worlds. diff --git a/src/main/java/net/minecraft/server/EntityProjectile.java b/src/main/java/net/minecraft/server/EntityProjectile.java -index f2f4b2d929..18d28a151a 100644 +index 9eed1dce3..6c091b680 100644 --- a/src/main/java/net/minecraft/server/EntityProjectile.java +++ b/src/main/java/net/minecraft/server/EntityProjectile.java @@ -0,0 +0,0 @@ public abstract class EntityProjectile extends Entity implements IProjectile { public EntityLiving getShooter() { - if (this.shooter == null && this.shooterId != null && this.world instanceof WorldServer) { + if ((this.shooter == null || this.shooter.dead) && this.shooterId != null && this.world instanceof WorldServer) { Entity entity = ((WorldServer) this.world).getEntity(this.shooterId); + // Paper start - MC-50319 - shooter might be in another world (arrows through portals) + if (entity == null) { @@ -31,10 +31,4 @@ index f2f4b2d929..18d28a151a 100644 if (entity instanceof EntityLiving) { this.shooter = (EntityLiving) entity; - } else { -- this.shooterId = null; -+ //this.shooterId = null; // Paper - don't unset shooterId - } - } - -- \ No newline at end of file diff --git a/Spigot-Server-Patches/Make-the-default-permission-message-configurable.patch b/Spigot-Server-Patches/Make-the-default-permission-message-configurable.patch index 93c726b3e7..1fcd586e0f 100644 --- a/Spigot-Server-Patches/Make-the-default-permission-message-configurable.patch +++ b/Spigot-Server-Patches/Make-the-default-permission-message-configurable.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Make the default permission message configurable diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java -index 8feb0efdcd..81987e4ad9 100644 +index 30f35304a..546a1cfe0 100644 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java @@ -0,0 +0,0 @@ import java.util.regex.Pattern; @@ -29,7 +29,7 @@ index 8feb0efdcd..81987e4ad9 100644 private static void savePlayerData() { savePlayerData = getBoolean("settings.save-player-data", savePlayerData); diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index dfa78653aa..b89486beb1 100644 +index 7e810e008..58b343259 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -0,0 +0,0 @@ public final class CraftServer implements Server { diff --git a/Spigot-Server-Patches/Mark-entities-as-being-ticked-when-notifying-navigat.patch b/Spigot-Server-Patches/Mark-entities-as-being-ticked-when-notifying-navigat.patch index c53070ad71..9fd5707ace 100644 --- a/Spigot-Server-Patches/Mark-entities-as-being-ticked-when-notifying-navigat.patch +++ b/Spigot-Server-Patches/Mark-entities-as-being-ticked-when-notifying-navigat.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Mark entities as being ticked when notifying navigation diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java -index 0761f705be..5df9b0ffda 100644 +index a5cce8f4e..8ea9b34a1 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 { @@ -13,7 +13,7 @@ index 0761f705be..5df9b0ffda 100644 if (VoxelShapes.c(voxelshape, voxelshape1, OperatorBoolean.NOT_SAME)) { + boolean wasTicking = this.tickingEntities; this.tickingEntities = true; // Paper - Iterator iterator = this.H.iterator(); + Iterator iterator = this.navigators.iterator(); while (iterator.hasNext()) { @@ -0,0 +0,0 @@ public class WorldServer extends World { diff --git a/Spigot-Server-Patches/Mob-Pathfinding-API.patch b/Spigot-Server-Patches/Mob-Pathfinding-API.patch index 4c074f89fa..2142e1ec89 100644 --- a/Spigot-Server-Patches/Mob-Pathfinding-API.patch +++ b/Spigot-Server-Patches/Mob-Pathfinding-API.patch @@ -7,7 +7,7 @@ Implements Pathfinding API for mobs diff --git a/src/main/java/com/destroystokyo/paper/entity/PaperPathfinder.java b/src/main/java/com/destroystokyo/paper/entity/PaperPathfinder.java new file mode 100644 -index 0000000000..f68a07cb96 +index 000000000..f68a07cb9 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/entity/PaperPathfinder.java @@ -0,0 +0,0 @@ @@ -123,7 +123,7 @@ index 0000000000..f68a07cb96 + } +} diff --git a/src/main/java/net/minecraft/server/NavigationAbstract.java b/src/main/java/net/minecraft/server/NavigationAbstract.java -index be6aa59749..66e10108d6 100644 +index bdd092e49..1cb03d464 100644 --- a/src/main/java/net/minecraft/server/NavigationAbstract.java +++ b/src/main/java/net/minecraft/server/NavigationAbstract.java @@ -0,0 +0,0 @@ public abstract class NavigationAbstract { @@ -158,11 +158,11 @@ index be6aa59749..66e10108d6 100644 - @Nullable + @Nullable public PathEntity getPathEntity() { return l(); } @Nullable // Paper - OBFHELPER - public PathEntity l() { + public PathEntity k() { return this.c; } @@ -0,0 +0,0 @@ public abstract class NavigationAbstract { - return this.c == null || this.c.b(); + return !this.m(); } + public void stopPathfinding() { o(); } // Paper - OBFHELPER @@ -170,7 +170,7 @@ index be6aa59749..66e10108d6 100644 this.c = null; } diff --git a/src/main/java/net/minecraft/server/PathEntity.java b/src/main/java/net/minecraft/server/PathEntity.java -index 312352ef84..dcb4e25080 100644 +index 312352ef8..dcb4e2508 100644 --- a/src/main/java/net/minecraft/server/PathEntity.java +++ b/src/main/java/net/minecraft/server/PathEntity.java @@ -0,0 +0,0 @@ import javax.annotation.Nullable; @@ -210,7 +210,7 @@ index 312352ef84..dcb4e25080 100644 return new Vec3D((double) pathpoint.a, (double) pathpoint.b, (double) pathpoint.c); diff --git a/src/main/java/net/minecraft/server/PathPoint.java b/src/main/java/net/minecraft/server/PathPoint.java -index b1db95daa9..18cdd2a6f2 100644 +index b1db95daa..18cdd2a6f 100644 --- a/src/main/java/net/minecraft/server/PathPoint.java +++ b/src/main/java/net/minecraft/server/PathPoint.java @@ -0,0 +0,0 @@ package net.minecraft.server; @@ -227,7 +227,7 @@ index b1db95daa9..18cdd2a6f2 100644 public int d = -1; public float e; diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java -index 5bf1cd06fa..53c2d154ed 100644 +index 5bf1cd06f..53c2d154e 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftMob.java @@ -0,0 +0,0 @@ import org.bukkit.loot.LootTable; diff --git a/Spigot-Server-Patches/Mob-Spawner-API-Enhancements.patch b/Spigot-Server-Patches/Mob-Spawner-API-Enhancements.patch index d5a658e6e5..9aba689083 100644 --- a/Spigot-Server-Patches/Mob-Spawner-API-Enhancements.patch +++ b/Spigot-Server-Patches/Mob-Spawner-API-Enhancements.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Mob Spawner API Enhancements diff --git a/src/main/java/net/minecraft/server/MobSpawnerAbstract.java b/src/main/java/net/minecraft/server/MobSpawnerAbstract.java -index 079099e30..b05f6c2f2 100644 +index 90ca1ee14..b179893a1 100644 --- a/src/main/java/net/minecraft/server/MobSpawnerAbstract.java +++ b/src/main/java/net/minecraft/server/MobSpawnerAbstract.java @@ -0,0 +0,0 @@ public abstract class MobSpawnerAbstract { diff --git a/Spigot-Server-Patches/Only-count-Natural-Spawned-mobs-towards-natural-spaw.patch b/Spigot-Server-Patches/Only-count-Natural-Spawned-mobs-towards-natural-spaw.patch index dd9d9d18fc..047a103852 100644 --- a/Spigot-Server-Patches/Only-count-Natural-Spawned-mobs-towards-natural-spaw.patch +++ b/Spigot-Server-Patches/Only-count-Natural-Spawned-mobs-towards-natural-spaw.patch @@ -17,7 +17,7 @@ This should fully solve all of the issues around it so that only natural influences natural spawns. diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index e9d1b47ad..59671c5c3 100644 +index 363676348..0a7e6fff1 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -0,0 +0,0 @@ public class PaperWorldConfig { @@ -38,7 +38,7 @@ index e9d1b47ad..59671c5c3 100644 public boolean asynchronous; public EngineMode engineMode; diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java -index 7c5349b17..0761f705b 100644 +index 0fd3cb54b..a5cce8f4e 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 { diff --git a/Spigot-Server-Patches/Optimize-Biome-Mob-Lookups-for-Mob-Spawning.patch b/Spigot-Server-Patches/Optimize-Biome-Mob-Lookups-for-Mob-Spawning.patch index b20c36a712..ebe7474834 100644 --- a/Spigot-Server-Patches/Optimize-Biome-Mob-Lookups-for-Mob-Spawning.patch +++ b/Spigot-Server-Patches/Optimize-Biome-Mob-Lookups-for-Mob-Spawning.patch @@ -6,24 +6,24 @@ Subject: [PATCH] Optimize Biome Mob Lookups for Mob Spawning Uses an EnumMap as well as a Set paired List for O(1) contains calls. diff --git a/src/main/java/net/minecraft/server/BiomeBase.java b/src/main/java/net/minecraft/server/BiomeBase.java -index 65daa76bc1..a86ece8344 100644 +index 253890e53..0102a170d 100644 --- a/src/main/java/net/minecraft/server/BiomeBase.java +++ b/src/main/java/net/minecraft/server/BiomeBase.java @@ -0,0 +0,0 @@ public abstract class BiomeBase { - protected final Map>> r = Maps.newHashMap(); - protected final List> s = Lists.newArrayList(); + protected final Map>> r = Maps.newHashMap(); + protected final List> s = Lists.newArrayList(); protected final Map, WorldGenFeatureConfiguration> t = Maps.newHashMap(); -- private final Map> u = Maps.newHashMap(); -+ private final java.util.EnumMap> u = Maps.newEnumMap(EnumCreatureType.class); // Paper - private final ThreadLocal v = ThreadLocal.withInitial(() -> { +- private final Map> v = Maps.newHashMap(); ++ private final java.util.EnumMap> v = Maps.newEnumMap(EnumCreatureType.class); // Paper + private final ThreadLocal w = ThreadLocal.withInitial(() -> { return (Long2FloatLinkedOpenHashMap) SystemUtils.a(() -> { Long2FloatLinkedOpenHashMap long2floatlinkedopenhashmap = new Long2FloatLinkedOpenHashMap(1024, 0.25F) { @@ -0,0 +0,0 @@ public abstract class BiomeBase { for (j = 0; j < i; ++j) { EnumCreatureType enumcreaturetype = aenumcreaturetype[j]; -- this.u.put(enumcreaturetype, Lists.newArrayList()); -+ this.u.put(enumcreaturetype, new MobList()); // Paper +- this.v.put(enumcreaturetype, Lists.newArrayList()); ++ this.v.put(enumcreaturetype, new MobList()); // Paper } } else { diff --git a/Spigot-Server-Patches/Optimize-Captured-TileEntity-Lookup.patch b/Spigot-Server-Patches/Optimize-Captured-TileEntity-Lookup.patch index 642578782d..2c3a542064 100644 --- a/Spigot-Server-Patches/Optimize-Captured-TileEntity-Lookup.patch +++ b/Spigot-Server-Patches/Optimize-Captured-TileEntity-Lookup.patch @@ -10,10 +10,10 @@ Optimize to check if the captured list even has values in it, and also to just do a get call since the value can never be null. diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java -index dad0c893f..dd2a9c6e5 100644 +index 61a3ddc4d..dfc695084 100644 --- a/src/main/java/net/minecraft/server/World.java +++ b/src/main/java/net/minecraft/server/World.java -@@ -0,0 +0,0 @@ public abstract class World implements IIBlockAccess, GeneratorAccess, AutoClose +@@ -0,0 +0,0 @@ public abstract class World implements GeneratorAccess, AutoCloseable { return null; } else { // CraftBukkit start @@ -29,5 +29,5 @@ index dad0c893f..dd2a9c6e5 100644 + //TileEntity tileentity = null; // Paper - move up if (this.tickingTileEntities) { - tileentity = this.A(blockposition); + tileentity = this.e(blockposition); -- \ No newline at end of file diff --git a/Spigot-Server-Patches/Optimize-Server-World-Map.patch b/Spigot-Server-Patches/Optimize-Server-World-Map.patch index bf2dc542c8..6ac5b80ef1 100644 --- a/Spigot-Server-Patches/Optimize-Server-World-Map.patch +++ b/Spigot-Server-Patches/Optimize-Server-World-Map.patch @@ -21,7 +21,7 @@ known NMS used methods, but we can add more if naughty plugins are found later. diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldMap.java b/src/main/java/com/destroystokyo/paper/PaperWorldMap.java new file mode 100644 -index 0000000000..6bb2f98b45 +index 000000000..6bb2f98b4 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/PaperWorldMap.java @@ -0,0 +0,0 @@ @@ -217,7 +217,7 @@ index 0000000000..6bb2f98b45 + } +} diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index de423de63b..a5447c6501 100644 +index ec575bc8f..3120f91de 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -0,0 +0,0 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant> f = Maps.newEnumMap(ImmutableMap.of(EnumDirection.NORTH, BlockRedstoneWire.NORTH, EnumDirection.EAST, BlockRedstoneWire.EAST, EnumDirection.SOUTH, BlockRedstoneWire.SOUTH, EnumDirection.WEST, BlockRedstoneWire.WEST)); protected static final VoxelShape[] g = new VoxelShape[]{Block.a(3.0D, 0.0D, 3.0D, 13.0D, 1.0D, 13.0D), Block.a(3.0D, 0.0D, 3.0D, 13.0D, 1.0D, 16.0D), Block.a(0.0D, 0.0D, 3.0D, 13.0D, 1.0D, 13.0D), Block.a(0.0D, 0.0D, 3.0D, 13.0D, 1.0D, 16.0D), Block.a(3.0D, 0.0D, 0.0D, 13.0D, 1.0D, 13.0D), Block.a(3.0D, 0.0D, 0.0D, 13.0D, 1.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 13.0D, 1.0D, 13.0D), Block.a(0.0D, 0.0D, 0.0D, 13.0D, 1.0D, 16.0D), Block.a(3.0D, 0.0D, 3.0D, 16.0D, 1.0D, 13.0D), Block.a(3.0D, 0.0D, 3.0D, 16.0D, 1.0D, 16.0D), Block.a(0.0D, 0.0D, 3.0D, 16.0D, 1.0D, 13.0D), Block.a(0.0D, 0.0D, 3.0D, 16.0D, 1.0D, 16.0D), Block.a(3.0D, 0.0D, 0.0D, 16.0D, 1.0D, 13.0D), Block.a(3.0D, 0.0D, 0.0D, 16.0D, 1.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 1.0D, 13.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 1.0D, 16.0D)}; - private boolean h = true; @@ -1124,10 +1124,10 @@ index cffb8de4f..337c03d1d 100644 c(iblockdata, world, blockposition); world.a(blockposition, false); diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java -index dc0d89ee6..6189935bd 100644 +index 8e3367435..e8f83627c 100644 --- a/src/main/java/net/minecraft/server/World.java +++ b/src/main/java/net/minecraft/server/World.java -@@ -0,0 +0,0 @@ public abstract class World implements IIBlockAccess, GeneratorAccess, AutoClose +@@ -0,0 +0,0 @@ public abstract class World implements GeneratorAccess, AutoCloseable { } @@ -1135,7 +1135,7 @@ index dc0d89ee6..6189935bd 100644 public void a(BlockPosition blockposition, Block block, BlockPosition blockposition1) { if (!this.isClientSide) { IBlockData iblockdata = this.getType(blockposition); -@@ -0,0 +0,0 @@ public abstract class World implements IIBlockAccess, GeneratorAccess, AutoClose +@@ -0,0 +0,0 @@ public abstract class World implements GeneratorAccess, AutoCloseable { return this.getBlockFacePower(blockposition.down(), EnumDirection.DOWN) > 0 ? true : (this.getBlockFacePower(blockposition.up(), EnumDirection.UP) > 0 ? true : (this.getBlockFacePower(blockposition.north(), EnumDirection.NORTH) > 0 ? true : (this.getBlockFacePower(blockposition.south(), EnumDirection.SOUTH) > 0 ? true : (this.getBlockFacePower(blockposition.west(), EnumDirection.WEST) > 0 ? true : this.getBlockFacePower(blockposition.east(), EnumDirection.EAST) > 0)))); } diff --git a/Spigot-Server-Patches/Per-Player-View-Distance-API-placeholders.patch b/Spigot-Server-Patches/Per-Player-View-Distance-API-placeholders.patch index e8f975abad..ce975c59a1 100644 --- a/Spigot-Server-Patches/Per-Player-View-Distance-API-placeholders.patch +++ b/Spigot-Server-Patches/Per-Player-View-Distance-API-placeholders.patch @@ -7,7 +7,7 @@ I hope to look at this more in-depth soon. It appears doable. However this should not block the update. diff --git a/src/main/java/net/minecraft/server/EntityEnderDragon.java b/src/main/java/net/minecraft/server/EntityEnderDragon.java -index c2f35f89d..c8c74f2b3 100644 +index 3df8e30ec..9acdd6e56 100644 --- a/src/main/java/net/minecraft/server/EntityEnderDragon.java +++ b/src/main/java/net/minecraft/server/EntityEnderDragon.java @@ -0,0 +0,0 @@ public class EntityEnderDragon extends EntityInsentient implements IMonster { @@ -20,10 +20,10 @@ index c2f35f89d..c8c74f2b3 100644 - final int viewDistance = player.getViewDistance(); // TODO apply view distance api patch + //final int viewDistance = player.getViewDistance(); // TODO apply view distance api patch // Paper end - double deltaX = this.locX - player.locX; - double deltaZ = this.locZ - player.locZ; + double deltaX = this.locX() - player.locX(); + double deltaZ = this.locZ() - player.locZ(); diff --git a/src/main/java/net/minecraft/server/EntityWither.java b/src/main/java/net/minecraft/server/EntityWither.java -index 18151acd5..8b3052b11 100644 +index 2e95069c1..8977c3516 100644 --- a/src/main/java/net/minecraft/server/EntityWither.java +++ b/src/main/java/net/minecraft/server/EntityWither.java @@ -0,0 +0,0 @@ public class EntityWither extends EntityMonster implements IRangedEntity { @@ -36,10 +36,10 @@ index 18151acd5..8b3052b11 100644 - final int viewDistance = player.getViewDistance(); // TODO apply view distance api patch + //final int viewDistance = player.getViewDistance(); // TODO apply view distance api patch // Paper end - double deltaX = this.locX - player.locX; - double deltaZ = this.locZ - player.locZ; + double deltaX = this.locX() - player.locX(); + double deltaZ = this.locZ() - player.locZ(); diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java -index 9fa55ef6f..e920545df 100644 +index 4b847ddd8..049ebf4c4 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 @@ public class CraftPlayer extends CraftHumanEntity implements Player { @@ -56,7 +56,7 @@ index 9fa55ef6f..e920545df 100644 + public void setViewDistance(int viewDistance) { + throw new NotImplementedException("Per-Player View Distance APIs need further understanding to properly implement"); // TODO + } - //Paper end + // Paper end // Spigot start -- \ No newline at end of file diff --git a/Spigot-Server-Patches/Performance-improvement-for-Chunk.getEntities.patch b/Spigot-Server-Patches/Performance-improvement-for-Chunk.getEntities.patch index 350381bdac..baeffdfe39 100644 --- a/Spigot-Server-Patches/Performance-improvement-for-Chunk.getEntities.patch +++ b/Spigot-Server-Patches/Performance-improvement-for-Chunk.getEntities.patch @@ -10,7 +10,7 @@ operation. This patch will reduce the load of plugins which for example implement custom moblimits and depend on Chunk.getEntities(). diff --git a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java -index b4551614..373bea4b 100644 +index 9dcba4d15..c8b49a389 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftChunk.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftChunk.java @@ -0,0 +0,0 @@ public class CraftChunk implements Chunk { diff --git a/Spigot-Server-Patches/PlayerDeathEvent-getItemsToKeep.patch b/Spigot-Server-Patches/PlayerDeathEvent-getItemsToKeep.patch index c05e64c2d6..ddd91052d2 100644 --- a/Spigot-Server-Patches/PlayerDeathEvent-getItemsToKeep.patch +++ b/Spigot-Server-Patches/PlayerDeathEvent-getItemsToKeep.patch @@ -8,7 +8,7 @@ Exposes a mutable array on items a player should keep on death Example Usage: https://gist.github.com/aikar/5bb202de6057a051a950ce1f29feb0b4 diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java -index a191d6246..18695d9b5 100644 +index cc9e69651..ca2398386 100644 --- a/src/main/java/net/minecraft/server/EntityPlayer.java +++ b/src/main/java/net/minecraft/server/EntityPlayer.java @@ -0,0 +0,0 @@ public class EntityPlayer extends EntityHuman implements ICrafting { diff --git a/Spigot-Server-Patches/PreSpawnerSpawnEvent.patch b/Spigot-Server-Patches/PreSpawnerSpawnEvent.patch index 0b8d9d2f09..f96b096661 100644 --- a/Spigot-Server-Patches/PreSpawnerSpawnEvent.patch +++ b/Spigot-Server-Patches/PreSpawnerSpawnEvent.patch @@ -9,22 +9,22 @@ SpawnerSpawnEvent gets called instead of the CreatureSpawnEvent for spawners. diff --git a/src/main/java/net/minecraft/server/MobSpawnerAbstract.java b/src/main/java/net/minecraft/server/MobSpawnerAbstract.java -index 6f5d8b0315..079099e308 100644 +index fe8bc7f75..90ca1ee14 100644 --- a/src/main/java/net/minecraft/server/MobSpawnerAbstract.java +++ b/src/main/java/net/minecraft/server/MobSpawnerAbstract.java @@ -0,0 +0,0 @@ public abstract class MobSpawnerAbstract { - org.bukkit.entity.EntityType type = org.bukkit.entity.EntityType.fromName(key); - if (type != null) { -- com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent event; -- event = new com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent( + org.bukkit.entity.EntityType type = org.bukkit.entity.EntityType.fromName(key); + if (type != null) { +- com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent event; +- event = new com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent( + com.destroystokyo.paper.event.entity.PreSpawnerSpawnEvent event; + event = new com.destroystokyo.paper.event.entity.PreSpawnerSpawnEvent( - MCUtil.toLocation(world, d3, d4, d5), - type, -- org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPAWNER + MCUtil.toLocation(world, d3, d4, d5), + type, +- org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPAWNER + MCUtil.toLocation(world, blockposition) - ); - if (!event.callEvent()) { - flag = true; + ); + if (!event.callEvent()) { + flag = true; -- \ No newline at end of file diff --git a/Spigot-Server-Patches/Preserve-old-flush-on-save-flag-for-reliable-regionf.patch b/Spigot-Server-Patches/Preserve-old-flush-on-save-flag-for-reliable-regionf.patch deleted file mode 100644 index 7bded1f6bf..0000000000 --- a/Spigot-Server-Patches/Preserve-old-flush-on-save-flag-for-reliable-regionf.patch +++ /dev/null @@ -1,21 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Spottedleaf -Date: Mon, 5 Aug 2019 08:24:01 -0700 -Subject: [PATCH] Preserve old flush on save flag for reliable regionfiles - -Originally this patch was in paper - -diff --git a/src/main/java/net/minecraft/server/RegionFile.java b/src/main/java/net/minecraft/server/RegionFile.java -index b487e8060..a8c8ace46 100644 ---- a/src/main/java/net/minecraft/server/RegionFile.java -+++ b/src/main/java/net/minecraft/server/RegionFile.java -@@ -0,0 +0,0 @@ public class RegionFile implements AutoCloseable { - } - - // Spigot start - Make region files reliable -- private static final boolean FLUSH_ON_SAVE = Boolean.getBoolean("spigot.flush-on-save"); -+ private static final boolean FLUSH_ON_SAVE = Boolean.getBoolean("spigot.flush-on-save") || Boolean.getBoolean("paper.flush-on-save"); // Paper - preserve old flag - private void syncRegionFile() throws IOException { - if (!FLUSH_ON_SAVE) { - return; --- \ No newline at end of file diff --git a/Spigot-Server-Patches/Prevent-Enderman-from-loading-chunks.patch b/Spigot-Server-Patches/Prevent-Enderman-from-loading-chunks.patch index ddb71c7ccb..e1a5007876 100644 --- a/Spigot-Server-Patches/Prevent-Enderman-from-loading-chunks.patch +++ b/Spigot-Server-Patches/Prevent-Enderman-from-loading-chunks.patch @@ -5,22 +5,22 @@ Subject: [PATCH] Prevent Enderman from loading chunks diff --git a/src/main/java/net/minecraft/server/EntityEnderman.java b/src/main/java/net/minecraft/server/EntityEnderman.java -index 2d29da8560..a94ed1ae08 100644 +index 9783576e3..13507edbc 100644 --- a/src/main/java/net/minecraft/server/EntityEnderman.java +++ b/src/main/java/net/minecraft/server/EntityEnderman.java @@ -0,0 +0,0 @@ public class EntityEnderman extends EntityMonster { - int j = MathHelper.floor(this.enderman.locY + random.nextDouble() * 3.0D); - int k = MathHelper.floor(this.enderman.locZ - 2.0D + random.nextDouble() * 4.0D); + int j = MathHelper.floor(this.enderman.locY() + random.nextDouble() * 3.0D); + int k = MathHelper.floor(this.enderman.locZ() - 2.0D + random.nextDouble() * 4.0D); BlockPosition blockposition = new BlockPosition(i, j, k); - IBlockData iblockdata = world.getType(blockposition); + IBlockData iblockdata = world.getTypeIfLoaded(blockposition); // Paper + if (iblockdata == null) return; // Paper Block block = iblockdata.getBlock(); - Vec3D vec3d = new Vec3D((double) MathHelper.floor(this.enderman.locX) + 0.5D, (double) j + 0.5D, (double) MathHelper.floor(this.enderman.locZ) + 0.5D); + Vec3D vec3d = new Vec3D((double) MathHelper.floor(this.enderman.locX()) + 0.5D, (double) j + 0.5D, (double) MathHelper.floor(this.enderman.locZ()) + 0.5D); Vec3D vec3d1 = new Vec3D((double) i + 0.5D, (double) j + 0.5D, (double) k + 0.5D); @@ -0,0 +0,0 @@ public class EntityEnderman extends EntityMonster { - int j = MathHelper.floor(this.a.locY + random.nextDouble() * 2.0D); - int k = MathHelper.floor(this.a.locZ - 1.0D + random.nextDouble() * 2.0D); + int j = MathHelper.floor(this.a.locY() + random.nextDouble() * 2.0D); + int k = MathHelper.floor(this.a.locZ() - 1.0D + random.nextDouble() * 2.0D); BlockPosition blockposition = new BlockPosition(i, j, k); - IBlockData iblockdata = world.getType(blockposition); + IBlockData iblockdata = world.getTypeIfLoaded(blockposition); // Paper diff --git a/Spigot-Server-Patches/Prevent-Mob-AI-Rules-from-Loading-Chunks.patch b/Spigot-Server-Patches/Prevent-Mob-AI-Rules-from-Loading-Chunks.patch index 93a9720d86..2990636709 100644 --- a/Spigot-Server-Patches/Prevent-Mob-AI-Rules-from-Loading-Chunks.patch +++ b/Spigot-Server-Patches/Prevent-Mob-AI-Rules-from-Loading-Chunks.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Prevent Mob AI Rules from Loading Chunks diff --git a/src/main/java/net/minecraft/server/PathfinderGoalRemoveBlock.java b/src/main/java/net/minecraft/server/PathfinderGoalRemoveBlock.java -index bf8ca9b32a..7ccb3d5c06 100644 +index bf8ca9b32..7ccb3d5c0 100644 --- a/src/main/java/net/minecraft/server/PathfinderGoalRemoveBlock.java +++ b/src/main/java/net/minecraft/server/PathfinderGoalRemoveBlock.java @@ -0,0 +0,0 @@ public class PathfinderGoalRemoveBlock extends PathfinderGoalGotoTarget { @@ -52,24 +52,25 @@ index bf8ca9b32a..7ccb3d5c06 100644 return ichunkaccess == null ? false : ichunkaccess.getType(blockposition).getBlock() == this.g && ichunkaccess.getType(blockposition.up()).isAir() && ichunkaccess.getType(blockposition.up(2)).isAir(); } diff --git a/src/main/java/net/minecraft/server/RandomPositionGenerator.java b/src/main/java/net/minecraft/server/RandomPositionGenerator.java -index 6e4da70b7e..643dc0241d 100644 +index d4cd50918..d6a3b9933 100644 --- a/src/main/java/net/minecraft/server/RandomPositionGenerator.java +++ b/src/main/java/net/minecraft/server/RandomPositionGenerator.java @@ -0,0 +0,0 @@ public class RandomPositionGenerator { } - blockposition2 = new BlockPosition((double) l + entitycreature.locX, (double) i1 + entitycreature.locY, (double) j1 + entitycreature.locZ); + blockposition2 = new BlockPosition((double) k1 + entitycreature.locX(), (double) l1 + entitycreature.locY(), (double) i2 + entitycreature.locZ()); + if (!entitycreature.world.isLoaded(blockposition2)) continue; // Paper - if ((!flag1 || entitycreature.a(blockposition2)) && navigationabstract.a(blockposition2)) { - if (!flag) { - blockposition2 = a(blockposition2, entitycreature); + if (blockposition2.getY() >= 0 && blockposition2.getY() <= entitycreature.world.getBuildHeight() && (!flag3 || entitycreature.a(blockposition2)) && (!flag2 || navigationabstract.a(blockposition2))) { + if (flag1) { + blockposition2 = a(blockposition2, random.nextInt(l + 1) + i1, entitycreature.world.getBuildHeight(), (blockposition3) -> { @@ -0,0 +0,0 @@ public class RandomPositionGenerator { - } + }); + } - private static boolean b(BlockPosition blockposition, EntityCreature entitycreature) { -- return entitycreature.world.getFluid(blockposition).a(TagsFluid.WATER); -+ Fluid fluid = entitycreature.world.getFluidIfLoaded(blockposition); // Paper -+ return fluid != null && fluid.a(TagsFluid.WATER); // Paper - } - } +- if (flag || !entitycreature.world.getFluid(blockposition2).a(TagsFluid.WATER)) { ++ Fluid fluid = entitycreature.world.getFluidIfLoaded(blockposition2); // Paper ++ if (flag || (fluid != null && !fluid.a(TagsFluid.WATER))) { // Paper + PathType pathtype = PathfinderNormal.b(entitycreature.world, blockposition2.getX(), blockposition2.getY(), blockposition2.getZ()); + + if (entitycreature.a(pathtype) == 0.0F) { -- \ No newline at end of file diff --git a/Spigot-Server-Patches/Prevent-chunk-loading-from-Fluid-Flowing.patch b/Spigot-Server-Patches/Prevent-chunk-loading-from-Fluid-Flowing.patch index 7f4b8a2e1b..2c29999edb 100644 --- a/Spigot-Server-Patches/Prevent-chunk-loading-from-Fluid-Flowing.patch +++ b/Spigot-Server-Patches/Prevent-chunk-loading-from-Fluid-Flowing.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Prevent chunk loading from Fluid Flowing diff --git a/src/main/java/net/minecraft/server/FluidTypeFlowing.java b/src/main/java/net/minecraft/server/FluidTypeFlowing.java -index 3e9433e283..4660b95389 100644 +index 3099a5e65..376dca188 100644 --- a/src/main/java/net/minecraft/server/FluidTypeFlowing.java +++ b/src/main/java/net/minecraft/server/FluidTypeFlowing.java @@ -0,0 +0,0 @@ public abstract class FluidTypeFlowing extends FluidType { @@ -25,7 +25,7 @@ index 3e9433e283..4660b95389 100644 - IBlockData iblockdata1 = iworldreader.getType(blockposition1); + IBlockData iblockdata1 = iworldreader.getTypeIfLoaded(blockposition1); // Paper + if (iblockdata1 == null) continue; // Paper - Fluid fluid = iblockdata1.p(); + Fluid fluid = iblockdata1.getFluid(); if (fluid.getType().a((FluidType) this) && this.a(enumdirection, (IBlockAccess) iworldreader, blockposition, iblockdata, blockposition1, iblockdata1)) { @@ -0,0 +0,0 @@ public abstract class FluidTypeFlowing extends FluidType { @@ -42,11 +42,10 @@ index 3e9433e283..4660b95389 100644 + continue; + } -- return Pair.of(iblockdata1, iblockdata1.p()); +- return Pair.of(iblockdata1, iblockdata1.getFluid()); - }); -+ pair = Pair.of(iblockdatax, iblockdatax.p()); ++ pair = Pair.of(iblockdatax, iblockdatax.getFluid()); + short2objectmap.put(short0, pair); -+ + } + // Paper end IBlockData iblockdata1 = (IBlockData) pair.getFirst(); @@ -59,7 +58,7 @@ index 3e9433e283..4660b95389 100644 - Pair pair = (Pair) short2objectmap.computeIfAbsent(short0, (j) -> { - IBlockData iblockdata1 = iworldreader.getType(blockposition1); - -- return Pair.of(iblockdata1, iblockdata1.p()); +- return Pair.of(iblockdata1, iblockdata1.getFluid()); - }); + // Paper start + Pair pair = (Pair) short2objectmap.get(short0); @@ -67,7 +66,7 @@ index 3e9433e283..4660b95389 100644 + IBlockData iblockdatax = iworldreader.getTypeIfLoaded(blockposition1); + if (iblockdatax == null) continue; + -+ pair = Pair.of(iblockdatax, iblockdatax.p()); ++ pair = Pair.of(iblockdatax, iblockdatax.getFluid()); + short2objectmap.put(short0, pair); + } + // Paper end diff --git a/Spigot-Server-Patches/Prevent-consuming-the-wrong-itemstack.patch b/Spigot-Server-Patches/Prevent-consuming-the-wrong-itemstack.patch index 1cdd00f657..f1b5605af8 100644 --- a/Spigot-Server-Patches/Prevent-consuming-the-wrong-itemstack.patch +++ b/Spigot-Server-Patches/Prevent-consuming-the-wrong-itemstack.patch @@ -5,11 +5,11 @@ Subject: [PATCH] Prevent consuming the wrong itemstack diff --git a/src/main/java/net/minecraft/server/EntityLiving.java b/src/main/java/net/minecraft/server/EntityLiving.java -index 31d14b19b..f675ad2f5 100644 +index 1b5acf77e..d04ea03bc 100644 --- a/src/main/java/net/minecraft/server/EntityLiving.java +++ b/src/main/java/net/minecraft/server/EntityLiving.java @@ -0,0 +0,0 @@ public abstract class EntityLiving extends Entity { - this.datawatcher.set(EntityLiving.ar, (byte) j); + this.datawatcher.set(EntityLiving.ao, (byte) j); } - public void c(EnumHand enumhand) { @@ -22,25 +22,25 @@ index 31d14b19b..f675ad2f5 100644 - if (!itemstack.isEmpty() && !this.isHandRaised()) { + if (!itemstack.isEmpty() && !this.isHandRaised() || forceUpdate) { // Paper use override flag this.activeItem = itemstack; - this.bo = itemstack.k(); + this.bl = itemstack.k(); if (!this.world.isClientSide) { @@ -0,0 +0,0 @@ public abstract class EntityLiving extends Entity { - - protected void q() { - if (!this.activeItem.isEmpty() && this.isHandRaised()) { -+ this.updateActiveItem(this.getRaisedHand(), true); // Paper - PlayerItemConsumeEvent event = null; // Paper - this.b(this.activeItem, 16); - // CraftBukkit start - fire PlayerItemConsumeEvent + this.clearActiveItem(); + } else { + if (!this.activeItem.isEmpty() && this.isHandRaised()) { ++ this.updateActiveItem(this.getRaisedHand(), true); // Paper + this.b(this.activeItem, 16); + // CraftBukkit start - fire PlayerItemConsumeEvent + ItemStack itemstack; @@ -0,0 +0,0 @@ public abstract class EntityLiving extends Entity { - this.a(this.getRaisedHand(), itemstack); - // CraftBukkit end - this.dp(); -- // Paper start - if the replacement is anything but the default, update the client inventory -- if (this instanceof EntityPlayer && !com.google.common.base.Objects.equal(defaultReplacement, itemstack)) { -+ // Paper start -+ if (this instanceof EntityPlayer) { - ((EntityPlayer) this).getBukkitEntity().updateInventory(); - } - // Paper end + this.a(this.getRaisedHand(), itemstack); + // CraftBukkit end + this.dH(); +- // Paper start - if the replacement is anything but the default, update the client inventory +- if (this instanceof EntityPlayer && !com.google.common.base.Objects.equal(defaultReplacement, itemstack)) { ++ // Paper start ++ if (this instanceof EntityPlayer) { + ((EntityPlayer) this).getBukkitEntity().updateInventory(); + } + // Paper end -- \ No newline at end of file diff --git a/Spigot-Server-Patches/Prevent-mob-spawning-from-loading-generating-chunks.patch b/Spigot-Server-Patches/Prevent-mob-spawning-from-loading-generating-chunks.patch index 8b64642966..5336f42217 100644 --- a/Spigot-Server-Patches/Prevent-mob-spawning-from-loading-generating-chunks.patch +++ b/Spigot-Server-Patches/Prevent-mob-spawning-from-loading-generating-chunks.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Prevent mob spawning from loading/generating chunks also prevents if out of world border bounds diff --git a/src/main/java/net/minecraft/server/SpawnerCreature.java b/src/main/java/net/minecraft/server/SpawnerCreature.java -index 30da877d53..c6ea37ffbd 100644 +index 487114367..bcca7faed 100644 --- a/src/main/java/net/minecraft/server/SpawnerCreature.java +++ b/src/main/java/net/minecraft/server/SpawnerCreature.java @@ -0,0 +0,0 @@ public final class SpawnerCreature { @@ -23,11 +23,11 @@ index 30da877d53..c6ea37ffbd 100644 @@ -0,0 +0,0 @@ public final class SpawnerCreature { if (entityhuman != null) { - double d0 = entityhuman.e((double) f, (double) k, (double) f1); + double d0 = entityhuman.g((double) f, (double) k, (double) f1); - if (d0 > 576.0D && !blockposition.a((IPosition) (new Vec3D((double) f, (double) k, (double) f1)), 24.0D)) { + if (d0 > 576.0D && !blockposition.a((IPosition) (new Vec3D((double) f, (double) k, (double) f1)), 24.0D) && world.isLoadedAndInBounds(blockposition_mutableblockposition)) { // Paper - don't load chunks for mob spawn ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(blockposition_mutableblockposition); - if (Objects.equals(chunkcoordintpair, chunk.getPos()) || world.getChunkProvider().a(chunkcoordintpair)) { + if (Objects.equals(chunkcoordintpair, chunk.getPos()) || worldserver.getChunkProvider().a(chunkcoordintpair)) { -- \ No newline at end of file diff --git a/Spigot-Server-Patches/Prevent-rayTrace-from-loading-chunks.patch b/Spigot-Server-Patches/Prevent-rayTrace-from-loading-chunks.patch index fa9b9fae81..39c1558874 100644 --- a/Spigot-Server-Patches/Prevent-rayTrace-from-loading-chunks.patch +++ b/Spigot-Server-Patches/Prevent-rayTrace-from-loading-chunks.patch @@ -7,7 +7,7 @@ ray tracing into an unloaded chunk should be treated as a miss this saves a ton of lag for when AI tries to raytrace near unloaded chunks. diff --git a/src/main/java/net/minecraft/server/IBlockAccess.java b/src/main/java/net/minecraft/server/IBlockAccess.java -index 0dff023529..29cdc00875 100644 +index 0dff02352..29cdc0087 100644 --- a/src/main/java/net/minecraft/server/IBlockAccess.java +++ b/src/main/java/net/minecraft/server/IBlockAccess.java @@ -0,0 +0,0 @@ public interface IBlockAccess { diff --git a/Spigot-Server-Patches/Reduce-sync-loads.patch b/Spigot-Server-Patches/Reduce-sync-loads.patch deleted file mode 100644 index ba84113fd2..0000000000 --- a/Spigot-Server-Patches/Reduce-sync-loads.patch +++ /dev/null @@ -1,347 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Spottedleaf -Date: Fri, 19 Jul 2019 03:29:14 -0700 -Subject: [PATCH] Reduce sync loads - -This reduces calls to getChunkAt which would load chunks. - -This patch also adds a tool to find calls which are doing this, however -it must be enabled by setting the startup flag -Dpaper.debug-sync-loads=true - -To get a debug log for sync loads, the command is /paper syncloadinfo - -diff --git a/src/main/java/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java -index 09efbf725..132397b3f 100644 ---- a/src/main/java/com/destroystokyo/paper/PaperCommand.java -+++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java -@@ -0,0 +0,0 @@ - package com.destroystokyo.paper; - -+import com.destroystokyo.paper.io.SyncLoadFinder; - import com.google.common.base.Functions; - import com.google.common.collect.Iterables; - import com.google.common.collect.Lists; - import com.google.common.collect.Maps; -+import com.google.gson.JsonObject; -+import com.google.gson.internal.Streams; -+import com.google.gson.stream.JsonWriter; - import net.minecraft.server.*; - import org.apache.commons.lang3.tuple.MutablePair; - import org.apache.commons.lang3.tuple.Pair; -@@ -0,0 +0,0 @@ import org.bukkit.craftbukkit.CraftWorld; - import org.bukkit.entity.Player; - - import java.io.File; -+import java.io.FileOutputStream; -+import java.io.PrintStream; -+import java.io.StringWriter; - import java.time.LocalDateTime; - import java.time.format.DateTimeFormatter; - import java.util.*; -@@ -0,0 +0,0 @@ public class PaperCommand extends Command { - case "chunkinfo": - doChunkInfo(sender, args); - break; -+ case "syncloadinfo": -+ this.doSyncLoadInfo(sender, args); -+ break; - case "ver": - case "version": - Command ver = org.bukkit.Bukkit.getServer().getCommandMap().getCommand("version"); -@@ -0,0 +0,0 @@ public class PaperCommand extends Command { - return true; - } - -+ private void doSyncLoadInfo(CommandSender sender, String[] args) { -+ if (!SyncLoadFinder.ENABLED) { -+ sender.sendMessage(ChatColor.RED + "This command requires the server startup flag '-Dpaper.debug-sync-loads=true' to be set."); -+ return; -+ } -+ File file = new File(new File(new File("."), "debug"), -+ "sync-load-info" + DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss").format(LocalDateTime.now()) + ".txt"); -+ file.getParentFile().mkdirs(); -+ sender.sendMessage(ChatColor.GREEN + "Writing sync load info to " + file.toString()); -+ -+ -+ try { -+ final JsonObject data = SyncLoadFinder.serialize(); -+ -+ StringWriter stringWriter = new StringWriter(); -+ JsonWriter jsonWriter = new JsonWriter(stringWriter); -+ jsonWriter.setIndent(" "); -+ jsonWriter.setLenient(false); -+ Streams.write(data, jsonWriter); -+ -+ String fileData = stringWriter.toString(); -+ -+ try ( -+ PrintStream out = new PrintStream(new FileOutputStream(file), false, "UTF-8") -+ ) { -+ out.print(fileData); -+ } -+ sender.sendMessage(ChatColor.GREEN + "Successfully written sync load information!"); -+ } catch (Throwable thr) { -+ sender.sendMessage(ChatColor.RED + "Failed to write sync load information"); -+ thr.printStackTrace(); -+ } -+ } -+ - private void doChunkInfo(CommandSender sender, String[] args) { - List worlds; - if (args.length < 2 || args[1].equals("*")) { -diff --git a/src/main/java/com/destroystokyo/paper/io/SyncLoadFinder.java b/src/main/java/com/destroystokyo/paper/io/SyncLoadFinder.java -new file mode 100644 -index 000000000..59aec1032 ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/io/SyncLoadFinder.java -@@ -0,0 +0,0 @@ -+package com.destroystokyo.paper.io; -+ -+import com.google.gson.JsonArray; -+import com.google.gson.JsonObject; -+import com.mojang.datafixers.util.Pair; -+import it.unimi.dsi.fastutil.longs.Long2IntMap; -+import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; -+import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; -+import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -+import net.minecraft.server.World; -+ -+import java.util.ArrayList; -+import java.util.List; -+import java.util.Map; -+import java.util.WeakHashMap; -+ -+public class SyncLoadFinder { -+ -+ public static final boolean ENABLED = Boolean.getBoolean("paper.debug-sync-loads"); -+ -+ private static final WeakHashMap> SYNC_LOADS = new WeakHashMap<>(); -+ -+ private static final class SyncLoadInformation { -+ -+ public int times; -+ -+ public final Long2IntOpenHashMap coordinateTimes = new Long2IntOpenHashMap(); -+ } -+ -+ public static void logSyncLoad(final World world, final int chunkX, final int chunkZ) { -+ if (!ENABLED) { -+ return; -+ } -+ -+ final ThrowableWithEquals stacktrace = new ThrowableWithEquals(Thread.currentThread().getStackTrace()); -+ -+ SYNC_LOADS.compute(world, (final World keyInMap, Object2ObjectOpenHashMap map) -> { -+ if (map == null) { -+ map = new Object2ObjectOpenHashMap<>(); -+ } -+ -+ map.compute(stacktrace, (ThrowableWithEquals keyInMap0, SyncLoadInformation valueInMap) -> { -+ if (valueInMap == null) { -+ valueInMap = new SyncLoadInformation(); -+ } -+ -+ ++valueInMap.times; -+ -+ valueInMap.coordinateTimes.compute(IOUtil.getCoordinateKey(chunkX, chunkZ), (Long keyInMap1, Integer valueInMap1) -> { -+ return valueInMap1 == null ? Integer.valueOf(1) : Integer.valueOf(valueInMap1.intValue() + 1); -+ }); -+ -+ return valueInMap; -+ }); -+ -+ return map; -+ }); -+ } -+ -+ public static JsonObject serialize() { -+ final JsonObject ret = new JsonObject(); -+ -+ final JsonArray worldsData = new JsonArray(); -+ -+ for (final Map.Entry> entry : SYNC_LOADS.entrySet()) { -+ final World world = entry.getKey(); -+ -+ final JsonObject worldData = new JsonObject(); -+ -+ worldData.addProperty("name", world.getWorld().getName()); -+ -+ final List> data = new ArrayList<>(); -+ -+ entry.getValue().forEach((ThrowableWithEquals stacktrace, SyncLoadInformation times) -> { -+ data.add(new Pair<>(stacktrace, times)); -+ }); -+ -+ data.sort((Pair pair1, Pair pair2) -> { -+ return Integer.compare(pair2.getSecond().times, pair1.getSecond().times); // reverse order -+ }); -+ -+ final JsonArray stacktraces = new JsonArray(); -+ -+ for (Pair pair : data) { -+ final JsonObject stacktrace = new JsonObject(); -+ -+ stacktrace.addProperty("times", pair.getSecond().times); -+ -+ final JsonArray traces = new JsonArray(); -+ -+ for (StackTraceElement element : pair.getFirst().stacktrace) { -+ traces.add(String.valueOf(element)); -+ } -+ -+ stacktrace.add("stacktrace", traces); -+ -+ final JsonArray coordinates = new JsonArray(); -+ -+ for (Long2IntMap.Entry coordinate : pair.getSecond().coordinateTimes.long2IntEntrySet()) { -+ final long key = coordinate.getLongKey(); -+ final int times = coordinate.getIntValue(); -+ coordinates.add("(" + IOUtil.getCoordinateX(key) + "," + IOUtil.getCoordinateZ(key) + "): " + times); -+ } -+ -+ stacktrace.add("coordinates", coordinates); -+ -+ stacktraces.add(stacktrace); -+ } -+ -+ -+ worldData.add("stacktraces", stacktraces); -+ worldsData.add(worldData); -+ } -+ -+ ret.add("worlds", worldsData); -+ -+ return ret; -+ } -+ -+ static final class ThrowableWithEquals { -+ -+ private final StackTraceElement[] stacktrace; -+ private final int hash; -+ -+ public ThrowableWithEquals(final StackTraceElement[] stacktrace) { -+ this.stacktrace = stacktrace; -+ this.hash = ThrowableWithEquals.hash(stacktrace); -+ } -+ -+ public static int hash(final StackTraceElement[] stacktrace) { -+ int hash = 0; -+ -+ for (int i = 0; i < stacktrace.length; ++i) { -+ hash *= 31; -+ hash += stacktrace[i].hashCode(); -+ } -+ -+ return hash; -+ } -+ -+ @Override -+ public int hashCode() { -+ return this.hash; -+ } -+ -+ @Override -+ public boolean equals(final Object obj) { -+ if (obj == null || obj.getClass() != this.getClass()) { -+ return false; -+ } -+ -+ final ThrowableWithEquals other = (ThrowableWithEquals)obj; -+ final StackTraceElement[] otherStackTrace = other.stacktrace; -+ -+ if (this.stacktrace.length != otherStackTrace.length) { -+ return false; -+ } -+ -+ if (this == obj) { -+ return true; -+ } -+ -+ for (int i = 0; i < this.stacktrace.length; ++i) { -+ if (!this.stacktrace[i].equals(otherStackTrace[i])) { -+ return false; -+ } -+ } -+ -+ return true; -+ } -+ } -+} -diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java -index 277c2245d..8d7971ad8 100644 ---- a/src/main/java/net/minecraft/server/ChunkProviderServer.java -+++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java -@@ -0,0 +0,0 @@ public class ChunkProviderServer extends IChunkProvider { - this.world.asyncChunkTaskManager.raisePriority(x, z, com.destroystokyo.paper.io.PrioritizedTaskQueue.HIGHEST_PRIORITY); - com.destroystokyo.paper.io.chunk.ChunkTaskManager.pushChunkWait(this.world, x, z); - // Paper end -+ com.destroystokyo.paper.io.SyncLoadFinder.logSyncLoad(this.world, x, z); // Paper - sync load info - this.world.timings.chunkAwait.startTiming(); // Paper - this.serverThreadQueue.awaitTasks(completablefuture::isDone); - com.destroystokyo.paper.io.chunk.ChunkTaskManager.popChunkWait(); // Paper - async chunk debug -diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java -index b81b37445..9d29fc8ca 100644 ---- a/src/main/java/net/minecraft/server/World.java -+++ b/src/main/java/net/minecraft/server/World.java -@@ -0,0 +0,0 @@ public abstract class World implements IIBlockAccess, GeneratorAccess, AutoClose - } - - public boolean n(BlockPosition blockposition) { -- return isOutsideWorld(blockposition) ? false : this.chunkProvider.b(blockposition.getX() >> 4, blockposition.getZ() >> 4); -+ return isOutsideWorld(blockposition) ? false : this.isLoaded(blockposition); // Paper - reduce sync loads - } - - public boolean a(BlockPosition blockposition, Entity entity) { - if (isOutsideWorld(blockposition)) { - return false; - } else { -- IChunkAccess ichunkaccess = this.getChunkAt(blockposition.getX() >> 4, blockposition.getZ() >> 4, ChunkStatus.FULL, false); -+ IChunkAccess ichunkaccess = this.getChunkIfLoadedImmediately(blockposition.getX() >> 4, blockposition.getZ() >> 4); // Paper - reduce sync loads - - return ichunkaccess == null ? false : ichunkaccess.getType(blockposition).a((IBlockAccess) this, blockposition, entity); - } -@@ -0,0 +0,0 @@ public abstract class World implements IIBlockAccess, GeneratorAccess, AutoClose - - for (int i1 = i; i1 <= j; ++i1) { - for (int j1 = k; j1 <= l; ++j1) { -- Chunk chunk = this.getChunkProvider().getChunkAt(i1, j1, false); -+ Chunk chunk = (Chunk)this.getChunkIfLoadedImmediately(i1, j1); // Paper - - if (chunk != null) { - chunk.a(entity, axisalignedbb, list, predicate); -@@ -0,0 +0,0 @@ public abstract class World implements IIBlockAccess, GeneratorAccess, AutoClose - - for (int i1 = i; i1 < j; ++i1) { - for (int j1 = k; j1 < l; ++j1) { -- Chunk chunk = this.getChunkProvider().getChunkAt(i1, j1, false); -+ Chunk chunk = (Chunk)this.getChunkIfLoadedImmediately(i1, j1); // Paper - - if (chunk != null) { - chunk.a(entitytypes, axisalignedbb, list, predicate); -@@ -0,0 +0,0 @@ public abstract class World implements IIBlockAccess, GeneratorAccess, AutoClose - - for (int i1 = i; i1 < j; ++i1) { - for (int j1 = k; j1 < l; ++j1) { -- Chunk chunk = ichunkprovider.getChunkAt(i1, j1, false); -+ Chunk chunk = (Chunk)this.getChunkIfLoadedImmediately(i1, j1); // Paper - - if (chunk != null) { - chunk.a(oclass, axisalignedbb, list, predicate); -diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java -index 3a6745dcc..b7878201b 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 { - }; - public final com.destroystokyo.paper.io.chunk.ChunkTaskManager asyncChunkTaskManager; - // Paper end -+ // Paper start -+ @Override -+ public boolean isChunkLoaded(int x, int z) { -+ return this.getChunkProvider().getChunkAtIfLoadedImmediately(x, z) != null; -+ } -+ // Paper end - - // Add env and gen to constructor - public WorldServer(MinecraftServer minecraftserver, Executor executor, WorldNBTStorage worldnbtstorage, WorldData worlddata, DimensionManager dimensionmanager, GameProfilerFiller gameprofilerfiller, WorldLoadListener worldloadlistener, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen) { --- \ No newline at end of file diff --git a/Spigot-Server-Patches/Reset-players-airTicks-on-respawn.patch b/Spigot-Server-Patches/Reset-players-airTicks-on-respawn.patch index 8f0071af66..b4f21826e3 100644 --- a/Spigot-Server-Patches/Reset-players-airTicks-on-respawn.patch +++ b/Spigot-Server-Patches/Reset-players-airTicks-on-respawn.patch @@ -5,19 +5,19 @@ Subject: [PATCH] Reset players airTicks on respawn diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java -index 4833a9652..19bbcc043 100644 +index 5d31068d7..365984bb8 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 INamableTileEntity, ICommandListener, Ke } -+ public int getMaxAirTicks() { return bp(); } // Paper - OBFHELPER - public int bp() { ++ public int getMaxAirTicks() { return bw(); } // Paper - OBFHELPER + public int bw() { return 300; } diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java -index ad5dcab78..ff398e5ed 100644 +index bdb73b68a..c15543a5c 100644 --- a/src/main/java/net/minecraft/server/EntityPlayer.java +++ b/src/main/java/net/minecraft/server/EntityPlayer.java @@ -0,0 +0,0 @@ public class EntityPlayer extends EntityHuman implements ICrafting { diff --git a/Spigot-Server-Patches/Restore-custom-InventoryHolder-support.patch b/Spigot-Server-Patches/Restore-custom-InventoryHolder-support.patch index 7c1632bbff..c09cf31066 100644 --- a/Spigot-Server-Patches/Restore-custom-InventoryHolder-support.patch +++ b/Spigot-Server-Patches/Restore-custom-InventoryHolder-support.patch @@ -17,7 +17,7 @@ will always work as intended in the past, those without will create implementati based inventories. diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftInventoryCreator.java b/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftInventoryCreator.java -index 9957ed0402..ae280dd40b 100644 +index 9957ed040..ae280dd40 100644 --- a/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftInventoryCreator.java +++ b/src/main/java/org/bukkit/craftbukkit/inventory/util/CraftInventoryCreator.java @@ -0,0 +0,0 @@ public final class CraftInventoryCreator { diff --git a/Spigot-Server-Patches/Server-Tick-Events.patch b/Spigot-Server-Patches/Server-Tick-Events.patch index 16b56f930a..1d42047b2e 100644 --- a/Spigot-Server-Patches/Server-Tick-Events.patch +++ b/Spigot-Server-Patches/Server-Tick-Events.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Server Tick Events Fires event at start and end of a server tick diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index a7b63ab421..24dccfb7a1 100644 +index 8fc55d31a..c023848c7 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -0,0 +0,0 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant= 600) { + ++this.bC; + if (this.bC >= 600) { this.startDrownedConversion(300); + this.lastTick = MinecraftServer.currentTick; // Paper - Make sure this is set at start of process - GH-1887 } } else { - this.bF = -1; + this.bC = -1; -- \ No newline at end of file diff --git a/Spigot-Server-Patches/Strip-private-area-unicode-characters-from-signs.patch b/Spigot-Server-Patches/Strip-private-area-unicode-characters-from-signs.patch index 7cf3d9d533..c0a93fb832 100644 --- a/Spigot-Server-Patches/Strip-private-area-unicode-characters-from-signs.patch +++ b/Spigot-Server-Patches/Strip-private-area-unicode-characters-from-signs.patch @@ -20,11 +20,11 @@ think of no reason to use it. Fixes GH-1571 diff --git a/src/main/java/net/minecraft/server/TileEntitySign.java b/src/main/java/net/minecraft/server/TileEntitySign.java -index 74fede54e1..1b01792730 100644 +index 03f6ddf00..4c2273497 100644 --- a/src/main/java/net/minecraft/server/TileEntitySign.java +++ b/src/main/java/net/minecraft/server/TileEntitySign.java @@ -0,0 +0,0 @@ public class TileEntitySign extends TileEntity implements ICommandListener { // - private final String[] k = new String[4]; + private final String[] g = new String[4]; private EnumColor color; + // Paper start - Strip invalid unicode from signs on load @@ -81,7 +81,7 @@ index 74fede54e1..1b01792730 100644 //IChatBaseComponent ichatbasecomponent = IChatBaseComponent.ChatSerializer.a(s.isEmpty() ? "\"\"" : s); // Paper - move down - the old format might throw a json error @@ -0,0 +0,0 @@ public class TileEntitySign extends TileEntity implements ICommandListener { // - this.k[i] = null; + this.g[i] = null; } + if (ranUnicodeRemoval) this.privateUnicodeRemoved = true; // Paper - Flag to write NBT diff --git a/Spigot-Server-Patches/Support-Overriding-World-Seeds.patch b/Spigot-Server-Patches/Support-Overriding-World-Seeds.patch index f9215465d5..0a3cfe2dbc 100644 --- a/Spigot-Server-Patches/Support-Overriding-World-Seeds.patch +++ b/Spigot-Server-Patches/Support-Overriding-World-Seeds.patch @@ -15,7 +15,7 @@ This seed will end up being saved to the world data file, so it is a permanent change in that it won't go back if you remove it from paper.yml diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java -index a13c8c68b6..ea3695b021 100644 +index 214b577b3..559e6b42b 100644 --- a/src/main/java/com/destroystokyo/paper/PaperConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperConfig.java @@ -0,0 +0,0 @@ import java.lang.reflect.Modifier; @@ -25,7 +25,7 @@ index a13c8c68b6..ea3695b021 100644 +import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.logging.Level; - import java.util.logging.Logger; + import java.util.regex.Pattern; @@ -0,0 +0,0 @@ import com.google.common.collect.Lists; import net.minecraft.server.MinecraftServer; import org.bukkit.Bukkit; @@ -59,7 +59,7 @@ index a13c8c68b6..ea3695b021 100644 + } } diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 8b08ebb7d5..ab72e5cd36 100644 +index 206a4ad64..ec575bc8f 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -0,0 +0,0 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant 200) { + } else if (this.g.bD > 200) { World world = this.g.world; // CraftBukkit start @@ -97,13 +97,13 @@ index 594cc70def..0bd80e5623 100644 @Override public boolean a() { -- return this.a.isBaby() ? false : (this.a.dV() ? true : (this.a.getRandom().nextInt(700) != 0 ? false : !this.a.dX().a((IPosition) this.a.getPositionVector(), 64.0D))); -+ return this.a.isBaby() ? false : (this.a.dW() ? true : (this.a.getRandom().nextInt(700) != 0 ? false : !this.a.dY().a((IPosition) this.a.getPositionVector(), 64.0D))) && new com.destroystokyo.paper.event.entity.TurtleGoHomeEvent((org.bukkit.entity.Turtle) this.a.getBukkitEntity()).callEvent(); // Paper +- return this.a.isBaby() ? false : (this.a.eq() ? true : (this.a.getRandom().nextInt(700) != 0 ? false : !this.a.es().a((IPosition) this.a.getPositionVector(), 64.0D))); ++ return this.a.isBaby() ? false : (this.a.eq() ? true : (this.a.getRandom().nextInt(700) != 0 ? false : !this.a.es().a((IPosition) this.a.getPositionVector(), 64.0D))) && new com.destroystokyo.paper.event.entity.TurtleGoHomeEvent((org.bukkit.entity.Turtle) this.a.getBukkitEntity()).callEvent(); // Paper } @Override diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftTurtle.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftTurtle.java -index 123a2c75ca..8edcf7af65 100644 +index 123a2c75c..8edcf7af6 100644 --- a/src/main/java/org/bukkit/craftbukkit/entity/CraftTurtle.java +++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftTurtle.java @@ -0,0 +0,0 @@ diff --git a/Spigot-Server-Patches/Update-entity-Metadata-for-all-tracked-players.patch b/Spigot-Server-Patches/Update-entity-Metadata-for-all-tracked-players.patch index c32319dc9d..3e768a689a 100644 --- a/Spigot-Server-Patches/Update-entity-Metadata-for-all-tracked-players.patch +++ b/Spigot-Server-Patches/Update-entity-Metadata-for-all-tracked-players.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Update entity Metadata for all tracked players diff --git a/src/main/java/net/minecraft/server/EntityTrackerEntry.java b/src/main/java/net/minecraft/server/EntityTrackerEntry.java -index cd7e0299ac..460c687d3e 100644 +index 3ff7a7b4a..3a88c9a67 100644 --- a/src/main/java/net/minecraft/server/EntityTrackerEntry.java +++ b/src/main/java/net/minecraft/server/EntityTrackerEntry.java @@ -0,0 +0,0 @@ public class EntityTrackerEntry { @@ -22,7 +22,7 @@ index cd7e0299ac..460c687d3e 100644 this.f.accept(packet); if (this.tracker instanceof EntityPlayer) { diff --git a/src/main/java/net/minecraft/server/PlayerConnection.java b/src/main/java/net/minecraft/server/PlayerConnection.java -index b030269f0a..79054b4519 100644 +index bd2ee459f..17eeecaf4 100644 --- a/src/main/java/net/minecraft/server/PlayerConnection.java +++ b/src/main/java/net/minecraft/server/PlayerConnection.java @@ -0,0 +0,0 @@ public class PlayerConnection implements PacketListenerPlayIn { diff --git a/Spigot-Server-Patches/Use-ChunkStatus-cache-when-saving-protochunks.patch b/Spigot-Server-Patches/Use-ChunkStatus-cache-when-saving-protochunks.patch index 34001d0a86..29b11ff185 100644 --- a/Spigot-Server-Patches/Use-ChunkStatus-cache-when-saving-protochunks.patch +++ b/Spigot-Server-Patches/Use-ChunkStatus-cache-when-saving-protochunks.patch @@ -7,7 +7,7 @@ The cache should contain the chunk status when saving. If not it will load it. diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java -index eb49e9021..b04657356 100644 +index 4379434f6..93729eea2 100644 --- a/src/main/java/net/minecraft/server/PlayerChunkMap.java +++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java @@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { diff --git a/Spigot-Server-Patches/Use-Vanilla-Minecart-Speeds.patch b/Spigot-Server-Patches/Use-Vanilla-Minecart-Speeds.patch index e5872a8d95..f1564f77d5 100644 --- a/Spigot-Server-Patches/Use-Vanilla-Minecart-Speeds.patch +++ b/Spigot-Server-Patches/Use-Vanilla-Minecart-Speeds.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Use Vanilla Minecart Speeds CraftBukkit changed the values on flying speed, restore back to vanilla diff --git a/src/main/java/net/minecraft/server/EntityMinecartAbstract.java b/src/main/java/net/minecraft/server/EntityMinecartAbstract.java -index e1a684b370..6df2930e2d 100644 +index 1b64ad824..4388186db 100644 --- a/src/main/java/net/minecraft/server/EntityMinecartAbstract.java +++ b/src/main/java/net/minecraft/server/EntityMinecartAbstract.java @@ -0,0 +0,0 @@ public abstract class EntityMinecartAbstract extends Entity { diff --git a/Spigot-Server-Patches/Use-getChunkIfLoadedImmediately-in-places.patch b/Spigot-Server-Patches/Use-getChunkIfLoadedImmediately-in-places.patch deleted file mode 100644 index fb63b05067..0000000000 --- a/Spigot-Server-Patches/Use-getChunkIfLoadedImmediately-in-places.patch +++ /dev/null @@ -1,81 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Spottedleaf -Date: Mon, 8 Jul 2019 00:13:36 -0700 -Subject: [PATCH] Use getChunkIfLoadedImmediately in places - -This prevents us from hitting chunk loads for chunks at or less-than -ticket level 33 (yes getChunkIfLoaded will actually perform a chunk -load in that case). - -diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java -index 55c73ffca..e8def7f81 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 INamableTileEntity, ICommandListener, Ke - } - - public boolean isChunkLoaded() { -- return world.isChunkLoaded((int) Math.floor(this.locX) >> 4, (int) Math.floor(this.locZ) >> 4); -+ return world.getChunkIfLoadedImmediately((int) Math.floor(this.locX) >> 4, (int) Math.floor(this.locZ) >> 4) != null; // Paper - } - // CraftBukkit end - -diff --git a/src/main/java/net/minecraft/server/PlayerConnection.java b/src/main/java/net/minecraft/server/PlayerConnection.java -index 4a16d6c14..ae17cdf23 100644 ---- a/src/main/java/net/minecraft/server/PlayerConnection.java -+++ b/src/main/java/net/minecraft/server/PlayerConnection.java -@@ -0,0 +0,0 @@ public class PlayerConnection implements PacketListenerPlayIn { - speed = player.abilities.walkSpeed * 10f; - } - // Paper start - Prevent moving into unloaded chunks -- if (player.world.paperConfig.preventMovingIntoUnloadedChunks && (this.player.locX != toX || this.player.locZ != toZ) && !worldserver.isChunkLoaded((int) Math.floor(toX) >> 4, (int) Math.floor(toZ) >> 4)) { -+ if (player.world.paperConfig.preventMovingIntoUnloadedChunks && (this.player.locX != toX || this.player.locZ != toZ) && worldserver.getChunkIfLoadedImmediately((int) Math.floor(toX) >> 4, (int) Math.floor(toZ) >> 4) == null) { // Paper - use getIfLoadedImmediately - this.internalTeleport(this.player.locX, this.player.locY, this.player.locZ, this.player.yaw, this.player.pitch, Collections.emptySet()); - return; - } -diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java -index ab98c7b79..b81b37445 100644 ---- a/src/main/java/net/minecraft/server/World.java -+++ b/src/main/java/net/minecraft/server/World.java -@@ -0,0 +0,0 @@ public abstract class World implements IIBlockAccess, GeneratorAccess, AutoClose - } - - public Chunk getChunkIfLoaded(int x, int z) { -- return ((ChunkProviderServer) this.chunkProvider).getChunkAt(x, z, false); -+ return ((ChunkProviderServer) this.chunkProvider).getChunkAtIfLoadedImmediately(x, z); // Paper - } - - protected World(WorldData worlddata, DimensionManager dimensionmanager, BiFunction bifunction, GameProfilerFiller gameprofilerfiller, boolean flag, org.bukkit.generator.ChunkGenerator gen, org.bukkit.World.Environment env) { -@@ -0,0 +0,0 @@ public abstract class World implements IIBlockAccess, GeneratorAccess, AutoClose - } - - public boolean isLoaded(BlockPosition blockposition) { -- return getChunkIfLoaded(blockposition.getX() >> 4, blockposition.getZ() >> 4) != null; // Paper -+ return getChunkIfLoadedImmediately(blockposition.getX() >> 4, blockposition.getZ() >> 4) != null; // Paper - } - - // Paper start - public boolean isLoadedAndInBounds(BlockPosition blockposition) { -- return getWorldBorder().isInBounds(blockposition) && getChunkIfLoaded(blockposition.getX() >> 4, blockposition.getZ() >> 4) != null; -+ return getWorldBorder().isInBounds(blockposition) && getChunkIfLoadedImmediately(blockposition.getX() >> 4, blockposition.getZ() >> 4) != null; - } - public Chunk getChunkIfLoaded(BlockPosition blockposition) { - return getChunkIfLoaded(blockposition.getX() >> 4, blockposition.getZ() >> 4); -diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org/spigotmc/ActivationRange.java -index f86404f83..92601c581 100644 ---- a/src/main/java/org/spigotmc/ActivationRange.java -+++ b/src/main/java/org/spigotmc/ActivationRange.java -@@ -0,0 +0,0 @@ public class ActivationRange - { - for ( int j1 = k; j1 <= l; ++j1 ) - { -- if ( world.getWorld().isChunkLoaded( i1, j1 ) ) -+ Chunk chunk = (Chunk) world.getChunkIfLoadedImmediately( i1, j1 ); -+ if ( chunk != null ) - { -- activateChunkEntities( world.getChunkAt( i1, j1 ) ); -+ activateChunkEntities( chunk ); - } - } - } --- \ No newline at end of file diff --git a/Spigot-Server-Patches/Use-more-reasonable-thread-count-default-for-bootstr.patch b/Spigot-Server-Patches/Use-more-reasonable-thread-count-default-for-bootstr.patch index f3b0c365c1..4065929519 100644 --- a/Spigot-Server-Patches/Use-more-reasonable-thread-count-default-for-bootstr.patch +++ b/Spigot-Server-Patches/Use-more-reasonable-thread-count-default-for-bootstr.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Use more reasonable thread count default for bootstrap diff --git a/src/main/java/net/minecraft/server/SystemUtils.java b/src/main/java/net/minecraft/server/SystemUtils.java -index 538ed5bb9b..35594821ce 100644 +index 7e224ebef..dc6d03062 100644 --- a/src/main/java/net/minecraft/server/SystemUtils.java +++ b/src/main/java/net/minecraft/server/SystemUtils.java @@ -0,0 +0,0 @@ public class SystemUtils { diff --git a/Spigot-Server-Patches/Use-proper-max-length-when-serialising-BungeeCord-te.patch b/Spigot-Server-Patches/Use-proper-max-length-when-serialising-BungeeCord-te.patch index ecdb4f728c..799702c78b 100644 --- a/Spigot-Server-Patches/Use-proper-max-length-when-serialising-BungeeCord-te.patch +++ b/Spigot-Server-Patches/Use-proper-max-length-when-serialising-BungeeCord-te.patch @@ -6,7 +6,7 @@ Subject: [PATCH] Use proper max length when serialising BungeeCord text diff --git a/src/main/java/net/minecraft/server/PacketPlayOutChat.java b/src/main/java/net/minecraft/server/PacketPlayOutChat.java -index 0ab611564e..f7b2095bb7 100644 +index 0ab611564..f7b2095bb 100644 --- a/src/main/java/net/minecraft/server/PacketPlayOutChat.java +++ b/src/main/java/net/minecraft/server/PacketPlayOutChat.java @@ -0,0 +0,0 @@ package net.minecraft.server; diff --git a/Spigot-Server-Patches/Workaround-for-vehicle-tracking-issue-on-disconnect.patch b/Spigot-Server-Patches/Workaround-for-vehicle-tracking-issue-on-disconnect.patch index 45cd081793..49730da800 100644 --- a/Spigot-Server-Patches/Workaround-for-vehicle-tracking-issue-on-disconnect.patch +++ b/Spigot-Server-Patches/Workaround-for-vehicle-tracking-issue-on-disconnect.patch @@ -5,12 +5,12 @@ Subject: [PATCH] Workaround for vehicle tracking issue on disconnect diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java -index 3897411e7..21b02aeca 100644 +index 1665a302a..cc9e69651 100644 --- a/src/main/java/net/minecraft/server/EntityPlayer.java +++ b/src/main/java/net/minecraft/server/EntityPlayer.java @@ -0,0 +0,0 @@ public class EntityPlayer extends EntityHuman implements ICrafting { public void n() { - this.ct = true; + this.cq = true; this.ejectPassengers(); + + // Paper start - Workaround an issue where the vehicle doesn't track the passenger disconnection dismount. @@ -20,6 +20,6 @@ index 3897411e7..21b02aeca 100644 + // Paper end + if (this.isSleeping()) { - this.wakeup(true, false, false); + this.wakeup(true, false); } -- \ No newline at end of file diff --git a/Spigot-Server-Patches/don-t-go-below-0-for-pickupDelay-breaks-picking-up-i.patch b/Spigot-Server-Patches/don-t-go-below-0-for-pickupDelay-breaks-picking-up-i.patch index 31c2ab70c2..553edfccea 100644 --- a/Spigot-Server-Patches/don-t-go-below-0-for-pickupDelay-breaks-picking-up-i.patch +++ b/Spigot-Server-Patches/don-t-go-below-0-for-pickupDelay-breaks-picking-up-i.patch @@ -6,7 +6,7 @@ Subject: [PATCH] don't go below 0 for pickupDelay, breaks picking up items vanilla checks for == 0 diff --git a/src/main/java/net/minecraft/server/EntityItem.java b/src/main/java/net/minecraft/server/EntityItem.java -index 6e026d34f0..2091698953 100644 +index 5f46c7f86..a0d1e7fd2 100644 --- a/src/main/java/net/minecraft/server/EntityItem.java +++ b/src/main/java/net/minecraft/server/EntityItem.java @@ -0,0 +0,0 @@ public class EntityItem extends Entity { diff --git a/Spigot-Server-Patches/force-entity-dismount-during-teleportation.patch b/Spigot-Server-Patches/force-entity-dismount-during-teleportation.patch index 32eeed6af9..a8ef8f6602 100644 --- a/Spigot-Server-Patches/force-entity-dismount-during-teleportation.patch +++ b/Spigot-Server-Patches/force-entity-dismount-during-teleportation.patch @@ -20,7 +20,7 @@ this is going to be the best soultion all around. Improvements/suggestions welcome! diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java -index 19bbcc043..75da08596 100644 +index 365984bb8..0f2480521 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 INamableTileEntity, ICommandListener, Ke @@ -72,7 +72,7 @@ index 19bbcc043..75da08596 100644 if (event.isCancelled()) { return false; diff --git a/src/main/java/net/minecraft/server/EntityHuman.java b/src/main/java/net/minecraft/server/EntityHuman.java -index 2342ea481..8a66b1dc5 100644 +index 78e48f478..4bb24de9d 100644 --- a/src/main/java/net/minecraft/server/EntityHuman.java +++ b/src/main/java/net/minecraft/server/EntityHuman.java @@ -0,0 +0,0 @@ public abstract class EntityHuman extends EntityLiving { @@ -91,11 +91,11 @@ index 2342ea481..8a66b1dc5 100644 } diff --git a/src/main/java/net/minecraft/server/EntityLiving.java b/src/main/java/net/minecraft/server/EntityLiving.java -index 1e53af2cc..d23968a7c 100644 +index c6bd68919..317321395 100644 --- a/src/main/java/net/minecraft/server/EntityLiving.java +++ b/src/main/java/net/minecraft/server/EntityLiving.java @@ -0,0 +0,0 @@ public abstract class EntityLiving extends Entity { - return ((Byte) this.datawatcher.get(EntityLiving.ar) & 4) != 0; + return ((Byte) this.datawatcher.get(EntityLiving.ao) & 4) != 0; } - @Override @@ -109,10 +109,10 @@ index 1e53af2cc..d23968a7c 100644 - super.stopRiding(); + super.stopRiding(suppressCancellation); // Paper - suppress if (entity != null && entity != this.getVehicle() && !this.world.isClientSide) { - this.B(entity); + this.a(entity); } diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java -index 517bbf602..8eb662cef 100644 +index c15543a5c..f41fd576d 100644 --- a/src/main/java/net/minecraft/server/EntityPlayer.java +++ b/src/main/java/net/minecraft/server/EntityPlayer.java @@ -0,0 +0,0 @@ public class EntityPlayer extends EntityHuman implements ICrafting { diff --git a/Spigot-Server-Patches/implement-optional-per-player-mob-spawns.patch b/Spigot-Server-Patches/implement-optional-per-player-mob-spawns.patch index 75c324c025..243ffd58e0 100644 --- a/Spigot-Server-Patches/implement-optional-per-player-mob-spawns.patch +++ b/Spigot-Server-Patches/implement-optional-per-player-mob-spawns.patch @@ -5,12 +5,12 @@ Subject: [PATCH] implement optional per player mob spawns diff --git a/src/main/java/co/aikar/timings/WorldTimingsHandler.java b/src/main/java/co/aikar/timings/WorldTimingsHandler.java -index 8de6c4816..ddec62fbf 100644 +index 3a79cde59..431534f4f 100644 --- a/src/main/java/co/aikar/timings/WorldTimingsHandler.java +++ b/src/main/java/co/aikar/timings/WorldTimingsHandler.java @@ -0,0 +0,0 @@ public class WorldTimingsHandler { - public final Timing chunkUnloadPOISerialization; - public final Timing chunkUnloadDataSave; + public final Timing chunkRangeCheckBig; + public final Timing chunkRangeCheckSmall; + public final Timing playerMobDistanceMapUpdate; + @@ -18,16 +18,16 @@ index 8de6c4816..ddec62fbf 100644 String name = server.worldData.getName() +" - "; @@ -0,0 +0,0 @@ public class WorldTimingsHandler { - chunkUnloadPrepareSave = Timings.ofSafe(name + "Chunk unload - Async Save Prepare"); - chunkUnloadPOISerialization = Timings.ofSafe(name + "Chunk unload - POI Data Serialization"); - chunkUnloadDataSave = Timings.ofSafe(name + "Chunk unload - Data Serialization"); + miscMobSpawning = Timings.ofSafe(name + "Mob spawning - Misc"); + chunkRangeCheckBig = Timings.ofSafe(name + "Chunk Tick Range - Big"); + chunkRangeCheckSmall = Timings.ofSafe(name + "Chunk Tick Range - Small"); + + playerMobDistanceMapUpdate = Timings.ofSafe(name + "Per Player Mob Spawning - Distance Map Update"); } public static Timing getTickList(WorldServer worldserver, String timingsType) { diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index b1ad22c9f..09c8ea2ed 100644 +index d0f5b2ab7..f63525d67 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -0,0 +0,0 @@ public class PaperWorldConfig { @@ -547,7 +547,7 @@ index 000000000..4f13d3ff8 + } +} diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java -index 8d7971ad8..e7539dd79 100644 +index f138b112f..109c6ada8 100644 --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java @@ -0,0 +0,0 @@ public class ChunkProviderServer extends IChunkProvider { @@ -579,7 +579,7 @@ index 8d7971ad8..e7539dd79 100644 int k1 = limit * l / ChunkProviderServer.b; // CraftBukkit - use per-world limits - if (object2intmap.getInt(enumcreaturetype) <= k1) { -- SpawnerCreature.a(enumcreaturetype, (World) this.world, chunk, blockposition); +- SpawnerCreature.a(enumcreaturetype, this.world, chunk, blockposition); + // Paper start - only allow spawns upto the limit per chunk and update count afterwards + int currEntityCount = worldMobCount[enumcreaturetype.ordinal()]; + int difference = k1 - currEntityCount; @@ -601,7 +601,7 @@ index 8d7971ad8..e7539dd79 100644 } } diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java -index 106b1ffe0..fa79d0bed 100644 +index ca2398386..19ba79c65 100644 --- a/src/main/java/net/minecraft/server/EntityPlayer.java +++ b/src/main/java/net/minecraft/server/EntityPlayer.java @@ -0,0 +0,0 @@ public class EntityPlayer extends EntityHuman implements ICrafting { @@ -625,33 +625,33 @@ index 106b1ffe0..fa79d0bed 100644 // Yes, this doesn't match Vanilla, but it's the best we can do for now. @@ -0,0 +0,0 @@ public class EntityPlayer extends EntityHuman implements ICrafting { - this.playerConnection.sendPacket(new PacketPlayOutUnloadChunk(chunkcoordintpair.x, chunkcoordintpair.z)); + } -+ public SectionPosition getPlayerMapSection() { return this.M(); } // Paper - OBFHELPER - public SectionPosition M() { - return this.cv; ++ public SectionPosition getPlayerMapSection() { return this.K(); } // Paper - OBFHELPER + public SectionPosition K() { + return this.cs; } diff --git a/src/main/java/net/minecraft/server/EntityTypes.java b/src/main/java/net/minecraft/server/EntityTypes.java -index a7fc34f85..612b9b7e3 100644 +index d49ad0308..2fb04e3e9 100644 --- a/src/main/java/net/minecraft/server/EntityTypes.java +++ b/src/main/java/net/minecraft/server/EntityTypes.java @@ -0,0 +0,0 @@ public class EntityTypes { - return this.be; + return this.bf; } + public EnumCreatureType getEnumCreatureType() { return this.e(); } // Paper - OBFHELPER public EnumCreatureType e() { - return this.ba; + return this.bb; } diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java -index 31d106f95..59e74900f 100644 +index fc6436c4f..67a909859 100644 --- a/src/main/java/net/minecraft/server/PlayerChunkMap.java +++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java @@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { private final PlayerMap playerMap; public final Int2ObjectMap trackedEntities; - private final Queue A; + private final Queue z; - private int viewDistance; + int viewDistance; // Paper - private -> package private + public final com.destroystokyo.paper.util.PlayerMobDistanceMap playerMobDistanceMap; // Paper @@ -659,8 +659,8 @@ index 31d106f95..59e74900f 100644 // CraftBukkit start - recursion-safe executor for Chunk loadCallback() and unloadCallback() public final CallbackExecutor callbackExecutor = new CallbackExecutor(); @@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { - this.m = supplier; - this.n = new VillagePlace(new File(this.x, "poi"), datafixer, this.world); // Paper + this.l = supplier; + this.m = new VillagePlace(new File(this.w, "poi"), datafixer); this.setViewDistance(i); + this.playerMobDistanceMap = this.world.paperConfig.perPlayerMobSpawns ? new com.destroystokyo.paper.util.PlayerMobDistanceMap() : null; // Paper + } @@ -684,7 +684,7 @@ index 31d106f95..59e74900f 100644 private static double a(ChunkCoordIntPair chunkcoordintpair, Entity entity) { diff --git a/src/main/java/net/minecraft/server/SpawnerCreature.java b/src/main/java/net/minecraft/server/SpawnerCreature.java -index c6ea37ffb..9d4a96ae4 100644 +index bcca7faed..57ce02a97 100644 --- a/src/main/java/net/minecraft/server/SpawnerCreature.java +++ b/src/main/java/net/minecraft/server/SpawnerCreature.java @@ -0,0 +0,0 @@ package net.minecraft.server; @@ -700,15 +700,15 @@ index c6ea37ffb..9d4a96ae4 100644 private static final Logger LOGGER = LogManager.getLogger(); + // Paper start - add maxSpawns parameter and return spawned mobs - public static void a(EnumCreatureType enumcreaturetype, World world, Chunk chunk, BlockPosition blockposition) { -+ spawnMobs(enumcreaturetype, world, chunk, blockposition, Integer.MAX_VALUE, null); + public static void a(EnumCreatureType enumcreaturetype, WorldServer worldserver, Chunk chunk, BlockPosition blockposition) { ++ spawnMobs(enumcreaturetype, worldserver, chunk, blockposition, Integer.MAX_VALUE, null); + } -+ public static int spawnMobs(EnumCreatureType enumcreaturetype, World world, Chunk chunk, BlockPosition blockposition, int maxSpawns, Consumer trackEntity) { ++ public static int spawnMobs(EnumCreatureType enumcreaturetype, WorldServer worldserver, Chunk chunk, BlockPosition blockposition, int maxSpawns, Consumer trackEntity) { + // Paper end - ChunkGenerator chunkgenerator = world.getChunkProvider().getChunkGenerator(); + ChunkGenerator chunkgenerator = worldserver.getChunkProvider().getChunkGenerator(); - int i = 0; + int i = 0; // Paper - force diff on name change - BlockPosition blockposition1 = getRandomPosition(world, chunk); + BlockPosition blockposition1 = getRandomPosition(worldserver, chunk); int j = blockposition1.getX(); int k = blockposition1.getY(); @@ -0,0 +0,0 @@ public final class SpawnerCreature { @@ -728,11 +728,11 @@ index c6ea37ffb..9d4a96ae4 100644 + return i; // Paper } - entityinsentient.setPositionRotation((double) f, (double) k, (double) f1, world.random.nextFloat() * 360.0F, 0.0F); + entityinsentient.setPositionRotation((double) f, (double) k, (double) f1, worldserver.random.nextFloat() * 360.0F, 0.0F); @@ -0,0 +0,0 @@ public final class SpawnerCreature { - groupdataentity = entityinsentient.prepare(world, world.getDamageScaler(new BlockPosition(entityinsentient)), EnumMobSpawn.NATURAL, groupdataentity, (NBTTagCompound) null); + groupdataentity = entityinsentient.prepare(worldserver, worldserver.getDamageScaler(new BlockPosition(entityinsentient)), EnumMobSpawn.NATURAL, groupdataentity, (NBTTagCompound) null); // CraftBukkit start - if (world.addEntity(entityinsentient, SpawnReason.NATURAL)) { + if (worldserver.addEntity(entityinsentient, SpawnReason.NATURAL)) { - ++i; + ++i; // Paper - force diff on name change ++i2; @@ -742,7 +742,7 @@ index c6ea37ffb..9d4a96ae4 100644 } + if (i >= maxSpawns) { return i; } // Paper // CraftBukkit end - if (i >= entityinsentient.dC()) { + if (i >= entityinsentient.getMaxSpawnGroup()) { - return; + return i; // Paper } @@ -757,7 +757,7 @@ index c6ea37ffb..9d4a96ae4 100644 @Nullable diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java -index 71939e808..bd1e4dbae 100644 +index 7b89b509a..fd42390d4 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 { diff --git a/Spigot-Server-Patches/improve-CraftWorld-isChunkLoaded.patch b/Spigot-Server-Patches/improve-CraftWorld-isChunkLoaded.patch index 8b7de5cec6..34346fffd9 100644 --- a/Spigot-Server-Patches/improve-CraftWorld-isChunkLoaded.patch +++ b/Spigot-Server-Patches/improve-CraftWorld-isChunkLoaded.patch @@ -9,7 +9,7 @@ waiting for the execution queue to get to our request; We can just query the chunk status and get a response now, vs having to wait diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index 95fdc3bf64..f2a68ec360 100644 +index 12362d560..29c14c707 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 { @@ -24,8 +24,8 @@ index 95fdc3bf64..f2a68ec360 100644 @Override public boolean isChunkGenerated(int x, int z) { try { -- return isChunkLoaded(x, z) || world.getChunkProvider().playerChunkMap.chunkExists(new ChunkCoordIntPair(x, z)); -+ return world.getChunkProvider().getChunkAtIfCachedImmediately(x, z) != null || world.getChunkProvider().playerChunkMap.chunkExists(new ChunkCoordIntPair(x, z)); // Paper +- return isChunkLoaded(x, z) || world.getChunkProvider().playerChunkMap.read(new ChunkCoordIntPair(x, z)) != null; ++ return world.getChunkProvider().getChunkAtIfCachedImmediately(x, z) != null || world.getChunkProvider().playerChunkMap.read(new ChunkCoordIntPair(x, z)) != null; // Paper (TODO check if the first part can be removed) } catch (IOException ex) { throw new RuntimeException(ex); } diff --git a/Spigot-Server-Patches/incremental-chunk-saving.patch b/Spigot-Server-Patches/incremental-chunk-saving.patch index dc713a4db9..6b7edd76b4 100644 --- a/Spigot-Server-Patches/incremental-chunk-saving.patch +++ b/Spigot-Server-Patches/incremental-chunk-saving.patch @@ -5,7 +5,7 @@ Subject: [PATCH] incremental chunk saving diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -index 9ff115294..e98b1f243 100644 +index 332f20ce8..f5ed0a698 100644 --- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java @@ -0,0 +0,0 @@ public class PaperWorldConfig { @@ -29,7 +29,7 @@ index 9ff115294..e98b1f243 100644 + } } diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java -index ee8f80174..2003522d9 100644 +index 64c327669..14ec31f0a 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 implements IChunkAccess { @@ -39,10 +39,10 @@ index ee8f80174..2003522d9 100644 - private long lastSaved; + public long lastSaved; // Paper private volatile boolean s; - private long t; + private long inhabitedTime; @Nullable diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java -index 02dfd91c5..8689e0f9f 100644 +index 9b2bafdbd..f138b112f 100644 --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java @@ -0,0 +0,0 @@ public class ChunkProviderServer extends IChunkProvider { @@ -62,7 +62,7 @@ index 02dfd91c5..8689e0f9f 100644 public void close() throws IOException { // CraftBukkit start diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index a6f112bd0..5238a1a7c 100644 +index 0a7648381..8e15aba7f 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -0,0 +0,0 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant 100) { // Spigot diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java -index 493770bf6..2be6fa0f0 100644 +index 827831aab..4379434f6 100644 --- a/src/main/java/net/minecraft/server/PlayerChunkMap.java +++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java @@ -0,0 +0,0 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d { @@ -149,11 +149,11 @@ index 493770bf6..2be6fa0f0 100644 if (flag) { List list = (List) this.visibleChunks.values().stream().filter(PlayerChunk::hasBeenLoaded).peek(PlayerChunk::m).collect(Collectors.toList()); diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java -index 3acea575d..7c5349b17 100644 +index c28c0431a..0fd3cb54b 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 { - return this.worldProvider.d(); + return this.worldProvider.c(); } + // Paper start - derived from below diff --git a/Spigot-Server-Patches/offset-item-frame-ticking.patch b/Spigot-Server-Patches/offset-item-frame-ticking.patch index b6b06bd2f0..392cdf3eaa 100644 --- a/Spigot-Server-Patches/offset-item-frame-ticking.patch +++ b/Spigot-Server-Patches/offset-item-frame-ticking.patch @@ -5,7 +5,7 @@ Subject: [PATCH] offset item frame ticking diff --git a/src/main/java/net/minecraft/server/EntityHanging.java b/src/main/java/net/minecraft/server/EntityHanging.java -index 3b282a18a..2b4a849f4 100644 +index e9cde9c6e..04864a36e 100644 --- a/src/main/java/net/minecraft/server/EntityHanging.java +++ b/src/main/java/net/minecraft/server/EntityHanging.java @@ -0,0 +0,0 @@ public abstract class EntityHanging extends Entity { diff --git a/Spigot-Server-Patches/only-add-passanger-entities-once-from-spawners.patch b/Spigot-Server-Patches/only-add-passanger-entities-once-from-spawners.patch index 32c9f3b5c5..4f055a53f8 100644 --- a/Spigot-Server-Patches/only-add-passanger-entities-once-from-spawners.patch +++ b/Spigot-Server-Patches/only-add-passanger-entities-once-from-spawners.patch @@ -5,7 +5,7 @@ Subject: [PATCH] only add passanger entities once from spawners diff --git a/src/main/java/net/minecraft/server/MobSpawnerAbstract.java b/src/main/java/net/minecraft/server/MobSpawnerAbstract.java -index b05f6c2f2..a4e6be549 100644 +index b179893a1..b96f6461d 100644 --- a/src/main/java/net/minecraft/server/MobSpawnerAbstract.java +++ b/src/main/java/net/minecraft/server/MobSpawnerAbstract.java @@ -0,0 +0,0 @@ public abstract class MobSpawnerAbstract {