From 70e494786713fc3139190403f1daddbe3b561ce8 Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Sun, 5 May 2019 04:32:20 -0700 Subject: [PATCH] Correct patches --- .../Add-LivingEntity-getTargetEntity.patch | 182 -- Spigot-Server-Patches/Anti-Xray.patch | 1598 ----------- .../Async-Chunk-Loading-and-Generation.patch | 2501 ----------------- ...arseException-in-Entity-and-TE-names.patch | 2 +- .../Fix-FileIOThread-concurrency-issues.patch | 33 - .../Fix-Sending-Chunks-to-Client.patch | 64 - ...h-entity-loss-due-to-unloaded-chunks.patch | 42 - ...ead-Entities-in-entityList-iteration.patch | 5 +- ...e-Dimension-NBT-field-in-Entity-data.patch | 2 +- ...s-and-setters-for-EntityItem-owner-a.patch | 80 - .../Improve-death-events.patch | 2 +- .../Optimize-Light-Recalculations.patch | 50 - .../Optimize-and-Fix-ExpiringMap-Issues.patch | 334 --- ...Optimize-getChunkIfLoaded-type-calls.patch | 79 - .../Reset-players-airTicks-on-respawn.patch | 2 +- .../Use-EntityTypes-for-living-entities.patch | 870 ------ ...entity-dismount-during-teleportation.patch | 2 +- 17 files changed, 7 insertions(+), 5841 deletions(-) delete mode 100644 Spigot-Server-Patches/Add-LivingEntity-getTargetEntity.patch delete mode 100644 Spigot-Server-Patches/Anti-Xray.patch delete mode 100644 Spigot-Server-Patches/Async-Chunk-Loading-and-Generation.patch delete mode 100644 Spigot-Server-Patches/Fix-FileIOThread-concurrency-issues.patch delete mode 100644 Spigot-Server-Patches/Fix-Sending-Chunks-to-Client.patch delete mode 100644 Spigot-Server-Patches/Fix-issues-with-entity-loss-due-to-unloaded-chunks.patch delete mode 100644 Spigot-Server-Patches/Implement-getters-and-setters-for-EntityItem-owner-a.patch delete mode 100644 Spigot-Server-Patches/Optimize-Light-Recalculations.patch delete mode 100644 Spigot-Server-Patches/Optimize-and-Fix-ExpiringMap-Issues.patch delete mode 100644 Spigot-Server-Patches/Optimize-getChunkIfLoaded-type-calls.patch delete mode 100644 Spigot-Server-Patches/Use-EntityTypes-for-living-entities.patch diff --git a/Spigot-Server-Patches/Add-LivingEntity-getTargetEntity.patch b/Spigot-Server-Patches/Add-LivingEntity-getTargetEntity.patch deleted file mode 100644 index 825a96427c..0000000000 --- a/Spigot-Server-Patches/Add-LivingEntity-getTargetEntity.patch +++ /dev/null @@ -1,182 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Sat, 22 Sep 2018 00:33:08 -0500 -Subject: [PATCH] Add LivingEntity#getTargetEntity - - -diff --git a/src/main/java/net/minecraft/server/AxisAlignedBB.java b/src/main/java/net/minecraft/server/AxisAlignedBB.java -index 1c0b783e8e..dad1ff7372 100644 ---- a/src/main/java/net/minecraft/server/AxisAlignedBB.java -+++ b/src/main/java/net/minecraft/server/AxisAlignedBB.java -@@ -0,0 +0,0 @@ public class AxisAlignedBB { - return new AxisAlignedBB(d3, d4, d5, d6, d7, d8); - } - -+ public AxisAlignedBB expand(double x, double y, double z) { return b(x, y, z); } // Paper - OBFHELPER - public AxisAlignedBB b(double d0, double d1, double d2) { - double d3 = this.minX; - double d4 = this.minY; -@@ -0,0 +0,0 @@ public class AxisAlignedBB { - return new AxisAlignedBB(d3, d4, d5, d6, d7, d8); - } - -+ // Paper start -+ public AxisAlignedBB grow(double d0) { -+ return grow(d0, d0, d0); -+ } -+ // Paper end -+ - public AxisAlignedBB grow(double d0, double d1, double d2) { - double d3 = this.minX - d0; - double d4 = this.minY - d1; -@@ -0,0 +0,0 @@ public class AxisAlignedBB { - return this.minX < d3 && this.maxX > d0 && this.minY < d4 && this.maxY > d1 && this.minZ < d5 && this.maxZ > d2; - } - -+ public boolean contains(Vec3D vec3d) { return b(vec3d); } // Paper - OBFHELPER - public boolean b(Vec3D vec3d) { - return this.e(vec3d.x, vec3d.y, vec3d.z); - } -@@ -0,0 +0,0 @@ public class AxisAlignedBB { - return this.g(-d0); - } - -+ public MovingObjectPosition calculateIntercept(Vec3D vec3d, Vec3D vec3d1) { return b(vec3d, vec3d1); } // Paper - OBFHELPER - @Nullable - public MovingObjectPosition b(Vec3D vec3d, Vec3D vec3d1) { - return this.a(vec3d, vec3d1, (BlockPosition) null); -diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java -index 3db1b6bb1b..de45e3c7a8 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 - return new Vec3D((double) (f5 * f6), (double) (-f7), (double) (f4 * f6)); - } - -+ public Vec3D getEyePosition(float partialTicks) { return i(partialTicks); } // Paper - OBFHELPER - public Vec3D i(float f) { - if (f == 1.0F) { - return new Vec3D(this.locX, this.locY + (double) this.getHeadHeight(), this.locZ); -@@ -0,0 +0,0 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke - return this.bP().size() < 1; - } - -+ public float getCollisionBorderSize() { return aM(); } // Paper - OBFHELPER - public float aM() { - return 0.0F; - } - -+ public Vec3D getLookVec() { return aN(); } // Paper - OBFHELPER - public Vec3D aN() { - return this.d(this.pitch, this.yaw); - } -diff --git a/src/main/java/net/minecraft/server/EntityLiving.java b/src/main/java/net/minecraft/server/EntityLiving.java -index 3d1cdd6271..14f1029687 100644 ---- a/src/main/java/net/minecraft/server/EntityLiving.java -+++ b/src/main/java/net/minecraft/server/EntityLiving.java -@@ -0,0 +0,0 @@ package net.minecraft.server; - import com.destroystokyo.paper.event.player.PlayerArmorChangeEvent; - import com.google.common.base.Objects; - import com.google.common.collect.Maps; -+ -+import java.util.Arrays; - import java.util.Collection; - import java.util.ConcurrentModificationException; - import java.util.Iterator; -@@ -0,0 +0,0 @@ import org.apache.logging.log4j.Logger; - - // CraftBukkit start - import java.util.ArrayList; -+import java.util.stream.Collectors; -+ - import com.google.common.base.Function; - import com.google.common.collect.Lists; - import org.bukkit.Location; -@@ -0,0 +0,0 @@ public abstract class EntityLiving extends Entity { - return world.rayTrace(start, end, fluidCollisionOption); - } - -+ public MovingObjectPosition getTargetEntity(int maxDistance) { -+ if (maxDistance < 1 || maxDistance > 120) { -+ throw new IllegalArgumentException("maxDistance must be between 1-120"); -+ } -+ -+ Vec3D start = getEyePosition(1.0F); -+ Vec3D direction = getLookVec(); -+ Vec3D end = start.add(direction.x * maxDistance, direction.y * maxDistance, direction.z * maxDistance); -+ -+ List entityList = world.getEntities(this, getBoundingBox().expand(direction.x * maxDistance, direction.y * maxDistance, direction.z * maxDistance).grow(1.0D, 1.0D, 1.0D), IEntitySelector.notSpectator().and(Entity::isInteractable)); -+ -+ double distance = 0.0D; -+ MovingObjectPosition rayTraceResult = null; -+ -+ for (Entity entity : entityList) { -+ AxisAlignedBB aabb = entity.getBoundingBox().grow((double) entity.getCollisionBorderSize()); -+ MovingObjectPosition rayTrace = aabb.calculateIntercept(start, end); -+ -+ if (rayTrace != null) { -+ double distanceTo = start.distanceSquared(rayTrace.pos); -+ if (distanceTo < distance || distance == 0.0D) { -+ rayTraceResult = new MovingObjectPosition(entity, rayTrace.pos); -+ distance = distanceTo; -+ } -+ } -+ } -+ -+ return rayTraceResult; -+ } -+ - public int shieldBlockingDelay = world.paperConfig.shieldBlockingDelay; - - public int getShieldBlockingDelay() { -diff --git a/src/main/java/net/minecraft/server/IEntitySelector.java b/src/main/java/net/minecraft/server/IEntitySelector.java -index f6916fd455..71f08d53c7 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 { - public static final Predicate e = (entity) -> { - return !(entity instanceof EntityHuman) || !((EntityHuman) entity).isSpectator() && !((EntityHuman) entity).u(); - }; -+ public static Predicate notSpectator() { return f; } // Paper - OBFHELPER - public static final Predicate f = (entity) -> { - return !(entity instanceof EntityHuman) || !((EntityHuman) entity).isSpectator(); - }; -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java -index eeab59379a..d6a4bc64ae 100644 ---- a/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java -+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftLivingEntity.java -@@ -0,0 +0,0 @@ public class CraftLivingEntity extends CraftEntity implements LivingEntity { - net.minecraft.server.MovingObjectPosition rayTrace = getHandle().getRayTrace(maxDistance, net.minecraft.server.MCUtil.getNMSFluidCollisionOption(fluidMode)); - return rayTrace == null ? null : new com.destroystokyo.paper.block.TargetBlockInfo(org.bukkit.craftbukkit.block.CraftBlock.at(getHandle().world, rayTrace.getBlockPosition()), net.minecraft.server.MCUtil.toBukkitBlockFace(rayTrace.direction)); - } -+ -+ public Entity getTargetEntity(int maxDistance, boolean ignoreBlocks) { -+ net.minecraft.server.MovingObjectPosition rayTrace = rayTraceEntity(maxDistance, ignoreBlocks); -+ return rayTrace == null ? null : rayTrace.entity.getBukkitEntity(); -+ } -+ -+ public com.destroystokyo.paper.entity.TargetEntityInfo getTargetEntityInfo(int maxDistance, boolean ignoreBlocks) { -+ net.minecraft.server.MovingObjectPosition rayTrace = rayTraceEntity(maxDistance, ignoreBlocks); -+ return rayTrace == null ? null : new com.destroystokyo.paper.entity.TargetEntityInfo(rayTrace.entity.getBukkitEntity(), new org.bukkit.util.Vector(rayTrace.pos.x, rayTrace.pos.y, rayTrace.pos.z)); -+ } -+ -+ public net.minecraft.server.MovingObjectPosition rayTraceEntity(int maxDistance, boolean ignoreBlocks) { -+ net.minecraft.server.MovingObjectPosition rayTrace = getHandle().getTargetEntity(maxDistance); -+ if (rayTrace == null) { -+ return null; -+ } -+ if (!ignoreBlocks) { -+ net.minecraft.server.MovingObjectPosition rayTraceBlocks = getHandle().getRayTrace(maxDistance, net.minecraft.server.FluidCollisionOption.NEVER); -+ if (rayTraceBlocks != null) { -+ net.minecraft.server.Vec3D eye = getHandle().getEyePosition(1.0F); -+ if (eye.distanceSquared(rayTraceBlocks.pos) <= eye.distanceSquared(rayTrace.pos)) { -+ return null; -+ } -+ } -+ } -+ return rayTrace; -+ } - // Paper end - - public List getLastTwoTargetBlocks(Set transparent, int maxDistance) { --- \ No newline at end of file diff --git a/Spigot-Server-Patches/Anti-Xray.patch b/Spigot-Server-Patches/Anti-Xray.patch deleted file mode 100644 index 0f7156e7b1..0000000000 --- a/Spigot-Server-Patches/Anti-Xray.patch +++ /dev/null @@ -1,1598 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: stonar96 -Date: Mon, 20 Aug 2018 03:03:58 +0200 -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 e87fb94c06..8f49262226 100644 ---- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -+++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java -@@ -0,0 +0,0 @@ - package com.destroystokyo.paper; - -+import java.util.Arrays; - import java.util.List; - -+import com.destroystokyo.paper.antixray.ChunkPacketBlockControllerAntiXray.ChunkEdgeMode; -+import com.destroystokyo.paper.antixray.ChunkPacketBlockControllerAntiXray.EngineMode; - import net.minecraft.server.MinecraftServer; - import org.bukkit.Bukkit; - import org.bukkit.configuration.file.YamlConfiguration; -@@ -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"); - } -+ -+ public boolean antiXray; -+ public boolean asynchronous; -+ public EngineMode engineMode; -+ public ChunkEdgeMode chunkEdgeMode; -+ public int maxChunkSectionIndex; -+ public int updateRadius; -+ public List hiddenBlocks; -+ public List replacementBlocks; -+ private void antiXray() { -+ antiXray = getBoolean("anti-xray.enabled", false); -+ asynchronous = true; -+ engineMode = EngineMode.getById(getInt("anti-xray.engine-mode", EngineMode.HIDE.getId())); -+ engineMode = engineMode == null ? EngineMode.HIDE : engineMode; -+ chunkEdgeMode = ChunkEdgeMode.getById(getInt("anti-xray.chunk-edge-mode", ChunkEdgeMode.WAIT.getId())); -+ chunkEdgeMode = chunkEdgeMode == null ? ChunkEdgeMode.DEFAULT : chunkEdgeMode; -+ -+ if (chunkEdgeMode != ChunkEdgeMode.WAIT) { -+ log("Migrating anti-xray chunk edge mode to " + ChunkEdgeMode.WAIT + " (" + ChunkEdgeMode.WAIT.getId() + ")"); -+ chunkEdgeMode = ChunkEdgeMode.WAIT; -+ set("anti-xray.chunk-edge-mode", ChunkEdgeMode.WAIT.getId()); -+ } -+ -+ maxChunkSectionIndex = getInt("anti-xray.max-chunk-section-index", 3); -+ maxChunkSectionIndex = maxChunkSectionIndex > 15 ? 15 : maxChunkSectionIndex; -+ updateRadius = getInt("anti-xray.update-radius", 2); -+ hiddenBlocks = getList("anti-xray.hidden-blocks", Arrays.asList("gold_ore", "iron_ore", "coal_ore", "lapis_ore", "mossy_cobblestone", "obsidian", "chest", "diamond_ore", "redstone_ore", "lit_redstone_ore", "clay", "emerald_ore", "ender_chest")); -+ replacementBlocks = getList("anti-xray.replacement-blocks", Arrays.asList("stone", "planks")); -+ log("Anti-Xray: " + (antiXray ? "enabled" : "disabled") + " / Engine Mode: " + engineMode.getDescription() + " / Chunk Edge Mode: " + chunkEdgeMode.getDescription() + " / Up to " + ((maxChunkSectionIndex + 1) * 16) + " blocks / Update Radius: " + updateRadius); -+ } - } -diff --git a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockController.java b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockController.java -new file mode 100644 -index 0000000000..1ba8477bf9 ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockController.java -@@ -0,0 +0,0 @@ -+package com.destroystokyo.paper.antixray; -+ -+import net.minecraft.server.BlockPosition; -+import net.minecraft.server.Chunk; -+import net.minecraft.server.ChunkSection; -+import net.minecraft.server.EnumDirection; -+import net.minecraft.server.IBlockData; -+import net.minecraft.server.IChunkAccess; -+import net.minecraft.server.IWorldReader; -+import net.minecraft.server.PacketPlayOutMapChunk; -+import net.minecraft.server.PlayerInteractManager; -+import net.minecraft.server.World; -+ -+public class ChunkPacketBlockController { -+ -+ public static final ChunkPacketBlockController NO_OPERATION_INSTANCE = new ChunkPacketBlockController(); -+ -+ protected ChunkPacketBlockController() { -+ -+ } -+ -+ public IBlockData[] getPredefinedBlockData(IWorldReader world, IChunkAccess chunk, ChunkSection chunkSection, boolean skyLight, boolean initializeBlocks) { -+ return null; -+ } -+ -+ public boolean onChunkPacketCreate(Chunk chunk, int chunkSectionSelector, boolean force) { -+ return true; -+ } -+ -+ public ChunkPacketInfo getChunkPacketInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector) { -+ return null; -+ } -+ -+ public void modifyBlocks(PacketPlayOutMapChunk packetPlayOutMapChunk, ChunkPacketInfo chunkPacketInfo) { -+ packetPlayOutMapChunk.setReady(true); -+ } -+ -+ public void onBlockChange(World world, BlockPosition blockPosition, IBlockData newBlockData, IBlockData oldBlockData, int flag) { -+ -+ } -+ -+ public void onPlayerLeftClickBlock(PlayerInteractManager playerInteractManager, BlockPosition blockPosition, EnumDirection enumDirection) { -+ -+ } -+} -diff --git a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java -new file mode 100644 -index 0000000000..e3da35b6ba ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketBlockControllerAntiXray.java -@@ -0,0 +0,0 @@ -+package com.destroystokyo.paper.antixray; -+ -+import java.util.HashSet; -+import java.util.Set; -+import java.util.concurrent.ExecutorService; -+import java.util.concurrent.Executors; -+ -+import net.minecraft.server.IRegistry; -+import net.minecraft.server.MinecraftKey; -+import org.bukkit.World.Environment; -+ -+import com.destroystokyo.paper.PaperWorldConfig; -+ -+import net.minecraft.server.Block; -+import net.minecraft.server.BlockPosition; -+import net.minecraft.server.Blocks; -+import net.minecraft.server.Chunk; -+import net.minecraft.server.ChunkSection; -+import net.minecraft.server.DataPalette; -+import net.minecraft.server.EnumDirection; -+import net.minecraft.server.GeneratorAccess; -+import net.minecraft.server.IBlockData; -+import net.minecraft.server.IChunkAccess; -+import net.minecraft.server.IWorldReader; -+import net.minecraft.server.MinecraftServer; -+import net.minecraft.server.PacketPlayOutMapChunk; -+import net.minecraft.server.PlayerInteractManager; -+import net.minecraft.server.World; -+import net.minecraft.server.WorldServer; -+ -+public class ChunkPacketBlockControllerAntiXray extends ChunkPacketBlockController { -+ -+ private static ExecutorService executorServiceInstance = null; -+ private final ExecutorService executorService; -+ private final boolean asynchronous; -+ private final EngineMode engineMode; -+ private final ChunkEdgeMode chunkEdgeMode; -+ private final int maxChunkSectionIndex; -+ private final int updateRadius; -+ private final IBlockData[] predefinedBlockData; -+ private final IBlockData[] predefinedBlockDataStone; -+ private final IBlockData[] predefinedBlockDataNetherrack; -+ private final IBlockData[] predefinedBlockDataEndStone; -+ private final int[] predefinedBlockDataBitsGlobal; -+ private final int[] predefinedBlockDataBitsStoneGlobal; -+ private final int[] predefinedBlockDataBitsNetherrackGlobal; -+ private final int[] predefinedBlockDataBitsEndStoneGlobal; -+ private final boolean[] solidGlobal = new boolean[Block.REGISTRY_ID.size()]; -+ private final boolean[] obfuscateGlobal = new boolean[Block.REGISTRY_ID.size()]; -+ private final ChunkSection[] emptyNearbyChunkSections = {Chunk.EMPTY_CHUNK_SECTION, Chunk.EMPTY_CHUNK_SECTION, Chunk.EMPTY_CHUNK_SECTION, Chunk.EMPTY_CHUNK_SECTION}; -+ private final int maxBlockYUpdatePosition; -+ -+ public ChunkPacketBlockControllerAntiXray(PaperWorldConfig paperWorldConfig) { -+ asynchronous = paperWorldConfig.asynchronous; -+ engineMode = paperWorldConfig.engineMode; -+ chunkEdgeMode = paperWorldConfig.chunkEdgeMode; -+ maxChunkSectionIndex = paperWorldConfig.maxChunkSectionIndex; -+ updateRadius = paperWorldConfig.updateRadius; -+ -+ if (asynchronous) { -+ executorService = getExecutorServiceInstance(); -+ } else { -+ executorService = null; -+ } -+ -+ if (engineMode == EngineMode.HIDE) { -+ predefinedBlockData = null; -+ predefinedBlockDataStone = new IBlockData[] {Blocks.STONE.getBlockData()}; -+ predefinedBlockDataNetherrack = new IBlockData[] {Blocks.NETHERRACK.getBlockData()}; -+ predefinedBlockDataEndStone = new IBlockData[] {Blocks.END_STONE.getBlockData()}; -+ predefinedBlockDataBitsGlobal = null; -+ predefinedBlockDataBitsStoneGlobal = new int[] {ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(Blocks.STONE.getBlockData())}; -+ predefinedBlockDataBitsNetherrackGlobal = new int[] {ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(Blocks.NETHERRACK.getBlockData())}; -+ predefinedBlockDataBitsEndStoneGlobal = new int[] {ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(Blocks.END_STONE.getBlockData())}; -+ } else { -+ Set predefinedBlockDataSet = new HashSet(); -+ -+ for (String id : paperWorldConfig.hiddenBlocks) { -+ Block block = IRegistry.BLOCK.get(new MinecraftKey(id)); -+ -+ if (block != null && !block.isTileEntity()) { -+ predefinedBlockDataSet.add(block.getBlockData()); -+ } -+ } -+ -+ predefinedBlockData = predefinedBlockDataSet.size() == 0 ? new IBlockData[] {Blocks.DIAMOND_ORE.getBlockData()} : predefinedBlockDataSet.toArray(new IBlockData[predefinedBlockDataSet.size()]); -+ predefinedBlockDataStone = null; -+ predefinedBlockDataNetherrack = null; -+ predefinedBlockDataEndStone = null; -+ predefinedBlockDataBitsGlobal = new int[predefinedBlockData.length]; -+ -+ for (int i = 0; i < predefinedBlockData.length; i++) { -+ predefinedBlockDataBitsGlobal[i] = ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(predefinedBlockData[i]); -+ } -+ -+ predefinedBlockDataBitsStoneGlobal = null; -+ predefinedBlockDataBitsNetherrackGlobal = null; -+ predefinedBlockDataBitsEndStoneGlobal = null; -+ } -+ -+ for (String id : (engineMode == EngineMode.HIDE) ? paperWorldConfig.hiddenBlocks : paperWorldConfig.replacementBlocks) { -+ Block block = IRegistry.BLOCK.get(new MinecraftKey(id)); -+ -+ if (block != null) { -+ obfuscateGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(block.getBlockData())] = true; -+ } -+ } -+ -+ for (int i = 0; i < solidGlobal.length; i++) { -+ IBlockData blockData = ChunkSection.GLOBAL_PALETTE.getObject(i); -+ -+ if (blockData != null) { -+ solidGlobal[i] = blockData.getBlock().isOccluding(blockData) && blockData.getBlock() != Blocks.SPAWNER && blockData.getBlock() != Blocks.BARRIER; -+ } -+ } -+ -+ this.maxBlockYUpdatePosition = (maxChunkSectionIndex + 1) * 16 + updateRadius - 1; -+ } -+ -+ private static ExecutorService getExecutorServiceInstance() { -+ if (executorServiceInstance == null) { -+ executorServiceInstance = Executors.newSingleThreadExecutor(); -+ } -+ -+ return executorServiceInstance; -+ } -+ -+ @Override -+ public IBlockData[] getPredefinedBlockData(IWorldReader world, IChunkAccess chunk, ChunkSection chunkSection, boolean skyLight, boolean initializeBlocks) { -+ //Return the block data which should be added to the data palettes so that they can be used for the obfuscation -+ if (chunkSection.getYPosition() >> 4 <= maxChunkSectionIndex) { -+ switch (engineMode) { -+ case HIDE: -+ if (world instanceof GeneratorAccess) { -+ switch (((GeneratorAccess) world).getMinecraftWorld().getWorld().getEnvironment()) { -+ case NETHER: -+ return predefinedBlockDataNetherrack; -+ case THE_END: -+ return predefinedBlockDataEndStone; -+ default: -+ return predefinedBlockDataStone; -+ } -+ } -+ -+ return null; -+ default: -+ return predefinedBlockData; -+ } -+ } -+ -+ return null; -+ } -+ -+ @Override -+ public boolean onChunkPacketCreate(Chunk chunk, int chunkSectionSelector, boolean force) { -+ //Load nearby chunks if necessary -+ if (force) { -+ // if forced, load NOW; -+ chunk.world.getChunkAt(chunk.locX - 1, chunk.locZ); -+ chunk.world.getChunkAt(chunk.locX + 1, chunk.locZ); -+ chunk.world.getChunkAt(chunk.locX, chunk.locZ - 1); -+ chunk.world.getChunkAt(chunk.locX, chunk.locZ + 1); -+ } else if (chunkEdgeMode == ChunkEdgeMode.WAIT && !force) { -+ if (chunk.world.getChunkIfLoaded(chunk.locX - 1, chunk.locZ) == null || chunk.world.getChunkIfLoaded(chunk.locX + 1, chunk.locZ) == null || chunk.world.getChunkIfLoaded(chunk.locX, chunk.locZ - 1) == null || chunk.world.getChunkIfLoaded(chunk.locX, chunk.locZ + 1) == null) { -+ //Don't create the chunk packet now, wait until nearby chunks are loaded and create it later -+ return false; -+ } -+ } else if (chunkEdgeMode == ChunkEdgeMode.LOAD) { -+ boolean missingChunk = false; -+ //noinspection ConstantConditions -+ missingChunk |= ((WorldServer)chunk.world).getChunkProvider().getChunkAt(chunk.locX - 1, chunk.locZ, true, true, c -> {}) == null; -+ missingChunk |= ((WorldServer)chunk.world).getChunkProvider().getChunkAt(chunk.locX + 1, chunk.locZ, true, true, c -> {}) == null; -+ missingChunk |= ((WorldServer)chunk.world).getChunkProvider().getChunkAt(chunk.locX, chunk.locZ - 1, true, true, c -> {}) == null; -+ missingChunk |= ((WorldServer)chunk.world).getChunkProvider().getChunkAt(chunk.locX, chunk.locZ + 1, true, true, c -> {}) == null; -+ -+ if (missingChunk) { -+ return false; -+ } -+ } -+ -+ //Create the chunk packet now -+ return true; -+ } -+ -+ @Override -+ public ChunkPacketInfoAntiXray getChunkPacketInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector) { -+ //Return a new instance to collect data and objects in the right state while creating the chunk packet for thread safe access later -+ ChunkPacketInfoAntiXray chunkPacketInfoAntiXray = new ChunkPacketInfoAntiXray(packetPlayOutMapChunk, chunk, chunkSectionSelector, this); -+ chunkPacketInfoAntiXray.setNearbyChunks(chunk.world.getChunkIfLoaded(chunk.locX - 1, chunk.locZ), chunk.world.getChunkIfLoaded(chunk.locX + 1, chunk.locZ), chunk.world.getChunkIfLoaded(chunk.locX, chunk.locZ - 1), chunk.world.getChunkIfLoaded(chunk.locX, chunk.locZ + 1)); -+ return chunkPacketInfoAntiXray; -+ } -+ -+ @Override -+ public void modifyBlocks(PacketPlayOutMapChunk packetPlayOutMapChunk, ChunkPacketInfo chunkPacketInfo) { -+ if (asynchronous) { -+ executorService.submit((ChunkPacketInfoAntiXray) chunkPacketInfo); -+ } else { -+ obfuscate((ChunkPacketInfoAntiXray) chunkPacketInfo); -+ } -+ } -+ -+ //Actually these fields should be variables inside the obfuscate method but in sync mode or with SingleThreadExecutor in async mode it's okay -+ private int[] predefinedBlockDataBits; -+ private final boolean[] solid = new boolean[Block.REGISTRY_ID.size()]; -+ private final boolean[] obfuscate = new boolean[Block.REGISTRY_ID.size()]; -+ //These boolean arrays represent chunk layers, true means don't obfuscate, false means obfuscate -+ private boolean[][] current = new boolean[16][16]; -+ private boolean[][] next = new boolean[16][16]; -+ private boolean[][] nextNext = new boolean[16][16]; -+ private final DataBitsReader dataBitsReader = new DataBitsReader(); -+ private final DataBitsWriter dataBitsWriter = new DataBitsWriter(); -+ private final ChunkSection[] nearbyChunkSections = new ChunkSection[4]; -+ -+ public void obfuscate(ChunkPacketInfoAntiXray chunkPacketInfoAntiXray) { -+ boolean[] solidTemp = null; -+ boolean[] obfuscateTemp = null; -+ dataBitsReader.setDataBits(chunkPacketInfoAntiXray.getData()); -+ dataBitsWriter.setDataBits(chunkPacketInfoAntiXray.getData()); -+ int counter = 0; -+ -+ for (int chunkSectionIndex = 0; chunkSectionIndex <= maxChunkSectionIndex; chunkSectionIndex++) { -+ if (chunkPacketInfoAntiXray.isWritten(chunkSectionIndex) && chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex) != null) { -+ int[] predefinedBlockDataBitsTemp; -+ -+ if (chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex) == ChunkSection.GLOBAL_PALETTE) { -+ predefinedBlockDataBitsTemp = engineMode == EngineMode.HIDE ? chunkPacketInfoAntiXray.getChunk().world.getWorld().getEnvironment() == Environment.NETHER ? predefinedBlockDataBitsNetherrackGlobal : chunkPacketInfoAntiXray.getChunk().world.getWorld().getEnvironment() == Environment.THE_END ? predefinedBlockDataBitsEndStoneGlobal : predefinedBlockDataBitsStoneGlobal : predefinedBlockDataBitsGlobal; -+ } else { -+ predefinedBlockDataBitsTemp = predefinedBlockDataBits == null ? predefinedBlockDataBits = engineMode == EngineMode.HIDE ? new int[1] : new int[predefinedBlockData.length] : predefinedBlockDataBits; -+ -+ for (int i = 0; i < predefinedBlockDataBitsTemp.length; i++) { -+ predefinedBlockDataBitsTemp[i] = chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex).getOrCreateIdFor(chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex)[i]); -+ } -+ } -+ -+ dataBitsWriter.setIndex(chunkPacketInfoAntiXray.getOrCreateIdForIndex(chunkSectionIndex)); -+ -+ //Check if the chunk section below was not obfuscated -+ if (chunkSectionIndex == 0 || !chunkPacketInfoAntiXray.isWritten(chunkSectionIndex - 1) || chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex - 1) == null) { -+ //If so, initialize some stuff -+ dataBitsReader.setBitsPerObject(chunkPacketInfoAntiXray.getBitsPerObject(chunkSectionIndex)); -+ dataBitsReader.setIndex(chunkPacketInfoAntiXray.getOrCreateIdForIndex(chunkSectionIndex)); -+ solidTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex), solid, solidGlobal); -+ obfuscateTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex), obfuscate, obfuscateGlobal); -+ //Read the blocks of the upper layer of the chunk section below if it exists -+ ChunkSection belowChunkSection = null; -+ boolean skipFirstLayer = chunkSectionIndex == 0 || (belowChunkSection = chunkPacketInfoAntiXray.getChunk().getSections()[chunkSectionIndex - 1]) == Chunk.EMPTY_CHUNK_SECTION; -+ -+ for (int z = 0; z < 16; z++) { -+ for (int x = 0; x < 16; x++) { -+ current[z][x] = true; -+ next[z][x] = skipFirstLayer || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(belowChunkSection.getType(x, 15, z))]; -+ } -+ } -+ -+ //Abuse the obfuscateLayer method to read the blocks of the first layer of the current chunk section -+ dataBitsWriter.setBitsPerObject(0); -+ obfuscateLayer(-1, dataBitsReader, dataBitsWriter, solidTemp, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, emptyNearbyChunkSections, counter); -+ } -+ -+ dataBitsWriter.setBitsPerObject(chunkPacketInfoAntiXray.getBitsPerObject(chunkSectionIndex)); -+ nearbyChunkSections[0] = chunkPacketInfoAntiXray.getNearbyChunks()[0] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[0].getSections()[chunkSectionIndex]; -+ nearbyChunkSections[1] = chunkPacketInfoAntiXray.getNearbyChunks()[1] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[1].getSections()[chunkSectionIndex]; -+ nearbyChunkSections[2] = chunkPacketInfoAntiXray.getNearbyChunks()[2] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[2].getSections()[chunkSectionIndex]; -+ nearbyChunkSections[3] = chunkPacketInfoAntiXray.getNearbyChunks()[3] == null ? Chunk.EMPTY_CHUNK_SECTION : chunkPacketInfoAntiXray.getNearbyChunks()[3].getSections()[chunkSectionIndex]; -+ -+ //Obfuscate all layers of the current chunk section except the upper one -+ for (int y = 0; y < 15; y++) { -+ boolean[][] temp = current; -+ current = next; -+ next = nextNext; -+ nextNext = temp; -+ counter = obfuscateLayer(y, dataBitsReader, dataBitsWriter, solidTemp, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, counter); -+ } -+ -+ //Check if the chunk section above doesn't need obfuscation -+ if (chunkSectionIndex == maxChunkSectionIndex || !chunkPacketInfoAntiXray.isWritten(chunkSectionIndex + 1) || chunkPacketInfoAntiXray.getPredefinedObjects(chunkSectionIndex + 1) == null) { -+ //If so, obfuscate the upper layer of the current chunk section by reading blocks of the first layer from the chunk section above if it exists -+ ChunkSection aboveChunkSection; -+ -+ if (chunkSectionIndex != 15 && (aboveChunkSection = chunkPacketInfoAntiXray.getChunk().getSections()[chunkSectionIndex + 1]) != Chunk.EMPTY_CHUNK_SECTION) { -+ boolean[][] temp = current; -+ current = next; -+ next = nextNext; -+ nextNext = temp; -+ -+ for (int z = 0; z < 16; z++) { -+ for (int x = 0; x < 16; x++) { -+ if (!solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(aboveChunkSection.getType(x, 0, z))]) { -+ current[z][x] = true; -+ } -+ } -+ } -+ -+ //There is nothing to read anymore -+ dataBitsReader.setBitsPerObject(0); -+ solid[0] = true; -+ counter = obfuscateLayer(15, dataBitsReader, dataBitsWriter, solid, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, counter); -+ } -+ } else { -+ //If not, initialize the reader and other stuff for the chunk section above to obfuscate the upper layer of the current chunk section -+ dataBitsReader.setBitsPerObject(chunkPacketInfoAntiXray.getBitsPerObject(chunkSectionIndex + 1)); -+ dataBitsReader.setIndex(chunkPacketInfoAntiXray.getOrCreateIdForIndex(chunkSectionIndex + 1)); -+ solidTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex + 1), solid, solidGlobal); -+ obfuscateTemp = readDataPalette(chunkPacketInfoAntiXray.getDataPalette(chunkSectionIndex + 1), obfuscate, obfuscateGlobal); -+ boolean[][] temp = current; -+ current = next; -+ next = nextNext; -+ nextNext = temp; -+ counter = obfuscateLayer(15, dataBitsReader, dataBitsWriter, solidTemp, obfuscateTemp, predefinedBlockDataBitsTemp, current, next, nextNext, nearbyChunkSections, counter); -+ } -+ -+ dataBitsWriter.finish(); -+ } -+ } -+ -+ chunkPacketInfoAntiXray.getPacketPlayOutMapChunk().setReady(true); -+ } -+ -+ private int obfuscateLayer(int y, DataBitsReader dataBitsReader, DataBitsWriter dataBitsWriter, boolean[] solid, boolean[] obfuscate, int[] predefinedBlockDataBits, boolean[][] current, boolean[][] next, boolean[][] nextNext, ChunkSection[] nearbyChunkSections, int counter) { -+ //First block of first line -+ int dataBits = dataBitsReader.read(); -+ -+ if (nextNext[0][0] = !solid[dataBits]) { -+ dataBitsWriter.skip(); -+ next[0][1] = true; -+ next[1][0] = true; -+ } else { -+ if (nearbyChunkSections[2] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[2].getType(0, y, 15))] || nearbyChunkSections[0] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[0].getType(15, y, 0))] || current[0][0]) { -+ dataBitsWriter.skip(); -+ } else { -+ if (counter >= predefinedBlockDataBits.length) { -+ counter = 0; -+ } -+ -+ dataBitsWriter.write(predefinedBlockDataBits[counter++]); -+ } -+ } -+ -+ if (!obfuscate[dataBits]) { -+ next[0][0] = true; -+ } -+ -+ //First line -+ for (int x = 1; x < 15; x++) { -+ dataBits = dataBitsReader.read(); -+ -+ if (nextNext[0][x] = !solid[dataBits]) { -+ dataBitsWriter.skip(); -+ next[0][x - 1] = true; -+ next[0][x + 1] = true; -+ next[1][x] = true; -+ } else { -+ if (nearbyChunkSections[2] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[2].getType(x, y, 15))] || current[0][x]) { -+ dataBitsWriter.skip(); -+ } else { -+ if (counter >= predefinedBlockDataBits.length) { -+ counter = 0; -+ } -+ -+ dataBitsWriter.write(predefinedBlockDataBits[counter++]); -+ } -+ } -+ -+ if (!obfuscate[dataBits]) { -+ next[0][x] = true; -+ } -+ } -+ -+ //Last block of first line -+ dataBits = dataBitsReader.read(); -+ -+ if (nextNext[0][15] = !solid[dataBits]) { -+ dataBitsWriter.skip(); -+ next[0][14] = true; -+ next[1][15] = true; -+ } else { -+ if (nearbyChunkSections[2] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[2].getType(15, y, 15))] || nearbyChunkSections[1] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[1].getType(0, y, 0))] || current[0][15]) { -+ dataBitsWriter.skip(); -+ } else { -+ if (counter >= predefinedBlockDataBits.length) { -+ counter = 0; -+ } -+ -+ dataBitsWriter.write(predefinedBlockDataBits[counter++]); -+ } -+ } -+ -+ if (!obfuscate[dataBits]) { -+ next[0][15] = true; -+ } -+ -+ //All inner lines -+ for (int z = 1; z < 15; z++) { -+ //First block -+ dataBits = dataBitsReader.read(); -+ -+ if (nextNext[z][0] = !solid[dataBits]) { -+ dataBitsWriter.skip(); -+ next[z][1] = true; -+ next[z - 1][0] = true; -+ next[z + 1][0] = true; -+ } else { -+ if (nearbyChunkSections[0] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[0].getType(15, y, z))] || current[z][0]) { -+ dataBitsWriter.skip(); -+ } else { -+ if (counter >= predefinedBlockDataBits.length) { -+ counter = 0; -+ } -+ -+ dataBitsWriter.write(predefinedBlockDataBits[counter++]); -+ } -+ } -+ -+ if (!obfuscate[dataBits]) { -+ next[z][0] = true; -+ } -+ -+ //All inner blocks -+ for (int x = 1; x < 15; x++) { -+ dataBits = dataBitsReader.read(); -+ -+ if (nextNext[z][x] = !solid[dataBits]) { -+ dataBitsWriter.skip(); -+ next[z][x - 1] = true; -+ next[z][x + 1] = true; -+ next[z - 1][x] = true; -+ next[z + 1][x] = true; -+ } else { -+ if (current[z][x]) { -+ dataBitsWriter.skip(); -+ } else { -+ if (counter >= predefinedBlockDataBits.length) { -+ counter = 0; -+ } -+ -+ dataBitsWriter.write(predefinedBlockDataBits[counter++]); -+ } -+ } -+ -+ if (!obfuscate[dataBits]) { -+ next[z][x] = true; -+ } -+ } -+ -+ //Last block -+ dataBits = dataBitsReader.read(); -+ -+ if (nextNext[z][15] = !solid[dataBits]) { -+ dataBitsWriter.skip(); -+ next[z][14] = true; -+ next[z - 1][15] = true; -+ next[z + 1][15] = true; -+ } else { -+ if (nearbyChunkSections[1] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[1].getType(0, y, z))] || current[z][15]) { -+ dataBitsWriter.skip(); -+ } else { -+ if (counter >= predefinedBlockDataBits.length) { -+ counter = 0; -+ } -+ -+ dataBitsWriter.write(predefinedBlockDataBits[counter++]); -+ } -+ } -+ -+ if (!obfuscate[dataBits]) { -+ next[z][15] = true; -+ } -+ } -+ -+ //First block of last line -+ dataBits = dataBitsReader.read(); -+ -+ if (nextNext[15][0] = !solid[dataBits]) { -+ dataBitsWriter.skip(); -+ next[15][1] = true; -+ next[14][0] = true; -+ } else { -+ if (nearbyChunkSections[3] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[3].getType(0, y, 0))] || nearbyChunkSections[0] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[0].getType(15, y, 15))] || current[15][0]) { -+ dataBitsWriter.skip(); -+ } else { -+ if (counter >= predefinedBlockDataBits.length) { -+ counter = 0; -+ } -+ -+ dataBitsWriter.write(predefinedBlockDataBits[counter++]); -+ } -+ } -+ -+ if (!obfuscate[dataBits]) { -+ next[15][0] = true; -+ } -+ -+ //Last line -+ for (int x = 1; x < 15; x++) { -+ dataBits = dataBitsReader.read(); -+ -+ if (nextNext[15][x] = !solid[dataBits]) { -+ dataBitsWriter.skip(); -+ next[15][x - 1] = true; -+ next[15][x + 1] = true; -+ next[14][x] = true; -+ } else { -+ if (nearbyChunkSections[3] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[3].getType(x, y, 0))] || current[15][x]) { -+ dataBitsWriter.skip(); -+ } else { -+ if (counter >= predefinedBlockDataBits.length) { -+ counter = 0; -+ } -+ -+ dataBitsWriter.write(predefinedBlockDataBits[counter++]); -+ } -+ } -+ -+ if (!obfuscate[dataBits]) { -+ next[15][x] = true; -+ } -+ } -+ -+ //Last block of last line -+ dataBits = dataBitsReader.read(); -+ -+ if (nextNext[15][15] = !solid[dataBits]) { -+ dataBitsWriter.skip(); -+ next[15][14] = true; -+ next[14][15] = true; -+ } else { -+ if (nearbyChunkSections[3] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[3].getType(15, y, 0))] || nearbyChunkSections[1] == Chunk.EMPTY_CHUNK_SECTION || !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(nearbyChunkSections[1].getType(0, y, 15))] || current[15][15]) { -+ dataBitsWriter.skip(); -+ } else { -+ if (counter >= predefinedBlockDataBits.length) { -+ counter = 0; -+ } -+ -+ dataBitsWriter.write(predefinedBlockDataBits[counter++]); -+ } -+ } -+ -+ if (!obfuscate[dataBits]) { -+ next[15][15] = true; -+ } -+ -+ return counter; -+ } -+ -+ private boolean[] readDataPalette(DataPalette dataPalette, boolean[] temp, boolean[] global) { -+ if (dataPalette == ChunkSection.GLOBAL_PALETTE) { -+ return global; -+ } -+ -+ IBlockData blockData; -+ -+ for (int i = 0; (blockData = dataPalette.getObject(i)) != null; i++) { -+ temp[i] = global[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(blockData)]; -+ } -+ -+ return temp; -+ } -+ -+ @Override -+ public void onBlockChange(World world, BlockPosition blockPosition, IBlockData newBlockData, IBlockData oldBlockData, int flag) { -+ if (oldBlockData != null && solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(oldBlockData)] && !solidGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(newBlockData)] && blockPosition.getY() <= maxBlockYUpdatePosition) { -+ updateNearbyBlocks(world, blockPosition); -+ } -+ } -+ -+ @Override -+ public void onPlayerLeftClickBlock(PlayerInteractManager playerInteractManager, BlockPosition blockPosition, EnumDirection enumDirection) { -+ if (blockPosition.getY() <= maxBlockYUpdatePosition) { -+ updateNearbyBlocks(playerInteractManager.world, blockPosition); -+ } -+ } -+ -+ private void updateNearbyBlocks(World world, BlockPosition blockPosition) { -+ if (updateRadius >= 2) { -+ BlockPosition temp = blockPosition.west(); -+ updateBlock(world, temp); -+ updateBlock(world, temp.west()); -+ updateBlock(world, temp.down()); -+ updateBlock(world, temp.up()); -+ updateBlock(world, temp.north()); -+ updateBlock(world, temp.south()); -+ updateBlock(world, temp = blockPosition.east()); -+ updateBlock(world, temp.east()); -+ updateBlock(world, temp.down()); -+ updateBlock(world, temp.up()); -+ updateBlock(world, temp.north()); -+ updateBlock(world, temp.south()); -+ updateBlock(world, temp = blockPosition.down()); -+ updateBlock(world, temp.down()); -+ updateBlock(world, temp.north()); -+ updateBlock(world, temp.south()); -+ updateBlock(world, temp = blockPosition.up()); -+ updateBlock(world, temp.up()); -+ updateBlock(world, temp.north()); -+ updateBlock(world, temp.south()); -+ updateBlock(world, temp = blockPosition.north()); -+ updateBlock(world, temp.north()); -+ updateBlock(world, temp = blockPosition.south()); -+ updateBlock(world, temp.south()); -+ } else if (updateRadius == 1) { -+ updateBlock(world, blockPosition.west()); -+ updateBlock(world, blockPosition.east()); -+ updateBlock(world, blockPosition.down()); -+ updateBlock(world, blockPosition.up()); -+ updateBlock(world, blockPosition.north()); -+ updateBlock(world, blockPosition.south()); -+ } else { -+ //Do nothing if updateRadius <= 0 (test mode) -+ } -+ } -+ -+ private void updateBlock(World world, BlockPosition blockPosition) { -+ IBlockData blockData = world.getTypeIfLoaded(blockPosition); -+ -+ if (blockData != null && obfuscateGlobal[ChunkSection.GLOBAL_PALETTE.getOrCreateIdFor(blockData)]) { -+ world.notify(blockPosition, blockData, blockData, 3); -+ } -+ } -+ -+ public enum EngineMode { -+ -+ HIDE(1, "hide ores"), -+ OBFUSCATE(2, "obfuscate"); -+ -+ private final int id; -+ private final String description; -+ -+ EngineMode(int id, String description) { -+ this.id = id; -+ this.description = description; -+ } -+ -+ public static EngineMode getById(int id) { -+ for (EngineMode engineMode : values()) { -+ if (engineMode.id == id) { -+ return engineMode; -+ } -+ } -+ -+ return null; -+ } -+ -+ public int getId() { -+ return id; -+ } -+ -+ public String getDescription() { -+ return description; -+ } -+ } -+ -+ public enum ChunkEdgeMode { -+ -+ DEFAULT(1, "default"), -+ WAIT(2, "wait until nearby chunks are loaded"), -+ LOAD(3, "load nearby chunks"); -+ -+ private final int id; -+ private final String description; -+ -+ ChunkEdgeMode(int id, String description) { -+ this.id = id; -+ this.description = description; -+ } -+ -+ public static ChunkEdgeMode getById(int id) { -+ for (ChunkEdgeMode chunkEdgeMode : values()) { -+ if (chunkEdgeMode.id == id) { -+ return chunkEdgeMode; -+ } -+ } -+ -+ return null; -+ } -+ -+ public int getId() { -+ return id; -+ } -+ -+ public String getDescription() { -+ return description; -+ } -+ } -+} -diff --git a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfo.java b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfo.java -new file mode 100644 -index 0000000000..a68bace353 ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfo.java -@@ -0,0 +0,0 @@ -+package com.destroystokyo.paper.antixray; -+ -+import net.minecraft.server.Chunk; -+import net.minecraft.server.DataPalette; -+import net.minecraft.server.PacketPlayOutMapChunk; -+ -+public class ChunkPacketInfo { -+ -+ private final PacketPlayOutMapChunk packetPlayOutMapChunk; -+ private final Chunk chunk; -+ private final int chunkSectionSelector; -+ private byte[] data; -+ private final int[] bitsPerObject = new int[16]; -+ private final Object[] dataPalettes = new Object[16]; -+ private final int[] dataBitsIndexes = new int[16]; -+ private final Object[][] predefinedObjects = new Object[16][]; -+ -+ public ChunkPacketInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector) { -+ this.packetPlayOutMapChunk = packetPlayOutMapChunk; -+ this.chunk = chunk; -+ this.chunkSectionSelector = chunkSectionSelector; -+ } -+ -+ public PacketPlayOutMapChunk getPacketPlayOutMapChunk() { -+ return packetPlayOutMapChunk; -+ } -+ -+ public Chunk getChunk() { -+ return chunk; -+ } -+ -+ public int getChunkSectionSelector() { -+ return chunkSectionSelector; -+ } -+ -+ public byte[] getData() { -+ return data; -+ } -+ -+ public void setData(byte[] data) { -+ this.data = data; -+ } -+ -+ public int getBitsPerObject(int chunkSectionIndex) { -+ return bitsPerObject[chunkSectionIndex]; -+ } -+ -+ public void setBitsPerObject(int chunkSectionIndex, int bitsPerObject) { -+ this.bitsPerObject[chunkSectionIndex] = bitsPerObject; -+ } -+ -+ @SuppressWarnings("unchecked") -+ public DataPalette getDataPalette(int chunkSectionIndex) { -+ return (DataPalette) dataPalettes[chunkSectionIndex]; -+ } -+ -+ public void setDataPalette(int chunkSectionIndex, DataPalette dataPalette) { -+ dataPalettes[chunkSectionIndex] = dataPalette; -+ } -+ -+ public int getOrCreateIdForIndex(int chunkSectionIndex) { -+ return dataBitsIndexes[chunkSectionIndex]; -+ } -+ -+ public void setDataBitsIndex(int chunkSectionIndex, int dataBitsIndex) { -+ dataBitsIndexes[chunkSectionIndex] = dataBitsIndex; -+ } -+ -+ @SuppressWarnings("unchecked") -+ public T[] getPredefinedObjects(int chunkSectionIndex) { -+ return (T[]) predefinedObjects[chunkSectionIndex]; -+ } -+ -+ public void setPredefinedObjects(int chunkSectionIndex, T[] predefinedObjects) { -+ this.predefinedObjects[chunkSectionIndex] = predefinedObjects; -+ } -+ -+ public boolean isWritten(int chunkSectionIndex) { -+ return bitsPerObject[chunkSectionIndex] != 0; -+ } -+} -diff --git a/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfoAntiXray.java b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfoAntiXray.java -new file mode 100644 -index 0000000000..e255a45fa3 ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/antixray/ChunkPacketInfoAntiXray.java -@@ -0,0 +0,0 @@ -+package com.destroystokyo.paper.antixray; -+ -+import net.minecraft.server.Chunk; -+import net.minecraft.server.IBlockData; -+import net.minecraft.server.PacketPlayOutMapChunk; -+ -+public class ChunkPacketInfoAntiXray extends ChunkPacketInfo implements Runnable { -+ -+ private Chunk[] nearbyChunks; -+ private final ChunkPacketBlockControllerAntiXray chunkPacketBlockControllerAntiXray; -+ -+ public ChunkPacketInfoAntiXray(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector, ChunkPacketBlockControllerAntiXray chunkPacketBlockControllerAntiXray) { -+ super(packetPlayOutMapChunk, chunk, chunkSectionSelector); -+ this.chunkPacketBlockControllerAntiXray = chunkPacketBlockControllerAntiXray; -+ } -+ -+ public Chunk[] getNearbyChunks() { -+ return nearbyChunks; -+ } -+ -+ public void setNearbyChunks(Chunk... nearbyChunks) { -+ this.nearbyChunks = nearbyChunks; -+ } -+ -+ @Override -+ public void run() { -+ chunkPacketBlockControllerAntiXray.obfuscate(this); -+ } -+} -diff --git a/src/main/java/com/destroystokyo/paper/antixray/DataBitsReader.java b/src/main/java/com/destroystokyo/paper/antixray/DataBitsReader.java -new file mode 100644 -index 0000000000..cc586827aa ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/antixray/DataBitsReader.java -@@ -0,0 +0,0 @@ -+package com.destroystokyo.paper.antixray; -+ -+public class DataBitsReader { -+ -+ private byte[] dataBits; -+ private int bitsPerObject; -+ private int mask; -+ private int longInDataBitsIndex; -+ private int bitInLongIndex; -+ private long current; -+ -+ public void setDataBits(byte[] dataBits) { -+ this.dataBits = dataBits; -+ } -+ -+ public void setBitsPerObject(int bitsPerObject) { -+ this.bitsPerObject = bitsPerObject; -+ mask = (1 << bitsPerObject) - 1; -+ } -+ -+ public void setIndex(int index) { -+ this.longInDataBitsIndex = index; -+ bitInLongIndex = 0; -+ init(); -+ } -+ -+ private void init() { -+ if (dataBits.length > longInDataBitsIndex + 7) { -+ current = ((((long) dataBits[longInDataBitsIndex]) << 56) -+ | (((long) dataBits[longInDataBitsIndex + 1] & 0xff) << 48) -+ | (((long) dataBits[longInDataBitsIndex + 2] & 0xff) << 40) -+ | (((long) dataBits[longInDataBitsIndex + 3] & 0xff) << 32) -+ | (((long) dataBits[longInDataBitsIndex + 4] & 0xff) << 24) -+ | (((long) dataBits[longInDataBitsIndex + 5] & 0xff) << 16) -+ | (((long) dataBits[longInDataBitsIndex + 6] & 0xff) << 8) -+ | (((long) dataBits[longInDataBitsIndex + 7] & 0xff))); -+ } -+ } -+ -+ public int read() { -+ int value = (int) (current >>> bitInLongIndex) & mask; -+ bitInLongIndex += bitsPerObject; -+ -+ if (bitInLongIndex > 63) { -+ bitInLongIndex -= 64; -+ longInDataBitsIndex += 8; -+ init(); -+ -+ if (bitInLongIndex > 0) { -+ value |= current << bitsPerObject - bitInLongIndex & mask; -+ } -+ } -+ -+ return value; -+ } -+} -diff --git a/src/main/java/com/destroystokyo/paper/antixray/DataBitsWriter.java b/src/main/java/com/destroystokyo/paper/antixray/DataBitsWriter.java -new file mode 100644 -index 0000000000..37093419cf ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/antixray/DataBitsWriter.java -@@ -0,0 +0,0 @@ -+package com.destroystokyo.paper.antixray; -+ -+public class DataBitsWriter { -+ -+ private byte[] dataBits; -+ private int bitsPerObject; -+ private long mask; -+ private int longInDataBitsIndex; -+ private int bitInLongIndex; -+ private long current; -+ private boolean dirty; -+ -+ public void setDataBits(byte[] dataBits) { -+ this.dataBits = dataBits; -+ } -+ -+ public void setBitsPerObject(int bitsPerObject) { -+ this.bitsPerObject = bitsPerObject; -+ mask = (1 << bitsPerObject) - 1; -+ } -+ -+ public void setIndex(int index) { -+ this.longInDataBitsIndex = index; -+ bitInLongIndex = 0; -+ init(); -+ } -+ -+ private void init() { -+ if (dataBits.length > longInDataBitsIndex + 7) { -+ current = ((((long) dataBits[longInDataBitsIndex]) << 56) -+ | (((long) dataBits[longInDataBitsIndex + 1] & 0xff) << 48) -+ | (((long) dataBits[longInDataBitsIndex + 2] & 0xff) << 40) -+ | (((long) dataBits[longInDataBitsIndex + 3] & 0xff) << 32) -+ | (((long) dataBits[longInDataBitsIndex + 4] & 0xff) << 24) -+ | (((long) dataBits[longInDataBitsIndex + 5] & 0xff) << 16) -+ | (((long) dataBits[longInDataBitsIndex + 6] & 0xff) << 8) -+ | (((long) dataBits[longInDataBitsIndex + 7] & 0xff))); -+ } -+ -+ dirty = false; -+ } -+ -+ public void finish() { -+ if (dirty && dataBits.length > longInDataBitsIndex + 7) { -+ dataBits[longInDataBitsIndex] = (byte) (current >> 56 & 0xff); -+ dataBits[longInDataBitsIndex + 1] = (byte) (current >> 48 & 0xff); -+ dataBits[longInDataBitsIndex + 2] = (byte) (current >> 40 & 0xff); -+ dataBits[longInDataBitsIndex + 3] = (byte) (current >> 32 & 0xff); -+ dataBits[longInDataBitsIndex + 4] = (byte) (current >> 24 & 0xff); -+ dataBits[longInDataBitsIndex + 5] = (byte) (current >> 16 & 0xff); -+ dataBits[longInDataBitsIndex + 6] = (byte) (current >> 8 & 0xff); -+ dataBits[longInDataBitsIndex + 7] = (byte) (current & 0xff); -+ } -+ } -+ -+ public void write(int value) { -+ current = current & ~(mask << bitInLongIndex) | (value & mask) << bitInLongIndex; -+ dirty = true; -+ bitInLongIndex += bitsPerObject; -+ -+ if (bitInLongIndex > 63) { -+ finish(); -+ bitInLongIndex -= 64; -+ longInDataBitsIndex += 8; -+ init(); -+ -+ if (bitInLongIndex > 0) { -+ current = current & ~(mask >>> bitsPerObject - bitInLongIndex) | (value & mask) >>> bitsPerObject - bitInLongIndex; -+ dirty = true; -+ } -+ } -+ } -+ -+ public void skip() { -+ bitInLongIndex += bitsPerObject; -+ -+ if (bitInLongIndex > 63) { -+ finish(); -+ bitInLongIndex -= 64; -+ longInDataBitsIndex += 8; -+ init(); -+ } -+ } -+} -diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java -index 4502ece4dd..4ae0acbf13 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 { - return null; - } - -- chunksection = new ChunkSection(j >> 4 << 4, this.world.worldProvider.g()); -+ chunksection = new ChunkSection(j >> 4 << 4, this.world.worldProvider.g(), this, this.world, true); // Paper - Anti-Xray - this.sections[j >> 4] = chunksection; - flag1 = j >= l; - } -@@ -0,0 +0,0 @@ public class Chunk implements IChunkAccess { - return; - } - -- chunksection = new ChunkSection(i1 << 4, flag); -+ chunksection = new ChunkSection(i1 << 4, flag, this, this.world, true); // Paper - Anti-Xray - this.sections[i1] = chunksection; - this.initLighting(); - } -diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java -index 60143ff63f..d938eb3749 100644 ---- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java -+++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java -@@ -0,0 +0,0 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver { - } - - ChunkConverter chunkconverter = nbttagcompound.hasKeyOfType("UpgradeData", 10) ? new ChunkConverter(nbttagcompound.getCompound("UpgradeData")) : ChunkConverter.a; -- ProtoChunk protochunk = new ProtoChunk(i, j, chunkconverter); -+ ProtoChunk protochunk = new ProtoChunk(i, j, chunkconverter, generatoraccess); // Paper - Anti-Xray - - protochunk.a(abiomebase); - protochunk.b(nbttagcompound.getLong("InhabitedTime")); -@@ -0,0 +0,0 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver { - for (int i = 0; i < nbttaglist.size(); ++i) { - NBTTagCompound nbttagcompound = nbttaglist.getCompound(i); - byte b0 = nbttagcompound.getByte("Y"); -- ChunkSection chunksection = new ChunkSection(b0 << 4, flag1); -+ ChunkSection chunksection = new ChunkSection(b0 << 4, flag1, null, iworldreader, false); // Paper - Anti-Xray - - chunksection.getBlocks().a(nbttagcompound, "Palette", "BlockStates"); - chunksection.a(new NibbleArray(nbttagcompound.getByteArray("BlockLight"))); -diff --git a/src/main/java/net/minecraft/server/ChunkSection.java b/src/main/java/net/minecraft/server/ChunkSection.java -index 621ed1fc53..2af07ae592 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 { - private NibbleArray emittedLight; - private NibbleArray skyLight; - -+ // Paper start - Anti-Xray - Support default constructor - public ChunkSection(int i, boolean flag) { -+ this(i, flag, null, null, true); -+ } -+ // Paper end -+ -+ public ChunkSection(int i, boolean flag, IChunkAccess chunk, IWorldReader world, boolean initializeBlocks) { // Paper - Anti-Xray - this.yPos = i; -- this.blockIds = new DataPaletteBlock<>(ChunkSection.GLOBAL_PALETTE, Block.REGISTRY_ID, GameProfileSerializer::d, GameProfileSerializer::a, Blocks.AIR.getBlockData()); -+ this.blockIds = new DataPaletteBlock<>(ChunkSection.GLOBAL_PALETTE, Block.REGISTRY_ID, GameProfileSerializer::d, GameProfileSerializer::a, Blocks.AIR.getBlockData(), world instanceof GeneratorAccess ? ((GeneratorAccess) world).getMinecraftWorld().chunkPacketBlockController.getPredefinedBlockData(world, chunk, this, flag, initializeBlocks) : null, initializeBlocks); // Paper - Anti-Xray - Add predefined block data - this.emittedLight = new NibbleArray(); - if (flag) { - this.skyLight = new NibbleArray(); -diff --git a/src/main/java/net/minecraft/server/ChunkTaskScheduler.java b/src/main/java/net/minecraft/server/ChunkTaskScheduler.java -index 56958a5ce3..d3898599f8 100644 ---- a/src/main/java/net/minecraft/server/ChunkTaskScheduler.java -+++ b/src/main/java/net/minecraft/server/ChunkTaskScheduler.java -@@ -0,0 +0,0 @@ public class ChunkTaskScheduler extends Scheduler implements DataPaletteExpandable { - private final Function e; - private final Function f; - private final T g; -+ private final T[] predefinedObjects; // Paper - Anti-Xray - Add predefined objects - protected DataBits a; protected DataBits getDataBits() { return this.a; } // Paper - OBFHELPER - private DataPalette h; private DataPalette getDataPalette() { return this.h; } // Paper - OBFHELPER - private int i; private int getBitsPerObject() { return this.i; } // Paper - OBFHELPER -@@ -0,0 +0,0 @@ public class DataPaletteBlock implements DataPaletteExpandable { - } - - public DataPaletteBlock(DataPalette datapalette, RegistryBlockID registryblockid, Function function, Function function1, T t0) { -+ // Paper start - Anti-Xray - Support default constructor -+ this(datapalette, registryblockid, function, function1, t0, null, true); -+ } -+ -+ public DataPaletteBlock(DataPalette datapalette, RegistryBlockID registryblockid, Function function, Function function1, T t0, T[] predefinedObjects, boolean initialize) { -+ // Paper end - Anti-Xray - Add predefined objects - this.b = datapalette; - this.d = registryblockid; - this.e = function; - this.f = function1; - this.g = t0; -- this.b(4); -+ // Paper start - Anti-Xray - Add predefined objects -+ this.predefinedObjects = predefinedObjects; -+ -+ if (initialize) { -+ if (predefinedObjects == null) { -+ // Default -+ this.initialize(4); -+ } else { -+ // TODO: MathHelper.d(int i) can be used here instead (see DataPaletteBlock#a(NBTTagCompound nbttagcompound, String s, String s1)) but I don't understand the implementation -+ // Count the bits of the maximum array index to initialize a data palette with enough space from the beginning -+ // The length of the array is used because air is also added to the data palette from the beginning -+ // Start with at least 4 -+ int maxIndex = predefinedObjects.length >> 4; -+ int bitCount = 4; -+ -+ while (maxIndex != 0) { -+ maxIndex >>= 1; -+ bitCount++; -+ } -+ -+ // Initialize with at least 15 free indixes -+ this.initialize((1 << bitCount) - predefinedObjects.length < 16 ? bitCount + 1 : bitCount); -+ this.addPredefinedObjects(); -+ } -+ } -+ // Paper end - } - - private static int b(int i, int j, int k) { -@@ -0,0 +0,0 @@ public class DataPaletteBlock implements DataPaletteExpandable { - } - } - -+ // Paper start - Anti-Xray - Add predefined objects -+ private void addPredefinedObjects() { -+ if (this.predefinedObjects != null && this.getDataPalette() != this.getDataPaletteGlobal()) { -+ for (int i = 0; i < this.predefinedObjects.length; i++) { -+ this.getDataPalette().getOrCreateIdFor(this.predefinedObjects[i]); -+ } -+ } -+ } -+ // Paper end -+ - public int onResize(int i, T t0) { - this.b(); - DataBits databits = this.a; -@@ -0,0 +0,0 @@ public class DataPaletteBlock implements DataPaletteExpandable { - - int j; - -+ this.addPredefinedObjects(); // Paper - Anti-Xray - Add predefined objects - for (j = 0; j < databits.b(); ++j) { - T t1 = datapalette.a(databits.a(j)); - -@@ -0,0 +0,0 @@ public class DataPaletteBlock implements DataPaletteExpandable { - return t0 == null ? this.g : t0; - } - -- public void writeDataPaletteBlock(PacketDataSerializer packetDataSerializer) { this.b(packetDataSerializer); } // Paper - OBFHELPER -+ // Paper start - Anti-Xray - Support default methods -+ public void writeDataPaletteBlock(PacketDataSerializer packetDataSerializer) { this.b(packetDataSerializer); } - public void b(PacketDataSerializer packetdataserializer) { -+ this.b(packetdataserializer, null, 0); -+ } -+ // Paper end -+ -+ public void writeDataPaletteBlock(PacketDataSerializer packetDataSerializer, ChunkPacketInfo chunkPacketInfo, int chunkSectionIndex) { this.b(packetDataSerializer, chunkPacketInfo, chunkSectionIndex); } // Paper - OBFHELPER // Paper - Anti-Xray - Add chunk packet info -+ public void b(PacketDataSerializer packetdataserializer, ChunkPacketInfo chunkPacketInfo, int chunkSectionIndex) { // Paper - Anti-Xray - Add chunk packet info - this.b(); - packetdataserializer.writeByte(this.i); - this.h.b(packetdataserializer); -+ -+ // Paper start - Anti-Xray - Add chunk packet info -+ if (chunkPacketInfo != null) { -+ chunkPacketInfo.setBitsPerObject(chunkSectionIndex, this.getBitsPerObject()); -+ chunkPacketInfo.setDataPalette(chunkSectionIndex, this.getDataPalette()); -+ chunkPacketInfo.setDataBitsIndex(chunkSectionIndex, packetdataserializer.writerIndex() + PacketDataSerializer.countBytes(this.getDataBits().getDataBits().length)); -+ chunkPacketInfo.setPredefinedObjects(chunkSectionIndex, this.predefinedObjects); -+ } -+ // Paper end -+ - packetdataserializer.a(this.a.a()); - this.c(); - } -@@ -0,0 +0,0 @@ public class DataPaletteBlock implements DataPaletteExpandable { - public void a(NBTTagCompound nbttagcompound, String s, String s1) { - this.b(); - NBTTagList nbttaglist = nbttagcompound.getList(s, 10); -- int i = Math.max(4, MathHelper.d(nbttaglist.size())); -+ // Paper - Anti-Xray - TODO: Should this.predefinedObjects.length just be added here (faster) or should the contents be compared to calculate the size (less RAM)? -+ int i = Math.max(4, MathHelper.d(nbttaglist.size() + (this.predefinedObjects == null ? 0 : this.predefinedObjects.length))); // Paper - Anti-Xray - Calculate the size with predefined objects - -- if (i != this.i) { -+ if (true || i != this.i) { // Paper - Anti-Xray - Not initialized yet - this.b(i); - } - - this.h.a(nbttaglist); -+ this.addPredefinedObjects(); // Paper - Anti-Xray - Add predefined objects - long[] along = nbttagcompound.o(s1); - int j = along.length * 64 / 4096; - -diff --git a/src/main/java/net/minecraft/server/NetworkManager.java b/src/main/java/net/minecraft/server/NetworkManager.java -index 4a50aab512..4c1110479c 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> { - } - - public void sendPacket(Packet packet, @Nullable GenericFutureListener> genericfuturelistener) { -- if (this.isConnected()) { -- this.o(); -+ if (this.isConnected() && this.sendPacketQueue() && !(packet instanceof PacketPlayOutMapChunk && !((PacketPlayOutMapChunk) packet).isReady())) { // Paper - Async-Anti-Xray - Add chunk packets which are not ready or all packets if the packet queue contains chunk packets which are not ready to the packet queue and send the packets later in the right order -+ //this.o(); // Paper - Async-Anti-Xray - Move to if statement (this.sendPacketQueue()) - this.b(packet, genericfuturelistener); - } else { - this.j.writeLock().lock(); -@@ -0,0 +0,0 @@ public class NetworkManager extends SimpleChannelInboundHandler> { - - } - -- private void sendPacketQueue() { this.o(); } // Paper - OBFHELPER -- private void o() { -+ // Paper start - Async-Anti-Xray - Stop dispatching further packets and return false if the peeked packet is a chunk packet which is not ready -+ private boolean sendPacketQueue() { return this.o(); } // OBFHELPER // void -> boolean -+ private boolean o() { // void -> boolean - if (this.channel != null && this.channel.isOpen()) { -- this.j.readLock().lock(); -+ if (this.packetQueue.isEmpty()) { // return if the packet queue is empty so that the write lock by Anti-Xray doesn't affect the vanilla performance at all -+ return true; -+ } -+ -+ this.j.writeLock().lock(); // readLock -> writeLock (because of race condition between peek and poll) - - try { - while (!this.packetQueue.isEmpty()) { -- NetworkManager.QueuedPacket networkmanager_queuedpacket = (NetworkManager.QueuedPacket) this.packetQueue.poll(); -- -- this.b(networkmanager_queuedpacket.a, networkmanager_queuedpacket.b); -+ NetworkManager.QueuedPacket networkmanager_queuedpacket = (NetworkManager.QueuedPacket) this.getPacketQueue().peek(); // poll -> peek -+ -+ if (networkmanager_queuedpacket != null) { // Fix NPE (Spigot bug caused by handleDisconnection()) -+ if (networkmanager_queuedpacket.getPacket() instanceof PacketPlayOutMapChunk && !((PacketPlayOutMapChunk) networkmanager_queuedpacket.getPacket()).isReady()) { // Check if the peeked packet is a chunk packet which is not ready -+ return false; // Return false if the peeked packet is a chunk packet which is not ready -+ } else { -+ this.getPacketQueue().poll(); // poll here -+ this.dispatchPacket(networkmanager_queuedpacket.getPacket(), networkmanager_queuedpacket.getGenericFutureListener()); // dispatch the packet -+ } -+ } - } - } finally { -- this.j.readLock().unlock(); -+ this.j.writeLock().unlock(); // readLock -> writeLock (because of race condition between peek and poll) - } - - } -+ -+ return true; // Return true if all packets were dispatched - } -+ // Paper end - - 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 18ef7232ec..8e35d14f97 100644 ---- a/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java -+++ b/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java -@@ -0,0 +0,0 @@ - package net.minecraft.server; - -+import com.destroystokyo.paper.antixray.ChunkPacketInfo; // Paper - Anti-Xray - import com.google.common.collect.Lists; - import io.netty.buffer.ByteBuf; - import io.netty.buffer.Unpooled; -@@ -0,0 +0,0 @@ public class PacketPlayOutMapChunk implements Packet { - private byte[] d; private byte[] getData() { return this.d; } // Paper - OBFHELPER - private List e; - private boolean f; -+ private volatile boolean ready = false; // Paper - Async-Anti-Xray - Ready flag for the network manager - -- public PacketPlayOutMapChunk() {} -+ public PacketPlayOutMapChunk() { -+ this.ready = true; // Paper - Async-Anti-Xray - Set the ready flag to true -+ } - - public PacketPlayOutMapChunk(Chunk chunk, int i) { -+ ChunkPacketInfo chunkPacketInfo = chunk.world.chunkPacketBlockController.getChunkPacketInfo(this, chunk, i); // Paper - Anti-Xray - Add chunk packet info - this.a = chunk.locX; - this.b = chunk.locZ; - this.f = i == 65535; - boolean flag = chunk.getWorld().worldProvider.g(); - - this.d = new byte[this.a(chunk, flag, i)]; -- this.c = this.a(new PacketDataSerializer(this.h()), chunk, flag, 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.h()), chunk, flag, i, chunkPacketInfo); // Paper - Anti-Xray - Add chunk packet info - this.e = Lists.newArrayList(); - Iterator iterator = chunk.getTileEntities().entrySet().iterator(); - -@@ -0,0 +0,0 @@ public class PacketPlayOutMapChunk implements Packet { - } - } - -+ chunk.world.chunkPacketBlockController.modifyBlocks(this, chunkPacketInfo); // 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 - - public void a(PacketDataSerializer packetdataserializer) throws IOException { - this.a = packetdataserializer.readInt(); -@@ -0,0 +0,0 @@ public class PacketPlayOutMapChunk implements Packet { - return bytebuf; - } - -- public int writeChunk(PacketDataSerializer packetDataSerializer, Chunk chunk, boolean writeSkyLightArray, int chunkSectionSelector) { return this.a(packetDataSerializer, chunk, writeSkyLightArray, chunkSectionSelector); } // Paper - OBFHELPER -+ // Paper start - Anti-Xray - Support default methods -+ public int writeChunk(PacketDataSerializer packetDataSerializer, Chunk chunk, boolean writeSkyLightArray, int chunkSectionSelector) { return this.a(packetDataSerializer, chunk, writeSkyLightArray, chunkSectionSelector); } - public int a(PacketDataSerializer packetdataserializer, Chunk chunk, boolean flag, int i) { -+ return this.a(packetdataserializer, chunk, flag, i, null); -+ } -+ // Paper end -+ -+ public int writeChunk(PacketDataSerializer packetDataSerializer, Chunk chunk, boolean writeSkyLightArray, int chunkSectionSelector, ChunkPacketInfo chunkPacketInfo) { return this.a(packetDataSerializer, chunk, writeSkyLightArray, chunkSectionSelector, chunkPacketInfo); } // Paper - OBFHELPER // Paper - Anti-Xray - Add chunk packet info -+ public int a(PacketDataSerializer packetdataserializer, Chunk chunk, boolean flag, int i, ChunkPacketInfo chunkPacketInfo) { // Paper - Anti-Xray - Add chunk packet info - int j = 0; - ChunkSection[] achunksection = chunk.getSections(); - int k = 0; -@@ -0,0 +0,0 @@ public class PacketPlayOutMapChunk implements Packet { - - if (chunksection != Chunk.a && (!this.f() || !chunksection.a()) && (i & 1 << k) != 0) { - j |= 1 << k; -- chunksection.getBlocks().b(packetdataserializer); -+ chunksection.getBlocks().writeDataPaletteBlock(packetdataserializer, chunkPacketInfo, k); // Paper - Anti-Xray - Add chunk packet info - packetdataserializer.writeBytes(chunksection.getEmittedLightArray().asBytes()); - if (flag) { - packetdataserializer.writeBytes(chunksection.getSkyLightArray().asBytes()); -diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java -index 7d3f846a19..240f590666 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 { - return false; - } else if (!this.chunk.isReady()) { - return false; -+ } else if (!this.chunk.world.chunkPacketBlockController.onChunkPacketCreate(this.chunk, '\uffff', false)) { // Paper - Anti-Xray - Load nearby chunks if necessary -+ return false; // Paper - Anti-Xray - Wait and try again later - } else { - this.dirtyCount = 0; - this.h = 0; -@@ -0,0 +0,0 @@ public class PlayerChunk { - - public void sendChunk(EntityPlayer entityplayer) { - if (this.done) { -+ this.chunk.world.chunkPacketBlockController.onChunkPacketCreate(this.chunk, '\uffff', true); // Paper - Anti-Xray - Load nearby chunks if necessary - entityplayer.playerConnection.sendPacket(new PacketPlayOutMapChunk(this.chunk, 65535)); - this.playerChunkMap.getWorld().getTracker().a(entityplayer, this.chunk); - } -@@ -0,0 +0,0 @@ public class PlayerChunk { - this.a(this.playerChunkMap.getWorld().getTileEntity(blockposition)); - } - } else if (this.dirtyCount == 64) { -+ // Paper - Anti-Xray - Loading chunks here could cause a ConcurrentModificationException #1104 -+ // Paper - Anti-Xray - TODO: Check if this is still the case for 1.13 -+ //this.chunk.world.chunkPacketBlockController.onChunkPacketCreate(this.chunk, this.h, true); // Paper - Anti-Xray - Load nearby chunks if necessary - this.a((Packet) (new PacketPlayOutMapChunk(this.chunk, this.h))); - } else { - this.a((Packet) (new PacketPlayOutMultiBlockChange(this.dirtyCount, this.dirtyBlocks, this.chunk))); -diff --git a/src/main/java/net/minecraft/server/PlayerInteractManager.java b/src/main/java/net/minecraft/server/PlayerInteractManager.java -index 07f9b8d2f7..22378b6bc8 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.world.chunkPacketBlockController.onPlayerLeftClickBlock(this, blockposition, enumdirection); // Paper - Anti-Xray - } - - public void a(BlockPosition blockposition) { -diff --git a/src/main/java/net/minecraft/server/ProtoChunk.java b/src/main/java/net/minecraft/server/ProtoChunk.java -index 16e3469d01..e4c0cc6a33 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 { - private long s; - private final Map t; - private boolean u; -+ private GeneratorAccess world; // Paper - Anti-Xray - -+ // Paper start - Anti-Xray - Support default constructors - public ProtoChunk(int i, int j, ChunkConverter chunkconverter) { -- this(new ChunkCoordIntPair(i, j), chunkconverter); -+ this(new ChunkCoordIntPair(i, j), chunkconverter, null); - } - - public ProtoChunk(ChunkCoordIntPair chunkcoordintpair, ChunkConverter chunkconverter) { -+ this(chunkcoordintpair, chunkconverter, null); -+ } -+ -+ public ProtoChunk(int i, int j, ChunkConverter chunkconverter, GeneratorAccess world) { -+ this(new ChunkCoordIntPair(i, j), chunkconverter, world); -+ } -+ -+ public ProtoChunk(ChunkCoordIntPair chunkcoordintpair, ChunkConverter chunkconverter, GeneratorAccess world) { -+ this.world = world; -+ // Paper end - this.d = new AtomicInteger(); - this.f = Maps.newEnumMap(HeightMap.Type.class); - this.g = ChunkStatus.EMPTY; -@@ -0,0 +0,0 @@ public class ProtoChunk implements IChunkAccess { - return iblockdata; - } - -- this.j[j >> 4] = new ChunkSection(j >> 4 << 4, this.x()); -+ this.j[j >> 4] = new ChunkSection(j >> 4 << 4, this.x(), this, this.world, true); // Paper - Anti-Xray - } - - IBlockData iblockdata1 = this.j[j >> 4].getType(i & 15, j & 15, k & 15); -@@ -0,0 +0,0 @@ public class ProtoChunk implements IChunkAccess { - return; - } - -- this.j[i1] = new ChunkSection(i1 << 4, this.x()); -+ this.j[i1] = new ChunkSection(i1 << 4, this.x(), this, this.world, true); // Paper - Anti-Xray - } - - if (enumskyblock == EnumSkyBlock.SKY) { -diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java -index b007eb36c7..14f419deb8 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; - - import co.aikar.timings.Timings; -+import com.destroystokyo.paper.antixray.ChunkPacketBlockController; // Paper - Anti-Xray -+import com.destroystokyo.paper.antixray.ChunkPacketBlockControllerAntiXray; // Paper - Anti-Xray - 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 IEntityAccess, GeneratorAccess, IIBlockAc - public final org.spigotmc.SpigotWorldConfig spigotConfig; // Spigot - - public final com.destroystokyo.paper.PaperWorldConfig paperConfig; // Paper -+ public final ChunkPacketBlockController chunkPacketBlockController; // Paper - Anti-Xray - - public final co.aikar.timings.WorldTimingsHandler timings; // Paper - public boolean guardEntityList; // Spigot // Paper - public -@@ -0,0 +0,0 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc - protected World(IDataManager idatamanager, @Nullable PersistentCollection persistentcollection, WorldData worlddata, WorldProvider worldprovider, MethodProfiler methodprofiler, boolean flag, 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 -+ this.chunkPacketBlockController = this.paperConfig.antiXray ? new ChunkPacketBlockControllerAntiXray(this.paperConfig) : ChunkPacketBlockController.NO_OPERATION_INSTANCE; // Paper - Anti-Xray - 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 IEntityAccess, GeneratorAccess, IIBlockAc - // CraftBukkit end - - IBlockData iblockdata1 = chunk.setType(blockposition, iblockdata, (i & 64) != 0, (i & 1024) == 0); // CraftBukkit custom NO_PLACE flag -+ this.chunkPacketBlockController.onBlockChange(this, blockposition, iblockdata, iblockdata1, i); // Paper - Anti-Xray - - if (iblockdata1 == null) { - // CraftBukkit start - remove blockstate if failed -diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java b/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java -index 791017258a..29695da2e5 100644 ---- a/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java -+++ b/src/main/java/org/bukkit/craftbukkit/generator/CraftChunkData.java -@@ -0,0 +0,0 @@ public final class CraftChunkData implements ChunkGenerator.ChunkData { - private final int maxHeight; - private final ChunkSection[] sections; - private Set tiles; -+ private World world; // Paper - Anti-Xray - - public CraftChunkData(World world) { - this(world.getMaxHeight()); -+ this.world = world; // Paper - Anti-Xray - } - - /* pp for tests */ CraftChunkData(int maxHeight) { -@@ -0,0 +0,0 @@ public final class CraftChunkData implements ChunkGenerator.ChunkData { - private ChunkSection getChunkSection(int y, boolean create) { - ChunkSection section = sections[y >> 4]; - if (create && section == null) { -- sections[y >> 4] = section = new ChunkSection(y, create); -+ sections[y >> 4] = section = new ChunkSection(y, create, null, world instanceof org.bukkit.craftbukkit.CraftWorld ? ((org.bukkit.craftbukkit.CraftWorld) world).getHandle() : null, true); // Paper - Anti-Xray - } - return section; - } --- \ No newline at end of file diff --git a/Spigot-Server-Patches/Async-Chunk-Loading-and-Generation.patch b/Spigot-Server-Patches/Async-Chunk-Loading-and-Generation.patch deleted file mode 100644 index 0abd990380..0000000000 --- a/Spigot-Server-Patches/Async-Chunk-Loading-and-Generation.patch +++ /dev/null @@ -1,2501 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Aikar -Date: Sat, 21 Jul 2018 16:55:04 -0400 -Subject: [PATCH] Async Chunk Loading and Generation - -This brings back parity to 1.12 and older versions in that any -chunk requested as part of the PlayerChunkMap can be loaded -asynchronously, since the chunk isn't needed "immediately". - -The previous system used by CraftBukkit has been completely abandoned, as -mojang has put more concurrency checks into the process. - -The new process is no longer lock free, but tries to maintain locks as -short as possible. - -But with 1.13, we now have Chunk Conversions too. A main issue about -keeping just loading parity to 1.12 is that standard loads now -are treated as generation level events, to run the converter on -another thread. - -However mojangs code was pretty bad here and doesn't actually provide -any concurrency... - -Mojangs code is still not thread safe, and can only operate on -one world per thread safely, but this is still a major improvement -to get world generation off of the main thread for exploration. - -This change brings Chunk Requests triggered by the Chunk Map to be -lazily loaded asynchronously. - -Standard chunk loads can load in parallel across a shared executor. - -However, chunk conversions and generations must only run one per world -at a time, so we have a single thread executor for those operations -per world, that all of those requests get scheduled to. - -getChunkAt method is now thread safe, but has not been tested in -use by other threads for generations, but should be safe to do. - -However, we are not encouraging plugins to go getting chunks async, -as while looking the chunk up may be safe, absolutely nothing about -reading or writing to the chunk will be safe, so plugins still -should not be touching chunks asynchronously! - -diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java -index 07d7976d21..c25db284ff 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 { - } - } - } -+ -+ public static boolean asyncChunks = false; -+ public static boolean asyncChunkGeneration = true; -+ public static boolean asyncChunkGenThreadPerWorld = true; -+ 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); -+ } -+ -+ asyncChunks = getBoolean("settings.async-chunks.enable", true); -+ asyncChunkGeneration = getBoolean("settings.async-chunks.generation", true); -+ asyncChunkGenThreadPerWorld = getBoolean("settings.async-chunks.thread-per-world-generation", true); -+ asyncChunkLoadThreads = getInt("settings.async-chunks.load-threads", -1); -+ if (asyncChunkLoadThreads <= 0) { -+ asyncChunkLoadThreads = (int) Math.min(Integer.getInteger("paper.maxChunkThreads", 8), Runtime.getRuntime().availableProcessors() * 1.5); -+ } -+ -+ // 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"); -+ 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 { -+ log("Async Chunks: Enabled - Chunks will be loaded much faster, without lag."); -+ 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/util/PriorityQueuedExecutor.java b/src/main/java/com/destroystokyo/paper/util/PriorityQueuedExecutor.java -new file mode 100644 -index 0000000000..8f18c28695 ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/util/PriorityQueuedExecutor.java -@@ -0,0 +0,0 @@ -+package com.destroystokyo.paper.util; -+ -+import javax.annotation.Nonnull; -+import java.util.ArrayList; -+import java.util.List; -+import java.util.concurrent.AbstractExecutorService; -+import java.util.concurrent.CompletableFuture; -+import java.util.concurrent.ConcurrentLinkedQueue; -+import java.util.concurrent.RejectedExecutionException; -+import java.util.concurrent.TimeUnit; -+import java.util.concurrent.atomic.AtomicBoolean; -+import java.util.concurrent.atomic.AtomicInteger; -+import java.util.function.Supplier; -+ -+/** -+ * Implements an Executor Service that allows specifying Task Priority -+ * and bumping of task priority. -+ * -+ * This is a non blocking executor with 3 priority levels. -+ * -+ * URGENT: Rarely used, something that is critical to take action now. -+ * HIGH: Something with more importance than the base tasks -+ * -+ * @author Daniel Ennis <aikar@aikar.co> -+ */ -+@SuppressWarnings({"WeakerAccess", "UnusedReturnValue", "unused"}) -+public class PriorityQueuedExecutor extends AbstractExecutorService { -+ -+ private final ConcurrentLinkedQueue urgent = new ConcurrentLinkedQueue<>(); -+ private final ConcurrentLinkedQueue high = new ConcurrentLinkedQueue<>(); -+ private final ConcurrentLinkedQueue normal = new ConcurrentLinkedQueue<>(); -+ private final List threads = new ArrayList<>(); -+ private final RejectionHandler handler; -+ -+ private volatile boolean shuttingDown = false; -+ private volatile boolean shuttingDownNow = false; -+ -+ public PriorityQueuedExecutor(String name) { -+ this(name, Math.max(1, Runtime.getRuntime().availableProcessors() - 1)); -+ } -+ -+ public PriorityQueuedExecutor(String name, int threads) { -+ this(name, threads, Thread.NORM_PRIORITY, null); -+ } -+ -+ public PriorityQueuedExecutor(String name, int threads, int threadPriority) { -+ this(name, threads, threadPriority, null); -+ } -+ -+ public PriorityQueuedExecutor(String name, int threads, RejectionHandler handler) { -+ this(name, threads, Thread.NORM_PRIORITY, handler); -+ } -+ -+ public PriorityQueuedExecutor(String name, int threads, int threadPriority, RejectionHandler handler) { -+ for (int i = 0; i < threads; i++) { -+ ExecutorThread thread = new ExecutorThread(this::processQueues); -+ thread.setDaemon(true); -+ thread.setName(threads == 1 ? name : name + "-" + (i + 1)); -+ thread.setPriority(threadPriority); -+ thread.start(); -+ this.threads.add(thread); -+ } -+ if (handler == null) { -+ handler = ABORT_POLICY; -+ } -+ this.handler = handler; -+ } -+ -+ /** -+ * If the Current thread belongs to a PriorityQueuedExecutor, return that Executro -+ * @return The executor that controls this thread -+ */ -+ public static PriorityQueuedExecutor getExecutor() { -+ if (!(Thread.currentThread() instanceof ExecutorThread)) { -+ return null; -+ } -+ return ((ExecutorThread) Thread.currentThread()).getExecutor(); -+ } -+ -+ public void shutdown() { -+ shuttingDown = true; -+ synchronized (this) { -+ this.notifyAll(); -+ } -+ } -+ -+ @Nonnull -+ @Override -+ public List shutdownNow() { -+ shuttingDown = true; -+ shuttingDownNow = true; -+ List tasks = new ArrayList<>(high.size() + normal.size()); -+ Runnable run; -+ while ((run = getTask()) != null) { -+ tasks.add(run); -+ } -+ -+ return tasks; -+ } -+ -+ @Override -+ public boolean isShutdown() { -+ return shuttingDown; -+ } -+ -+ @Override -+ public boolean isTerminated() { -+ if (!shuttingDown) { -+ return false; -+ } -+ return high.isEmpty() && normal.isEmpty(); -+ } -+ -+ @Override -+ public boolean awaitTermination(long timeout, @Nonnull TimeUnit unit) { -+ synchronized (this) { -+ this.notifyAll(); -+ } -+ final long wait = unit.toNanos(timeout); -+ final long max = System.nanoTime() + wait; -+ for (;!threads.isEmpty() && System.nanoTime() < max;) { -+ threads.removeIf(thread -> !thread.isAlive()); -+ } -+ return isTerminated(); -+ } -+ -+ -+ public PendingTask createPendingTask(Runnable task) { -+ return createPendingTask(task, Priority.NORMAL); -+ } -+ public PendingTask createPendingTask(Runnable task, Priority priority) { -+ return createPendingTask(() -> { -+ task.run(); -+ return null; -+ }, priority); -+ } -+ -+ public PendingTask createPendingTask(Supplier task) { -+ return createPendingTask(task, Priority.NORMAL); -+ } -+ -+ public PendingTask createPendingTask(Supplier task, Priority priority) { -+ return new PendingTask<>(task, priority); -+ } -+ -+ public PendingTask submitTask(Runnable run) { -+ return createPendingTask(run).submit(); -+ } -+ -+ public PendingTask submitTask(Runnable run, Priority priority) { -+ return createPendingTask(run, priority).submit(); -+ } -+ -+ public PendingTask submitTask(Supplier run) { -+ return createPendingTask(run).submit(); -+ } -+ -+ public PendingTask submitTask(Supplier run, Priority priority) { -+ PendingTask task = createPendingTask(run, priority); -+ return task.submit(); -+ } -+ -+ @Override -+ public void execute(@Nonnull Runnable command) { -+ submitTask(command); -+ } -+ -+ public boolean isCurrentThread() { -+ final Thread thread = Thread.currentThread(); -+ if (!(thread instanceof ExecutorThread)) { -+ return false; -+ } -+ return ((ExecutorThread) thread).getExecutor() == this; -+ } -+ -+ public Runnable getUrgentTask() { -+ return urgent.poll(); -+ } -+ -+ public Runnable getTask() { -+ Runnable run = urgent.poll(); -+ if (run != null) { -+ return run; -+ } -+ run = high.poll(); -+ if (run != null) { -+ return run; -+ } -+ return normal.poll(); -+ } -+ -+ private void processQueues() { -+ Runnable run = null; -+ while (true) { -+ if (run != null) { -+ run.run(); -+ } -+ if (shuttingDownNow) { -+ return; -+ } -+ if ((run = getTask()) != null) { -+ continue; -+ } -+ synchronized (PriorityQueuedExecutor.this) { -+ if ((run = getTask()) != null) { -+ continue; -+ } -+ -+ if (shuttingDown || shuttingDownNow) { -+ return; -+ } -+ try { -+ PriorityQueuedExecutor.this.wait(); -+ } catch (InterruptedException ignored) { -+ } -+ } -+ } -+ } -+ -+ public boolean processUrgentTasks() { -+ Runnable run; -+ boolean hadTask = false; -+ while ((run = getUrgentTask()) != null) { -+ run.run(); -+ hadTask = true; -+ } -+ return hadTask; -+ } -+ -+ public enum Priority { -+ NORMAL, HIGH, URGENT -+ } -+ -+ public class ExecutorThread extends Thread { -+ public ExecutorThread(Runnable runnable) { -+ super(runnable); -+ } -+ -+ public PriorityQueuedExecutor getExecutor() { -+ return PriorityQueuedExecutor.this; -+ } -+ } -+ -+ public class PendingTask implements Runnable { -+ -+ private final AtomicBoolean hasRan = new AtomicBoolean(); -+ private final AtomicInteger submitted = new AtomicInteger(-1); -+ private final AtomicInteger priority; -+ private final Supplier run; -+ private final CompletableFuture future = new CompletableFuture<>(); -+ private volatile PriorityQueuedExecutor executor; -+ -+ public PendingTask(Supplier run) { -+ this(run, Priority.NORMAL); -+ } -+ -+ public PendingTask(Supplier run, Priority priority) { -+ this.priority = new AtomicInteger(priority.ordinal()); -+ this.run = run; -+ } -+ -+ public boolean cancel() { -+ return hasRan.compareAndSet(false, true); -+ } -+ -+ @Override -+ public void run() { -+ if (!hasRan.compareAndSet(false, true)) { -+ return; -+ } -+ -+ try { -+ future.complete(run.get()); -+ } catch (Throwable e) { -+ future.completeExceptionally(e); -+ } -+ } -+ -+ public void bumpPriority() { -+ bumpPriority(Priority.HIGH); -+ } -+ -+ public void bumpPriority(Priority newPriority) { -+ for (;;) { -+ int current = this.priority.get(); -+ int ordinal = newPriority.ordinal(); -+ if (current >= ordinal || priority.compareAndSet(current, ordinal)) { -+ break; -+ } -+ } -+ -+ -+ if (this.submitted.get() == -1 || this.hasRan.get()) { -+ return; -+ } -+ -+ // Only resubmit if it hasnt ran yet and has been submitted -+ submit(); -+ } -+ -+ public CompletableFuture onDone() { -+ return future; -+ } -+ -+ public PendingTask submit() { -+ if (shuttingDown) { -+ handler.onRejection(this, PriorityQueuedExecutor.this); -+ return this; -+ } -+ for (;;) { -+ final int submitted = this.submitted.get(); -+ final int priority = this.priority.get(); -+ if (submitted == priority) { -+ return this; -+ } -+ if (this.submitted.compareAndSet(submitted, priority)) { -+ if (priority == Priority.URGENT.ordinal()) { -+ urgent.add(this); -+ } else if (priority == Priority.HIGH.ordinal()) { -+ high.add(this); -+ } else { -+ normal.add(this); -+ } -+ -+ break; -+ } -+ } -+ -+ synchronized (PriorityQueuedExecutor.this) { -+ // Wake up a thread to take this work -+ PriorityQueuedExecutor.this.notify(); -+ } -+ return this; -+ } -+ } -+ public interface RejectionHandler { -+ void onRejection(Runnable run, PriorityQueuedExecutor executor); -+ } -+ -+ public static final RejectionHandler ABORT_POLICY = (run, executor) -> { -+ throw new RejectedExecutionException("Executor has been shutdown"); -+ }; -+ public static final RejectionHandler CALLER_RUNS_POLICY = (run, executor) -> { -+ run.run(); -+ }; -+ -+} -diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java -index 4ae0acbf13..4f64072a7b 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 { - - for (k = 0; k < this.sections.length; ++k) { - this.sections[k] = protochunk.getSections()[k]; -+ if (this.sections[k] != null) this.sections[k].disableLocks(); // Paper - Async Chunks - disable locks used during world gen - } - - Iterator iterator = protochunk.s().iterator(); -diff --git a/src/main/java/net/minecraft/server/ChunkMap.java b/src/main/java/net/minecraft/server/ChunkMap.java -index 2021c0d02e..154ab09e0c 100644 ---- a/src/main/java/net/minecraft/server/ChunkMap.java -+++ b/src/main/java/net/minecraft/server/ChunkMap.java -@@ -0,0 +0,0 @@ public class ChunkMap extends Long2ObjectOpenHashMap { - } - - public Chunk put(long i, Chunk chunk) { -+ org.spigotmc.AsyncCatcher.catchOp("Async Chunk put"); // Paper - chunk.world.timings.syncChunkLoadPostTimer.startTiming(); // Paper - lastChunkByPos = chunk; // Paper -- Chunk chunk1 = (Chunk) super.put(i, chunk); -+ // Paper start -+ Chunk chunk1; -+ synchronized (this) { -+ // synchronize so any async gets are safe -+ chunk1 = (Chunk) super.put(i, chunk); -+ } -+ if (chunk1 == null) { // Paper - we should never be overwriting chunks -+ // Paper end - ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(i); - - for (int j = chunkcoordintpair.x - 1; j <= chunkcoordintpair.x + 1; ++j) { -@@ -0,0 +0,0 @@ public class ChunkMap extends Long2ObjectOpenHashMap { - chunk.setNeighborLoaded(x, z); - } - } -+ // Paper start -+ } } else { -+ a.error("Overwrote existing chunk! (" + chunk.world.getWorld().getName() + ":" + chunk.locX+"," + chunk.locZ + ")", new IllegalStateException()); - } -+ // Paper end - // Paper start - if this is a spare chunk (not part of any players view distance), go ahead and queue it for unload. - if (!((WorldServer)chunk.world).getPlayerChunkMap().isChunkInUse(chunk.locX, chunk.locZ)) { - if (chunk.world.paperConfig.delayChunkUnloadsBy > 0) { -@@ -0,0 +0,0 @@ public class ChunkMap extends Long2ObjectOpenHashMap { - } - - public Chunk put(Long olong, Chunk chunk) { -- return this.put(olong, chunk); -+ return MCUtil.ensureMain("Chunk Put", () -> this.put(olong.longValue(), chunk)); // Paper - } - - public Chunk remove(long i) { -- Chunk chunk = (Chunk) super.remove(i); -+ // Paper start -+ org.spigotmc.AsyncCatcher.catchOp("Async Chunk remove"); -+ Chunk chunk; -+ synchronized (this) { -+ // synchronize so any async gets are safe -+ chunk = super.remove(i); -+ } -+ if (chunk != null) { // Paper - don't decrement if we didn't remove anything -+ // Paper end - ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(i); - - for (int j = chunkcoordintpair.x - 1; j <= chunkcoordintpair.x + 1; ++j) { -@@ -0,0 +0,0 @@ public class ChunkMap extends Long2ObjectOpenHashMap { - } - - // Paper start -+ } // close if (chunk != null) - if (lastChunkByPos != null && i == lastChunkByPos.chunkKey) { - lastChunkByPos = null; - } -@@ -0,0 +0,0 @@ public class ChunkMap extends Long2ObjectOpenHashMap { - - @Override - public Chunk get(long l) { -- if (lastChunkByPos != null && l == lastChunkByPos.chunkKey) { -- return lastChunkByPos; -+ if (MCUtil.isMainThread()) { -+ if (lastChunkByPos != null && l == lastChunkByPos.chunkKey) { -+ return lastChunkByPos; -+ } -+ final Chunk chunk = super.get(l); -+ return chunk != null ? (lastChunkByPos = chunk) : null; -+ } else { -+ synchronized (this) { -+ return super.get(l); -+ } - } -- return lastChunkByPos = super.get(l); - } - // Paper end - - public Chunk remove(Object object) { -- return this.remove((Long) object); -+ return MCUtil.ensureMain("Chunk Remove", () -> this.remove(((Long) object).longValue())); // Paper - } - - public void putAll(Map map) { -diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java -index 186cfda7e4..781e068770 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 implements IChunkProvider { - private long lastProcessedSaves = 0L; // Paper - private long lastSaveStatPrinted = System.currentTimeMillis(); - // Paper end -- public final Long2ObjectMap chunks = Long2ObjectMaps.synchronize(new ChunkMap(8192)); -+ public final Long2ObjectMap chunks = new ChunkMap(8192); // Paper - remove synchronize - we keep everything on main for manip - private Chunk lastChunk; - private final ChunkTaskScheduler chunkScheduler; -- private final SchedulerBatch batchScheduler; -+ final SchedulerBatch batchScheduler; // Paper - public final WorldServer world; -- private final IAsyncTaskHandler asyncTaskHandler; -+ final IAsyncTaskHandler asyncTaskHandler; // Paper - - public ChunkProviderServer(WorldServer worldserver, IChunkLoader ichunkloader, ChunkGenerator chunkgenerator, IAsyncTaskHandler iasynctaskhandler) { - this.world = worldserver; -@@ -0,0 +0,0 @@ public class ChunkProviderServer implements IChunkProvider { - this.unloadQueue.remove(ChunkCoordIntPair.a(i, j)); - } - -+ // Paper start - defaults if Async Chunks is not enabled -+ boolean chunkGoingToExists(int x, int z) { -+ final long k = ChunkCoordIntPair.asLong(x, z); -+ return chunkScheduler.progressCache.containsKey(k); -+ } -+ public void bumpPriority(ChunkCoordIntPair coords) { -+ // do nothing, override in async -+ } -+ -+ public List getSpiralOutChunks(BlockPosition blockposition, int radius) { -+ List list = com.google.common.collect.Lists.newArrayList(); -+ -+ list.add(new ChunkCoordIntPair(blockposition.getX() >> 4, blockposition.getZ() >> 4)); -+ for (int r = 1; r <= radius; r++) { -+ int x = -r; -+ int z = r; -+ -+ // Iterates the edge of half of the box; then negates for other half. -+ while (x <= r && z > -r) { -+ list.add(new ChunkCoordIntPair((blockposition.getX() + (x << 4)) >> 4, (blockposition.getZ() + (z << 4)) >> 4)); -+ list.add(new ChunkCoordIntPair((blockposition.getX() - (x << 4)) >> 4, (blockposition.getZ() - (z << 4)) >> 4)); -+ -+ if (x < r) { -+ x++; -+ } else { -+ z--; -+ } -+ } -+ } -+ return list; -+ } -+ -+ public Chunk getChunkAt(int x, int z, boolean load, boolean gen, Consumer consumer) { -+ return getChunkAt(x, z, load, gen, false, consumer); -+ } -+ public Chunk getChunkAt(int x, int z, boolean load, boolean gen, boolean priority, Consumer consumer) { -+ Chunk chunk = getChunkAt(x, z, load, gen); -+ if (consumer != null) { -+ consumer.accept(chunk); -+ } -+ return chunk; -+ } -+ -+ PaperAsyncChunkProvider.CancellableChunkRequest requestChunk(int x, int z, boolean gen, boolean priority, Consumer consumer) { -+ Chunk chunk = getChunkAt(x, z, gen, priority, consumer); -+ return new PaperAsyncChunkProvider.CancellableChunkRequest() { -+ @Override -+ public void cancel() { -+ -+ } -+ -+ @Override -+ public Chunk getChunk() { -+ return chunk; -+ } -+ }; -+ } -+ // Paper end -+ - @Nullable - public Chunk getChunkAt(int i, int j, boolean flag, boolean flag1) { - IChunkLoader ichunkloader = this.chunkLoader; - Chunk chunk; -+ // Paper start - do already loaded checks before synchronize -+ long k = ChunkCoordIntPair.a(i, j); -+ chunk = (Chunk) this.chunks.get(k); -+ if (chunk != null) { -+ //this.lastChunk = chunk; // Paper remove vanilla lastChunk -+ return chunk; -+ } -+ // Paper end - - synchronized (this.chunkLoader) { - // Paper start - remove vanilla lastChunk, we do it more accurately -@@ -0,0 +0,0 @@ public class ChunkProviderServer implements IChunkProvider { - return this.lastChunk; - }*/ // Paper end - -- long k = ChunkCoordIntPair.a(i, j); -+ // Paper start - move up -+ //long k = ChunkCoordIntPair.a(i, j); - -- chunk = (Chunk) this.chunks.get(k); -+ /*chunk = (Chunk) this.chunks.get(k); - if (chunk != null) { - //this.lastChunk = chunk; // Paper remove vanilla lastChunk - return chunk; -- } -+ }*/ -+ // Paper end - - if (flag) { - try (co.aikar.timings.Timing timing = world.timings.syncChunkLoadTimer.startTiming()) { // Paper -@@ -0,0 +0,0 @@ public class ChunkProviderServer implements IChunkProvider { - return (IChunkAccess) (chunk != null ? chunk : (IChunkAccess) this.chunkScheduler.b(new ChunkCoordIntPair(i, j), flag)); - } - -- public CompletableFuture a(Iterable iterable, Consumer consumer) { -+ public CompletableFuture loadAllChunks(Iterable iterable, Consumer consumer) { return a(iterable, consumer).thenCompose(protoChunk -> null); } // Paper - overriden in async chunk provider -+ private CompletableFuture a(Iterable iterable, Consumer consumer) { // Paper - mark private, use above method - this.batchScheduler.b(); - Iterator iterator = iterable.iterator(); - -@@ -0,0 +0,0 @@ public class ChunkProviderServer implements IChunkProvider { - return this.batchScheduler.c(); - } - -+ ReportedException generateChunkError(int i, int j, Throwable throwable) { return a(i, j, throwable); } // Paper - OBFHELPER - private ReportedException a(int i, int j, Throwable throwable) { - CrashReport crashreport = CrashReport.a(throwable, "Exception generating new chunk"); - CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Chunk to be generated"); -@@ -0,0 +0,0 @@ public class ChunkProviderServer implements IChunkProvider { - } - - public void close() { -- try { -+ // Paper start - we do not need to wait for chunk generations to finish on close -+ /*try { - this.batchScheduler.a(); - } catch (InterruptedException interruptedexception) { - ChunkProviderServer.a.error("Couldn't stop taskManager", interruptedexception); -- } -+ }*/ -+ // Paper end - - } - -diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java -index d938eb3749..7734712af9 100644 ---- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java -+++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java -@@ -0,0 +0,0 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver { - // CraftBukkit start - private boolean check(ChunkProviderServer cps, int x, int z) throws IOException { - if (cps != null) { -- com.google.common.base.Preconditions.checkState(org.bukkit.Bukkit.isPrimaryThread(), "primary thread"); -+ //com.google.common.base.Preconditions.checkState(org.bukkit.Bukkit.isPrimaryThread(), "primary thread"); // Paper - this is safe - if (cps.isLoaded(x, z)) { - return true; - } -@@ -0,0 +0,0 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver { - } - } - -+ private final Object legacyStructureLock = new Object(); // Paper -+ public void getPersistentStructureLegacy(DimensionManager dimensionmanager, @Nullable PersistentCollection persistentcollection) { this.a(dimensionmanager, persistentcollection); } // Paper - public void a(DimensionManager dimensionmanager, @Nullable PersistentCollection persistentcollection) { - if (this.e == null) { -+ synchronized (legacyStructureLock){ if (this.e == null) { // Paper - this.e = PersistentStructureLegacy.a(dimensionmanager, persistentcollection); -- } -+ } } } // Paper - - } - -@@ -0,0 +0,0 @@ public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver { - } - }; - } else { -+ /* // Paper start - we will never invoke this in an unsafe way - NBTTagCompound nbttagcompound2 = this.a(world, chunkcoordintpair.x, chunkcoordintpair.z); - - if (nbttagcompound2 != null && this.a(nbttagcompound2) == ChunkStatus.Type.LEVELCHUNK) { - return; -- } -+ }*/ // Paper end - - completion = new Supplier() { - public NBTTagCompound get() { -diff --git a/src/main/java/net/minecraft/server/ChunkSection.java b/src/main/java/net/minecraft/server/ChunkSection.java -index 2af07ae592..9c6844d441 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 { - this.skyLight = new NibbleArray(); - } - -+ // Paper start - Async Chunks - Lock during world gen -+ if (chunk instanceof ProtoChunk) { -+ this.blockIds.enableLocks(); -+ } else { -+ this.blockIds.disableLocks(); -+ } -+ } -+ void disableLocks() { -+ this.blockIds.disableLocks(); - } -+ // Paper end - - public IBlockData getType(int i, int j, int k) { - return (IBlockData) this.blockIds.a(i, j, k); -diff --git a/src/main/java/net/minecraft/server/ChunkTaskScheduler.java b/src/main/java/net/minecraft/server/ChunkTaskScheduler.java -index d3898599f8..8f061f5ca3 100644 ---- a/src/main/java/net/minecraft/server/ChunkTaskScheduler.java -+++ b/src/main/java/net/minecraft/server/ChunkTaskScheduler.java -@@ -0,0 +0,0 @@ public class ChunkTaskScheduler extends Scheduler d; - private final IChunkLoader e; - private final IAsyncTaskHandler f; -- private final Long2ObjectMap.a> progressCache = new ExpiringMap.a>(8192, 5000) { -+ protected final Long2ObjectMap.a> progressCache = new ExpiringMap.a>(8192, 5000) { // Paper - protected - protected boolean a(Scheduler.a scheduler_a) { - ProtoChunk protochunk = (ProtoChunk) scheduler_a.a(); - - return !protochunk.ab_() /*&& !protochunk.h()*/; // Paper - } - }; -+ private final Long2ObjectMap> pendingSchedulers = new it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap<>(); // Paper - - public ChunkTaskScheduler(int i, World world, ChunkGenerator chunkgenerator, IChunkLoader ichunkloader, IAsyncTaskHandler iasynctaskhandler) { - super("WorldGen", i, ChunkStatus.FINALIZED, () -> { -@@ -0,0 +0,0 @@ public class ChunkTaskScheduler extends Scheduler.a a(ChunkCoordIntPair chunkcoordintpair, boolean flag) { - IChunkLoader ichunkloader = this.e; - -- synchronized (this.e) { -- return flag ? (Scheduler.a) this.progressCache.computeIfAbsent(chunkcoordintpair.a(), (i) -> { -+ // Paper start - refactor a lot of this - avoid generating a chunk while holding lock on expiring map -+ java.util.concurrent.CompletableFuture pending = null; -+ boolean created = false; -+ long key = chunkcoordintpair.a(); -+ synchronized (pendingSchedulers) { -+ Scheduler.a existing = this.progressCache.get(key); -+ if (existing != null) { -+ return existing; -+ } -+ pending = this.pendingSchedulers.get(key); -+ if (pending == null) { -+ if (!flag) { -+ return null; -+ } -+ created = true; -+ pending = new java.util.concurrent.CompletableFuture<>(); -+ pendingSchedulers.put(key, pending); -+ } -+ } -+ if (created) { -+ java.util.function.Function get = (i) -> { -+ // Paper end - ProtoChunk protochunk; - - try { -@@ -0,0 +0,0 @@ public class ChunkTaskScheduler extends Scheduler map) { -diff --git a/src/main/java/net/minecraft/server/DataPaletteBlock.java b/src/main/java/net/minecraft/server/DataPaletteBlock.java -index 454903a0e7..dcbcb655c5 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; - import com.destroystokyo.paper.antixray.ChunkPacketInfo; // Paper - Anti-Xray - import java.util.Arrays; - import java.util.Objects; --import java.util.concurrent.locks.ReentrantLock; -+import java.util.concurrent.locks.ReentrantReadWriteLock; - import java.util.function.Function; - import java.util.stream.Collectors; - -@@ -0,0 +0,0 @@ public class DataPaletteBlock implements DataPaletteExpandable { - protected DataBits a; protected DataBits getDataBits() { return this.a; } // Paper - OBFHELPER - private DataPalette h; private DataPalette getDataPalette() { return this.h; } // Paper - OBFHELPER - private int i; private int getBitsPerObject() { return this.i; } // Paper - OBFHELPER -- private final ReentrantLock j = new ReentrantLock(); -+ // Paper start - use read write locks only during generation, disable once back on main thread -+ private static final NoopLock NOOP_LOCK = new NoopLock(); -+ private java.util.concurrent.locks.Lock readLock = NOOP_LOCK; -+ private java.util.concurrent.locks.Lock writeLock = NOOP_LOCK; -+ -+ private static class NoopLock extends ReentrantReadWriteLock.WriteLock { -+ private NoopLock() { -+ super(new ReentrantReadWriteLock()); -+ } -+ -+ @Override -+ public final void lock() { -+ } -+ -+ @Override -+ public final void unlock() { - -- private void b() { -- if (this.j.isLocked() && !this.j.isHeldByCurrentThread()) { -- String s = (String) Thread.getAllStackTraces().keySet().stream().filter(Objects::nonNull).map((thread) -> { -- return thread.getName() + ": \n\tat " + (String) Arrays.stream(thread.getStackTrace()).map(Object::toString).collect(Collectors.joining("\n\tat ")); -- }).collect(Collectors.joining("\n")); -- CrashReport crashreport = new CrashReport("Writing into PalettedContainer from multiple threads", new IllegalStateException()); -- CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Thread dumps"); -- -- crashreportsystemdetails.a("Thread dumps", (Object) s); -- throw new ReportedException(crashreport); -- } else { -- this.j.lock(); - } - } - -+ synchronized void enableLocks() { -+ ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); -+ readLock = lock.readLock(); -+ writeLock = lock.writeLock(); -+ } -+ synchronized void disableLocks() { -+ readLock = NOOP_LOCK; -+ writeLock = NOOP_LOCK; -+ } -+ -+ private void b() { -+ writeLock.lock(); -+ } - private void c() { -- this.j.unlock(); -+ writeLock.unlock(); - } -+ // Paper end - - public DataPaletteBlock(DataPalette datapalette, RegistryBlockID registryblockid, Function function, Function function1, T t0) { - // Paper start - Anti-Xray - Support default constructor -@@ -0,0 +0,0 @@ public class DataPaletteBlock implements DataPaletteExpandable { - } - - protected T a(int i) { -- T t0 = this.h.a(this.a.a(i)); -- -- return t0 == null ? this.g : t0; -+ try { // Paper start - read lock -+ readLock.lock(); -+ T object = this.h.a(this.a.a(i)); // Paper - decompile fix -+ return (T)(object == null ? this.g : object); -+ } finally { -+ readLock.unlock(); -+ } // Paper end - } - - // Paper start - Anti-Xray - Support default methods -diff --git a/src/main/java/net/minecraft/server/DefinedStructureManager.java b/src/main/java/net/minecraft/server/DefinedStructureManager.java -index f5a6387f27..f456850997 100644 ---- a/src/main/java/net/minecraft/server/DefinedStructureManager.java -+++ b/src/main/java/net/minecraft/server/DefinedStructureManager.java -@@ -0,0 +0,0 @@ import org.apache.logging.log4j.Logger; - public class DefinedStructureManager implements IResourcePackListener { - - private static final Logger a = LogManager.getLogger(); -- private final Map b = Maps.newHashMap(); -+ private final Map b = Maps.newConcurrentMap(); // Paper - private final DataFixer c; - private final MinecraftServer d; - private final java.nio.file.Path e; -diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java -index 3d90fdb642..d6d0dd6d88 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.random = SHARED_RANDOM; // Paper - this.fireTicks = -this.getMaxFireTicks(); - this.justCreated = true; -- this.uniqueID = MathHelper.a(this.random); -+ this.uniqueID = MathHelper.a(java.util.concurrent.ThreadLocalRandom.current()); // Paper - this.au = this.uniqueID.toString(); - this.aJ = Sets.newHashSet(); - this.aL = new double[] { 0.0D, 0.0D, 0.0D}; -diff --git a/src/main/java/net/minecraft/server/IChunkLoader.java b/src/main/java/net/minecraft/server/IChunkLoader.java -index 4698ee99f8..431f4ab189 100644 ---- a/src/main/java/net/minecraft/server/IChunkLoader.java -+++ b/src/main/java/net/minecraft/server/IChunkLoader.java -@@ -0,0 +0,0 @@ import javax.annotation.Nullable; - - public interface IChunkLoader { - -+ void getPersistentStructureLegacy(DimensionManager dimensionmanager, @Nullable PersistentCollection persistentcollection); // Paper -+ void loadEntities(NBTTagCompound nbttagcompound, Chunk chunk); // Paper - Async Chunks -+ Object[] loadChunk(GeneratorAccess generatoraccess, int i, int j, Consumer consumer) throws IOException; // Paper - Async Chunks - @Nullable - Chunk a(GeneratorAccess generatoraccess, int i, int j, Consumer consumer) throws IOException; - -diff --git a/src/main/java/net/minecraft/server/MathHelper.java b/src/main/java/net/minecraft/server/MathHelper.java -index 8bb2593aa9..67bb289545 100644 ---- a/src/main/java/net/minecraft/server/MathHelper.java -+++ b/src/main/java/net/minecraft/server/MathHelper.java -@@ -0,0 +0,0 @@ import java.util.function.IntPredicate; - public class MathHelper { - - public static final float a = c(2.0F); -- private static final float[] b = (float[]) SystemUtils.a((Object) (new float[65536]), (afloat) -> { -+ private static final float[] b = (float[]) SystemUtils.a((new float[65536]), (afloat) -> { // Paper - Decompile fix - for (int i = 0; i < afloat.length; ++i) { - afloat[i] = (float) Math.sin((double) i * 3.141592653589793D * 2.0D / 65536.0D); - } -@@ -0,0 +0,0 @@ public class MathHelper { - return Math.floorMod(i, j); - } - -+ public static float normalizeYaw(float fx) { return g(fx); } // Paper - OBFHELPER - public static float g(float f) { - f %= 360.0F; - if (f >= 180.0F) { -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index d2ee4e5781..236fbafeb5 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 implements IAsyncTaskHandler, IMojangStati - - // CraftBukkit start - fire WorldLoadEvent and handle whether or not to keep the spawn in memory - Stopwatch stopwatch = Stopwatch.createStarted(); -+ boolean waitForChunks = Boolean.getBoolean("paper.waitforchunks"); // Paper - for (WorldServer worldserver : this.getWorlds()) { - MinecraftServer.LOGGER.info("Preparing start region for level " + worldserver.dimension + " (Seed: " + worldserver.getSeed() + ")"); - if (!worldserver.getWorld().getKeepSpawnInMemory()) { -@@ -0,0 +0,0 @@ public abstract class MinecraftServer implements IAsyncTaskHandler, IMojangStati - } - - BlockPosition blockposition = worldserver.getSpawn(); -- List list = Lists.newArrayList(); -+ List list = worldserver.getChunkProvider().getSpiralOutChunks(blockposition, worldserver.paperConfig.keepLoadedRange >> 4); // Paper - Set set = Sets.newConcurrentHashSet(); - -- // Paper start -- short radius = worldserver.paperConfig.keepLoadedRange; -- for (int i = -radius; i <= radius && this.isRunning(); i += 16) { -- for (int j = -radius; j <= radius && this.isRunning(); j += 16) { -- // Paper end -- list.add(new ChunkCoordIntPair(blockposition.getX() + i >> 4, blockposition.getZ() + j >> 4)); -- } -- } // Paper -+ // Paper - remove arraylist creation, call spiral above - if (this.isRunning()) { // Paper - int expected = list.size(); // Paper - -- -- CompletableFuture completablefuture = worldserver.getChunkProvider().a((Iterable) list, (chunk) -> { -+ CompletableFuture completablefuture = worldserver.getChunkProvider().loadAllChunks(list, (chunk) -> { // Paper - set.add(chunk.getPos()); -- if (set.size() < expected && set.size() % 25 == 0) this.a(new ChatMessage("menu.preparingSpawn", new Object[0]), set.size() * 100 / expected); // Paper -+ if (waitForChunks && (set.size() == expected || (set.size() < expected && set.size() % (set.size() / 10) == 0))) { -+ this.a(new ChatMessage("menu.preparingSpawn", new Object[0]), set.size() * 100 / expected); // Paper -+ } - }); - -- while (!completablefuture.isDone()) { -+ while (waitForChunks && !completablefuture.isDone() && isRunning()) { // Paper - try { -- completablefuture.get(1L, TimeUnit.SECONDS); -+ PaperAsyncChunkProvider.processMainThreadQueue(this); // Paper -+ completablefuture.get(50L, TimeUnit.MILLISECONDS); // Paper - } catch (InterruptedException interruptedexception) { - throw new RuntimeException(interruptedexception); - } catch (ExecutionException executionexception) { -@@ -0,0 +0,0 @@ public abstract class MinecraftServer implements IAsyncTaskHandler, IMojangStati - - throw new RuntimeException(executionexception.getCause()); - } catch (TimeoutException timeoutexception) { -- this.a(new ChatMessage("menu.preparingSpawn", new Object[0]), set.size() * 100 / expected); // Paper -+ //this.a(new ChatMessage("menu.preparingSpawn", new Object[0]), set.size() * 100 / expected); // Paper - } - } - -- this.a(new ChatMessage("menu.preparingSpawn", new Object[0]), set.size() * 100 / expected); // Paper -+ if (waitForChunks) this.a(new ChatMessage("menu.preparingSpawn", new Object[0]), set.size() * 100 / expected); // Paper - } - } - -@@ -0,0 +0,0 @@ public abstract class MinecraftServer implements IAsyncTaskHandler, IMojangStati - if (hasStopped) return; - hasStopped = true; - } -+ PaperAsyncChunkProvider.stop(this); // Paper - // CraftBukkit end - MinecraftServer.LOGGER.info("Stopping server"); - MinecraftTimings.stopServer(); // Paper -@@ -0,0 +0,0 @@ public abstract class MinecraftServer implements IAsyncTaskHandler, IMojangStati - while ((futuretask = (FutureTask) this.f.poll()) != null) { - SystemUtils.a(futuretask, MinecraftServer.LOGGER); - } -+ PaperAsyncChunkProvider.processMainThreadQueue(this); // Paper - MinecraftTimings.minecraftSchedulerTimer.stopTiming(); // Paper - - this.methodProfiler.exitEnter("commandFunctions"); -@@ -0,0 +0,0 @@ public abstract class MinecraftServer implements IAsyncTaskHandler, IMojangStati - // CraftBukkit - dropTickTime - for (Iterator iterator = this.getWorlds().iterator(); iterator.hasNext();) { - WorldServer worldserver = (WorldServer) iterator.next(); -+ PaperAsyncChunkProvider.processMainThreadQueue(worldserver); // Paper - worldserver.hasPhysicsEvent = org.bukkit.event.block.BlockPhysicsEvent.getHandlerList().getRegisteredListeners().length > 0; // Paper - TileEntityHopper.skipHopperEvents = worldserver.paperConfig.disableHopperMoveEvents || org.bukkit.event.inventory.InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; // Paper - i = SystemUtils.getMonotonicNanos(); -diff --git a/src/main/java/net/minecraft/server/PaperAsyncChunkProvider.java b/src/main/java/net/minecraft/server/PaperAsyncChunkProvider.java -new file mode 100644 -index 0000000000..851756d65e ---- /dev/null -+++ b/src/main/java/net/minecraft/server/PaperAsyncChunkProvider.java -@@ -0,0 +0,0 @@ -+/* -+ * This file is licensed under the MIT License (MIT). -+ * -+ * Copyright (c) 2018 Daniel Ennis -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a copy -+ * of this software and associated documentation files (the "Software"), to deal -+ * in the Software without restriction, including without limitation the rights -+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -+ * copies of the Software, and to permit persons to whom the Software is -+ * furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice shall be included in -+ * all copies or substantial portions of the Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -+ * THE SOFTWARE. -+ */ -+package net.minecraft.server; -+ -+import com.destroystokyo.paper.PaperConfig; -+import com.destroystokyo.paper.util.PriorityQueuedExecutor; -+import com.destroystokyo.paper.util.PriorityQueuedExecutor.Priority; -+import it.unimi.dsi.fastutil.longs.Long2ObjectMap; -+import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; -+import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; -+import org.bukkit.Bukkit; -+import org.bukkit.craftbukkit.generator.CustomChunkGenerator; -+import org.bukkit.craftbukkit.generator.InternalChunkGenerator; -+ -+import javax.annotation.Nullable; -+import java.io.IOException; -+import java.util.ArrayList; -+import java.util.Iterator; -+import java.util.List; -+import java.util.concurrent.CompletableFuture; -+import java.util.concurrent.ConcurrentLinkedDeque; -+import java.util.concurrent.atomic.AtomicBoolean; -+import java.util.concurrent.atomic.AtomicInteger; -+import java.util.function.Consumer; -+ -+@SuppressWarnings("unused") -+public class PaperAsyncChunkProvider extends ChunkProviderServer { -+ -+ private static final int GEN_THREAD_PRIORITY = Integer.getInteger("paper.genThreadPriority", 3); -+ private static final int LOAD_THREAD_PRIORITY = Integer.getInteger("paper.loadThreadPriority", 4); -+ private static final PriorityQueuedExecutor EXECUTOR = new PriorityQueuedExecutor("PaperChunkLoader", PaperConfig.asyncChunks ? PaperConfig.asyncChunkLoadThreads : 0, LOAD_THREAD_PRIORITY); -+ private static final PriorityQueuedExecutor SINGLE_GEN_EXECUTOR = new PriorityQueuedExecutor("PaperChunkGenerator", PaperConfig.asyncChunks && PaperConfig.asyncChunkGeneration && !PaperConfig.asyncChunkGenThreadPerWorld ? 1 : 0, GEN_THREAD_PRIORITY); -+ private static final ConcurrentLinkedDeque MAIN_THREAD_QUEUE = new ConcurrentLinkedDeque<>(); -+ -+ private final PriorityQueuedExecutor generationExecutor; -+ //private static final PriorityQueuedExecutor generationExecutor = new PriorityQueuedExecutor("PaperChunkGen", 1); -+ private final Long2ObjectMap pendingChunks = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>()); -+ private final IAsyncTaskHandler asyncHandler; -+ -+ private final WorldServer world; -+ private final IChunkLoader chunkLoader; -+ private final MinecraftServer server; -+ private final boolean shouldGenSync; -+ -+ public PaperAsyncChunkProvider(WorldServer world, IChunkLoader chunkLoader, InternalChunkGenerator generator, MinecraftServer server) { -+ super(world, chunkLoader, generator, server); -+ -+ this.server = world.getMinecraftServer(); -+ this.world = world; -+ this.asyncHandler = server; -+ this.chunkLoader = chunkLoader; -+ String worldName = this.world.getWorld().getName(); -+ this.shouldGenSync = generator instanceof CustomChunkGenerator && !(((CustomChunkGenerator) generator).asyncSupported) || !PaperConfig.asyncChunkGeneration; -+ this.generationExecutor = PaperConfig.asyncChunkGenThreadPerWorld ? new PriorityQueuedExecutor("PaperChunkGen-" + worldName, shouldGenSync ? 0 : 1, GEN_THREAD_PRIORITY) : SINGLE_GEN_EXECUTOR; -+ } -+ -+ private static Priority calculatePriority(boolean isBlockingMain, boolean priority) { -+ if (isBlockingMain) { -+ return Priority.URGENT; -+ } -+ -+ if (priority) { -+ return Priority.HIGH; -+ } -+ -+ return Priority.NORMAL; -+ } -+ -+ static void stop(MinecraftServer server) { -+ for (WorldServer world : server.getWorlds()) { -+ world.getPlayerChunkMap().shutdown(); -+ } -+ } -+ -+ static void processMainThreadQueue(MinecraftServer server) { -+ for (WorldServer world : server.getWorlds()) { -+ processMainThreadQueue(world); -+ } -+ } -+ -+ static void processMainThreadQueue(World world) { -+ IChunkProvider chunkProvider = world.getChunkProvider(); -+ if (chunkProvider instanceof PaperAsyncChunkProvider) { -+ ((PaperAsyncChunkProvider) chunkProvider).processMainThreadQueue(); -+ } -+ } -+ -+ private void processMainThreadQueue() { -+ processMainThreadQueue((PendingChunk) null); -+ } -+ private boolean processMainThreadQueue(PendingChunk pending) { -+ Runnable run; -+ boolean hadLoad = false; -+ while ((run = MAIN_THREAD_QUEUE.poll()) != null) { -+ run.run(); -+ hadLoad = true; -+ if (pending != null && pending.hasPosted) { -+ break; -+ } -+ } -+ return hadLoad; -+ } -+ -+ @Override -+ public void bumpPriority(ChunkCoordIntPair coords) { -+ final PendingChunk pending = pendingChunks.get(coords.asLong()); -+ if (pending != null) { -+ pending.bumpPriority(Priority.HIGH); -+ } -+ } -+ -+ @Nullable -+ @Override -+ public Chunk getChunkAt(int x, int z, boolean load, boolean gen) { -+ return getChunkAt(x, z, load, gen, null); -+ } -+ -+ @Nullable -+ @Override -+ public Chunk getChunkAt(int x, int z, boolean load, boolean gen, boolean priority, Consumer consumer) { -+ final long key = ChunkCoordIntPair.asLong(x, z); -+ final Chunk chunk = this.chunks.get(key); -+ if (chunk != null || !load) { // return null if we aren't loading -+ if (consumer != null) { -+ consumer.accept(chunk); -+ } -+ return chunk; -+ } -+ return loadOrGenerateChunk(x, z, gen, priority, consumer); // Async overrides this method -+ } -+ -+ private Chunk loadOrGenerateChunk(int x, int z, boolean gen, boolean priority, Consumer consumer) { -+ return requestChunk(x, z, gen, priority, consumer).getChunk(); -+ } -+ -+ final PendingChunkRequest requestChunk(int x, int z, boolean gen, boolean priority, Consumer consumer) { -+ try (co.aikar.timings.Timing timing = world.timings.syncChunkLoadTimer.startTiming()) { -+ final long key = ChunkCoordIntPair.asLong(x, z); -+ final boolean isChunkThread = isChunkThread(); -+ final boolean isBlockingMain = consumer == null && server.isMainThread(); -+ final boolean loadOnThisThread = isChunkThread || isBlockingMain; -+ final Priority taskPriority = calculatePriority(isBlockingMain, priority); -+ -+ // Obtain a PendingChunk -+ final PendingChunk pending; -+ synchronized (pendingChunks) { -+ PendingChunk pendingChunk = pendingChunks.get(key); -+ if (pendingChunk == null) { -+ pending = new PendingChunk(x, z, key, gen, taskPriority); -+ pendingChunks.put(key, pending); -+ } else if (pendingChunk.hasFinished && gen && !pendingChunk.canGenerate && pendingChunk.chunk == null) { -+ // need to overwrite the old -+ pending = new PendingChunk(x, z, key, true, taskPriority); -+ pendingChunks.put(key, pending); -+ } else { -+ pending = pendingChunk; -+ if (pending.taskPriority != taskPriority) { -+ pending.bumpPriority(taskPriority); -+ } -+ } -+ } -+ -+ // Listen for when result is ready -+ final CompletableFuture future = new CompletableFuture<>(); -+ final PendingChunkRequest request = pending.addListener(future, gen, !loadOnThisThread); -+ -+ // Chunk Generation can trigger Chunk Loading, those loads may need to convert, and could be slow -+ // Give an opportunity for urgent tasks to jump in at these times -+ if (isChunkThread) { -+ processUrgentTasks(); -+ } -+ -+ if (loadOnThisThread) { -+ // do loads on main if blocking, or on current if we are a load/gen thread -+ // gen threads do trigger chunk loads -+ pending.loadTask.run(); -+ } -+ -+ if (isBlockingMain) { -+ while (!future.isDone()) { -+ // We aren't done, obtain lock on queue -+ synchronized (MAIN_THREAD_QUEUE) { -+ // We may of received our request now, check it -+ if (processMainThreadQueue(pending)) { -+ // If we processed SOMETHING, don't wait -+ continue; -+ } -+ try { -+ // We got nothing from the queue, wait until something has been added -+ MAIN_THREAD_QUEUE.wait(1); -+ } catch (InterruptedException ignored) { -+ } -+ } -+ // Queue has been notified or timed out, process it -+ processMainThreadQueue(pending); -+ } -+ // We should be done AND posted into chunk map now, return it -+ request.initialReturnChunk = pending.postChunk(); -+ } else if (consumer == null) { -+ // This is on another thread -+ request.initialReturnChunk = future.join(); -+ } else { -+ future.thenAccept((c) -> this.asyncHandler.postToMainThread(() -> consumer.accept(c))); -+ } -+ -+ return request; -+ } -+ } -+ -+ private void processUrgentTasks() { -+ final PriorityQueuedExecutor executor = PriorityQueuedExecutor.getExecutor(); -+ if (executor != null) { -+ executor.processUrgentTasks(); -+ } -+ } -+ -+ @Override -+ public CompletableFuture loadAllChunks(Iterable iterable, Consumer consumer) { -+ final Iterator iterator = iterable.iterator(); -+ -+ final List> all = new ArrayList<>(); -+ while (iterator.hasNext()) { -+ final ChunkCoordIntPair chunkcoordintpair = iterator.next(); -+ final CompletableFuture future = new CompletableFuture<>(); -+ all.add(future); -+ this.getChunkAt(chunkcoordintpair.x, chunkcoordintpair.z, true, true, chunk -> { -+ future.complete(chunk); -+ if (consumer != null) { -+ consumer.accept(chunk); -+ } -+ }); -+ } -+ return CompletableFuture.allOf(all.toArray(new CompletableFuture[0])); -+ } -+ -+ boolean chunkGoingToExists(int x, int z) { -+ synchronized (pendingChunks) { -+ PendingChunk pendingChunk = pendingChunks.get(ChunkCoordIntPair.asLong(x, z)); -+ return pendingChunk != null && pendingChunk.canGenerate; -+ } -+ } -+ -+ private enum PendingStatus { -+ /** -+ * Request has just started -+ */ -+ STARTED, -+ /** -+ * Chunk is attempting to be loaded from disk -+ */ -+ LOADING, -+ /** -+ * Chunk must generate on main and is pending main -+ */ -+ GENERATION_PENDING, -+ /** -+ * Chunk is generating -+ */ -+ GENERATING, -+ /** -+ * Chunk is ready and is pending post to main -+ */ -+ PENDING_MAIN, -+ /** -+ * Could not load chunk, and did not need to generat -+ */ -+ FAIL, -+ /** -+ * Fully done with this request (may or may not of loaded) -+ */ -+ DONE, -+ /** -+ * Chunk load was cancelled (no longer needed) -+ */ -+ CANCELLED -+ } -+ -+ public interface CancellableChunkRequest { -+ void cancel(); -+ Chunk getChunk(); -+ } -+ -+ public static class PendingChunkRequest implements CancellableChunkRequest { -+ private final PendingChunk pending; -+ private final AtomicBoolean cancelled = new AtomicBoolean(false); -+ private volatile boolean generating; -+ private volatile Chunk initialReturnChunk; -+ -+ private PendingChunkRequest(PendingChunk pending) { -+ this.pending = pending; -+ this.cancelled.set(true); -+ } -+ -+ private PendingChunkRequest(PendingChunk pending, boolean gen) { -+ this.pending = pending; -+ this.generating = gen; -+ } -+ -+ public void cancel() { -+ this.pending.cancel(this); -+ } -+ -+ /** -+ * Will be null on asynchronous loads -+ */ -+ @Override @Nullable -+ public Chunk getChunk() { -+ return initialReturnChunk; -+ } -+ } -+ -+ private boolean isLoadThread() { -+ return EXECUTOR.isCurrentThread(); -+ } -+ -+ private boolean isGenThread() { -+ return generationExecutor.isCurrentThread(); -+ } -+ private boolean isChunkThread() { -+ return isLoadThread() || isGenThread(); -+ } -+ -+ private class PendingChunk implements Runnable { -+ private final int x; -+ private final int z; -+ private final long key; -+ private final long started = System.currentTimeMillis(); -+ private final CompletableFuture loadOnly = new CompletableFuture<>(); -+ private final CompletableFuture generate = new CompletableFuture<>(); -+ private final AtomicInteger requests = new AtomicInteger(0); -+ -+ private volatile PendingStatus status = PendingStatus.STARTED; -+ private volatile PriorityQueuedExecutor.PendingTask loadTask; -+ private volatile PriorityQueuedExecutor.PendingTask genTask; -+ private volatile Priority taskPriority; -+ private volatile boolean generating; -+ private volatile boolean canGenerate; -+ private volatile boolean isHighPriority; -+ private volatile boolean hasPosted; -+ private volatile boolean hasFinished; -+ private volatile Chunk chunk; -+ private volatile NBTTagCompound pendingLevel; -+ -+ PendingChunk(int x, int z, long key, boolean canGenerate, boolean priority) { -+ this.x = x; -+ this.z = z; -+ this.key = key; -+ this.canGenerate = canGenerate; -+ taskPriority = priority ? Priority.HIGH : Priority.NORMAL; -+ } -+ -+ PendingChunk(int x, int z, long key, boolean canGenerate, Priority taskPriority) { -+ this.x = x; -+ this.z = z; -+ this.key = key; -+ this.canGenerate = canGenerate; -+ this.taskPriority = taskPriority; -+ } -+ -+ private synchronized void setStatus(PendingStatus status) { -+ this.status = status; -+ } -+ -+ private Chunk loadChunk(int x, int z) throws IOException { -+ setStatus(PendingStatus.LOADING); -+ Object[] data = chunkLoader.loadChunk(world, x, z, null); -+ if (data != null) { -+ // Level must be loaded on main -+ this.pendingLevel = ((NBTTagCompound) data[1]).getCompound("Level"); -+ return (Chunk) data[0]; -+ } else { -+ return null; -+ } -+ } -+ -+ private Chunk generateChunk() { -+ synchronized (this) { -+ if (requests.get() <= 0) { -+ return null; -+ } -+ } -+ -+ try { -+ CompletableFuture pending = new CompletableFuture<>(); -+ batchScheduler.startBatch(); -+ batchScheduler.add(new ChunkCoordIntPair(x, z)); -+ -+ ProtoChunk protoChunk = batchScheduler.executeBatch().join(); -+ boolean saved = false; -+ if (!Bukkit.isPrimaryThread()) { -+ // If we are async, dispatch later -+ try { -+ chunkLoader.saveChunk(world, protoChunk, true); -+ saved = true; -+ } catch (IOException | ExceptionWorldConflict e) { -+ e.printStackTrace(); -+ } -+ } -+ Chunk chunk = new Chunk(world, protoChunk, x, z); -+ if (saved) { -+ chunk.setLastSaved(world.getTime()); -+ } -+ generateFinished(chunk); -+ -+ return chunk; -+ } catch (Throwable e) { -+ MinecraftServer.LOGGER.error("Couldn't generate chunk (" +world.getWorld().getName() + ":" + x + "," + z + ")", e); -+ generateFinished(null); -+ return null; -+ } -+ } -+ -+ boolean loadFinished(Chunk chunk) { -+ if (chunk != null) { -+ postChunkToMain(chunk); -+ return false; -+ } -+ loadOnly.complete(null); -+ -+ synchronized (this) { -+ boolean cancelled = requests.get() <= 0; -+ if (!canGenerate || cancelled) { -+ if (!cancelled) { -+ setStatus(PendingStatus.FAIL); -+ } -+ this.chunk = null; -+ this.hasFinished = true; -+ pendingChunks.remove(key); -+ return false; -+ } else { -+ setStatus(PendingStatus.GENERATING); -+ generating = true; -+ return true; -+ } -+ } -+ } -+ -+ void generateFinished(Chunk chunk) { -+ synchronized (this) { -+ this.chunk = chunk; -+ this.hasFinished = true; -+ } -+ if (chunk != null) { -+ postChunkToMain(chunk); -+ } else { -+ synchronized (this) { -+ pendingChunks.remove(key); -+ completeFutures(null); -+ } -+ } -+ } -+ -+ synchronized private void completeFutures(Chunk chunk) { -+ loadOnly.complete(chunk); -+ generate.complete(chunk); -+ } -+ -+ private void postChunkToMain(Chunk chunk) { -+ synchronized (this) { -+ setStatus(PendingStatus.PENDING_MAIN); -+ this.chunk = chunk; -+ this.hasFinished = true; -+ } -+ -+ if (server.isMainThread()) { -+ postChunk(); -+ return; -+ } -+ -+ // Don't post here, even if on main, it must enter the queue so we can exit any open batch -+ // schedulers, as post stage may trigger a new generation and cause errors -+ synchronized (MAIN_THREAD_QUEUE) { -+ if (this.taskPriority == Priority.URGENT) { -+ MAIN_THREAD_QUEUE.addFirst(this::postChunk); -+ } else { -+ MAIN_THREAD_QUEUE.addLast(this::postChunk); -+ } -+ MAIN_THREAD_QUEUE.notify(); -+ } -+ } -+ -+ Chunk postChunk() { -+ if (!server.isMainThread()) { -+ throw new IllegalStateException("Must post from main"); -+ } -+ synchronized (this) { -+ if (hasPosted || requests.get() <= 0) { // if pending is 0, all were cancelled -+ return chunk; -+ } -+ hasPosted = true; -+ } -+ try { -+ if (chunk == null) { -+ chunk = chunks.get(key); -+ completeFutures(chunk); -+ return chunk; -+ } -+ if (pendingLevel != null) { -+ chunkLoader.loadEntities(pendingLevel, chunk); -+ pendingLevel = null; -+ } -+ synchronized (chunks) { -+ final Chunk other = chunks.get(key); -+ if (other != null) { -+ this.chunk = other; -+ completeFutures(other); -+ return other; -+ } -+ if (chunk != null) { -+ chunks.put(key, chunk); -+ } -+ } -+ -+ chunk.addEntities(); -+ -+ completeFutures(chunk); -+ return chunk; -+ } finally { -+ pendingChunks.remove(key); -+ setStatus(PendingStatus.DONE); -+ } -+ } -+ -+ synchronized PendingChunkRequest addListener(CompletableFuture future, boolean gen, boolean autoSubmit) { -+ requests.incrementAndGet(); -+ if (loadTask == null) { -+ // Take care of a race condition in that a request could be cancelled after the synchronize -+ // on pendingChunks, but before a listener is added, which would erase these pending tasks. -+ genTask = generationExecutor.createPendingTask(this::generateChunk, taskPriority); -+ loadTask = EXECUTOR.createPendingTask(this, taskPriority); -+ if (autoSubmit) { -+ // We will execute it outside of the synchronized context immediately after -+ loadTask.submit(); -+ } -+ } -+ -+ if (hasFinished) { -+ future.complete(chunk); -+ return new PendingChunkRequest(this); -+ } else if (gen) { -+ canGenerate = true; -+ generate.thenAccept(future::complete); -+ } else { -+ if (generating) { -+ future.complete(null); -+ return new PendingChunkRequest(this); -+ } else { -+ loadOnly.thenAccept(future::complete); -+ } -+ } -+ -+ return new PendingChunkRequest(this, gen); -+ } -+ -+ @Override -+ public void run() { -+ try { -+ if (!loadFinished(loadChunk(x, z))) { -+ return; -+ } -+ } catch (Throwable ex) { -+ MinecraftServer.LOGGER.error("Couldn't load chunk (" +world.getWorld().getName() + ":" + x + "," + z + ")", ex); -+ if (ex instanceof IOException) { -+ generateFinished(null); -+ return; -+ } -+ } -+ -+ if (shouldGenSync) { -+ synchronized (this) { -+ setStatus(PendingStatus.GENERATION_PENDING); -+ if (this.taskPriority == Priority.URGENT) { -+ MAIN_THREAD_QUEUE.addFirst(() -> generateFinished(this.generateChunk())); -+ } else { -+ MAIN_THREAD_QUEUE.addLast(() -> generateFinished(this.generateChunk())); -+ } -+ -+ } -+ synchronized (MAIN_THREAD_QUEUE) { -+ MAIN_THREAD_QUEUE.notify(); -+ } -+ } else { -+ if (isGenThread()) { -+ // ideally we should never run into 1 chunk generating another chunk... -+ // but if we do, let's apply same solution -+ genTask.run(); -+ } else { -+ genTask.submit(); -+ } -+ } -+ } -+ -+ void bumpPriority() { -+ bumpPriority(Priority.HIGH); -+ } -+ -+ void bumpPriority(Priority newPriority) { -+ if (taskPriority.ordinal() >= newPriority.ordinal()) { -+ return; -+ } -+ -+ this.taskPriority = newPriority; -+ PriorityQueuedExecutor.PendingTask loadTask = this.loadTask; -+ PriorityQueuedExecutor.PendingTask genTask = this.genTask; -+ if (loadTask != null) { -+ loadTask.bumpPriority(newPriority); -+ } -+ if (genTask != null) { -+ genTask.bumpPriority(newPriority); -+ } -+ } -+ -+ public synchronized boolean isCancelled() { -+ return requests.get() <= 0; -+ } -+ -+ public synchronized void cancel(PendingChunkRequest request) { -+ synchronized (pendingChunks) { -+ if (!request.cancelled.compareAndSet(false, true)) { -+ return; -+ } -+ -+ if (requests.decrementAndGet() > 0) { -+ return; -+ } -+ -+ boolean c1 = genTask.cancel(); -+ boolean c2 = loadTask.cancel(); -+ loadTask = null; -+ genTask = null; -+ pendingChunks.remove(key); -+ setStatus(PendingStatus.CANCELLED); -+ } -+ } -+ } -+ -+} -diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java -index 240f590666..e4cf8548d3 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 { - private long i; - private boolean done; - boolean chunkExists; // Paper -+ // Paper start -+ PaperAsyncChunkProvider.CancellableChunkRequest chunkRequest; -+ private java.util.function.Consumer chunkLoadedConsumer = chunk -> { -+ chunkRequest = null; -+ PlayerChunk pChunk = PlayerChunk.this; -+ pChunk.chunk = chunk; -+ markChunkUsed(); // Paper - delay chunk unloads -+ }; -+ private boolean markedHigh = false; -+ void checkHighPriority(EntityPlayer player) { -+ if (done || markedHigh || chunk != null) { -+ return; -+ } -+ final double dist = getDistance(player.locX, player.locZ); -+ if (dist > 8) { -+ return; -+ } -+ if (dist >= 3 && getDistance(player, 5) > 3.5) { -+ return; -+ } -+ -+ markedHigh = true; -+ playerChunkMap.getWorld().getChunkProvider().bumpPriority(location); -+ if (chunkRequest == null) { -+ requestChunkIfNeeded(PlayerChunkMap.CAN_GEN_CHUNKS.test(player)); -+ } -+ } -+ private void requestChunkIfNeeded(boolean flag) { -+ if (chunkRequest == null) { -+ chunkRequest = this.playerChunkMap.getWorld().getChunkProvider().requestChunk(this.location.x, this.location.z, flag, markedHigh, chunkLoadedConsumer); -+ this.chunk = chunkRequest.getChunk(); // Paper) -+ markChunkUsed(); // Paper - delay chunk unloads -+ } -+ } -+ private double getDistance(EntityPlayer player, int inFront) { -+ final float yaw = MathHelper.normalizeYaw(player.yaw); -+ final double x = player.locX + (inFront * Math.cos(Math.toRadians(yaw))); -+ final double z = player.locZ + (inFront * Math.sin(Math.toRadians(yaw))); -+ return getDistance(x, z); -+ } -+ -+ private double getDistance(double blockX, double blockZ) { -+ final double x = location.x - ((int)Math.floor(blockX) >> 4); -+ final double z = location.z - ((int)Math.floor(blockZ) >> 4); -+ return Math.sqrt((x * x) + (z * z)); -+ } - - public PlayerChunk(PlayerChunkMap playerchunkmap, int i, int j) { - this.playerChunkMap = playerchunkmap; -@@ -0,0 +0,0 @@ public class PlayerChunk { - ChunkProviderServer chunkproviderserver = playerchunkmap.getWorld().getChunkProvider(); - - chunkproviderserver.a(i, j); -- this.chunk = chunkproviderserver.getChunkAt(i, j, true, false); -- this.chunkExists = this.chunk != null || org.bukkit.craftbukkit.chunkio.ChunkIOExecutor.hasQueuedChunkLoad(playerChunkMap.getWorld(), i, j); // Paper -+ this.chunk = chunkproviderserver.getChunkAt(i, j, false, false); // Paper -+ this.chunkExists = this.chunk != null || chunkproviderserver.chunkGoingToExists(i, j); // Paper - markChunkUsed(); // Paper - delay chunk unloads - } - - // Paper start - private void markChunkUsed() { -+ if (!chunkHasPlayers && chunkRequest != null) { -+ chunkRequest.cancel(); -+ chunkRequest = null; -+ } - if (chunk == null) { - return; - } -@@ -0,0 +0,0 @@ public class PlayerChunk { - this.players.add(entityplayer); - if (this.done) { - this.sendChunk(entityplayer); -- } -+ } else checkHighPriority(entityplayer); // Paper - - } - } -@@ -0,0 +0,0 @@ public class PlayerChunk { - if (this.chunk != null) { - return true; - } else { -- this.chunk = this.playerChunkMap.getWorld().getChunkProvider().getChunkAt(this.location.x, this.location.z, true, flag); -- markChunkUsed(); // Paper - delay chunk unloads -+ // Paper start - async chunks -+ requestChunkIfNeeded(flag); -+ // Paper end - return this.chunk != null; - } - } -diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java -index 679488a3cf..b7dda8e282 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 { - }; - private static final Predicate b = (entityplayer) -> { - return entityplayer != null && (!entityplayer.isSpectator() || entityplayer.getWorldServer().getGameRules().getBoolean("spectatorsGenerateChunks")); -- }; -+ }; static final Predicate CAN_GEN_CHUNKS = b; // Paper - OBFHELPER - private final WorldServer world; - private final List managedPlayers = Lists.newArrayList(); -- private final Long2ObjectMap e = new Long2ObjectOpenHashMap(4096); -+ private final Long2ObjectMap e = new Long2ObjectOpenHashMap(4096); Long2ObjectMap getChunks() { return e; } // Paper - OBFHELPER - private final Set f = Sets.newHashSet(); - private final List g = Lists.newLinkedList(); - private final List h = Lists.newLinkedList(); -@@ -0,0 +0,0 @@ public class PlayerChunkMap { - if (playerchunk != null) { - playerchunk.b(entityplayer); - } -+ } else { // Paper start -+ PlayerChunk playerchunk = this.getChunk(l1 - j1, i2 - k1); -+ if (playerchunk != null) { -+ playerchunk.checkHighPriority(entityplayer); // Paper -+ } - } -+ // Paper end - } - } - -@@ -0,0 +0,0 @@ public class PlayerChunkMap { - // CraftBukkit start - send nearest chunks first - Collections.sort(chunksToLoad, new ChunkCoordComparator(entityplayer)); - for (ChunkCoordIntPair pair : chunksToLoad) { -- this.c(pair.x, pair.z).a(entityplayer); -+ // Paper start -+ PlayerChunk c = this.c(pair.x, pair.z); -+ c.checkHighPriority(entityplayer); -+ c.a(entityplayer); -+ // Paper end - } - // CraftBukkit end - } -@@ -0,0 +0,0 @@ public class PlayerChunkMap { - } - } - } -+ -+ void shutdown() { -+ getChunks().values().forEach(pchunk -> { -+ PaperAsyncChunkProvider.CancellableChunkRequest chunkRequest = pchunk.chunkRequest; -+ if (chunkRequest != null) { -+ chunkRequest.cancel(); -+ } -+ }); -+ } - // Paper end - - private void e() { -diff --git a/src/main/java/net/minecraft/server/RegionLimitedWorldAccess.java b/src/main/java/net/minecraft/server/RegionLimitedWorldAccess.java -index de6dd4fed7..da6df06d84 100644 ---- a/src/main/java/net/minecraft/server/RegionLimitedWorldAccess.java -+++ b/src/main/java/net/minecraft/server/RegionLimitedWorldAccess.java -@@ -0,0 +0,0 @@ public class RegionLimitedWorldAccess implements GeneratorAccess { - this.d = l; - this.e = i; - this.f = j; -- this.g = world; -+ this.g = world.regionLimited(this); // Paper - this.h = world.getSeed(); - this.m = world.getChunkProvider().getChunkGenerator().getSettings(); - this.i = world.getSeaLevel(); -diff --git a/src/main/java/net/minecraft/server/SchedulerBatch.java b/src/main/java/net/minecraft/server/SchedulerBatch.java -index 8e909d9caf..f214a74a29 100644 ---- a/src/main/java/net/minecraft/server/SchedulerBatch.java -+++ b/src/main/java/net/minecraft/server/SchedulerBatch.java -@@ -0,0 +0,0 @@ public class SchedulerBatch, R> { - private final Scheduler b; - private boolean c; - private int d = 1000; -+ private final java.util.concurrent.locks.ReentrantLock lock = new java.util.concurrent.locks.ReentrantLock(true); // Paper - - public SchedulerBatch(Scheduler scheduler) { - this.b = scheduler; -@@ -0,0 +0,0 @@ public class SchedulerBatch, R> { - this.b.b(); - } - -+ public void startBatch() { b(); } // Paper - OBFHELPER - public void b() { -- if (this.c) { -+ lock.lock(); // Paper -+ if (false && this.c) { // Paper - throw new RuntimeException("Batch already started."); - } else { - this.d = 1000; -@@ -0,0 +0,0 @@ public class SchedulerBatch, R> { - } - } - -+ public CompletableFuture add(K k0) { return a(k0); } // Paper - OBFHELPER - public CompletableFuture a(K k0) { - if (!this.c) { - throw new RuntimeException("Batch not properly started. Please use startBatch to create a new batch."); -@@ -0,0 +0,0 @@ public class SchedulerBatch, R> { - } - } - -+ public CompletableFuture executeBatch() { return c(); } // Paper - OBFHELPER - public CompletableFuture c() { -- if (!this.c) { -+ // Paper start -+ if (!lock.isHeldByCurrentThread()) { -+ throw new IllegalStateException("Current thread does not hold the write lock"); -+ } -+ try {// Paper end -+ if (false && !this.c) { // Paper - throw new RuntimeException("Batch not properly started. Please use startBatch to create a new batch."); - } else { - if (this.d != 1000) { -@@ -0,0 +0,0 @@ public class SchedulerBatch, R> { - this.c = false; - return this.b.c(); - } -+ } finally { lock.unlock(); } // Paper - } - } -diff --git a/src/main/java/net/minecraft/server/StructurePiece.java b/src/main/java/net/minecraft/server/StructurePiece.java -index d9def71353..945a005e90 100644 ---- a/src/main/java/net/minecraft/server/StructurePiece.java -+++ b/src/main/java/net/minecraft/server/StructurePiece.java -@@ -0,0 +0,0 @@ public abstract class StructurePiece { - private EnumBlockMirror b; - private EnumBlockRotation c; - protected int o; -- private static final Set d = ImmutableSet.builder().add(Blocks.NETHER_BRICK_FENCE).add(Blocks.TORCH).add(Blocks.WALL_TORCH).add(Blocks.OAK_FENCE).add(Blocks.SPRUCE_FENCE).add(Blocks.DARK_OAK_FENCE).add(Blocks.ACACIA_FENCE).add(Blocks.BIRCH_FENCE).add(Blocks.JUNGLE_FENCE).add(Blocks.LADDER).add(Blocks.IRON_BARS).build(); -+ private static final Set d = ImmutableSet.builder().add(Blocks.NETHER_BRICK_FENCE).add(Blocks.TORCH).add(Blocks.WALL_TORCH).add(Blocks.OAK_FENCE).add(Blocks.SPRUCE_FENCE).add(Blocks.DARK_OAK_FENCE).add(Blocks.ACACIA_FENCE).add(Blocks.BIRCH_FENCE).add(Blocks.JUNGLE_FENCE).add(Blocks.LADDER).add(Blocks.IRON_BARS).build(); // Paper - decompile error - - public StructurePiece() {} - -@@ -0,0 +0,0 @@ public abstract class StructurePiece { - } - - public static StructurePiece a(List list, StructureBoundingBox structureboundingbox) { -+ StructurePiece structurepiece; // Paper -+ synchronized (list) { // Paper - synchronize main structure list - Iterator iterator = list.iterator(); - -- StructurePiece structurepiece; -+ //StructurePiece structurepiece; // Paper - move up - - do { - if (!iterator.hasNext()) { -@@ -0,0 +0,0 @@ public abstract class StructurePiece { - - structurepiece = (StructurePiece) iterator.next(); - } while (structurepiece.d() == null || !structurepiece.d().a(structureboundingbox)); -- -+ } // Paper - return structurepiece; - } - -diff --git a/src/main/java/net/minecraft/server/StructureStart.java b/src/main/java/net/minecraft/server/StructureStart.java -index 284e96710a..8b08efe1f4 100644 ---- a/src/main/java/net/minecraft/server/StructureStart.java -+++ b/src/main/java/net/minecraft/server/StructureStart.java -@@ -0,0 +0,0 @@ import java.util.Random; - - public abstract class StructureStart { - -- protected final List a = Lists.newArrayList(); -+ protected final List a = java.util.Collections.synchronizedList(Lists.newArrayList()); // Paper - protected StructureBoundingBox b; - protected int c; - protected int d; -@@ -0,0 +0,0 @@ public abstract class StructureStart { - - protected void a(IBlockAccess iblockaccess) { - this.b = StructureBoundingBox.a(); -+ synchronized (this.a) { // Paper - synchronize - Iterator iterator = this.a.iterator(); - - while (iterator.hasNext()) { - StructurePiece structurepiece = (StructurePiece) iterator.next(); - - this.b.b(structurepiece.d()); -- } -+ }} // Paper - - } - -@@ -0,0 +0,0 @@ public abstract class StructureStart { - int l = k - this.b.e; - - this.b.a(0, l, 0); -+ synchronized (this.a) { // Paper - synchronize - Iterator iterator = this.a.iterator(); - - while (iterator.hasNext()) { - StructurePiece structurepiece = (StructurePiece) iterator.next(); - - structurepiece.a(0, l, 0); -- } -+ }} // Paper - - } - -@@ -0,0 +0,0 @@ public abstract class StructureStart { - int i1 = l - this.b.b; - - this.b.a(0, i1, 0); -+ synchronized (this.a) { - Iterator iterator = this.a.iterator(); - - while (iterator.hasNext()) { - StructurePiece structurepiece = (StructurePiece) iterator.next(); - - structurepiece.a(0, i1, 0); -- } -+ }} // Paper - - } - -diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java -index 0d45e21573..97a0fbd55c 100644 ---- a/src/main/java/net/minecraft/server/World.java -+++ b/src/main/java/net/minecraft/server/World.java -@@ -0,0 +0,0 @@ import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; - import org.bukkit.generator.ChunkGenerator; - // CraftBukkit end - --public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAccess, AutoCloseable { -+public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAccess, AutoCloseable, Cloneable { // Paper - - protected static final Logger e = LogManager.getLogger(); - private static final EnumDirection[] a = EnumDirection.values(); -@@ -0,0 +0,0 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc - protected PersistentVillage villages; - public final MethodProfiler methodProfiler; - public final boolean isClientSide; -+ // Paper start - yes this is hacky as shit -+ RegionLimitedWorldAccess regionLimited; -+ World originalWorld; -+ public World regionLimited(RegionLimitedWorldAccess limitedWorldAccess) { -+ try { -+ World clone = (World) super.clone(); -+ clone.regionLimited = limitedWorldAccess; -+ clone.originalWorld = this; -+ return clone; -+ } catch (CloneNotSupportedException e1) { -+ } -+ return null; -+ } -+ ChunkCoordIntPair[] strongholdCoords; -+ final java.util.concurrent.atomic.AtomicBoolean -+ strongholdInit = new java.util.concurrent.atomic.AtomicBoolean -+ (false); -+ // Paper end - public boolean allowMonsters; - public boolean allowAnimals; - private boolean J; -@@ -0,0 +0,0 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc - - } - -- public IBlockData getType(BlockPosition blockposition) { -- // CraftBukkit start - tree generation -+ // Paper - async variant -+ public java.util.concurrent.CompletableFuture getTypeAsync(BlockPosition blockposition) { -+ int x = blockposition.getX(); -+ int z = blockposition.getZ(); - if (captureTreeGeneration) { - Iterator it = capturedBlockStates.iterator(); - while (it.hasNext()) { - CraftBlockState previous = it.next(); -- if (previous.getPosition().equals(blockposition)) { -- return previous.getHandle(); -+ if (previous.getX() == x && previous.getY() == blockposition.getY() && previous.getZ() == z) { -+ return java.util.concurrent.CompletableFuture.completedFuture(previous.getHandle()); - } - } - } -+ if (blockposition.isInvalidYLocation()) { -+ return java.util.concurrent.CompletableFuture.completedFuture(Blocks.VOID_AIR.getBlockData()); -+ } else { -+ java.util.concurrent.CompletableFuture future = new java.util.concurrent.CompletableFuture<>(); -+ ((ChunkProviderServer) chunkProvider).getChunkAt(x << 4, z << 4, true, true, (chunk) -> { -+ future.complete(chunk.getType(blockposition)); -+ }); -+ return future; -+ } -+ } -+ // Paper end -+ -+ public IBlockData getType(BlockPosition blockposition) { -+ // CraftBukkit start - tree generation -+ if (captureTreeGeneration) { // If any of this logic updates, update async variant above -+ Iterator it = capturedBlockStates.iterator(); -+ while (it.hasNext()) { // If any of this logic updates, update async variant above -+ CraftBlockState previous = it.next(); -+ if (previous.getX() == blockposition.getX() && previous.getY() == blockposition.getY() && previous.getZ() == blockposition.getZ()) { -+ return previous.getHandle(); // If any of this logic updates, update async variant above -+ } -+ } // If any of this logic updates, update async variant above -+ } - // CraftBukkit end - if (blockposition.isInvalidYLocation()) { // Paper - return Blocks.VOID_AIR.getBlockData(); -@@ -0,0 +0,0 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc - } - - public boolean addEntity(Entity entity, SpawnReason spawnReason) { // Changed signature, added SpawnReason -+ // Paper start -+ if (regionLimited != null) { -+ return regionLimited.addEntity(entity, spawnReason); -+ } -+ // Paper end - org.spigotmc.AsyncCatcher.catchOp( "entity add"); // Spigot - if (entity.valid) { MinecraftServer.LOGGER.error("Attempted Double World add on " + entity, new Throwable()); return true; } // Paper - if (!CraftEventFactory.doEntityAddEventCalling(this, entity, spawnReason)) { -diff --git a/src/main/java/net/minecraft/server/WorldGenStronghold.java b/src/main/java/net/minecraft/server/WorldGenStronghold.java -index 69d8a25bdf..d0eaa9e9f8 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; - - public class WorldGenStronghold extends StructureGenerator { - -- private boolean b; -- private ChunkCoordIntPair[] c; -- private long d; -+ // Paper start - no shared state -+ //private boolean b; -+ //private ChunkCoordIntPair[] c; -+ //private long d; -+ // Paper end - - public WorldGenStronghold() {} - - protected boolean a(ChunkGenerator chunkgenerator, Random random, int i, int j) { -- if (this.d != chunkgenerator.getSeed()) { -+ // Paper start -+ /*if (this.d != chunkgenerator.getSeed()) { - this.c(); -- } -+ }*/ - -- if (!this.b) { -+ synchronized (chunkgenerator.getWorld().strongholdInit) { -+ if (chunkgenerator.getWorld().strongholdInit.compareAndSet(false, true)) { // Paper - this.a(chunkgenerator); -- this.b = true; -- } -+ //this.b = true; -+ }} // Paper -+ // Paper end - -- ChunkCoordIntPair[] achunkcoordintpair = this.c; -+ ChunkCoordIntPair[] achunkcoordintpair = chunkgenerator.getWorld().strongholdCoords; // Paper - int k = achunkcoordintpair.length; - - for (int l = 0; l < k; ++l) { -@@ -0,0 +0,0 @@ public class WorldGenStronghold extends StructureGenerator chunkgenerator) { -- this.d = chunkgenerator.getSeed(); -+ //this.d = chunkgenerator.getSeed(); // Paper - List list = Lists.newArrayList(); - Iterator iterator = IRegistry.BIOME.iterator(); - -@@ -0,0 +0,0 @@ public class WorldGenStronghold extends StructureGenerator long2objectmap = chunkgenerator.getStructureStartCache(this); - -@@ -0,0 +0,0 @@ public class WorldGenStronghold extends StructureGenerator= i1) { -- this.c[l1] = new ChunkCoordIntPair(i2, j2); -+ strongholdCoords[l1] = new ChunkCoordIntPair(i2, j2); // Paper - } - - d0 += 6.283185307179586D / (double) k; -@@ -0,0 +0,0 @@ public class WorldGenStronghold extends StructureGenerator this.getChunkProvider().chunkLoader.getPersistentStructureLegacy(dimensionmanager, worldMaps)); // Paper - } - - public WorldServer i_() { -@@ -0,0 +0,0 @@ public class WorldServer extends World implements IAsyncTaskHandler { - gen = new org.bukkit.craftbukkit.generator.NormalChunkGenerator(this, this.getSeed()); - } - -- return new ChunkProviderServer(this, ichunkloader, gen, this.server); -+ return com.destroystokyo.paper.PaperConfig.asyncChunks ? new PaperAsyncChunkProvider(this, ichunkloader, gen, this.server) : new ChunkProviderServer(this, ichunkloader, gen, this.server); // Paper - async chunks - // CraftBukkit end - } - -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java -index d4d8fbb556..af065bd705 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 { - if (internal.getWorld().getKeepSpawnInMemory()) { - short short1 = internal.paperConfig.keepLoadedRange; // Paper - long i = System.currentTimeMillis(); -- for (int j = -short1; j <= short1; j += 16) { -- for (int k = -short1; k <= short1; k += 16) { -+ // Paper start -+ for (ChunkCoordIntPair coords : internal.getChunkProvider().getSpiralOutChunks(internal.getSpawn(), short1 >> 4)) {{ -+ int j = coords.x; -+ int k = coords.z; -+ // Paper end -+ - long l = System.currentTimeMillis(); - - if (l < i) { -@@ -0,0 +0,0 @@ public final class CraftServer implements Server { - } - - BlockPosition chunkcoordinates = internal.getSpawn(); -- internal.getChunkProvider().getChunkAt(chunkcoordinates.getX() + j >> 4, chunkcoordinates.getZ() + k >> 4, true, true); -+ internal.getChunkProvider().getChunkAt(chunkcoordinates.getX() + j >> 4, chunkcoordinates.getZ() + k >> 4, true, true, c -> {}); // Paper - } - } - } -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index 0e4455d66e..eacecccfdb 100644 ---- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -@@ -0,0 +0,0 @@ public class CraftWorld implements World { - } - } - -+ // Paper start - Async chunk load API -+ @Override -+ public java.util.concurrent.CompletableFuture getChunkAtAsync(final int x, final int z, final boolean gen) { -+ final ChunkProviderServer cps = this.world.getChunkProvider(); -+ java.util.concurrent.CompletableFuture future = new java.util.concurrent.CompletableFuture<>(); -+ cps.getChunkAt(x, z, true, gen, chunk -> future.complete(chunk != null ? chunk.bukkitChunk : null)); -+ return future; -+ } -+ // Paper end -+ - public Chunk getChunkAt(int x, int z) { - return this.world.getChunkProvider().getChunkAt(x, z, true, true).bukkitChunk; - } -@@ -0,0 +0,0 @@ public class CraftWorld implements World { - int chunkCoordZ = chunkcoordinates.getZ() >> 4; - // Cycle through the 25x25 Chunks around it to load/unload the chunks. - int radius = world.paperConfig.keepLoadedRange / 16; // Paper -- for (int x = -radius; x <= radius; x++) { // Paper -- for (int z = -radius; z <= radius; z++) { // Paper -+ // Paper start -+ for (ChunkCoordIntPair coords : world.getChunkProvider().getSpiralOutChunks(world.getSpawn(), radius)) {{ -+ int x = coords.x; -+ int z = coords.z; -+ // Paper end - if (keepLoaded) { -- loadChunk(chunkCoordX + x, chunkCoordZ + z); -+ getChunkAtAsync(chunkCoordX + x, chunkCoordZ + z, chunk -> {}); // Paper - Async Chunks - } else { - if (isChunkLoaded(chunkCoordX + x, chunkCoordZ + z)) { - unloadChunk(chunkCoordX + x, chunkCoordZ + z); -diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java -index 68e30185a2..7905420cac 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 { - public static final DamageSource POISON = CraftDamageSource.copyOf(DamageSource.MAGIC); - public static org.bukkit.block.Block blockDamage; // For use in EntityDamageByBlockEvent - public static Entity entityDamage; // For use in EntityDamageByEntityEvent -+ public static boolean isWorldGen(GeneratorAccess world) { return world instanceof net.minecraft.server.RegionLimitedWorldAccess; } // Paper - - // helper methods - private static boolean canBuild(CraftWorld world, Player player, int x, int z) { -@@ -0,0 +0,0 @@ public class CraftEventFactory { - CraftServer craftServer = (CraftServer) entity.getServer(); - - CreatureSpawnEvent event = new CreatureSpawnEvent(entity, spawnReason); -+ if (isWorldGen(entityliving.world)) return event; // Paper - do not call during world gen - craftServer.getPluginManager().callEvent(event); - return event; - } -@@ -0,0 +0,0 @@ public class CraftEventFactory { - } - - BlockIgniteEvent event = new BlockIgniteEvent(bukkitWorld.getBlockAt(block.getX(), block.getY(), block.getZ()), cause, igniter); -+ if (isWorldGen(world)) return event; // Paper - do not call during world gen - world.getServer().getPluginManager().callEvent(event); - return event; - } -@@ -0,0 +0,0 @@ public class CraftEventFactory { - } - - BlockIgniteEvent event = new BlockIgniteEvent(bukkitWorld.getBlockAt(pos.getX(), pos.getY(), pos.getZ()), cause, bukkitIgniter); -+ if (isWorldGen(world)) return event; // Paper - do not call during world gen - world.getServer().getPluginManager().callEvent(event); - return event; - } -@@ -0,0 +0,0 @@ public class CraftEventFactory { - public static BlockPhysicsEvent callBlockPhysicsEvent(GeneratorAccess world, BlockPosition blockposition) { - org.bukkit.block.Block block = CraftBlock.at(world, blockposition); - BlockPhysicsEvent event = new BlockPhysicsEvent(block, block.getBlockData()); -- world.getMinecraftWorld().getMinecraftServer().server.getPluginManager().callEvent(event); -+ if (isWorldGen(world)) return event; // Paper - do not call during world gen -+ Bukkit.getPluginManager().callEvent(event); // Paper - return event; - } - -@@ -0,0 +0,0 @@ public class CraftEventFactory { - } - - EntityPotionEffectEvent event = new EntityPotionEffectEvent((LivingEntity) entity.getBukkitEntity(), bukkitOldEffect, bukkitNewEffect, cause, action, willOverride); -+ if (isWorldGen(entity.world)) return event; // Paper - do not call during world gen - Bukkit.getPluginManager().callEvent(event); - - return event; -@@ -0,0 +0,0 @@ public class CraftEventFactory { - blockState.setData(block); - - BlockFormEvent event = (entity == null) ? new BlockFormEvent(blockState.getBlock(), blockState) : new EntityBlockFormEvent(entity.getBukkitEntity(), blockState.getBlock(), blockState); -+ if (isWorldGen(world)) return true; // Paper - do not call during world gen - world.getServer().getPluginManager().callEvent(event); - - if (!event.isCancelled()) { -diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java b/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java -index 9e553866eb..04c0adf7c7 100644 ---- a/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java -+++ b/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java -@@ -0,0 +0,0 @@ public class CustomChunkGenerator extends InternalChunkGenerator -Date: Wed, 3 Oct 2018 19:04:53 +0100 -Subject: [PATCH] Fix FileIOThread concurrency issues - -FileIOThread was using two volatile counters in order to track if -any pending work was in the queue, this causes potential concurrency -issues when this counter is updated from multiple threads, potentially -causing these counters to desync due to the unsafe volatile update - -diff --git a/src/main/java/net/minecraft/server/FileIOThread.java b/src/main/java/net/minecraft/server/FileIOThread.java -index 3c688f546c..570624600d 100644 ---- a/src/main/java/net/minecraft/server/FileIOThread.java -+++ b/src/main/java/net/minecraft/server/FileIOThread.java -@@ -0,0 +0,0 @@ public class FileIOThread implements Runnable { - - private static final Logger a = LogManager.getLogger(); - private static final FileIOThread b = new FileIOThread(); -- private final List c = Collections.synchronizedList(Lists.newArrayList()); -+ private final List c = Collections.synchronizedList(Lists.newArrayList()); private List getThreadedIOQueue() { return c; } // Paper - OBFHELPER - private volatile long d; - private volatile long e; - private volatile boolean f; -@@ -0,0 +0,0 @@ public class FileIOThread implements Runnable { - public void b() throws InterruptedException { - this.f = true; - -- while (this.d != this.e) { -+ while(!this.getThreadedIOQueue().isEmpty()) { // Paper - check actual list size - Thread.sleep(10L); - } - --- \ No newline at end of file diff --git a/Spigot-Server-Patches/Fix-Sending-Chunks-to-Client.patch b/Spigot-Server-Patches/Fix-Sending-Chunks-to-Client.patch deleted file mode 100644 index d2c267bae8..0000000000 --- a/Spigot-Server-Patches/Fix-Sending-Chunks-to-Client.patch +++ /dev/null @@ -1,64 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Aikar -Date: Sat, 29 Sep 2018 01:18:16 -0400 -Subject: [PATCH] Fix Sending Chunks to Client - -Vanilla has some screwy logic that doesn't send a chunk until -it has been post processed. This is an issue as post processing -doesn't occur until all neighbor chunks have been loaded. - -This can reduce view distance while generating terrain, but also -cause bugs where chunks are never sent to the client. - -This fix always sends chunks to the client, and simply updates -the client anytime post processing is triggered with the new chunk data. - -diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java -index 0d51c1baeb..46804203fe 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 { - } - - public boolean isReady() { -- return this.C.a(ChunkStatus.POSTPROCESSED); -+ return true; // Paper - Always send chunks - } - - public boolean v() { -@@ -0,0 +0,0 @@ public class Chunk implements IChunkAccess { - this.h.clear(); - this.a(ChunkStatus.POSTPROCESSED); - this.m.a(this); -+ // Paper start - resend chunk after post process -+ PlayerChunk playerChunk = ((WorldServer) world).getPlayerChunkMap().getChunk(locX, locZ); -+ if (playerChunk != null) { -+ playerChunk.done = false; -+ playerChunk.sendAll(); -+ } -+ // Paper end - } - } - -diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java -index e4cf8548d3..ac5d158093 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 { - private int dirtyCount; - private int h; - private long i; -- private boolean done; -+ boolean done; // Paper - package-private - boolean chunkExists; // Paper - // Paper start - PaperAsyncChunkProvider.CancellableChunkRequest chunkRequest; -@@ -0,0 +0,0 @@ public class PlayerChunk { - } - } - -+ public boolean sendAll() { return b(); } // Paper - OBFHELPER - public boolean b() { - if (this.done) { - return true; --- \ 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 deleted file mode 100644 index 9169c5b545..0000000000 --- a/Spigot-Server-Patches/Fix-issues-with-entity-loss-due-to-unloaded-chunks.patch +++ /dev/null @@ -1,42 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Aikar -Date: Fri, 28 Sep 2018 21:49:53 -0400 -Subject: [PATCH] Fix issues with entity loss due to unloaded chunks - -Vanilla has risk of losing entities by causing them to be -removed from all chunks if they try to move into an unloaded chunk. - -This pretty much means high chance this entity will be lost in this scenario. - -There is another case that adding an entity to the world can fail if -the chunk isn't loaded. - -Lots of the server is designed around addEntity never expecting to fail for these reasons, -nor is it really logical. - -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/World.java b/src/main/java/net/minecraft/server/World.java -index fb71879ac0..728319968b 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 IEntityAccess, GeneratorAccess, IIBlockAc - - int i = MathHelper.floor(entity.locX / 16.0D); - int j = MathHelper.floor(entity.locZ / 16.0D); -- boolean flag = entity.attachedToPlayer; -+ boolean flag = true; // Paper - always load chunks for entity adds - - // Paper start - Set origin location when the entity is being added to the world - if (entity.origin == null) { -@@ -0,0 +0,0 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc - this.getChunkAt(entity.chunkX, entity.chunkZ).a(entity, entity.chunkY); - } - -- if (!entity.bN() && !this.isChunkLoaded(i, k, true)) { -+ if (!entity.valid && !entity.bN() && !this.isChunkLoaded(i, k, true)) { // Paper - always load chunks to register valid entities location - entity.inChunk = false; - } else { - this.getChunkAt(i, k).a(entity); --- \ No newline at end of file diff --git a/Spigot-Server-Patches/Ignore-Dead-Entities-in-entityList-iteration.patch b/Spigot-Server-Patches/Ignore-Dead-Entities-in-entityList-iteration.patch index 81a2998efe..4390de41d6 100644 --- a/Spigot-Server-Patches/Ignore-Dead-Entities-in-entityList-iteration.patch +++ b/Spigot-Server-Patches/Ignore-Dead-Entities-in-entityList-iteration.patch @@ -51,14 +51,13 @@ index 0c8a2ac90..21fae98c1 100644 if (oclass.isInstance(t0) && t0.getBoundingBox().c(axisalignedbb) && (predicate == null || predicate.test(t0))) { // Spigot - instance check list.add(t0); diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java -index 4648a93b0..99abf7f9c 100644 +index 4648a93b0..33b2dd3ff 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 dead; public float D; public float E; -- public float F; + public float F; + public boolean shouldBeRemoved; // Paper public float fallDistance; private float av; diff --git a/Spigot-Server-Patches/Ignore-Dimension-NBT-field-in-Entity-data.patch b/Spigot-Server-Patches/Ignore-Dimension-NBT-field-in-Entity-data.patch index 451a514068..689c841ecf 100644 --- a/Spigot-Server-Patches/Ignore-Dimension-NBT-field-in-Entity-data.patch +++ b/Spigot-Server-Patches/Ignore-Dimension-NBT-field-in-Entity-data.patch @@ -14,7 +14,7 @@ DimensionManager set to the world it is being placed into. This fixes corrupt entities breaking chunk saving in custom worlds. diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java -index c9aa5ae16..464e1d2d9 100644 +index b4fd3f0dc..57bc805cf 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 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 deleted file mode 100644 index 929390ec49..0000000000 --- a/Spigot-Server-Patches/Implement-getters-and-setters-for-EntityItem-owner-a.patch +++ /dev/null @@ -1,80 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Sat, 6 Oct 2018 20:54:23 -0500 -Subject: [PATCH] Implement getters and setters for EntityItem owner and - thrower - - -diff --git a/src/main/java/net/minecraft/server/EntityItem.java b/src/main/java/net/minecraft/server/EntityItem.java -index c9473087ad..e723bd339e 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 { - this.getDataWatcher().markDirty(EntityItem.b); // CraftBukkit - SPIGOT-4591, must mark dirty - } - -- @Nullable -- public UUID k() { -+ @Nullable public UUID getOwner() { return k(); } // Paper - OBFHELPER -+ @Nullable public UUID k() { // Paper - return this.g; - } - -+ public void setOwner(@Nullable UUID owner) { b(owner); } // Paper - OBFHELPER - public void b(@Nullable UUID uuid) { - this.g = uuid; - } - -- @Nullable -- public UUID l() { -+ @Nullable public UUID getThrower() { return l(); } // Paper - OBFHELPER -+ @Nullable public UUID l() { // Paper - return this.f; - } - -+ public void setThrower(@Nullable UUID thrower) { c(thrower); } // Paper - OBFHELPER - public void c(@Nullable UUID uuid) { - this.f = uuid; - } -diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftItem.java -index 6036592f76..4128ba4c06 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.inventory.ItemStack; - import org.bukkit.craftbukkit.inventory.CraftItemStack; - import org.bukkit.craftbukkit.CraftServer; - -+// Paper start -+import javax.annotation.Nullable; -+import java.util.UUID; -+// Paper end -+ - public class CraftItem extends CraftEntity implements Item { - private final EntityItem item; - -@@ -0,0 +0,0 @@ public class CraftItem extends CraftEntity implements Item { - public void setCanMobPickup(boolean canMobPickup) { - item.canMobPickup = canMobPickup; - } -+ -+ @Nullable -+ public UUID getOwner() { -+ return item.getOwner(); -+ } -+ -+ public void setOwner(@Nullable UUID owner) { -+ item.setOwner(owner); -+ } -+ -+ @Nullable -+ public UUID getThrower() { -+ return item.getThrower(); -+ } -+ -+ public void setThrower(@Nullable UUID thrower) { -+ item.setThrower(thrower); -+ } - // Paper End - - @Override --- \ 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 45e5c717c1..69e16ecf17 100644 --- a/Spigot-Server-Patches/Improve-death-events.patch +++ b/Spigot-Server-Patches/Improve-death-events.patch @@ -27,7 +27,7 @@ index 20db76abd..a148cd437 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 2dfa7d251..03e0e9e26 100644 +index 784520342..ef7f0213b 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 diff --git a/Spigot-Server-Patches/Optimize-Light-Recalculations.patch b/Spigot-Server-Patches/Optimize-Light-Recalculations.patch deleted file mode 100644 index 3795953ab1..0000000000 --- a/Spigot-Server-Patches/Optimize-Light-Recalculations.patch +++ /dev/null @@ -1,50 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Aikar -Date: Fri, 28 Sep 2018 20:46:29 -0400 -Subject: [PATCH] Optimize Light Recalculations - -Optimizes to not repeatedly look up the same chunk for -light lookups. - -diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java -index 4f64072a7b..966879a894 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 { - private void a(int i, int j, int k, int l) { - if (l > k && this.areNeighborsLoaded(1)) { // Paper - for (int i1 = k; i1 < l; ++i1) { -- this.world.c(EnumSkyBlock.SKY, new BlockPosition(i, i1, j)); -+ this.world.updateBrightness(EnumSkyBlock.SKY, new BlockPosition(i, i1, j), this); // Paper - } - - this.x = true; -diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java -index 97a0fbd55c..fb71879ac0 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 IEntityAccess, GeneratorAccess, IIBlockAc - } - - if (this.worldProvider.g()) { -- for (i1 = k; i1 <= l; ++i1) { -- this.c(EnumSkyBlock.SKY, new BlockPosition(i, i1, j)); -+ Chunk chunk = getChunkIfLoaded(i >> 4, j >> 4); // Paper -+ for (i1 = k; chunk != null && i1 <= l; ++i1) { // Paper -+ this.updateBrightness(EnumSkyBlock.SKY, new BlockPosition(i, i1, j), chunk); // Paper - } - } - -@@ -0,0 +0,0 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc - public boolean c(EnumSkyBlock enumskyblock, BlockPosition blockposition) { - // CraftBukkit start - Use neighbor cache instead of looking up - Chunk chunk = this.getChunkIfLoaded(blockposition.getX() >> 4, blockposition.getZ() >> 4); -+ // Paper start - optimize light updates where chunk is known -+ return updateBrightness(enumskyblock, blockposition, chunk); -+ } -+ public boolean updateBrightness(EnumSkyBlock enumskyblock, BlockPosition blockposition, Chunk chunk) { -+ // Paper end - if (chunk == null || !chunk.areNeighborsLoaded(1) /*!this.areChunksLoaded(blockposition, 17, false)*/) { - // CraftBukkit end - return false; --- \ No newline at end of file diff --git a/Spigot-Server-Patches/Optimize-and-Fix-ExpiringMap-Issues.patch b/Spigot-Server-Patches/Optimize-and-Fix-ExpiringMap-Issues.patch deleted file mode 100644 index 7d8aa928a6..0000000000 --- a/Spigot-Server-Patches/Optimize-and-Fix-ExpiringMap-Issues.patch +++ /dev/null @@ -1,334 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Aikar -Date: Sun, 16 Sep 2018 00:00:16 -0400 -Subject: [PATCH] Optimize and Fix ExpiringMap Issues - -computeIfAbsent would leak as the entry was never -registered into the ttl map, as well as some other -methods were at risk, so they were added. - -This also synchronizes all access make the map thread safe. - -This also redesigns cleaning to not run on every -manipulation, and instead to run clean -once per tick per active expiring map. - -diff --git a/src/main/java/net/minecraft/server/ChunkGeneratorAbstract.java b/src/main/java/net/minecraft/server/ChunkGeneratorAbstract.java -index a237684410..835a2aae40 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 implem - - public Long2ObjectMap getStructureStartCache(StructureGenerator structuregenerator) { - return (Long2ObjectMap) this.d.computeIfAbsent(structuregenerator, (structuregenerator1) -> { -- return Long2ObjectMaps.synchronize(new ExpiringMap<>(8192, 10000)); -+ return new ExpiringMap<>(8192, 10000); // Paper - already synchronized - }); - } - - public Long2ObjectMap getStructureCache(StructureGenerator structuregenerator) { - return (Long2ObjectMap) this.e.computeIfAbsent(structuregenerator, (structuregenerator1) -> { -- return Long2ObjectMaps.synchronize(new ExpiringMap<>(8192, 10000)); -+ return new ExpiringMap<>(8192, 10000); // Paper - already synchronized - }); - } - -diff --git a/src/main/java/net/minecraft/server/ExpiringMap.java b/src/main/java/net/minecraft/server/ExpiringMap.java -index bd36364796..bf6095137a 100644 ---- a/src/main/java/net/minecraft/server/ExpiringMap.java -+++ b/src/main/java/net/minecraft/server/ExpiringMap.java -@@ -0,0 +0,0 @@ package net.minecraft.server; - - import it.unimi.dsi.fastutil.longs.Long2LongLinkedOpenHashMap; - import it.unimi.dsi.fastutil.longs.Long2LongMap; -+import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; - import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; --import it.unimi.dsi.fastutil.longs.Long2LongMap.Entry; - import it.unimi.dsi.fastutil.objects.ObjectCollection; - import it.unimi.dsi.fastutil.objects.ObjectIterator; - import java.util.Map; -+import java.util.function.BiFunction; -+import java.util.function.Function; - import java.util.function.LongFunction; - --public class ExpiringMap extends Long2ObjectOpenHashMap { -- -+public class ExpiringMap extends Long2ObjectMaps.SynchronizedMap { // paper - synchronize accesss - private final int a; -- private final Long2LongMap b = new Long2LongLinkedOpenHashMap(); -+ private final Long2LongMap ttl = new Long2LongLinkedOpenHashMap(); // Paper -+ private static final boolean DEBUG_EXPIRING_MAP = Boolean.getBoolean("debug.expiringmap"); - - public ExpiringMap(int i, int j) { -- super(i); -+ super(new Long2ObjectOpenHashMap<>(i)); // Paper - this.a = j; - } - -+ // Paper start -+ private void setAccess(long i) { a(i); } // Paper - OBFHELPER - private void a(long i) { -- long j = SystemUtils.getMonotonicMillis(); -+ synchronized (this.sync) { -+ long j = System.currentTimeMillis(); // Paper -+ this.ttl.put(i, j); -+ if (!registered) { -+ registered = true; -+ MinecraftServer.getServer().expiringMaps.add(this); -+ } -+ } -+ } - -- this.b.put(i, j); -- // CraftBukkit start -- cleanup(); -+ @Override -+ public T compute(long l, BiFunction biFunction) { -+ setAccess(l); -+ return super.compute(l, biFunction); - } - -- public void cleanup() { -- long j = SystemUtils.getMonotonicMillis(); -- // CraftBukkit end -- ObjectIterator objectiterator = this.b.long2LongEntrySet().iterator(); -+ @Override -+ public T putIfAbsent(long l, T t) { -+ setAccess(l); -+ return super.putIfAbsent(l, t); -+ } - -- while (objectiterator.hasNext()) { -- Long2LongMap.Entry entry = (Long2LongMap.Entry) objectiterator.next(); // CraftBukkit - decompile error -- T t0 = super.get(entry.getLongKey()); -+ @Override -+ public T computeIfPresent(long l, BiFunction biFunction) { -+ setAccess(l); -+ return super.computeIfPresent(l, biFunction); -+ } - -- if (j - entry.getLongValue() <= (long) this.a) { -- break; -- } -+ @Override -+ public T computeIfAbsent(long l, LongFunction longFunction) { -+ setAccess(l); -+ return super.computeIfAbsent(l, longFunction); -+ } - -- if (t0 != null && this.a(t0)) { -- super.remove(entry.getLongKey()); -- objectiterator.remove(); -- } -+ -+ @Override -+ public boolean replace(long l, T t, T v1) { -+ setAccess(l); -+ return super.replace(l, t, v1); -+ } -+ -+ @Override -+ public T replace(long l, T t) { -+ setAccess(l); -+ return super.replace(l, t); -+ } -+ -+ @Override -+ public T putIfAbsent(Long aLong, T t) { -+ setAccess(aLong); -+ return super.putIfAbsent(aLong, t); -+ } -+ -+ @Override -+ public boolean replace(Long aLong, T t, T v1) { -+ setAccess(aLong); -+ return super.replace(aLong, t, v1); -+ } -+ -+ @Override -+ public T replace(Long aLong, T t) { -+ setAccess(aLong); -+ return super.replace(aLong, t); -+ } -+ -+ @Override -+ public T computeIfAbsent(Long aLong, Function function) { -+ setAccess(aLong); -+ return super.computeIfAbsent(aLong, function); -+ } -+ -+ @Override -+ public T computeIfPresent(Long aLong, BiFunction biFunction) { -+ setAccess(aLong); -+ return super.computeIfPresent(aLong, biFunction); -+ } -+ -+ @Override -+ public T compute(Long aLong, BiFunction biFunction) { -+ setAccess(aLong); -+ return super.compute(aLong, biFunction); -+ } -+ -+ @Override -+ public void clear() { -+ synchronized (this.sync) { -+ ttl.clear(); -+ super.clear(); - } -+ } - -+ private boolean registered = false; -+ -+ // Break clean to its own method to be ticked -+ boolean clean() { -+ synchronized (this.sync) { -+ long now = System.currentTimeMillis(); -+ ObjectIterator objectiterator = this.ttl.long2LongEntrySet().iterator(); // Paper -+ -+ while (objectiterator.hasNext()) { -+ Long2LongMap.Entry entry = objectiterator.next(); // Paper -+ T object = super.get(entry.getLongKey()); // Paper -+ if (now - entry.getLongValue() <= (long) this.a) { -+ break; -+ } -+ -+ if (object != null && this.a(object)) { -+ super.remove(entry.getLongKey()); -+ objectiterator.remove(); -+ } -+ } -+ int ttlSize = this.ttl.size(); -+ int thisSize = this.size(); -+ if (ttlSize < thisSize) { -+ if (DEBUG_EXPIRING_MAP) { -+ MinecraftServer.LOGGER.warn("WARNING: ExpiringMap desync (ttl:" + ttlSize + " < actual:" + thisSize + ")"); -+ } -+ try { -+ for (Entry entry : this.long2ObjectEntrySet()) { -+ ttl.putIfAbsent(entry.getLongKey(), now); -+ } -+ } catch (Exception ignored) { -+ } // Ignore any como's -+ } else if (ttlSize > this.size()) { -+ if (DEBUG_EXPIRING_MAP) { -+ MinecraftServer.LOGGER.warn("WARNING: ExpiringMap desync (ttl:" + ttlSize + " > actual:" + thisSize + ")"); -+ } -+ try { -+ this.ttl.long2LongEntrySet().removeIf(entry -> !this.containsKey(entry.getLongKey())); -+ } catch (Exception ignored) { -+ } // Ignore any como's -+ } -+ if (isEmpty()) { -+ registered = false; -+ return true; -+ } -+ return false; -+ } -+ // Paper end - } - -- protected boolean a(T t0) { -+ protected boolean a(T var1) { - return true; - } - -- public T put(long i, T t0) { -+ public T put(long i, T object) { - this.a(i); -- return super.put(i, t0); -+ return (T)super.put(i, object); - } - -- public T put(Long olong, T t0) { -+ public T put(Long olong, T object) { - this.a(olong); -- return super.put(olong, t0); -+ return (T)super.put(olong, object); - } - - public T get(long i) { -- this.a(i); -- return super.get(i); -+ // Paper start - don't setAccess unless a hit -+ T t = super.get(i); -+ if (t != null) { -+ this.setAccess(i); -+ } -+ return t; -+ // Paper end - } - -- public void putAll(Map map) { -+ public void putAll(Map var1) { - throw new RuntimeException("Not implemented"); - } - -- public T remove(long i) { -+ public T remove(long var1) { - throw new RuntimeException("Not implemented"); - } - -- public T remove(Object object) { -+ public T remove(Object var1) { - throw new RuntimeException("Not implemented"); - } - -+ // Paper start -+ /* - // CraftBukkit start - @Override - public T computeIfAbsent(long l, LongFunction lf) { -- this.b.put(l, SystemUtils.getMonotonicMillis()); -+ this.ttl.put(l, SystemUtils.getMonotonicMillis()); // Paper - return super.computeIfAbsent(l, lf); - } - -@@ -0,0 +0,0 @@ public class ExpiringMap extends Long2ObjectOpenHashMap { - return super.values(); - } - // CraftBukkit end -+ */ // Paper end - } -diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java -index 38c0201acb..ae271a57fc 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 implements IAsyncTaskHandler, IMojangStati - public int autosavePeriod; - public File bukkitDataPackFolder; - public CommandDispatcher vanillaCommandDispatcher; -+ public List expiringMaps = java.util.Collections.synchronizedList(new java.util.ArrayList<>()); // Paper - // CraftBukkit end - // Spigot start - public static final int TPS = 20; -@@ -0,0 +0,0 @@ public abstract class MinecraftServer implements IAsyncTaskHandler, IMojangStati - this.methodProfiler.exit(); - org.spigotmc.WatchdogThread.tick(); // Spigot - PaperLightingQueue.processQueue(startTime); // Paper -+ expiringMaps.removeIf(ExpiringMap::clean); // Paper - this.slackActivityAccountant.tickEnded(l); // Spigot - co.aikar.timings.TimingsManager.FULL_SERVER_TICK.stopTiming(); // Paper - } -diff --git a/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java b/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java -index 23f4e24cea..9e553866eb 100644 ---- a/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java -+++ b/src/main/java/org/bukkit/craftbukkit/generator/CustomChunkGenerator.java -@@ -0,0 +0,0 @@ public class CustomChunkGenerator extends InternalChunkGenerator getStructureStartCache(StructureGenerator structuregenerator) { - return (Long2ObjectMap) this.structureStartCache.computeIfAbsent(structuregenerator, (s) -> { -- return Long2ObjectMaps.synchronize(new ExpiringMap(8192, 10000)); -+ return new ExpiringMap(8192, 10000); // Paper - already synchronized - }); - } - -@@ -0,0 +0,0 @@ public class CustomChunkGenerator extends InternalChunkGenerator getStructureCache(StructureGenerator structuregenerator) { - return (Long2ObjectMap) this.structureCache.computeIfAbsent(structuregenerator, (s) -> { -- return Long2ObjectMaps.synchronize(new ExpiringMap(8192, 10000)); -+ return new ExpiringMap(8192, 10000); // Paper - already synchronized - }); - } - --- \ No newline at end of file diff --git a/Spigot-Server-Patches/Optimize-getChunkIfLoaded-type-calls.patch b/Spigot-Server-Patches/Optimize-getChunkIfLoaded-type-calls.patch deleted file mode 100644 index 400afbc669..0000000000 --- a/Spigot-Server-Patches/Optimize-getChunkIfLoaded-type-calls.patch +++ /dev/null @@ -1,79 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Aikar -Date: Wed, 29 Aug 2018 21:59:22 -0400 -Subject: [PATCH] Optimize getChunkIfLoaded type calls - -Uses optimized check to avoid major locks and large method. - -Will improve inlining across many hot methods. - -Improve getBrightness to not do double chunk map lookups. - -diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java -index 41926a361b..186cfda7e4 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 implements IChunkProvider { - continue; - } - -- Chunk neighbor = this.getChunkAt(chunk.locX + x, chunk.locZ + z, false, false); -+ Chunk neighbor = this.chunks.get(chunk.chunkKey); // Paper - if (neighbor != null) { - neighbor.setNeighborUnloaded(-x, -z); - chunk.setNeighborUnloaded(x, z); -diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java -index 14f419deb8..630ebfb37c 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 IEntityAccess, GeneratorAccess, IIBlockAc - } - - public Chunk getChunkIfLoaded(int x, int z) { -- return ((ChunkProviderServer) this.chunkProvider).getChunkAt(x, z, false, false); -+ return ((ChunkProviderServer) this.chunkProvider).chunks.get(ChunkCoordIntPair.a(x, z)); // Paper - optimize getChunkIfLoaded - } - - protected World(IDataManager idatamanager, @Nullable PersistentCollection persistentcollection, WorldData worlddata, WorldProvider worldprovider, MethodProfiler methodprofiler, boolean flag, ChunkGenerator gen, org.bukkit.World.Environment env) { -@@ -0,0 +0,0 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc - blockposition = new BlockPosition(blockposition.getX(), 0, blockposition.getZ()); - } - -- return !blockposition.isValidLocation() ? enumskyblock.c : (!this.isLoaded(blockposition) ? enumskyblock.c : this.getChunkAtWorldCoords(blockposition).getBrightness(enumskyblock, blockposition)); // Paper -+ Chunk chunk; // Paper -+ return !blockposition.isValidLocation() ? enumskyblock.c : ((chunk = this.getChunkIfLoaded(blockposition)) == null ? enumskyblock.c : chunk.getBrightness(enumskyblock, blockposition)); // Paper - optimize ifChunkLoaded - } - - public void a(EnumSkyBlock enumskyblock, BlockPosition blockposition, int i) { -@@ -0,0 +0,0 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc - if (blockposition.isInvalidYLocation()) { // Paper - return false; - } else { -- Chunk chunk = this.chunkProvider.getChunkAt(blockposition.getX() >> 4, blockposition.getZ() >> 4, false, false); -+ Chunk chunk = this.getChunkIfLoaded(blockposition.getX() >> 4, blockposition.getZ() >> 4); // Paper - optimize ifLoaded - - return chunk != null && !chunk.isEmpty(); - } -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index 72eb8ed4f4..7e52859c1d 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 false; - } - -- net.minecraft.server.Chunk chunk = world.getChunkProvider().getChunkAt(x, z, false, false); -+ net.minecraft.server.Chunk chunk = world.getChunkIfLoaded(x, z); // Paper - optimize ifLaoded - if (chunk != null) { - world.getChunkProvider().unload(chunk); - } -@@ -0,0 +0,0 @@ public class CraftWorld implements World { - - private boolean unloadChunk0(int x, int z, boolean save) { - Boolean result = MCUtil.ensureMain("Unload Chunk", () -> { // Paper - Ensure never async -- net.minecraft.server.Chunk chunk = world.getChunkProvider().getChunkAt(x, z, false, false); -+ net.minecraft.server.Chunk chunk = world.getChunkIfLoaded(x, z); // Paper - optimize ifLoaded - if (chunk == null) { - return true; - } --- \ 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 ebbdecb00c..f380ed20dd 100644 --- a/Spigot-Server-Patches/Reset-players-airTicks-on-respawn.patch +++ b/Spigot-Server-Patches/Reset-players-airTicks-on-respawn.patch @@ -5,7 +5,7 @@ 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 464e1d2d9..4bdeba399 100644 +index 57bc805cf..0871e185e 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 diff --git a/Spigot-Server-Patches/Use-EntityTypes-for-living-entities.patch b/Spigot-Server-Patches/Use-EntityTypes-for-living-entities.patch deleted file mode 100644 index d204d51cf4..0000000000 --- a/Spigot-Server-Patches/Use-EntityTypes-for-living-entities.patch +++ /dev/null @@ -1,870 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: BillyGalbreath -Date: Thu, 4 Oct 2018 10:08:02 -0500 -Subject: [PATCH] Use EntityTypes for living entities - - -diff --git a/src/main/java/net/minecraft/server/BlockMonsterEggs.java b/src/main/java/net/minecraft/server/BlockMonsterEggs.java -index 5a0cc6d058..d385f647e7 100644 ---- a/src/main/java/net/minecraft/server/BlockMonsterEggs.java -+++ b/src/main/java/net/minecraft/server/BlockMonsterEggs.java -@@ -0,0 +0,0 @@ public class BlockMonsterEggs extends Block { - - public void dropNaturally(IBlockData iblockdata, World world, BlockPosition blockposition, float f, int i) { - if (!world.isClientSide && world.getGameRules().getBoolean("doTileDrops")) { -- EntitySilverfish entitysilverfish = new EntitySilverfish(world); -+ EntitySilverfish entitysilverfish = EntityTypes.SILVERFISH.create(world); // Paper - - entitysilverfish.setPositionRotation((double) blockposition.getX() + 0.5D, (double) blockposition.getY(), (double) blockposition.getZ() + 0.5D, 0.0F, 0.0F); - world.addEntity(entitysilverfish, SpawnReason.SILVERFISH_BLOCK); // CraftBukkit - add SpawnReason -diff --git a/src/main/java/net/minecraft/server/BlockPumpkinCarved.java b/src/main/java/net/minecraft/server/BlockPumpkinCarved.java -index 75622fbdf8..2653699840 100644 ---- a/src/main/java/net/minecraft/server/BlockPumpkinCarved.java -+++ b/src/main/java/net/minecraft/server/BlockPumpkinCarved.java -@@ -0,0 +0,0 @@ public class BlockPumpkinCarved extends BlockFacingHorizontal { - blockList.setTypeAndData(shapedetectorblock1.getPosition(), Blocks.AIR.getBlockData(), 2); // CraftBukkit - } - -- EntitySnowman entitysnowman = new EntitySnowman(world); -+ EntitySnowman entitysnowman = EntityTypes.SNOW_GOLEM.create(world); // Paper - BlockPosition blockposition1 = shapedetector_shapedetectorcollection.a(0, 2, 0).getPosition(); - - entitysnowman.setPositionRotation((double) blockposition1.getX() + 0.5D, (double) blockposition1.getY() + 0.05D, (double) blockposition1.getZ() + 0.5D, 0.0F, 0.0F); -@@ -0,0 +0,0 @@ public class BlockPumpkinCarved extends BlockFacingHorizontal { - } - - BlockPosition blockposition2 = shapedetector_shapedetectorcollection.a(1, 2, 0).getPosition(); -- EntityIronGolem entityirongolem = new EntityIronGolem(world); -+ EntityIronGolem entityirongolem = EntityTypes.IRON_GOLEM.create(world); // Paper - - entityirongolem.setPlayerCreated(true); - entityirongolem.setPositionRotation((double) blockposition2.getX() + 0.5D, (double) blockposition2.getY() + 0.05D, (double) blockposition2.getZ() + 0.5D, 0.0F, 0.0F); -diff --git a/src/main/java/net/minecraft/server/BlockTurtleEgg.java b/src/main/java/net/minecraft/server/BlockTurtleEgg.java -index 0f0872c1e0..1c1bf85a0e 100644 ---- a/src/main/java/net/minecraft/server/BlockTurtleEgg.java -+++ b/src/main/java/net/minecraft/server/BlockTurtleEgg.java -@@ -0,0 +0,0 @@ public class BlockTurtleEgg extends Block { - if (!world.isClientSide) { - for (int j = 0; j < (Integer) iblockdata.get(BlockTurtleEgg.b); ++j) { - world.triggerEffect(2001, blockposition, Block.getCombinedId(iblockdata)); -- EntityTurtle entityturtle = new EntityTurtle(world); -+ EntityTurtle entityturtle = EntityTypes.TURTLE.create(world); // Paper - - entityturtle.setAgeRaw(-24000); - entityturtle.g(blockposition); -diff --git a/src/main/java/net/minecraft/server/BlockWitherSkull.java b/src/main/java/net/minecraft/server/BlockWitherSkull.java -index 93bf32dc1a..e6063bb462 100644 ---- a/src/main/java/net/minecraft/server/BlockWitherSkull.java -+++ b/src/main/java/net/minecraft/server/BlockWitherSkull.java -@@ -0,0 +0,0 @@ public class BlockWitherSkull extends BlockSkull { - } - - BlockPosition blockposition1 = shapedetector_shapedetectorcollection.a(1, 0, 0).getPosition(); -- EntityWither entitywither = new EntityWither(world); -+ EntityWither entitywither = EntityTypes.WITHER.create(world); // Paper - BlockPosition blockposition2 = shapedetector_shapedetectorcollection.a(1, 2, 0).getPosition(); - - entitywither.setPositionRotation((double) blockposition2.getX() + 0.5D, (double) blockposition2.getY() + 0.55D, (double) blockposition2.getZ() + 0.5D, shapedetector_shapedetectorcollection.getFacing().k() == EnumDirection.EnumAxis.X ? 0.0F : 90.0F, 0.0F); -diff --git a/src/main/java/net/minecraft/server/EnderDragonBattle.java b/src/main/java/net/minecraft/server/EnderDragonBattle.java -index aad7ce93f6..09eabf1235 100644 ---- a/src/main/java/net/minecraft/server/EnderDragonBattle.java -+++ b/src/main/java/net/minecraft/server/EnderDragonBattle.java -@@ -0,0 +0,0 @@ public class EnderDragonBattle { - - private EntityEnderDragon n() { - this.d.getChunkAtWorldCoords(new BlockPosition(0, 128, 0)); -- EntityEnderDragon entityenderdragon = new EntityEnderDragon(this.d); -+ EntityEnderDragon entityenderdragon = EntityTypes.ENDER_DRAGON.create(this.d); // Paper - - entityenderdragon.getDragonControllerManager().setControllerPhase(DragonControllerPhase.HOLDING_PATTERN); - entityenderdragon.setPositionRotation(0.0D, 128.0D, 0.0D, this.d.random.nextFloat() * 360.0F, 0.0F); -diff --git a/src/main/java/net/minecraft/server/EntityChicken.java b/src/main/java/net/minecraft/server/EntityChicken.java -index ee159e0a81..070a9e7b14 100644 ---- a/src/main/java/net/minecraft/server/EntityChicken.java -+++ b/src/main/java/net/minecraft/server/EntityChicken.java -@@ -0,0 +0,0 @@ public class EntityChicken extends EntityAnimal { - } - - public EntityChicken createChild(EntityAgeable entityageable) { -- return new EntityChicken(this.world); -+ return EntityTypes.CHICKEN.create(world); // Paper - } - - public boolean f(ItemStack itemstack) { -diff --git a/src/main/java/net/minecraft/server/EntityCow.java b/src/main/java/net/minecraft/server/EntityCow.java -index 5874d2993c..cc53e915d7 100644 ---- a/src/main/java/net/minecraft/server/EntityCow.java -+++ b/src/main/java/net/minecraft/server/EntityCow.java -@@ -0,0 +0,0 @@ public class EntityCow extends EntityAnimal { - } - - public EntityCow createChild(EntityAgeable entityageable) { -- return new EntityCow(this.world); -+ return EntityTypes.COW.create(world); // Paper - } - - public float getHeadHeight() { -diff --git a/src/main/java/net/minecraft/server/EntityEnderPearl.java b/src/main/java/net/minecraft/server/EntityEnderPearl.java -index 961afa5c42..a372f6508f 100644 ---- a/src/main/java/net/minecraft/server/EntityEnderPearl.java -+++ b/src/main/java/net/minecraft/server/EntityEnderPearl.java -@@ -0,0 +0,0 @@ public class EntityEnderPearl extends EntityProjectile { - - if (!teleEvent.isCancelled() && !entityplayer.playerConnection.isDisconnected()) { - if (this.random.nextFloat() < 0.05F && this.world.getGameRules().getBoolean("doMobSpawning")) { -- EntityEndermite entityendermite = new EntityEndermite(this.world); -+ EntityEndermite entityendermite = EntityTypes.ENDERMITE.create(world); // Paper - - entityendermite.setPlayerSpawned(true); - entityendermite.setPositionRotation(entityliving.locX, entityliving.locY, entityliving.locZ, entityliving.yaw, entityliving.pitch); -diff --git a/src/main/java/net/minecraft/server/EntityEvoker.java b/src/main/java/net/minecraft/server/EntityEvoker.java -index 963b6fbb9c..fc20bbe272 100644 ---- a/src/main/java/net/minecraft/server/EntityEvoker.java -+++ b/src/main/java/net/minecraft/server/EntityEvoker.java -@@ -0,0 +0,0 @@ public class EntityEvoker extends EntityIllagerWizard { - protected void j() { - for (int i = 0; i < 3; ++i) { - BlockPosition blockposition = (new BlockPosition(EntityEvoker.this)).a(-2 + EntityEvoker.this.random.nextInt(5), 1, -2 + EntityEvoker.this.random.nextInt(5)); -- EntityVex entityvex = new EntityVex(EntityEvoker.this.world); -- -+ EntityVex entityvex = EntityTypes.VEX.create(EntityEvoker.this.world); // Paper - entityvex.setPositionRotation(blockposition, 0.0F, 0.0F); - entityvex.prepare(EntityEvoker.this.world.getDamageScaler(blockposition), (GroupDataEntity) null, (NBTTagCompound) null); - entityvex.a((EntityInsentient) EntityEvoker.this); -diff --git a/src/main/java/net/minecraft/server/EntityHorse.java b/src/main/java/net/minecraft/server/EntityHorse.java -index 4e8a97c557..1b9425f3e6 100644 ---- a/src/main/java/net/minecraft/server/EntityHorse.java -+++ b/src/main/java/net/minecraft/server/EntityHorse.java -@@ -0,0 +0,0 @@ public class EntityHorse extends EntityHorseAbstract { - Object object; - - if (entityageable instanceof EntityHorseDonkey) { -- object = new EntityHorseMule(this.world); -+ object = EntityTypes.MULE.create(world); // Paper - } else { - EntityHorse entityhorse = (EntityHorse) entityageable; - -- object = new EntityHorse(this.world); -+ object = EntityTypes.HORSE.create(world); // Paper - int i = this.random.nextInt(9); - int j; - -diff --git a/src/main/java/net/minecraft/server/EntityHorseDonkey.java b/src/main/java/net/minecraft/server/EntityHorseDonkey.java -index 72eed22eb9..65c40e72bf 100644 ---- a/src/main/java/net/minecraft/server/EntityHorseDonkey.java -+++ b/src/main/java/net/minecraft/server/EntityHorseDonkey.java -@@ -0,0 +0,0 @@ public class EntityHorseDonkey extends EntityHorseChestedAbstract { - } - - public EntityAgeable createChild(EntityAgeable entityageable) { -- Object object = entityageable instanceof EntityHorse ? new EntityHorseMule(this.world) : new EntityHorseDonkey(this.world); -+ Object object = entityageable instanceof EntityHorse ? EntityTypes.MULE.create(world) : EntityTypes.DONKEY.create(world); // Paper - - this.a(entityageable, (EntityHorseAbstract) object); - return (EntityAgeable) object; -diff --git a/src/main/java/net/minecraft/server/EntityHorseSkeleton.java b/src/main/java/net/minecraft/server/EntityHorseSkeleton.java -index eae2b26655..0a092acdfe 100644 ---- a/src/main/java/net/minecraft/server/EntityHorseSkeleton.java -+++ b/src/main/java/net/minecraft/server/EntityHorseSkeleton.java -@@ -0,0 +0,0 @@ public class EntityHorseSkeleton extends EntityHorseAbstract { - - @Nullable - public EntityAgeable createChild(EntityAgeable entityageable) { -- return new EntityHorseSkeleton(this.world); -+ return EntityTypes.SKELETON_HORSE.create(world); // Paper - } - - public boolean a(EntityHuman entityhuman, EnumHand enumhand) { -diff --git a/src/main/java/net/minecraft/server/EntityHorseZombie.java b/src/main/java/net/minecraft/server/EntityHorseZombie.java -index c23bc72fc8..a1873f557c 100644 ---- a/src/main/java/net/minecraft/server/EntityHorseZombie.java -+++ b/src/main/java/net/minecraft/server/EntityHorseZombie.java -@@ -0,0 +0,0 @@ public class EntityHorseZombie extends EntityHorseAbstract { - - @Nullable - public EntityAgeable createChild(EntityAgeable entityageable) { -- return new EntityHorseZombie(this.world); -+ return EntityTypes.ZOMBIE_HORSE.create(world); // Paper - } - - public boolean a(EntityHuman entityhuman, EnumHand enumhand) { -diff --git a/src/main/java/net/minecraft/server/EntityLlama.java b/src/main/java/net/minecraft/server/EntityLlama.java -index 5e19768710..82a32c61ed 100644 ---- a/src/main/java/net/minecraft/server/EntityLlama.java -+++ b/src/main/java/net/minecraft/server/EntityLlama.java -@@ -0,0 +0,0 @@ public class EntityLlama extends EntityHorseChestedAbstract implements IRangedEn - } - - public EntityLlama createChild(EntityAgeable entityageable) { -- EntityLlama entityllama = new EntityLlama(this.world); -+ EntityLlama entityllama = EntityTypes.LLAMA.create(world); // Paper - - this.a(entityageable, (EntityHorseAbstract) entityllama); - EntityLlama entityllama1 = (EntityLlama) entityageable; -diff --git a/src/main/java/net/minecraft/server/EntityMushroomCow.java b/src/main/java/net/minecraft/server/EntityMushroomCow.java -index dde9f1e61e..638dbe978d 100644 ---- a/src/main/java/net/minecraft/server/EntityMushroomCow.java -+++ b/src/main/java/net/minecraft/server/EntityMushroomCow.java -@@ -0,0 +0,0 @@ public class EntityMushroomCow extends EntityCow { - this.world.addParticle(Particles.u, this.locX, this.locY + (double) (this.length / 2.0F), this.locZ, 0.0D, 0.0D, 0.0D); - if (!this.world.isClientSide) { - // this.die(); // CraftBukkit - moved down -- EntityCow entitycow = new EntityCow(this.world); -+ EntityCow entitycow = EntityTypes.COW.create(world); // Paper - - entitycow.setPositionRotation(this.locX, this.locY, this.locZ, this.yaw, this.pitch); - entitycow.setHealth(this.getHealth()); -@@ -0,0 +0,0 @@ public class EntityMushroomCow extends EntityCow { - } - - public EntityMushroomCow createChild(EntityAgeable entityageable) { -- return new EntityMushroomCow(this.world); -+ return EntityTypes.MOOSHROOM.create(world); // Paper - } - - @Nullable -diff --git a/src/main/java/net/minecraft/server/EntityOcelot.java b/src/main/java/net/minecraft/server/EntityOcelot.java -index ba074c10c6..13c84bda84 100644 ---- a/src/main/java/net/minecraft/server/EntityOcelot.java -+++ b/src/main/java/net/minecraft/server/EntityOcelot.java -@@ -0,0 +0,0 @@ public class EntityOcelot extends EntityTameableAnimal { - } - - public EntityOcelot createChild(EntityAgeable entityageable) { -- EntityOcelot entityocelot = new EntityOcelot(this.world); -+ EntityOcelot entityocelot = EntityTypes.OCELOT.create(world); // Paper - - if (this.isTamed()) { - entityocelot.setOwnerUUID(this.getOwnerUUID()); -@@ -0,0 +0,0 @@ public class EntityOcelot extends EntityTameableAnimal { - groupdataentity = super.prepare(difficultydamagescaler, groupdataentity, nbttagcompound); - if (spawnBonus && this.getCatType() == 0 && this.world.random.nextInt(7) == 0) { // Spigot - for (int i = 0; i < 2; ++i) { -- EntityOcelot entityocelot = new EntityOcelot(this.world); -+ EntityOcelot entityocelot = EntityTypes.OCELOT.create(world); // Paper - - entityocelot.setPositionRotation(this.locX, this.locY, this.locZ, this.yaw, 0.0F); - entityocelot.setAgeRaw(-24000); -diff --git a/src/main/java/net/minecraft/server/EntityPig.java b/src/main/java/net/minecraft/server/EntityPig.java -index 9dc2d8be27..d1689dc33a 100644 ---- a/src/main/java/net/minecraft/server/EntityPig.java -+++ b/src/main/java/net/minecraft/server/EntityPig.java -@@ -0,0 +0,0 @@ public class EntityPig extends EntityAnimal { - - public void onLightningStrike(EntityLightning entitylightning) { - if (!this.world.isClientSide && !this.dead) { -- EntityPigZombie entitypigzombie = new EntityPigZombie(this.world); -+ EntityPigZombie entitypigzombie = EntityTypes.ZOMBIE_PIGMAN.create(world); // Paper - - entitypigzombie.setSlot(EnumItemSlot.MAINHAND, new ItemStack(Items.GOLDEN_SWORD)); - entitypigzombie.setPositionRotation(this.locX, this.locY, this.locZ, this.yaw, this.pitch); -@@ -0,0 +0,0 @@ public class EntityPig extends EntityAnimal { - } - - public EntityPig createChild(EntityAgeable entityageable) { -- return new EntityPig(this.world); -+ return EntityTypes.PIG.create(world); // Paper - } - - public boolean f(ItemStack itemstack) { -diff --git a/src/main/java/net/minecraft/server/EntityPolarBear.java b/src/main/java/net/minecraft/server/EntityPolarBear.java -index a02020d5fc..dbb534c9cd 100644 ---- a/src/main/java/net/minecraft/server/EntityPolarBear.java -+++ b/src/main/java/net/minecraft/server/EntityPolarBear.java -@@ -0,0 +0,0 @@ public class EntityPolarBear extends EntityAnimal { - } - - public EntityAgeable createChild(EntityAgeable entityageable) { -- return new EntityPolarBear(this.world); -+ return EntityTypes.POLAR_BEAR.create(world); // Paper - } - - public boolean f(ItemStack itemstack) { -diff --git a/src/main/java/net/minecraft/server/EntityRabbit.java b/src/main/java/net/minecraft/server/EntityRabbit.java -index e545b1c9b3..d6bac06a7a 100644 ---- a/src/main/java/net/minecraft/server/EntityRabbit.java -+++ b/src/main/java/net/minecraft/server/EntityRabbit.java -@@ -0,0 +0,0 @@ public class EntityRabbit extends EntityAnimal { - } - - public EntityRabbit createChild(EntityAgeable entityageable) { -- EntityRabbit entityrabbit = new EntityRabbit(this.world); -+ EntityRabbit entityrabbit = EntityTypes.RABBIT.create(world); // Paper - int i = this.dJ(); - - if (this.random.nextInt(20) != 0) { -diff --git a/src/main/java/net/minecraft/server/EntitySheep.java b/src/main/java/net/minecraft/server/EntitySheep.java -index f7a25c1483..c35d1eef43 100644 ---- a/src/main/java/net/minecraft/server/EntitySheep.java -+++ b/src/main/java/net/minecraft/server/EntitySheep.java -@@ -0,0 +0,0 @@ public class EntitySheep extends EntityAnimal { - - public EntitySheep createChild(EntityAgeable entityageable) { - EntitySheep entitysheep = (EntitySheep) entityageable; -- EntitySheep entitysheep1 = new EntitySheep(this.world); -+ EntitySheep entitysheep1 = EntityTypes.SHEEP.create(world); // Paper - - entitysheep1.setColor(this.a((EntityAnimal) this, (EntityAnimal) entitysheep)); - return entitysheep1; -diff --git a/src/main/java/net/minecraft/server/EntitySpider.java b/src/main/java/net/minecraft/server/EntitySpider.java -index a42b8d554f..9ef1c9baf2 100644 ---- a/src/main/java/net/minecraft/server/EntitySpider.java -+++ b/src/main/java/net/minecraft/server/EntitySpider.java -@@ -0,0 +0,0 @@ public class EntitySpider extends EntityMonster { - Object object = super.prepare(difficultydamagescaler, groupdataentity, nbttagcompound); - - if (this.world.random.nextInt(100) == 0) { -- EntitySkeleton entityskeleton = new EntitySkeleton(this.world); -+ EntitySkeleton entityskeleton = EntityTypes.SKELETON.create(world); // Paper - - entityskeleton.setPositionRotation(this.locX, this.locY, this.locZ, this.yaw, 0.0F); - entityskeleton.prepare(difficultydamagescaler, (GroupDataEntity) null, (NBTTagCompound) null); -diff --git a/src/main/java/net/minecraft/server/EntityTurtle.java b/src/main/java/net/minecraft/server/EntityTurtle.java -index a533e0eb5b..270b950820 100644 ---- a/src/main/java/net/minecraft/server/EntityTurtle.java -+++ b/src/main/java/net/minecraft/server/EntityTurtle.java -@@ -0,0 +0,0 @@ public class EntityTurtle extends EntityAnimal { - - @Nullable - public EntityAgeable createChild(EntityAgeable entityageable) { -- return new EntityTurtle(this.world); -+ return EntityTypes.TURTLE.create(world); // Paper - } - - public boolean f(ItemStack itemstack) { -diff --git a/src/main/java/net/minecraft/server/EntityTypes.java b/src/main/java/net/minecraft/server/EntityTypes.java -index d74bfa1201..24ca351194 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.aX; - } - -+ @Nullable public T create(World world) { return a(world); } // Paper - OBFHELPER - @Nullable - public T a(World world) { - return this.aT.apply(world); // CraftBukkit - decompile error -diff --git a/src/main/java/net/minecraft/server/EntityVillager.java b/src/main/java/net/minecraft/server/EntityVillager.java -index f01e776fe5..40b3ffd8ca 100644 ---- a/src/main/java/net/minecraft/server/EntityVillager.java -+++ b/src/main/java/net/minecraft/server/EntityVillager.java -@@ -0,0 +0,0 @@ public class EntityVillager extends EntityAgeable implements NPC, IMerchant { - } - - public EntityVillager createChild(EntityAgeable entityageable) { -- EntityVillager entityvillager = new EntityVillager(this.world); -+ EntityVillager entityvillager = EntityTypes.VILLAGER.create(world); // Paper - - entityvillager.prepare(this.world.getDamageScaler(new BlockPosition(entityvillager)), (GroupDataEntity) null, (NBTTagCompound) null); - return entityvillager; -@@ -0,0 +0,0 @@ public class EntityVillager extends EntityAgeable implements NPC, IMerchant { - - public void onLightningStrike(EntityLightning entitylightning) { - if (!this.world.isClientSide && !this.dead) { -- EntityWitch entitywitch = new EntityWitch(this.world); -+ EntityWitch entitywitch = EntityTypes.WITCH.create(world); // Paper - - // Paper start - if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityZapEvent(this, entitylightning, entitywitch).isCancelled()) { -diff --git a/src/main/java/net/minecraft/server/EntityWolf.java b/src/main/java/net/minecraft/server/EntityWolf.java -index 4f1696d018..46d8e0a1f4 100644 ---- a/src/main/java/net/minecraft/server/EntityWolf.java -+++ b/src/main/java/net/minecraft/server/EntityWolf.java -@@ -0,0 +0,0 @@ public class EntityWolf extends EntityTameableAnimal { - } - - public EntityWolf createChild(EntityAgeable entityageable) { -- EntityWolf entitywolf = new EntityWolf(this.world); -+ EntityWolf entitywolf = EntityTypes.WOLF.create(world); // Paper - UUID uuid = this.getOwnerUUID(); - - if (uuid != null) { -diff --git a/src/main/java/net/minecraft/server/EntityZombie.java b/src/main/java/net/minecraft/server/EntityZombie.java -index 7998b80c17..81cc0c3b33 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 { - } - - protected void dE() { -- this.a((EntityZombie) (new EntityDrowned(this.world))); -+ this.a((EntityZombie) EntityTypes.DROWNED.create(world)); // Paper - this.world.a((EntityHuman) null, 1040, new BlockPosition((int) this.locX, (int) this.locY, (int) this.locZ), 0); - } - -@@ -0,0 +0,0 @@ public class EntityZombie extends EntityMonster { - int i = MathHelper.floor(this.locX); - int j = MathHelper.floor(this.locY); - int k = MathHelper.floor(this.locZ); -- EntityZombie entityzombie = new EntityZombie(this.world); -+ EntityZombie entityzombie = EntityTypes.ZOMBIE.create(world); // Paper - - for (int l = 0; l < 50; ++l) { - int i1 = i + MathHelper.nextInt(this.random, 7, 40) * MathHelper.nextInt(this.random, -1, 1); -@@ -0,0 +0,0 @@ public class EntityZombie extends EntityMonster { - } - - EntityVillager entityvillager = (EntityVillager) entityliving; -- EntityZombieVillager entityzombievillager = new EntityZombieVillager(this.world); -+ EntityZombieVillager entityzombievillager = EntityTypes.ZOMBIE_VILLAGER.create(world); // Paper - - entityzombievillager.u(entityvillager); - // this.world.kill(entityvillager); // CraftBukkit - moved down -@@ -0,0 +0,0 @@ public class EntityZombie extends EntityMonster { - this.startRiding(entitychicken); - } - } else if ((double) this.world.random.nextFloat() < 0.05D) { -- EntityChicken entitychicken1 = new EntityChicken(this.world); -+ EntityChicken entitychicken1 = EntityTypes.CHICKEN.create(world); // Paper - - entitychicken1.setPositionRotation(this.locX, this.locY, this.locZ, this.yaw, 0.0F); - entitychicken1.prepare(difficultydamagescaler, (GroupDataEntity) null, (NBTTagCompound) null); -diff --git a/src/main/java/net/minecraft/server/EntityZombieHusk.java b/src/main/java/net/minecraft/server/EntityZombieHusk.java -index 85d402965b..0cca7b6d51 100644 ---- a/src/main/java/net/minecraft/server/EntityZombieHusk.java -+++ b/src/main/java/net/minecraft/server/EntityZombieHusk.java -@@ -0,0 +0,0 @@ public class EntityZombieHusk extends EntityZombie { - } - - protected void dE() { -- this.a(new EntityZombie(this.world)); -+ this.a(EntityTypes.ZOMBIE.create(world)); // Paper - this.world.a((EntityHuman) null, 1041, new BlockPosition((int) this.locX, (int) this.locY, (int) this.locZ), 0); - } - -diff --git a/src/main/java/net/minecraft/server/EntityZombieVillager.java b/src/main/java/net/minecraft/server/EntityZombieVillager.java -index 359ac8b88c..96a1b1d3f2 100644 ---- a/src/main/java/net/minecraft/server/EntityZombieVillager.java -+++ b/src/main/java/net/minecraft/server/EntityZombieVillager.java -@@ -0,0 +0,0 @@ public class EntityZombieVillager extends EntityZombie { - } - - protected void dJ() { -- EntityVillager entityvillager = new EntityVillager(this.world); -+ EntityVillager entityvillager = EntityTypes.VILLAGER.create(world); // Paper - - entityvillager.u(this); - entityvillager.setProfession(this.getProfession()); -diff --git a/src/main/java/net/minecraft/server/ItemArmorStand.java b/src/main/java/net/minecraft/server/ItemArmorStand.java -index 576b3c5650..4dd0e39ec3 100644 ---- a/src/main/java/net/minecraft/server/ItemArmorStand.java -+++ b/src/main/java/net/minecraft/server/ItemArmorStand.java -@@ -0,0 +0,0 @@ public class ItemArmorStand extends Item { - if (!world.isClientSide) { - world.setAir(blockposition); - world.setAir(blockposition1); -- EntityArmorStand entityarmorstand = new EntityArmorStand(world, d0 + 0.5D, d1, d2 + 0.5D); -+ EntityArmorStand entityarmorstand = EntityTypes.ARMOR_STAND.create(world); // Paper - float f = (float) MathHelper.d((MathHelper.g(itemactioncontext.h() - 180.0F) + 22.5F) / 45.0F) * 45.0F; - - entityarmorstand.setPositionRotation(d0 + 0.5D, d1, d2 + 0.5D, f, 0.0F); -diff --git a/src/main/java/net/minecraft/server/MobSpawnerPhantom.java b/src/main/java/net/minecraft/server/MobSpawnerPhantom.java -index 5ddf66eef5..bb7e072ee1 100644 ---- a/src/main/java/net/minecraft/server/MobSpawnerPhantom.java -+++ b/src/main/java/net/minecraft/server/MobSpawnerPhantom.java -@@ -0,0 +0,0 @@ public class MobSpawnerPhantom { - continue; - } - // Paper end -- EntityPhantom entityphantom = new EntityPhantom(world); -+ EntityPhantom entityphantom = EntityTypes.PHANTOM.create(world); // Paper - entityphantom.spawningEntity = entityhuman.uniqueID; // Paper - entityphantom.setPositionRotation(blockposition1, 0.0F, 0.0F); - groupdataentity = entityphantom.prepare(difficultydamagescaler, groupdataentity, (NBTTagCompound) null); -diff --git a/src/main/java/net/minecraft/server/PathfinderGoalHorseTrap.java b/src/main/java/net/minecraft/server/PathfinderGoalHorseTrap.java -index d4fdcbdfd6..887e4461f3 100644 ---- a/src/main/java/net/minecraft/server/PathfinderGoalHorseTrap.java -+++ b/src/main/java/net/minecraft/server/PathfinderGoalHorseTrap.java -@@ -0,0 +0,0 @@ public class PathfinderGoalHorseTrap extends PathfinderGoal { - } - - private EntityHorseAbstract a(DifficultyDamageScaler difficultydamagescaler) { -- EntityHorseSkeleton entityhorseskeleton = new EntityHorseSkeleton(this.a.world); -+ EntityHorseSkeleton entityhorseskeleton = EntityTypes.SKELETON_HORSE.create(a.world); // Paper - - entityhorseskeleton.prepare(difficultydamagescaler, (GroupDataEntity) null, (NBTTagCompound) null); - entityhorseskeleton.setPosition(this.a.locX, this.a.locY, this.a.locZ); -@@ -0,0 +0,0 @@ public class PathfinderGoalHorseTrap extends PathfinderGoal { - } - - private EntitySkeleton a(DifficultyDamageScaler difficultydamagescaler, EntityHorseAbstract entityhorseabstract) { -- EntitySkeleton entityskeleton = new EntitySkeleton(entityhorseabstract.world); -+ EntitySkeleton entityskeleton = EntityTypes.SKELETON.create(entityhorseabstract.world); // Paper - - entityskeleton.prepare(difficultydamagescaler, (GroupDataEntity) null, (NBTTagCompound) null); - entityskeleton.setPosition(entityhorseabstract.locX, entityhorseabstract.locY, entityhorseabstract.locZ); -diff --git a/src/main/java/net/minecraft/server/VillageSiege.java b/src/main/java/net/minecraft/server/VillageSiege.java -index 0ac1fb53a4..509d62f6b6 100644 ---- a/src/main/java/net/minecraft/server/VillageSiege.java -+++ b/src/main/java/net/minecraft/server/VillageSiege.java -@@ -0,0 +0,0 @@ public class VillageSiege { - EntityZombie entityzombie; - - try { -- entityzombie = new EntityZombie(this.a); -+ entityzombie = EntityTypes.ZOMBIE.create(this.a); // Paper - entityzombie.prepare(this.a.getDamageScaler(new BlockPosition(entityzombie)), (GroupDataEntity) null, (NBTTagCompound) null); - } catch (Exception exception) { - exception.printStackTrace(); -diff --git a/src/main/java/net/minecraft/server/WorldGenEndCityPieces.java b/src/main/java/net/minecraft/server/WorldGenEndCityPieces.java -index 94b21693e2..0a223cfe5a 100644 ---- a/src/main/java/net/minecraft/server/WorldGenEndCityPieces.java -+++ b/src/main/java/net/minecraft/server/WorldGenEndCityPieces.java -@@ -0,0 +0,0 @@ public class WorldGenEndCityPieces { - TileEntityLootable.a(generatoraccess, random, blockposition1, LootTables.c); - } - } else if (s.startsWith("Sentry")) { -- EntityShulker entityshulker = new EntityShulker(generatoraccess.getMinecraftWorld()); -+ EntityShulker entityshulker = EntityTypes.SHULKER.create(generatoraccess.getMinecraftWorld()); // Paper - - entityshulker.setPosition((double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 0.5D, (double) blockposition.getZ() + 0.5D); - entityshulker.g(blockposition); -diff --git a/src/main/java/net/minecraft/server/WorldGenFeatureOceanRuinPieces.java b/src/main/java/net/minecraft/server/WorldGenFeatureOceanRuinPieces.java -index 2def56b067..abeb4aa025 100644 ---- a/src/main/java/net/minecraft/server/WorldGenFeatureOceanRuinPieces.java -+++ b/src/main/java/net/minecraft/server/WorldGenFeatureOceanRuinPieces.java -@@ -0,0 +0,0 @@ public class WorldGenFeatureOceanRuinPieces { - ((TileEntityChest) tileentity).setLootTable(this.h ? LootTables.q : LootTables.p, random.nextLong()); - } - } else if ("drowned".equals(s)) { -- EntityDrowned entitydrowned = new EntityDrowned(generatoraccess.getMinecraftWorld()); -- -+ EntityDrowned entitydrowned = EntityTypes.DROWNED.create(generatoraccess.getMinecraftWorld()); // Paper - entitydrowned.di(); - entitydrowned.setPositionRotation(blockposition, 0.0F, 0.0F); - entitydrowned.prepare(generatoraccess.getDamageScaler(blockposition), (GroupDataEntity) null, (NBTTagCompound) null); -diff --git a/src/main/java/net/minecraft/server/WorldGenMonumentPieces.java b/src/main/java/net/minecraft/server/WorldGenMonumentPieces.java -index 0e7aed09d1..493a86e1bf 100644 ---- a/src/main/java/net/minecraft/server/WorldGenMonumentPieces.java -+++ b/src/main/java/net/minecraft/server/WorldGenMonumentPieces.java -@@ -0,0 +0,0 @@ public class WorldGenMonumentPieces { - protected static final IBlockData d = WorldGenMonumentPieces.WorldGenMonumentPiece.b; - protected static final IBlockData e = Blocks.SEA_LANTERN.getBlockData(); - protected static final IBlockData f = Blocks.WATER.getBlockData(); -- protected static final Set g = ImmutableSet.builder().add(Blocks.ICE).add(Blocks.PACKED_ICE).add(Blocks.BLUE_ICE).add(WorldGenMonumentPieces.WorldGenMonumentPiece.f.getBlock()).build(); -+ protected static final Set g = ImmutableSet.builder().add(Blocks.ICE).add(Blocks.PACKED_ICE).add(Blocks.BLUE_ICE).add(WorldGenMonumentPieces.WorldGenMonumentPiece.f.getBlock()).build(); // Paper - decompile fix - protected static final int h = b(2, 0, 0); - protected static final int i = b(2, 2, 0); - protected static final int j = b(0, 1, 0); -@@ -0,0 +0,0 @@ public class WorldGenMonumentPieces { - int j1 = this.b(i, k); - - if (structureboundingbox.b((BaseBlockPosition) (new BlockPosition(l, i1, j1)))) { -- EntityGuardianElder entityguardianelder = new EntityGuardianElder(generatoraccess.getMinecraftWorld()); -+ EntityGuardianElder entityguardianelder = EntityTypes.ELDER_GUARDIAN.create(generatoraccess.getMinecraftWorld()); // Paper - - entityguardianelder.heal(entityguardianelder.getMaxHealth()); - entityguardianelder.setPositionRotation((double) l + 0.5D, (double) i1, (double) j1 + 0.5D, 0.0F, 0.0F); -diff --git a/src/main/java/net/minecraft/server/WorldGenVillagePieces.java b/src/main/java/net/minecraft/server/WorldGenVillagePieces.java -index 5fa2987d2a..967e33b3d7 100644 ---- a/src/main/java/net/minecraft/server/WorldGenVillagePieces.java -+++ b/src/main/java/net/minecraft/server/WorldGenVillagePieces.java -@@ -0,0 +0,0 @@ public class WorldGenVillagePieces { - - ++this.a; - if (this.h) { -- EntityZombieVillager entityzombievillager = new EntityZombieVillager(generatoraccess.getMinecraftWorld()); -+ EntityZombieVillager entityzombievillager = EntityTypes.ZOMBIE_VILLAGER.create(generatoraccess.getMinecraftWorld()); // Paper - - entityzombievillager.setPositionRotation((double) j1 + 0.5D, (double) k1, (double) l1 + 0.5D, 0.0F, 0.0F); - entityzombievillager.prepare(generatoraccess.getDamageScaler(new BlockPosition(entityzombievillager)), (GroupDataEntity) null, (NBTTagCompound) null); -@@ -0,0 +0,0 @@ public class WorldGenVillagePieces { - entityzombievillager.di(); - generatoraccess.addEntity(entityzombievillager, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.CHUNK_GEN); // CraftBukkit - add SpawnReason - } else { -- EntityVillager entityvillager = new EntityVillager(generatoraccess.getMinecraftWorld()); -+ EntityVillager entityvillager = EntityTypes.VILLAGER.create(generatoraccess.getMinecraftWorld()); // Paper - - entityvillager.setPositionRotation((double) j1 + 0.5D, (double) k1, (double) l1 + 0.5D, 0.0F, 0.0F); - entityvillager.setProfession(this.c(i1, generatoraccess.m().nextInt(6))); -diff --git a/src/main/java/net/minecraft/server/WorldGenWitchHut.java b/src/main/java/net/minecraft/server/WorldGenWitchHut.java -index efb0379ce3..3d8193c477 100644 ---- a/src/main/java/net/minecraft/server/WorldGenWitchHut.java -+++ b/src/main/java/net/minecraft/server/WorldGenWitchHut.java -@@ -0,0 +0,0 @@ public class WorldGenWitchHut extends WorldGenScatteredPiece { - - if (structureboundingbox.b((BaseBlockPosition) (new BlockPosition(j, i, k)))) { - this.e = true; -- EntityWitch entitywitch = new EntityWitch(generatoraccess.getMinecraftWorld()); -+ EntityWitch entitywitch = EntityTypes.WITCH.create(generatoraccess.getMinecraftWorld()); // Paper - - entitywitch.di(); - entitywitch.setPositionRotation((double) j + 0.5D, (double) i, (double) k + 0.5D, 0.0F, 0.0F); -diff --git a/src/main/java/net/minecraft/server/WorldGenWoodlandMansionPieces.java b/src/main/java/net/minecraft/server/WorldGenWoodlandMansionPieces.java -index 11010d8e12..4eb746ebb0 100644 ---- a/src/main/java/net/minecraft/server/WorldGenWoodlandMansionPieces.java -+++ b/src/main/java/net/minecraft/server/WorldGenWoodlandMansionPieces.java -@@ -0,0 +0,0 @@ public class WorldGenWoodlandMansionPieces { - static class h extends WorldGenWoodlandMansionPieces.f { - - private h() { -- super(null); -+ super(); // Paper - decompile fix - } - } - - static class f extends WorldGenWoodlandMansionPieces.b { - - private f() { -- super(null); -+ super(); // Paper - decompile fix - } - - public String a(Random random) { -@@ -0,0 +0,0 @@ public class WorldGenWoodlandMansionPieces { - static class a extends WorldGenWoodlandMansionPieces.b { - - private a() { -- super(null); -+ super(); // Paper - decompile fix - } - - public String a(Random random) { -@@ -0,0 +0,0 @@ public class WorldGenWoodlandMansionPieces { - - this.a(generatoraccess, structureboundingbox, random, blockposition, LootTables.o, iblockdata); - } else if ("Mage".equals(s)) { -- EntityEvoker entityevoker = new EntityEvoker(generatoraccess.getMinecraftWorld()); -- -+ EntityEvoker entityevoker = EntityTypes.EVOKER.create(generatoraccess.getMinecraftWorld()); // Paper - entityevoker.di(); - entityevoker.setPositionRotation(blockposition, 0.0F, 0.0F); - generatoraccess.addEntity(entityevoker); - generatoraccess.setTypeAndData(blockposition, Blocks.AIR.getBlockData(), 2); - } else if ("Warrior".equals(s)) { -- EntityVindicator entityvindicator = new EntityVindicator(generatoraccess.getMinecraftWorld()); -- -+ EntityVindicator entityvindicator = EntityTypes.VINDICATOR.create(generatoraccess.getMinecraftWorld()); // Paper - entityvindicator.di(); - entityvindicator.setPositionRotation(blockposition, 0.0F, 0.0F); - entityvindicator.prepare(generatoraccess.getDamageScaler(new BlockPosition(entityvindicator)), (GroupDataEntity) null, (NBTTagCompound) null); -diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java -index 53e7834cca..5c2421ac38 100644 ---- a/src/main/java/net/minecraft/server/WorldServer.java -+++ b/src/main/java/net/minecraft/server/WorldServer.java -@@ -0,0 +0,0 @@ public class WorldServer extends World implements IAsyncTaskHandler { - boolean flag2 = this.getGameRules().getBoolean("doMobSpawning") && this.random.nextDouble() < (double) difficultydamagescaler.b() * paperConfig.skeleHorseSpawnChance; // Paper - - if (flag2) { -- EntityHorseSkeleton entityhorseskeleton = new EntityHorseSkeleton(this); -+ EntityHorseSkeleton entityhorseskeleton = EntityTypes.SKELETON_HORSE.create(this); // Paper - - entityhorseskeleton.s(true); - entityhorseskeleton.setAgeRaw(0); -diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java -index 7c0a530533..40ee34675c 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 { - entity.setPositionRotation(x, y, z, 0, 0); - } else if (LivingEntity.class.isAssignableFrom(clazz)) { - if (Chicken.class.isAssignableFrom(clazz)) { -- entity = new EntityChicken(world); -+ entity = EntityTypes.CHICKEN.create(world); // Paper - } else if (Cow.class.isAssignableFrom(clazz)) { - if (MushroomCow.class.isAssignableFrom(clazz)) { -- entity = new EntityMushroomCow(world); -+ entity = EntityTypes.MOOSHROOM.create(world); // Paper - } else { -- entity = new EntityCow(world); -+ entity = EntityTypes.COW.create(world); // Paper - } - } else if (Golem.class.isAssignableFrom(clazz)) { - if (Snowman.class.isAssignableFrom(clazz)) { -- entity = new EntitySnowman(world); -+ entity = EntityTypes.SNOW_GOLEM.create(world); // Paper - } else if (IronGolem.class.isAssignableFrom(clazz)) { -- entity = new EntityIronGolem(world); -+ entity = EntityTypes.IRON_GOLEM.create(world); // Paper - } else if (Shulker.class.isAssignableFrom(clazz)) { -- entity = new EntityShulker(world); -+ entity = EntityTypes.SHULKER.create(world); // Paper - } - } else if (Creeper.class.isAssignableFrom(clazz)) { -- entity = new EntityCreeper(world); -+ entity = EntityTypes.CREEPER.create(world); // Paper - } else if (Ghast.class.isAssignableFrom(clazz)) { -- entity = new EntityGhast(world); -+ entity = EntityTypes.GHAST.create(world); // Paper - } else if (Pig.class.isAssignableFrom(clazz)) { -- entity = new EntityPig(world); -+ entity = EntityTypes.PIG.create(world); // Paper - } else if (Player.class.isAssignableFrom(clazz)) { - // need a net server handler for this one - } else if (Sheep.class.isAssignableFrom(clazz)) { -- entity = new EntitySheep(world); -+ entity = EntityTypes.SHEEP.create(world); // Paper - } else if (AbstractHorse.class.isAssignableFrom(clazz)) { - if (ChestedHorse.class.isAssignableFrom(clazz)) { - if (Donkey.class.isAssignableFrom(clazz)) { -- entity = new EntityHorseDonkey(world); -+ entity = EntityTypes.DONKEY.create(world); // Paper - } else if (Mule.class.isAssignableFrom(clazz)) { -- entity = new EntityHorseMule(world); -+ entity = EntityTypes.MULE.create(world); // Paper - } else if (Llama.class.isAssignableFrom(clazz)) { -- entity = new EntityLlama(world); -+ entity = EntityTypes.LLAMA.create(world); // Paper - } - } else if (SkeletonHorse.class.isAssignableFrom(clazz)) { -- entity = new EntityHorseSkeleton(world); -+ entity = EntityTypes.SKELETON_HORSE.create(world); // Paper - } else if (ZombieHorse.class.isAssignableFrom(clazz)) { -- entity = new EntityHorseZombie(world); -+ entity = EntityTypes.ZOMBIE_HORSE.create(world); // Paper - } else { -- entity = new EntityHorse(world); -+ entity = EntityTypes.HORSE.create(world); // Paper - } - } else if (Skeleton.class.isAssignableFrom(clazz)) { - if (Stray.class.isAssignableFrom(clazz)){ -- entity = new EntitySkeletonStray(world); -+ entity = EntityTypes.STRAY.create(world); // Paper - } else if (WitherSkeleton.class.isAssignableFrom(clazz)) { -- entity = new EntitySkeletonWither(world); -+ entity = EntityTypes.WITHER_SKELETON.create(world); // Paper - } else { -- entity = new EntitySkeleton(world); -+ entity = EntityTypes.SKELETON.create(world); // Paper - } - } else if (Slime.class.isAssignableFrom(clazz)) { - if (MagmaCube.class.isAssignableFrom(clazz)) { -- entity = new EntityMagmaCube(world); -+ entity = EntityTypes.MAGMA_CUBE.create(world); // Paper - } else { -- entity = new EntitySlime(world); -+ entity = EntityTypes.SLIME.create(world); // Paper - } - } else if (Spider.class.isAssignableFrom(clazz)) { - if (CaveSpider.class.isAssignableFrom(clazz)) { -- entity = new EntityCaveSpider(world); -+ entity = EntityTypes.CAVE_SPIDER.create(world); // Paper - } else { -- entity = new EntitySpider(world); -+ entity = EntityTypes.SPIDER.create(world); // Paper - } - } else if (Squid.class.isAssignableFrom(clazz)) { -- entity = new EntitySquid(world); -+ entity = EntityTypes.SQUID.create(world); // Paper - } else if (Tameable.class.isAssignableFrom(clazz)) { - if (Wolf.class.isAssignableFrom(clazz)) { -- entity = new EntityWolf(world); -+ entity = EntityTypes.WOLF.create(world); // Paper - } else if (Ocelot.class.isAssignableFrom(clazz)) { -- entity = new EntityOcelot(world); -+ entity = EntityTypes.OCELOT.create(world); // Paper - } else if (Parrot.class.isAssignableFrom(clazz)) { -- entity = new EntityParrot(world); -+ entity = EntityTypes.PARROT.create(world); // Paper - } - } else if (PigZombie.class.isAssignableFrom(clazz)) { -- entity = new EntityPigZombie(world); -+ entity = EntityTypes.ZOMBIE_PIGMAN.create(world); // Paper - } else if (Zombie.class.isAssignableFrom(clazz)) { - if (Husk.class.isAssignableFrom(clazz)) { -- entity = new EntityZombieHusk(world); -+ entity = EntityTypes.HUSK.create(world); // Paper - } else if (ZombieVillager.class.isAssignableFrom(clazz)) { -- entity = new EntityZombieVillager(world); -+ entity = EntityTypes.ZOMBIE_VILLAGER.create(world); // Paper - } else if (Drowned.class.isAssignableFrom(clazz)) { -- entity = new EntityDrowned(world); -+ entity = EntityTypes.DROWNED.create(world); // Paper - } else { -- entity = new EntityZombie(world); -+ entity = EntityTypes.ZOMBIE.create(world); // Paper - } - } else if (Giant.class.isAssignableFrom(clazz)) { -- entity = new EntityGiantZombie(world); -+ entity = EntityTypes.GIANT.create(world); // Paper - } else if (Silverfish.class.isAssignableFrom(clazz)) { -- entity = new EntitySilverfish(world); -+ entity = EntityTypes.SILVERFISH.create(world); // Paper - } else if (Enderman.class.isAssignableFrom(clazz)) { -- entity = new EntityEnderman(world); -+ entity = EntityTypes.ENDERMAN.create(world); // Paper - } else if (Blaze.class.isAssignableFrom(clazz)) { -- entity = new EntityBlaze(world); -+ entity = EntityTypes.BLAZE.create(world); // Paper - } else if (Villager.class.isAssignableFrom(clazz)) { -- entity = new EntityVillager(world); -+ entity = EntityTypes.VILLAGER.create(world); // Paper - } else if (Witch.class.isAssignableFrom(clazz)) { -- entity = new EntityWitch(world); -+ entity = EntityTypes.WITCH.create(world); // Paper - } else if (Wither.class.isAssignableFrom(clazz)) { -- entity = new EntityWither(world); -+ entity = EntityTypes.WITHER.create(world); // Paper - } else if (ComplexLivingEntity.class.isAssignableFrom(clazz)) { - if (EnderDragon.class.isAssignableFrom(clazz)) { -- entity = new EntityEnderDragon(world); -+ entity = EntityTypes.ENDER_DRAGON.create(world); // Paper - } - } else if (Ambient.class.isAssignableFrom(clazz)) { - if (Bat.class.isAssignableFrom(clazz)) { -- entity = new EntityBat(world); -+ entity = EntityTypes.BAT.create(world); // Paper - } - } else if (Rabbit.class.isAssignableFrom(clazz)) { -- entity = new EntityRabbit(world); -+ entity = EntityTypes.RABBIT.create(world); // Paper - } else if (Endermite.class.isAssignableFrom(clazz)) { -- entity = new EntityEndermite(world); -+ entity = EntityTypes.ENDERMITE.create(world); // Paper - } else if (Guardian.class.isAssignableFrom(clazz)) { - if (ElderGuardian.class.isAssignableFrom(clazz)){ -- entity = new EntityGuardianElder(world); -+ entity = EntityTypes.ELDER_GUARDIAN.create(world); // Paper - } else { -- entity = new EntityGuardian(world); -+ entity = EntityTypes.GUARDIAN.create(world); // Paper - } - } else if (ArmorStand.class.isAssignableFrom(clazz)) { -- entity = new EntityArmorStand(world, x, y, z); -+ entity = EntityTypes.ARMOR_STAND.create(world); // Paper - } else if (PolarBear.class.isAssignableFrom(clazz)) { -- entity = new EntityPolarBear(world); -+ entity = EntityTypes.POLAR_BEAR.create(world); // Paper - } else if (Vex.class.isAssignableFrom(clazz)) { -- entity = new EntityVex(world); -+ entity = EntityTypes.VEX.create(world); // Paper - } else if (Illager.class.isAssignableFrom(clazz)) { - if (Spellcaster.class.isAssignableFrom(clazz)) { - if (Evoker.class.isAssignableFrom(clazz)) { -- entity = new EntityEvoker(world); -+ entity = EntityTypes.EVOKER.create(world); // Paper - } else if (Illusioner.class.isAssignableFrom(clazz)) { -- entity = new EntityIllagerIllusioner(world); -+ entity = EntityTypes.ILLUSIONER.create(world); // Paper - } - } else if (Vindicator.class.isAssignableFrom(clazz)) { -- entity = new EntityVindicator(world); -+ entity = EntityTypes.VINDICATOR.create(world); // Paper - } - } else if (Turtle.class.isAssignableFrom(clazz)) { -- entity = new EntityTurtle(world); -+ entity = EntityTypes.TURTLE.create(world); // Paper - } else if (Phantom.class.isAssignableFrom(clazz)) { -- entity = new EntityPhantom(world); -+ entity = EntityTypes.PHANTOM.create(world); // Paper - } else if (Fish.class.isAssignableFrom(clazz)) { - if (Cod.class.isAssignableFrom(clazz)) { -- entity = new EntityCod(world); -+ entity = EntityTypes.COD.create(world); // Paper - } else if (PufferFish.class.isAssignableFrom(clazz)) { -- entity = new EntityPufferFish(world); -+ entity = EntityTypes.PUFFERFISH.create(world); // Paper - } else if (Salmon.class.isAssignableFrom(clazz)) { -- entity = new EntitySalmon(world); -+ entity = EntityTypes.SALMON.create(world); // Paper - } else if (TropicalFish.class.isAssignableFrom(clazz)) { -- entity = new EntityTropicalFish(world); -+ entity = EntityTypes.TROPICAL_FISH.create(world); // Paper - } - } else if (Dolphin.class.isAssignableFrom(clazz)) { -- entity = new EntityDolphin(world); -+ entity = EntityTypes.DOLPHIN.create(world); // Paper - } - - if (entity != null) { --- \ No newline at end of file diff --git a/Spigot-Server-Patches/force-entity-dismount-during-teleportation.patch b/Spigot-Server-Patches/force-entity-dismount-during-teleportation.patch index a9375ba0e1..472f940541 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 4bdeba399..dec531ab7 100644 +index 0871e185e..f945a2df4 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