From 1b856bff3ec4ae600d4f5f9339e68b2644a6da7f Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Mon, 28 Mar 2016 20:55:47 -0400
Subject: [PATCH] MC Utils


diff --git a/src/main/java/net/minecraft/server/AttributeInstance.java b/src/main/java/net/minecraft/server/AttributeInstance.java
index 2f2b103641..b1900ba364 100644
--- a/src/main/java/net/minecraft/server/AttributeInstance.java
+++ b/src/main/java/net/minecraft/server/AttributeInstance.java
@@ -21,8 +21,10 @@ public interface AttributeInstance {
     @Nullable
     AttributeModifier a(UUID uuid);
 
+    default void addModifier(AttributeModifier modifier) { b(modifier); } // Paper - OBFHELPER
     void b(AttributeModifier attributemodifier);
 
+    default void removeModifier(AttributeModifier modifier) { c(modifier); } // Paper - OBFHELPER
     void c(AttributeModifier attributemodifier);
 
     void b(UUID uuid);
diff --git a/src/main/java/net/minecraft/server/BlockAccessAir.java b/src/main/java/net/minecraft/server/BlockAccessAir.java
index e8ab5d3221..d7a68a1ef7 100644
--- a/src/main/java/net/minecraft/server/BlockAccessAir.java
+++ b/src/main/java/net/minecraft/server/BlockAccessAir.java
@@ -14,6 +14,18 @@ public enum BlockAccessAir implements IBlockAccess {
         return null;
     }
 
+    // Paper start - If loaded util
+    @Override
+    public Fluid getFluidIfLoaded(BlockPosition blockposition) {
+        return this.getFluid(blockposition);
+    }
+
+    @Override
+    public IBlockData getTypeIfLoaded(BlockPosition blockposition) {
+        return this.getType(blockposition);
+    }
+    // Paper end
+
     @Override
     public IBlockData getType(BlockPosition blockposition) {
         return Blocks.AIR.getBlockData();
diff --git a/src/main/java/net/minecraft/server/BlockDataAbstract.java b/src/main/java/net/minecraft/server/BlockDataAbstract.java
index b370316574..841c7d1904 100644
--- a/src/main/java/net/minecraft/server/BlockDataAbstract.java
+++ b/src/main/java/net/minecraft/server/BlockDataAbstract.java
@@ -80,6 +80,7 @@ public abstract class BlockDataAbstract<O, S> implements IBlockDataHolder<S> {
         return Collections.unmodifiableCollection(this.d.keySet());
     }
 
+    public final <T extends Comparable<T>> boolean hasProperty(IBlockState<T> iblockstate) { return this.b(iblockstate); } // Paper - OBFHELPER
     public <T extends Comparable<T>> boolean b(IBlockState<T> iblockstate) {
         return this.d.containsKey(iblockstate);
     }
diff --git a/src/main/java/net/minecraft/server/BlockPosition.java b/src/main/java/net/minecraft/server/BlockPosition.java
index 70d9342422..b56a044b3a 100644
--- a/src/main/java/net/minecraft/server/BlockPosition.java
+++ b/src/main/java/net/minecraft/server/BlockPosition.java
@@ -120,6 +120,7 @@ public class BlockPosition extends BaseBlockPosition implements MinecraftSeriali
         return d0 == 0.0D && d1 == 0.0D && d2 == 0.0D ? this : new BlockPosition((double) this.getX() + d0, (double) this.getY() + d1, (double) this.getZ() + d2);
     }
 
+    public BlockPosition add(int i, int j, int k) {return b(i, j, k);} // Paper - OBFHELPER
     public BlockPosition b(int i, int j, int k) {
         return i == 0 && j == 0 && k == 0 ? this : new BlockPosition(this.getX() + i, this.getY() + j, this.getZ() + k);
     }
@@ -207,6 +208,8 @@ public class BlockPosition extends BaseBlockPosition implements MinecraftSeriali
         return new BlockPosition(this.getY() * baseblockposition.getZ() - this.getZ() * baseblockposition.getY(), this.getZ() * baseblockposition.getX() - this.getX() * baseblockposition.getZ(), this.getX() * baseblockposition.getY() - this.getY() * baseblockposition.getX());
     }
 
+    @Deprecated // We'll replace this...
+    public BlockPosition asImmutable() { return immutableCopy(); } // Paper - OBFHELPER
     public BlockPosition immutableCopy() {
         return this;
     }
@@ -391,6 +394,7 @@ public class BlockPosition extends BaseBlockPosition implements MinecraftSeriali
             return this.d;
         }
 
+        public BlockPosition.MutableBlockPosition setValues(int i, int j, int k) { return d(i, j, k);} // Paper - OBFHELPER
         public BlockPosition.MutableBlockPosition d(int i, int j, int k) {
             this.b = i;
             this.c = j;
@@ -402,6 +406,7 @@ public class BlockPosition extends BaseBlockPosition implements MinecraftSeriali
             return this.c(entity.locX, entity.locY, entity.locZ);
         }
 
+        public BlockPosition.MutableBlockPosition setValues(double d0, double d1, double d2) { return c(d0, d1, d2);} // Paper - OBFHELPER
         public BlockPosition.MutableBlockPosition c(double d0, double d1, double d2) {
             return this.d(MathHelper.floor(d0), MathHelper.floor(d1), MathHelper.floor(d2));
         }
diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java
index bdda2e63ae..0905fbdc41 100644
--- a/src/main/java/net/minecraft/server/Chunk.java
+++ b/src/main/java/net/minecraft/server/Chunk.java
@@ -25,7 +25,7 @@ import org.apache.logging.log4j.Logger;
 public class Chunk implements IChunkAccess {
 
     private static final Logger LOGGER = LogManager.getLogger();
-    public static final ChunkSection a = null;
+    public static final ChunkSection a = null; public static final ChunkSection EMPTY_CHUNK_SECTION = Chunk.a; // Paper - OBFHELPER
     private final ChunkSection[] sections;
     private final BiomeBase[] d;
     private final Map<BlockPosition, NBTTagCompound> e;
@@ -213,6 +213,18 @@ public class Chunk implements IChunkAccess {
         }
     }
 
+    // Paper start - If loaded util
+    @Override
+    public Fluid getFluidIfLoaded(BlockPosition blockposition) {
+        return this.getFluid(blockposition);
+    }
+
+    @Override
+    public IBlockData getTypeIfLoaded(BlockPosition blockposition) {
+        return this.getType(blockposition);
+    }
+    // Paper end
+
     @Override
     public Fluid getFluid(BlockPosition blockposition) {
         return this.a(blockposition.getX(), blockposition.getY(), blockposition.getZ());
@@ -400,6 +412,7 @@ public class Chunk implements IChunkAccess {
         return this.a(blockposition, Chunk.EnumTileEntityState.CHECK);
     }
 
+    @Nullable public final TileEntity getTileEntityImmediately(BlockPosition pos) { return this.a(pos, EnumTileEntityState.IMMEDIATE); } // Paper - OBFHELPER
     @Nullable
     public TileEntity a(BlockPosition blockposition, Chunk.EnumTileEntityState chunk_enumtileentitystate) {
         // CraftBukkit start
diff --git a/src/main/java/net/minecraft/server/ChunkCache.java b/src/main/java/net/minecraft/server/ChunkCache.java
index 2c9bf7d00f..ccbc1dde09 100644
--- a/src/main/java/net/minecraft/server/ChunkCache.java
+++ b/src/main/java/net/minecraft/server/ChunkCache.java
@@ -47,6 +47,30 @@ public class ChunkCache implements IWorldReader {
         return this.e.getLightLevel(blockposition, i);
     }
 
+    // Paper start - if loaded util
+    @Nullable
+    @Override
+    public IChunkAccess getChunkIfLoadedImmediately(int x, int z) {
+        IChunkAccess chunk = this.getChunkAt(x, z, ChunkStatus.FULL, false);
+        if (chunk instanceof ChunkEmpty) {
+            return null;
+        }
+        return chunk;
+    }
+
+    @Override
+    public Fluid getFluidIfLoaded(BlockPosition blockposition) {
+        IChunkAccess chunk = this.getChunkIfLoadedImmediately(blockposition.getX() >> 4, blockposition.getZ() >> 4);
+        return chunk == null ? null : chunk.getFluid(blockposition);
+    }
+
+    @Override
+    public IBlockData getTypeIfLoaded(BlockPosition blockposition) {
+        IChunkAccess chunk = this.getChunkIfLoadedImmediately(blockposition.getX() >> 4, blockposition.getZ() >> 4);
+        return chunk == null ? null : chunk.getType(blockposition);
+    }
+    // Paper end
+
     @Nullable
     @Override
     public IChunkAccess getChunkAt(int i, int j, ChunkStatus chunkstatus, boolean flag) {
diff --git a/src/main/java/net/minecraft/server/ChunkCoordIntPair.java b/src/main/java/net/minecraft/server/ChunkCoordIntPair.java
index 857b2f8868..bbf136614c 100644
--- a/src/main/java/net/minecraft/server/ChunkCoordIntPair.java
+++ b/src/main/java/net/minecraft/server/ChunkCoordIntPair.java
@@ -31,7 +31,9 @@ public class ChunkCoordIntPair {
         return pair(this.x, this.z);
     }
 
-    public static long pair(int i, int j) {
+    public static long asLong(final BlockPosition pos) { return pair(pos.getX() >> 4, pos.getZ() >> 4); } // Paper - OBFHELPER
+    public static long asLong(int x, int z) { return pair(x, z); } // Paper - OBFHELPER
+        public static long pair(int i, int j) {
         return (long) i & 4294967295L | ((long) j & 4294967295L) << 32;
     }
 
diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java
index dcf2d38df5..95356b82ff 100644
--- a/src/main/java/net/minecraft/server/ChunkProviderServer.java
+++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java
@@ -66,6 +66,58 @@ public class ChunkProviderServer extends IChunkProvider {
         return this.playerChunkMap.c();
     }
 
+    // Paper start - "real" get chunk if loaded
+    // Note: Partially copied from the getChunkAt method below
+    @Nullable
+    public Chunk getChunkAtIfCachedImmediately(int x, int z) {
+        if (Thread.currentThread() != this.serverThread) {
+            return CompletableFuture.supplyAsync(() -> {
+                return this.getChunkAtIfCachedImmediately(x, z);
+            }, this.serverThreadQueue).join();
+        }
+
+        long k = ChunkCoordIntPair.pair(x, z);
+
+        IChunkAccess ichunkaccess;
+
+        for (int l = 0; l < 4; ++l) {
+            if (k == this.cachePos[l] && ChunkStatus.FULL == this.cacheStatus[l]) {
+                ichunkaccess = this.cacheChunk[l];
+                if (ichunkaccess instanceof Chunk) { // CraftBukkit - the chunk can become accessible in the meantime TODO for non-null chunks it might also make sense to check that the chunk's state hasn't changed in the meantime
+                    return (Chunk)ichunkaccess;
+                }
+            }
+        }
+
+        PlayerChunk playerChunk = this.getChunk(k);
+        if (playerChunk == null) {
+            return null;
+        }
+
+        return playerChunk.getFullChunkIfCached();
+    }
+
+    @Nullable
+    public Chunk getChunkAtIfLoadedImmediately(int x, int z) {
+        if (Thread.currentThread() != this.serverThread) {
+            return CompletableFuture.supplyAsync(() -> {
+                return this.getChunkAtIfLoadedImmediately(x, z);
+            }, this.serverThreadQueue).join();
+        }
+
+        long k = ChunkCoordIntPair.pair(x, z);
+
+        // Note: Bypass cache since we need to check ticket level
+
+        PlayerChunk playerChunk = this.getChunk(k);
+        if (playerChunk == null) {
+            return null;
+        }
+
+        return playerChunk.getFullChunk();
+    }
+    // Paper end
+
     @Nullable
     @Override
     public IChunkAccess getChunkAt(int i, int j, ChunkStatus chunkstatus, boolean flag) {
diff --git a/src/main/java/net/minecraft/server/DataBits.java b/src/main/java/net/minecraft/server/DataBits.java
index 8bda055159..409dc837c6 100644
--- a/src/main/java/net/minecraft/server/DataBits.java
+++ b/src/main/java/net/minecraft/server/DataBits.java
@@ -82,6 +82,7 @@ public class DataBits {
         }
     }
 
+    public long[] getDataBits() { return this.a(); } // Paper - OBFHELPER
     public long[] a() {
         return this.a;
     }
diff --git a/src/main/java/net/minecraft/server/DataPalette.java b/src/main/java/net/minecraft/server/DataPalette.java
index 75ba698868..45403fbe30 100644
--- a/src/main/java/net/minecraft/server/DataPalette.java
+++ b/src/main/java/net/minecraft/server/DataPalette.java
@@ -4,10 +4,12 @@ import javax.annotation.Nullable;
 
 public interface DataPalette<T> {
 
+    default int getOrCreateIdFor(T object) { return this.a(object); } // Paper - OBFHELPER
     int a(T t0);
 
     boolean b(T t0);
 
+    @Nullable default T getObject(int dataBits) { return this.a(dataBits); } // Paper - OBFHELPER
     @Nullable
     T a(int i);
 
diff --git a/src/main/java/net/minecraft/server/DataPaletteBlock.java b/src/main/java/net/minecraft/server/DataPaletteBlock.java
index 4411d5640a..28a6be7ab0 100644
--- a/src/main/java/net/minecraft/server/DataPaletteBlock.java
+++ b/src/main/java/net/minecraft/server/DataPaletteBlock.java
@@ -8,7 +8,7 @@ import java.util.stream.Collectors;
 
 public class DataPaletteBlock<T> implements DataPaletteExpandable<T> {
 
-    private final DataPalette<T> b;
+    private final DataPalette<T> b; private final DataPalette<T> getDataPaletteGlobal() { return this.b; } // Paper - OBFHELPER
     private final DataPaletteExpandable<T> c = (i, object) -> {
         return 0;
     };
@@ -16,9 +16,9 @@ public class DataPaletteBlock<T> implements DataPaletteExpandable<T> {
     private final Function<NBTTagCompound, T> e;
     private final Function<T, NBTTagCompound> f;
     private final T g;
-    protected DataBits a;
-    private DataPalette<T> h;
-    private int i;
+    protected DataBits a; protected DataBits getDataBits() { return this.a; } // Paper - OBFHELPER
+    private DataPalette<T> h; private DataPalette<T> getDataPalette() { return this.h; } // Paper - OBFHELPER
+    private int i; private int getBitsPerObject() { return this.i; } // Paper - OBFHELPER
     private final ReentrantLock j = new ReentrantLock();
 
     public void a() {
@@ -53,6 +53,7 @@ public class DataPaletteBlock<T> implements DataPaletteExpandable<T> {
         return j << 8 | k << 4 | i;
     }
 
+    private void initialize(int bitsPerObject) { this.b(bitsPerObject); } // Paper - OBFHELPER
     private void b(int i) {
         if (i != this.i) {
             this.i = i;
@@ -130,6 +131,7 @@ public class DataPaletteBlock<T> implements DataPaletteExpandable<T> {
         return t0 == null ? this.g : t0;
     }
 
+    public void writeDataPaletteBlock(PacketDataSerializer packetDataSerializer) { this.b(packetDataSerializer); } // Paper - OBFHELPER
     public void b(PacketDataSerializer packetdataserializer) {
         this.a();
         packetdataserializer.writeByte(this.i);
diff --git a/src/main/java/net/minecraft/server/EntityCreature.java b/src/main/java/net/minecraft/server/EntityCreature.java
index 4f69e23271..8f5f61f602 100644
--- a/src/main/java/net/minecraft/server/EntityCreature.java
+++ b/src/main/java/net/minecraft/server/EntityCreature.java
@@ -6,6 +6,8 @@ import org.bukkit.event.entity.EntityUnleashEvent;
 
 public abstract class EntityCreature extends EntityInsentient {
 
+    public org.bukkit.craftbukkit.entity.CraftCreature getBukkitCreature() { return (org.bukkit.craftbukkit.entity.CraftCreature) super.getBukkitEntity(); } // Paper
+
     protected EntityCreature(EntityTypes<? extends EntityCreature> entitytypes, World world) {
         super(entitytypes, world);
     }
diff --git a/src/main/java/net/minecraft/server/EntityInsentient.java b/src/main/java/net/minecraft/server/EntityInsentient.java
index d4fff5b72e..ec1da887c6 100644
--- a/src/main/java/net/minecraft/server/EntityInsentient.java
+++ b/src/main/java/net/minecraft/server/EntityInsentient.java
@@ -143,6 +143,7 @@ public abstract class EntityInsentient extends EntityLiving {
         return this.goalTarget;
     }
 
+    public org.bukkit.craftbukkit.entity.CraftMob getBukkitMob() { return (org.bukkit.craftbukkit.entity.CraftMob) super.getBukkitEntity(); } // Paper
     public void setGoalTarget(@Nullable EntityLiving entityliving) {
         // CraftBukkit start - fire event
         setGoalTarget(entityliving, EntityTargetEvent.TargetReason.UNKNOWN, true);
diff --git a/src/main/java/net/minecraft/server/EntityLiving.java b/src/main/java/net/minecraft/server/EntityLiving.java
index 61319d6c20..40173aa35b 100644
--- a/src/main/java/net/minecraft/server/EntityLiving.java
+++ b/src/main/java/net/minecraft/server/EntityLiving.java
@@ -126,6 +126,7 @@ public abstract class EntityLiving extends Entity {
     public org.bukkit.craftbukkit.attribute.CraftAttributeMap craftAttributes;
     public boolean collides = true;
     public boolean canPickUpLoot;
+    public org.bukkit.craftbukkit.entity.CraftLivingEntity getBukkitLivingEntity() { return (org.bukkit.craftbukkit.entity.CraftLivingEntity) super.getBukkitEntity(); } // Paper
 
     @Override
     public float getBukkitYaw() {
diff --git a/src/main/java/net/minecraft/server/EntityMonster.java b/src/main/java/net/minecraft/server/EntityMonster.java
index 6ab4c78b35..76142d5dc2 100644
--- a/src/main/java/net/minecraft/server/EntityMonster.java
+++ b/src/main/java/net/minecraft/server/EntityMonster.java
@@ -4,6 +4,7 @@ import java.util.function.Predicate;
 
 public abstract class EntityMonster extends EntityCreature implements IMonster {
 
+    public org.bukkit.craftbukkit.entity.CraftMonster getBukkitMonster() { return (org.bukkit.craftbukkit.entity.CraftMonster) super.getBukkitEntity(); } // Paper
     protected EntityMonster(EntityTypes<? extends EntityMonster> entitytypes, World world) {
         super(entitytypes, world);
         this.f = 5;
diff --git a/src/main/java/net/minecraft/server/EntityTypes.java b/src/main/java/net/minecraft/server/EntityTypes.java
index 8c918d0d68..56a2d7df5e 100644
--- a/src/main/java/net/minecraft/server/EntityTypes.java
+++ b/src/main/java/net/minecraft/server/EntityTypes.java
@@ -5,6 +5,7 @@ import com.mojang.datafixers.types.Type;
 import java.util.Collections;
 import java.util.Optional;
 import java.util.Set; // Paper
+import java.util.Map; // Paper
 import java.util.UUID;
 import java.util.function.Function;
 import java.util.stream.Stream;
@@ -287,8 +288,8 @@ public class EntityTypes<T extends Entity> {
         return this.bi.height;
     }
 
-    @Nullable
-    public T a(World world) {
+    public T create(World world) { return this.a(world); } // Paper - OBFHELPER
+    @Nullable public T a(World world) { // Paper - OBFHELPER
         return this.aZ.create(this, world);
     }
 
diff --git a/src/main/java/net/minecraft/server/IAsyncTaskHandler.java b/src/main/java/net/minecraft/server/IAsyncTaskHandler.java
index 4510018d6f..c0d5112444 100644
--- a/src/main/java/net/minecraft/server/IAsyncTaskHandler.java
+++ b/src/main/java/net/minecraft/server/IAsyncTaskHandler.java
@@ -64,6 +64,15 @@ public abstract class IAsyncTaskHandler<R extends Runnable> implements Mailbox<R
 
     }
 
+    // Paper start
+    public void scheduleOnMain(Runnable r0) {
+        // postToMainThread does not work the same as older versions of mc
+        // This method is actually used to create a TickTask, which can then be posted onto main
+        this.addTask(this.postToMainThread(r0));
+    }
+    // Paper end
+
+    public void addTask(R r0) { a(r0); }; // Paper - OBFHELPER
     public void a(R r0) {
         this.d.add(r0);
         LockSupport.unpark(this.getThread());
diff --git a/src/main/java/net/minecraft/server/IBlockAccess.java b/src/main/java/net/minecraft/server/IBlockAccess.java
index 6e365f402c..577b227758 100644
--- a/src/main/java/net/minecraft/server/IBlockAccess.java
+++ b/src/main/java/net/minecraft/server/IBlockAccess.java
@@ -9,10 +9,24 @@ public interface IBlockAccess {
     @Nullable
     TileEntity getTileEntity(BlockPosition blockposition);
 
+    IBlockData getTypeIfLoaded(BlockPosition blockposition); // Paper - if loaded util
     IBlockData getType(BlockPosition blockposition);
 
+    Fluid getFluidIfLoaded(BlockPosition blockposition); // Paper - if loaded util
     Fluid getFluid(BlockPosition blockposition);
 
+    // Paper start - if loaded util
+    default Material getMaterialIfLoaded(BlockPosition blockposition) {
+        IBlockData type = this.getTypeIfLoaded(blockposition);
+        return type == null ? null : type.getMaterial();
+    }
+
+    default Block getBlockIfLoaded(BlockPosition blockposition) {
+        IBlockData type = this.getTypeIfLoaded(blockposition);
+        return type == null ? null : type.getBlock();
+    }
+    // Paper end
+
     default int h(BlockPosition blockposition) {
         return this.getType(blockposition).h();
     }
diff --git a/src/main/java/net/minecraft/server/IWorldReader.java b/src/main/java/net/minecraft/server/IWorldReader.java
index 729bac3ab5..7308c0c319 100644
--- a/src/main/java/net/minecraft/server/IWorldReader.java
+++ b/src/main/java/net/minecraft/server/IWorldReader.java
@@ -39,6 +39,7 @@ public interface IWorldReader extends IIBlockAccess {
 
     int getLightLevel(BlockPosition blockposition, int i);
 
+    @Nullable IChunkAccess getChunkIfLoadedImmediately(int x, int z); // Paper - ifLoaded api (we need this since current impl blocks if the chunk is loading)
     @Nullable
     IChunkAccess getChunkAt(int i, int j, ChunkStatus chunkstatus, boolean flag);
 
diff --git a/src/main/java/net/minecraft/server/ItemStack.java b/src/main/java/net/minecraft/server/ItemStack.java
index e975265a28..aa6a58dd88 100644
--- a/src/main/java/net/minecraft/server/ItemStack.java
+++ b/src/main/java/net/minecraft/server/ItemStack.java
@@ -37,10 +37,19 @@ import org.bukkit.event.world.StructureGrowEvent;
 public final class ItemStack {
 
     private static final Logger LOGGER = LogManager.getLogger();
-    public static final ItemStack a = new ItemStack((Item) null);
+    public static final ItemStack a = new ItemStack((Item) null);public static final ItemStack NULL_ITEM = a; // Paper - OBFHELPER
     public static final DecimalFormat b = F();
     private int count;
     private int e;
+    // Paper start
+    private org.bukkit.craftbukkit.inventory.CraftItemStack bukkitStack;
+    public org.bukkit.inventory.ItemStack getBukkitStack() {
+        if (bukkitStack == null || bukkitStack.getHandle() != this) {
+            bukkitStack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(this);
+        }
+        return bukkitStack;
+    }
+    // Paper end
     @Deprecated
     private Item item;
     private NBTTagCompound tag;
@@ -591,6 +600,17 @@ public final class ItemStack {
         return this.tag != null ? this.tag.getList("Enchantments", 10) : new NBTTagList();
     }
 
+    // Paper start - (this is just a good no conflict location)
+    public org.bukkit.inventory.ItemStack asBukkitMirror() {
+        return CraftItemStack.asCraftMirror(this);
+    }
+    public org.bukkit.inventory.ItemStack asBukkitCopy() {
+        return CraftItemStack.asCraftMirror(this.cloneItemStack());
+    }
+    public static ItemStack fromBukkitCopy(org.bukkit.inventory.ItemStack itemstack) {
+        return CraftItemStack.asNMSCopy(itemstack);
+    }
+    // Paper end
     public void setTag(@Nullable NBTTagCompound nbttagcompound) {
         this.tag = nbttagcompound;
     }
@@ -675,6 +695,7 @@ public final class ItemStack {
         return this.tag != null && this.tag.hasKeyOfType("Enchantments", 9) ? !this.tag.getList("Enchantments", 10).isEmpty() : false;
     }
 
+    public void getOrCreateTagAndSet(String s, NBTBase nbtbase) { a(s, nbtbase);} // Paper - OBFHELPER
     public void a(String s, NBTBase nbtbase) {
         this.getOrCreateTag().set(s, nbtbase);
     }
@@ -748,6 +769,7 @@ public final class ItemStack {
     // CraftBukkit start
     @Deprecated
     public void setItem(Item item) {
+        this.bukkitStack = null; // Paper
         this.item = item;
     }
     // CraftBukkit end
diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java
new file mode 100644
index 0000000000..c96f3ed176
--- /dev/null
+++ b/src/main/java/net/minecraft/server/MCUtil.java
@@ -0,0 +1,316 @@
+package net.minecraft.server;
+
+import com.destroystokyo.paper.block.TargetBlockInfo;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import org.bukkit.Location;
+import org.bukkit.block.BlockFace;
+import org.bukkit.craftbukkit.CraftWorld;
+import org.bukkit.craftbukkit.util.Waitable;
+import org.spigotmc.AsyncCatcher;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.Queue;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Supplier;
+
+public final class MCUtil {
+    private static final Executor asyncExecutor = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("Paper Async Task Handler Thread - %1$d").build());
+
+    private MCUtil() {}
+
+
+    public static boolean isMainThread() {
+        return MinecraftServer.getServer().isMainThread();
+    }
+
+    private static class DelayedRunnable implements Runnable {
+
+        private final int ticks;
+        private final Runnable run;
+
+        private DelayedRunnable(int ticks, Runnable run) {
+            this.ticks = ticks;
+            this.run = run;
+        }
+
+        @Override
+        public void run() {
+            if (ticks <= 0) {
+                run.run();
+            } else {
+                scheduleTask(ticks-1, run);
+            }
+        }
+    }
+
+    public static void scheduleTask(int ticks, Runnable runnable) {
+        // We use post to main instead of process queue as we don't want to process these mid tick if
+        // Someone uses processQueueWhileWaiting
+        MinecraftServer.getServer().scheduleOnMain(new DelayedRunnable(ticks, runnable));
+    }
+
+    public static void processQueue() {
+        Runnable runnable;
+        Queue<Runnable> processQueue = getProcessQueue();
+        while ((runnable = processQueue.poll()) != null) {
+            try {
+                runnable.run();
+            } catch (Exception e) {
+                MinecraftServer.LOGGER.error("Error executing task", e);
+            }
+        }
+    }
+    public static <T> T processQueueWhileWaiting(CompletableFuture <T> future) {
+        try {
+            if (isMainThread()) {
+                while (!future.isDone()) {
+                    try {
+                        return future.get(1, TimeUnit.MILLISECONDS);
+                    } catch (TimeoutException ignored) {
+                        processQueue();
+                    }
+                }
+            }
+            return future.get();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static void ensureMain(Runnable run) {
+        ensureMain(null, run);
+    }
+    /**
+     * Ensures the target code is running on the main thread
+     * @param reason
+     * @param run
+     * @return
+     */
+    public static void ensureMain(String reason, Runnable run) {
+        if (AsyncCatcher.enabled && Thread.currentThread() != MinecraftServer.getServer().serverThread) {
+            if (reason != null) {
+                new IllegalStateException("Asynchronous " + reason + "!").printStackTrace();
+            }
+            getProcessQueue().add(run);
+            return;
+        }
+        run.run();
+    }
+
+    private static Queue<Runnable> getProcessQueue() {
+        return MinecraftServer.getServer().processQueue;
+    }
+
+    public static <T> T ensureMain(Supplier<T> run) {
+        return ensureMain(null, run);
+    }
+    /**
+     * Ensures the target code is running on the main thread
+     * @param reason
+     * @param run
+     * @param <T>
+     * @return
+     */
+    public static <T> T ensureMain(String reason, Supplier<T> run) {
+        if (AsyncCatcher.enabled && Thread.currentThread() != MinecraftServer.getServer().serverThread) {
+            if (reason != null) {
+                new IllegalStateException("Asynchronous " + reason + "! Blocking thread until it returns ").printStackTrace();
+            }
+            Waitable<T> wait = new Waitable<T>() {
+                @Override
+                protected T evaluate() {
+                    return run.get();
+                }
+            };
+            getProcessQueue().add(wait);
+            try {
+                return wait.get();
+            } catch (InterruptedException | ExecutionException e) {
+                e.printStackTrace();
+            }
+            return null;
+        }
+        return run.get();
+    }
+
+    /**
+     * Calculates distance between 2 entities
+     * @param e1
+     * @param e2
+     * @return
+     */
+    public static double distance(Entity e1, Entity e2) {
+        return Math.sqrt(distanceSq(e1, e2));
+    }
+
+
+    /**
+     * Calculates distance between 2 block positions
+     * @param e1
+     * @param e2
+     * @return
+     */
+    public static double distance(BlockPosition e1, BlockPosition e2) {
+        return Math.sqrt(distanceSq(e1, e2));
+    }
+
+    /**
+     * Gets the distance between 2 positions
+     * @param x1
+     * @param y1
+     * @param z1
+     * @param x2
+     * @param y2
+     * @param z2
+     * @return
+     */
+    public static double distance(double x1, double y1, double z1, double x2, double y2, double z2) {
+        return Math.sqrt(distanceSq(x1, y1, z1, x2, y2, z2));
+    }
+
+    /**
+     * Get's the distance squared between 2 entities
+     * @param e1
+     * @param e2
+     * @return
+     */
+    public static double distanceSq(Entity e1, Entity e2) {
+        return distanceSq(e1.locX,e1.locY,e1.locZ, e2.locX,e2.locY,e2.locZ);
+    }
+
+    /**
+     * Gets the distance sqaured between 2 block positions
+     * @param pos1
+     * @param pos2
+     * @return
+     */
+    public static double distanceSq(BlockPosition pos1, BlockPosition pos2) {
+        return distanceSq(pos1.getX(), pos1.getY(), pos1.getZ(), pos2.getX(), pos2.getY(), pos2.getZ());
+    }
+
+    /**
+     * Gets the distance squared between 2 positions
+     * @param x1
+     * @param y1
+     * @param z1
+     * @param x2
+     * @param y2
+     * @param z2
+     * @return
+     */
+    public static double distanceSq(double x1, double y1, double z1, double x2, double y2, double z2) {
+        return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) + (z1 - z2) * (z1 - z2);
+    }
+
+    /**
+     * Converts a NMS World/BlockPosition to Bukkit Location
+     * @param world
+     * @param x
+     * @param y
+     * @param z
+     * @return
+     */
+    public static Location toLocation(World world, double x, double y, double z) {
+        return new Location(world.getWorld(), x, y, z);
+    }
+
+    /**
+     * Converts a NMS World/BlockPosition to Bukkit Location
+     * @param world
+     * @param pos
+     * @return
+     */
+    public static Location toLocation(World world, BlockPosition pos) {
+        return new Location(world.getWorld(), pos.getX(), pos.getY(), pos.getZ());
+    }
+
+    /**
+     * Converts an NMS entity's current location to a Bukkit Location
+     * @param entity
+     * @return
+     */
+    public static Location toLocation(Entity entity) {
+        return new Location(entity.getWorld().getWorld(), entity.locX, entity.locY, entity.locZ);
+    }
+
+    public static org.bukkit.block.Block toBukkitBlock(World world, BlockPosition pos) {
+        return world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ());
+    }
+
+    public static BlockPosition toBlockPosition(Location loc) {
+        return new BlockPosition(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ());
+    }
+
+    public static boolean isEdgeOfChunk(BlockPosition pos) {
+        final int modX = pos.getX() & 15;
+        final int modZ = pos.getZ() & 15;
+        return (modX == 0 || modX == 15 || modZ == 0 || modZ == 15);
+    }
+
+    /**
+     * Posts a task to be executed asynchronously
+     * @param run
+     */
+    public static void scheduleAsyncTask(Runnable run) {
+        asyncExecutor.execute(run);
+    }
+
+    @Nullable
+    public static TileEntityHopper getHopper(World world, BlockPosition pos) {
+        Chunk chunk = world.getChunkIfLoaded(pos.getX() >> 4, pos.getZ() >> 4);
+        if (chunk != null && chunk.getType(new BlockPosition(pos.getX(), pos.getY(), pos.getZ())).getBlock() == Blocks.HOPPER) {
+            TileEntity tileEntity = chunk.getTileEntityImmediately(pos);
+            if (tileEntity instanceof TileEntityHopper) {
+                return (TileEntityHopper) tileEntity;
+            }
+        }
+        return null;
+    }
+
+    @Nonnull
+    public static World getNMSWorld(@Nonnull org.bukkit.World world) {
+        return ((CraftWorld) world).getHandle();
+    }
+
+    public static World getNMSWorld(@Nonnull org.bukkit.entity.Entity entity) {
+        return getNMSWorld(entity.getWorld());
+    }
+
+    public static RayTrace.FluidCollisionOption getNMSFluidCollisionOption(TargetBlockInfo.FluidMode fluidMode) {
+        if (fluidMode == TargetBlockInfo.FluidMode.NEVER) {
+            return RayTrace.FluidCollisionOption.NONE;
+        }
+        if (fluidMode == TargetBlockInfo.FluidMode.SOURCE_ONLY) {
+            return RayTrace.FluidCollisionOption.SOURCE_ONLY;
+        }
+        if (fluidMode == TargetBlockInfo.FluidMode.ALWAYS) {
+            return RayTrace.FluidCollisionOption.ANY;
+        }
+        return null;
+    }
+
+    public static BlockFace toBukkitBlockFace(EnumDirection enumDirection) {
+        switch (enumDirection) {
+            case DOWN:
+                return BlockFace.DOWN;
+            case UP:
+                return BlockFace.UP;
+            case NORTH:
+                return BlockFace.NORTH;
+            case SOUTH:
+                return BlockFace.SOUTH;
+            case WEST:
+                return BlockFace.WEST;
+            case EAST:
+                return BlockFace.EAST;
+            default:
+                return null;
+        }
+    }
+}
diff --git a/src/main/java/net/minecraft/server/NBTTagCompound.java b/src/main/java/net/minecraft/server/NBTTagCompound.java
index e16a579b55..3f6d2676e7 100644
--- a/src/main/java/net/minecraft/server/NBTTagCompound.java
+++ b/src/main/java/net/minecraft/server/NBTTagCompound.java
@@ -23,7 +23,7 @@ public class NBTTagCompound implements NBTBase {
 
     private static final Logger LOGGER = LogManager.getLogger();
     private static final Pattern g = Pattern.compile("[A-Za-z0-9._+-]+");
-    private final Map<String, NBTBase> map = Maps.newHashMap();
+    public final Map<String, NBTBase> map = Maps.newHashMap(); // Paper
 
     public NBTTagCompound() {}
 
@@ -99,11 +99,15 @@ public class NBTTagCompound implements NBTBase {
         this.map.put(s, new NBTTagLong(i));
     }
 
+    public void setUUID(String prefix, UUID uuid) { a(prefix, uuid); } // Paper - OBFHELPER
     public void a(String s, UUID uuid) {
         this.setLong(s + "Most", uuid.getMostSignificantBits());
         this.setLong(s + "Least", uuid.getLeastSignificantBits());
     }
 
+
+    @Nullable public UUID getUUID(String prefix) { return a(prefix); } // Paper - OBFHELPER
+    @Nullable
     public UUID a(String s) {
         return new UUID(this.getLong(s + "Most"), this.getLong(s + "Least"));
     }
diff --git a/src/main/java/net/minecraft/server/NetworkManager.java b/src/main/java/net/minecraft/server/NetworkManager.java
index 1c32e6ef47..d4c59baf8f 100644
--- a/src/main/java/net/minecraft/server/NetworkManager.java
+++ b/src/main/java/net/minecraft/server/NetworkManager.java
@@ -43,7 +43,7 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
         return new DefaultEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Local Client IO #%d").setDaemon(true).build());
     });
     private final EnumProtocolDirection h;
-    private final Queue<NetworkManager.QueuedPacket> packetQueue = Queues.newConcurrentLinkedQueue();
+    private final Queue<NetworkManager.QueuedPacket> packetQueue = Queues.newConcurrentLinkedQueue();  private final Queue<NetworkManager.QueuedPacket> getPacketQueue() { return this.packetQueue; } // Paper - OBFHELPER
     private final ReentrantReadWriteLock j = new ReentrantReadWriteLock();
     public Channel channel;
     public SocketAddress socketAddress;
@@ -167,6 +167,7 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
 
     }
 
+    private void dispatchPacket(Packet<?> packet, @Nullable GenericFutureListener<? extends Future<? super Void>> genericFutureListener) { this.b(packet, genericFutureListener); } // Paper - OBFHELPER
     private void b(Packet<?> packet, @Nullable GenericFutureListener<? extends Future<? super Void>> genericfuturelistener) {
         EnumProtocol enumprotocol = EnumProtocol.a(packet);
         EnumProtocol enumprotocol1 = (EnumProtocol) this.channel.attr(NetworkManager.c).get();
@@ -207,6 +208,7 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
 
     }
 
+    private void sendPacketQueue() { this.o(); } // Paper - OBFHELPER
     private void o() {
         if (this.channel != null && this.channel.isOpen()) {
             this.j.readLock().lock();
@@ -337,9 +339,9 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
 
     static class QueuedPacket {
 
-        private final Packet<?> a;
+        private final Packet<?> a; private final Packet<?> getPacket() { return this.a; } // Paper - OBFHELPER
         @Nullable
-        private final GenericFutureListener<? extends Future<? super Void>> b;
+        private final GenericFutureListener<? extends Future<? super Void>> b; private final GenericFutureListener<? extends Future<? super Void>> getGenericFutureListener() { return this.b; } // Paper - OBFHELPER
 
         public QueuedPacket(Packet<?> packet, @Nullable GenericFutureListener<? extends Future<? super Void>> genericfuturelistener) {
             this.a = packet;
diff --git a/src/main/java/net/minecraft/server/PacketDataSerializer.java b/src/main/java/net/minecraft/server/PacketDataSerializer.java
index db2fe836c2..0d67676f7d 100644
--- a/src/main/java/net/minecraft/server/PacketDataSerializer.java
+++ b/src/main/java/net/minecraft/server/PacketDataSerializer.java
@@ -33,6 +33,7 @@ public class PacketDataSerializer extends ByteBuf {
         this.a = bytebuf;
     }
 
+    public static int countBytes(int i) { return PacketDataSerializer.a(i); } // Paper - OBFHELPER
     public static int a(int i) {
         for (int j = 1; j < 5; ++j) {
             if ((i & -1 << j * 7) == 0) {
diff --git a/src/main/java/net/minecraft/server/PacketEncoder.java b/src/main/java/net/minecraft/server/PacketEncoder.java
index 90223deae3..63c4dbd327 100644
--- a/src/main/java/net/minecraft/server/PacketEncoder.java
+++ b/src/main/java/net/minecraft/server/PacketEncoder.java
@@ -42,6 +42,7 @@ public class PacketEncoder extends MessageToByteEncoder<Packet<?>> {
                     packet.b(packetdataserializer);
                 } catch (Throwable throwable) {
                     PacketEncoder.LOGGER.error(throwable);
+                    throwable.printStackTrace(); // Paper - WHAT WAS IT? WHO DID THIS TO YOU? WHAT DID YOU SEE?
                     if (packet.a()) {
                         throw new SkipEncodeException(throwable);
                     } else {
diff --git a/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java b/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java
index 4e20cfba41..363ab5da12 100644
--- a/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java
+++ b/src/main/java/net/minecraft/server/PacketPlayOutMapChunk.java
@@ -14,7 +14,7 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
     private int b;
     private int c;
     private NBTTagCompound d;
-    private byte[] e;
+    private byte[] e; private byte[] getData() { return this.e; } // Paper - OBFHELPER
     private List<NBTTagCompound> f;
     private boolean g;
 
@@ -114,6 +114,7 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
         return bytebuf;
     }
 
+    public int writeChunk(PacketDataSerializer packetDataSerializer, Chunk chunk, int chunkSectionSelector) { return this.a(packetDataSerializer, chunk, chunkSectionSelector); } // Paper - OBFHELPER
     public int a(PacketDataSerializer packetdataserializer, Chunk chunk, int i) {
         int j = 0;
         ChunkSection[] achunksection = chunk.getSections();
diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java
index 67c8a5efc7..78dca8932f 100644
--- a/src/main/java/net/minecraft/server/PlayerChunk.java
+++ b/src/main/java/net/minecraft/server/PlayerChunk.java
@@ -63,6 +63,14 @@ public class PlayerChunk {
         return either == null ? null : (Chunk) either.left().orElse(null);
     }
     // CraftBukkit end
+    // Paper start - "real" get full chunk immediately
+    public Chunk getFullChunkIfCached() {
+        // Note: Copied from above without ticket level check
+        CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> statusFuture = this.getStatusFutureUnchecked(ChunkStatus.FULL);
+        Either<IChunkAccess, PlayerChunk.Failure> either = (Either<IChunkAccess, PlayerChunk.Failure>) statusFuture.getNow(null);
+        return either == null ? null : (Chunk) either.left().orElse(null);
+    }
+    // Paper end
 
     public CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> getStatusFutureUnchecked(ChunkStatus chunkstatus) {
         CompletableFuture<Either<IChunkAccess, PlayerChunk.Failure>> completablefuture = (CompletableFuture) this.statusFutures.get(chunkstatus.c());
diff --git a/src/main/java/net/minecraft/server/PlayerConnection.java b/src/main/java/net/minecraft/server/PlayerConnection.java
index 58f9946455..ae194b981f 100644
--- a/src/main/java/net/minecraft/server/PlayerConnection.java
+++ b/src/main/java/net/minecraft/server/PlayerConnection.java
@@ -67,9 +67,9 @@ public class PlayerConnection implements PacketListenerPlayIn {
     private final MinecraftServer minecraftServer;
     public EntityPlayer player;
     private int e;
-    private long lastKeepAlive;
-    private boolean awaitingKeepAlive;
-    private long h;
+    private long lastKeepAlive; private void setLastPing(long lastPing) { this.lastKeepAlive = lastPing;}; private long getLastPing() { return this.lastKeepAlive;}; // Paper - OBFHELPER
+    private boolean awaitingKeepAlive; private void setPendingPing(boolean isPending) { this.awaitingKeepAlive = isPending;}; private boolean isPendingPing() { return this.awaitingKeepAlive;}; // Paper - OBFHELPER
+    private long h; private void setKeepAliveID(long keepAliveID) { this.h = keepAliveID;}; private long getKeepAliveID() {return this.h; };  // Paper - OBFHELPER
     // CraftBukkit start - multithreaded fields
     private volatile int chatThrottle;
     private static final AtomicIntegerFieldUpdater chatSpamField = AtomicIntegerFieldUpdater.newUpdater(PlayerConnection.class, "chatThrottle");
diff --git a/src/main/java/net/minecraft/server/PlayerInventory.java b/src/main/java/net/minecraft/server/PlayerInventory.java
index 2fe3d5d4c2..4aee712a6f 100644
--- a/src/main/java/net/minecraft/server/PlayerInventory.java
+++ b/src/main/java/net/minecraft/server/PlayerInventory.java
@@ -17,7 +17,7 @@ public class PlayerInventory implements IInventory, INamableTileEntity {
     public final NonNullList<ItemStack> items;
     public final NonNullList<ItemStack> armor;
     public final NonNullList<ItemStack> extraSlots;
-    private final List<NonNullList<ItemStack>> f;
+    private final List<NonNullList<ItemStack>> f;List<NonNullList<ItemStack>> getComponents() { return f; } // Paper - OBFHELPER
     public int itemInHandIndex;
     public final EntityHuman player;
     private ItemStack carried;
diff --git a/src/main/java/net/minecraft/server/PotionUtil.java b/src/main/java/net/minecraft/server/PotionUtil.java
index b3824898da..bf4172be52 100644
--- a/src/main/java/net/minecraft/server/PotionUtil.java
+++ b/src/main/java/net/minecraft/server/PotionUtil.java
@@ -110,6 +110,7 @@ public class PotionUtil {
         return nbttagcompound == null ? Potions.EMPTY : PotionRegistry.a(nbttagcompound.getString("Potion"));
     }
 
+    public static ItemStack addPotionToItemStack(ItemStack itemstack, PotionRegistry potionregistry) { return a(itemstack, potionregistry); } // Paper - OBFHELPER
     public static ItemStack a(ItemStack itemstack, PotionRegistry potionregistry) {
         MinecraftKey minecraftkey = IRegistry.POTION.getKey(potionregistry);
 
diff --git a/src/main/java/net/minecraft/server/ProtoChunk.java b/src/main/java/net/minecraft/server/ProtoChunk.java
index 2158e239e0..6bdd7dda04 100644
--- a/src/main/java/net/minecraft/server/ProtoChunk.java
+++ b/src/main/java/net/minecraft/server/ProtoChunk.java
@@ -79,6 +79,18 @@ public class ProtoChunk implements IChunkAccess {
 
     }
 
+    // Paper start - If loaded util
+    @Override
+    public Fluid getFluidIfLoaded(BlockPosition blockposition) {
+        return this.getFluid(blockposition);
+    }
+
+    @Override
+    public IBlockData getTypeIfLoaded(BlockPosition blockposition) {
+        return this.getType(blockposition);
+    }
+    // Paper end
+
     @Override
     public IBlockData getType(BlockPosition blockposition) {
         int i = blockposition.getY();
diff --git a/src/main/java/net/minecraft/server/RegionLimitedWorldAccess.java b/src/main/java/net/minecraft/server/RegionLimitedWorldAccess.java
index 33db1403fe..aff3591ec8 100644
--- a/src/main/java/net/minecraft/server/RegionLimitedWorldAccess.java
+++ b/src/main/java/net/minecraft/server/RegionLimitedWorldAccess.java
@@ -106,6 +106,26 @@ public class RegionLimitedWorldAccess implements GeneratorAccess {
         return i >= ichunkaccess.getPos().x && i <= ichunkaccess1.getPos().x && j >= ichunkaccess.getPos().z && j <= ichunkaccess1.getPos().z;
     }
 
+    // Paper start - if loaded util
+    @Nullable
+    @Override
+    public IChunkAccess getChunkIfLoadedImmediately(int x, int z) {
+        return this.getChunkAt(x, z, ChunkStatus.FULL, false);
+    }
+
+    @Override
+    public IBlockData getTypeIfLoaded(BlockPosition blockposition) {
+        IChunkAccess chunk = this.getChunkIfLoadedImmediately(blockposition.getX() >> 4, blockposition.getZ() >> 4);
+        return chunk == null ? null : chunk.getType(blockposition);
+    }
+
+    @Override
+    public Fluid getFluidIfLoaded(BlockPosition blockposition) {
+        IChunkAccess chunk = this.getChunkIfLoadedImmediately(blockposition.getX() >> 4, blockposition.getZ() >> 4);
+        return chunk == null ? null : chunk.getFluid(blockposition);
+    }
+    // Paper end
+
     @Override
     public IBlockData getType(BlockPosition blockposition) {
         return this.getChunkAt(blockposition.getX() >> 4, blockposition.getZ() >> 4).getType(blockposition);
diff --git a/src/main/java/net/minecraft/server/RegistryBlockID.java b/src/main/java/net/minecraft/server/RegistryBlockID.java
index 4efcb8b595..60948afa4e 100644
--- a/src/main/java/net/minecraft/server/RegistryBlockID.java
+++ b/src/main/java/net/minecraft/server/RegistryBlockID.java
@@ -57,6 +57,7 @@ public class RegistryBlockID<T> implements Registry<T> {
         return Iterators.filter(this.c.iterator(), Predicates.notNull());
     }
 
+    public int size() { return this.a(); } // Paper - OBFHELPER
     public int a() {
         return this.b.size();
     }
diff --git a/src/main/java/net/minecraft/server/SystemUtils.java b/src/main/java/net/minecraft/server/SystemUtils.java
index b3799ab564..5fae5a1233 100644
--- a/src/main/java/net/minecraft/server/SystemUtils.java
+++ b/src/main/java/net/minecraft/server/SystemUtils.java
@@ -58,7 +58,7 @@ public class SystemUtils {
     }
 
     public static long getMonotonicNanos() {
-        return SystemUtils.a.getAsLong();
+        return System.nanoTime(); // Paper
     }
 
     public static long getTimeMillis() {
diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java
index 91e50ccea1..a2b1aba49f 100644
--- a/src/main/java/net/minecraft/server/World.java
+++ b/src/main/java/net/minecraft/server/World.java
@@ -210,6 +210,40 @@ public abstract class World implements IIBlockAccess, GeneratorAccess, AutoClose
         return (Chunk) this.getChunkAt(i, j, ChunkStatus.FULL);
     }
 
+    // Paper start - if loaded
+    @Nullable
+    @Override
+    public IChunkAccess getChunkIfLoadedImmediately(int x, int z) {
+        return ((ChunkProviderServer)this.chunkProvider).getChunkAtIfLoadedImmediately(x, z);
+    }
+
+    @Override
+    public IBlockData getTypeIfLoaded(BlockPosition blockposition) {
+        // CraftBukkit start - tree generation
+        if (captureTreeGeneration) {
+            for (CraftBlockState previous : capturedBlockStates) {
+                if (previous.getX() == blockposition.getX() && previous.getY() == blockposition.getY() && previous.getZ() == blockposition.getZ()) {
+                    return previous.getHandle();
+                }
+            }
+        }
+        // CraftBukkit end
+        if (!isValidLocation(blockposition)) {
+            return Blocks.AIR.getBlockData();
+        }
+        IChunkAccess chunk = this.getChunkIfLoadedImmediately(blockposition.getX() >> 4, blockposition.getZ() >> 4);
+
+        return chunk == null ? null : chunk.getType(blockposition);
+    }
+
+    @Override
+    public Fluid getFluidIfLoaded(BlockPosition blockposition) {
+        IChunkAccess chunk = this.getChunkIfLoadedImmediately(blockposition.getX() >> 4, blockposition.getZ() >> 4);
+
+        return chunk == null ? null : chunk.getFluid(blockposition);
+    }
+    // Paper end
+
     @Override
     public IChunkAccess getChunkAt(int i, int j, ChunkStatus chunkstatus, boolean flag) {
         IChunkAccess ichunkaccess = this.chunkProvider.getChunkAt(i, j, chunkstatus, flag);
@@ -371,8 +405,9 @@ public abstract class World implements IIBlockAccess, GeneratorAccess, AutoClose
 
     public void a(BlockPosition blockposition, IBlockData iblockdata, IBlockData iblockdata1) {}
 
-    @Override
-    public boolean a(BlockPosition blockposition, boolean flag) {
+    public boolean setAir(BlockPosition blockposition) { return this.a(blockposition, false); } // Paper - OBFHELPER
+    public boolean setAir(BlockPosition blockposition, boolean moved) { return this.a(blockposition, moved); } // Paper - OBFHELPER
+    @Override public boolean a(BlockPosition blockposition, boolean flag) { // Paper - OBFHELPER
         Fluid fluid = this.getFluid(blockposition);
 
         return this.setTypeAndData(blockposition, fluid.getBlockData(), 3 | (flag ? 64 : 0));
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
index 2a9e2ab58a..4eb7438f25 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftItemStack.java
@@ -85,6 +85,7 @@ public final class CraftItemStack extends ItemStack {
     }
 
     net.minecraft.server.ItemStack handle;
+    public net.minecraft.server.ItemStack getHandle() { return handle; } // Paper
 
     /**
      * Mirror
diff --git a/src/main/java/org/bukkit/craftbukkit/util/DummyGeneratorAccess.java b/src/main/java/org/bukkit/craftbukkit/util/DummyGeneratorAccess.java
index 755f7f2649..cd910f3dcb 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/DummyGeneratorAccess.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/DummyGeneratorAccess.java
@@ -201,4 +201,22 @@ public class DummyGeneratorAccess implements GeneratorAccess {
     public boolean b(BlockPosition blockposition, boolean flag) {
         throw new UnsupportedOperationException("Not supported yet.");
     }
+
+    // Paper start - if loaded util
+    @javax.annotation.Nullable
+    @Override
+    public IChunkAccess getChunkIfLoadedImmediately(int x, int z) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public IBlockData getTypeIfLoaded(BlockPosition blockposition) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    @Override
+    public Fluid getFluidIfLoaded(BlockPosition blockposition) {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+    // Paper end
 }
-- 
2.21.0

