From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <spottedleaf@spottedleaf.dev>
Date: Mon, 4 May 2020 10:06:24 -0700
Subject: [PATCH] Highly optimise single and multi-AABB VoxelShapes and
 collisions


diff --git a/src/main/java/com/tuinity/tuinity/util/CachedLists.java b/src/main/java/com/tuinity/tuinity/util/CachedLists.java
new file mode 100644
index 0000000000000000000000000000000000000000..866f6e701c6a10a3ac1e58d12bba966c478d2e77
--- /dev/null
+++ b/src/main/java/com/tuinity/tuinity/util/CachedLists.java
@@ -0,0 +1,53 @@
+package com.tuinity.tuinity.util;
+
+import net.minecraft.world.phys.AxisAlignedBB;
+import net.minecraft.world.entity.Entity;
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.util.UnsafeList;
+import java.util.List;
+
+public class CachedLists {
+
+    static final UnsafeList<AxisAlignedBB> TEMP_COLLISION_LIST = new UnsafeList<>(1024);
+    static boolean tempCollisionListInUse;
+
+    public static UnsafeList<AxisAlignedBB> getTempCollisionList() {
+        if (!Bukkit.isPrimaryThread() || tempCollisionListInUse) {
+            return new UnsafeList<>(16);
+        }
+        tempCollisionListInUse = true;
+        return TEMP_COLLISION_LIST;
+    }
+
+    public static void returnTempCollisionList(List<AxisAlignedBB> list) {
+        if (list != TEMP_COLLISION_LIST) {
+            return;
+        }
+        ((UnsafeList)list).setSize(0);
+        tempCollisionListInUse = false;
+    }
+
+    static final UnsafeList<Entity> TEMP_GET_ENTITIES_LIST = new UnsafeList<>(1024);
+    static boolean tempGetEntitiesListInUse;
+
+    public static UnsafeList<Entity> getTempGetEntitiesList() {
+        if (!Bukkit.isPrimaryThread() || tempGetEntitiesListInUse) {
+            return new UnsafeList<>(16);
+        }
+        tempGetEntitiesListInUse = true;
+        return TEMP_GET_ENTITIES_LIST;
+    }
+
+    public static void returnTempGetEntitiesList(List<Entity> list) {
+        if (list != TEMP_GET_ENTITIES_LIST) {
+            return;
+        }
+        ((UnsafeList)list).setSize(0);
+        tempGetEntitiesListInUse = false;
+    }
+
+    public static void reset() {
+        TEMP_COLLISION_LIST.completeReset();
+        TEMP_GET_ENTITIES_LIST.completeReset();
+    }
+}
diff --git a/src/main/java/com/tuinity/tuinity/voxel/AABBVoxelShape.java b/src/main/java/com/tuinity/tuinity/voxel/AABBVoxelShape.java
new file mode 100644
index 0000000000000000000000000000000000000000..ccc6935f45f91eac17cf09d044060b5bb4c2c935
--- /dev/null
+++ b/src/main/java/com/tuinity/tuinity/voxel/AABBVoxelShape.java
@@ -0,0 +1,165 @@
+package com.tuinity.tuinity.voxel;
+
+import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
+import it.unimi.dsi.fastutil.doubles.DoubleList;
+import net.minecraft.world.phys.AxisAlignedBB;
+import net.minecraft.core.EnumDirection;
+import net.minecraft.world.phys.shapes.VoxelShape;
+import net.minecraft.world.phys.shapes.VoxelShapes;
+import java.util.ArrayList;
+import java.util.List;
+
+public final class AABBVoxelShape extends VoxelShape {
+
+    public final AxisAlignedBB aabb;
+
+    public AABBVoxelShape(AxisAlignedBB aabb) {
+        super(VoxelShapes.getFullUnoptimisedCube().getShape());
+        this.aabb = aabb;
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return this.aabb.isEmpty();
+    }
+
+    @Override
+    public double b(EnumDirection.EnumAxis enumdirection_enumaxis) { // getMin
+        switch (enumdirection_enumaxis.ordinal()) {
+            case 0:
+                return this.aabb.minX;
+            case 1:
+                return this.aabb.minY;
+            case 2:
+                return this.aabb.minZ;
+            default:
+                throw new IllegalStateException("Unknown axis requested");
+        }
+    }
+
+    @Override
+    public double c(EnumDirection.EnumAxis enumdirection_enumaxis) { //getMax
+        switch (enumdirection_enumaxis.ordinal()) {
+            case 0:
+                return this.aabb.maxX;
+            case 1:
+                return this.aabb.maxY;
+            case 2:
+                return this.aabb.maxZ;
+            default:
+                throw new IllegalStateException("Unknown axis requested");
+        }
+    }
+
+    @Override
+    public AxisAlignedBB getBoundingBox() { // rets bounding box enclosing this entire shape
+        return this.aabb;
+    }
+
+    // enum direction axis is from 0 -> 2, so we keep the lower bits for direction axis.
+    @Override
+    protected double a(EnumDirection.EnumAxis enumdirection_enumaxis, int i) { // getPointFromIndex
+        switch (enumdirection_enumaxis.ordinal() | (i << 2)) {
+            case (0 | (0 << 2)):
+                return this.aabb.minX;
+            case (1 | (0 << 2)):
+                return this.aabb.minY;
+            case (2 | (0 << 2)):
+                return this.aabb.minZ;
+            case (0 | (1 << 2)):
+                return this.aabb.maxX;
+            case (1 | (1 << 2)):
+                return this.aabb.maxY;
+            case (2 | (1 << 2)):
+                return this.aabb.maxZ;
+            default:
+                throw new IllegalStateException("Unknown axis requested");
+        }
+    }
+
+    private DoubleList cachedListX;
+    private DoubleList cachedListY;
+    private DoubleList cachedListZ;
+
+    @Override
+    protected DoubleList a(EnumDirection.EnumAxis enumdirection_enumaxis) { // getPoints
+        switch (enumdirection_enumaxis.ordinal()) {
+            case 0:
+                return this.cachedListX == null ? this.cachedListX = DoubleArrayList.wrap(new double[] { this.aabb.minX, this.aabb.maxX }) : this.cachedListX;
+            case 1:
+                return this.cachedListY == null ? this.cachedListY = DoubleArrayList.wrap(new double[] { this.aabb.minY, this.aabb.maxY }) : this.cachedListY;
+            case 2:
+                return this.cachedListZ == null ? this.cachedListZ = DoubleArrayList.wrap(new double[] { this.aabb.minZ, this.aabb.maxZ }) : this.cachedListZ;
+            default:
+                throw new IllegalStateException("Unknown axis requested");
+        }
+    }
+
+    @Override
+    public VoxelShape a(double d0, double d1, double d2) { // createOffset
+        return new AABBVoxelShape(this.aabb.offset(d0, d1, d2));
+    }
+
+    @Override
+    public VoxelShape c() { // simplify
+        return this;
+    }
+
+    @Override
+    public void b(VoxelShapes.a voxelshapes_a) { // forEachAABB
+        voxelshapes_a.consume(this.aabb.minX, this.aabb.minY, this.aabb.minZ, this.aabb.maxX, this.aabb.maxY, this.aabb.maxZ);
+    }
+
+    @Override
+    public List<AxisAlignedBB> d() { // getAABBs
+        List<AxisAlignedBB> ret = new ArrayList<>(1);
+        ret.add(this.aabb);
+        return ret;
+    }
+
+    @Override
+    protected int a(EnumDirection.EnumAxis enumdirection_enumaxis, double d0) { // findPointIndexAfterOffset
+        switch (enumdirection_enumaxis.ordinal()) {
+            case 0:
+                return d0 < this.aabb.maxX ? (d0 < this.aabb.minX ? -1 : 0) : 1;
+            case 1:
+                return d0 < this.aabb.maxY ? (d0 < this.aabb.minY ? -1 : 0) : 1;
+            case 2:
+                return d0 < this.aabb.maxZ ? (d0 < this.aabb.minZ ? -1 : 0) : 1;
+            default:
+                throw new IllegalStateException("Unknown axis requested");
+        }
+    }
+
+    @Override
+    protected boolean b(double d0, double d1, double d2) { // containsPoint
+        return this.aabb.contains(d0, d1, d2);
+    }
+
+    @Override
+    public VoxelShape a(EnumDirection enumdirection) { // unknown
+        return super.a(enumdirection);
+    }
+
+    @Override
+    public double a(EnumDirection.EnumAxis enumdirection_enumaxis, AxisAlignedBB axisalignedbb, double d0) { // collide
+        if (this.aabb.isEmpty() || axisalignedbb.isEmpty()) {
+            return d0;
+        }
+        switch (enumdirection_enumaxis.ordinal()) {
+            case 0:
+                return AxisAlignedBB.collideX(this.aabb, axisalignedbb, d0);
+            case 1:
+                return AxisAlignedBB.collideY(this.aabb, axisalignedbb, d0);
+            case 2:
+                return AxisAlignedBB.collideZ(this.aabb, axisalignedbb, d0);
+            default:
+                throw new IllegalStateException("Unknown axis requested");
+        }
+    }
+
+    @Override
+    public boolean intersects(AxisAlignedBB axisalingedbb) {
+        return this.aabb.voxelShapeIntersect(axisalingedbb);
+    }
+}
diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java
index 006e7076932f6be576a64da09c4d84ca4a15f5dd..e832ea0497b2d6af7556bda7f6728e72c48d80a8 100644
--- a/src/main/java/net/minecraft/server/MCUtil.java
+++ b/src/main/java/net/minecraft/server/MCUtil.java
@@ -55,6 +55,7 @@ import java.util.function.Consumer;
 import java.util.function.Supplier;
 
 public final class MCUtil {
+    public static final double COLLISION_EPSILON = 1.0E-7; // Tuinity - Just in case mojang changes this...
     public static final ThreadPoolExecutor asyncExecutor = new ThreadPoolExecutor(
         0, 2, 60L, TimeUnit.SECONDS,
         new LinkedBlockingQueue<Runnable>(),
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index dae8824a6034cdaaecbedeba5e05c62f4565583f..29677908376e111abead33b42920e07aeb1d4b02 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -1451,6 +1451,8 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant<TickTas
         }
         // Paper end
 
+        com.tuinity.tuinity.util.CachedLists.reset(); // Tuinity
+
         // Paper start
         long endTime = System.nanoTime();
         long remaining = (TICK_TIME - (endTime - lastTick)) - catchupTime;
diff --git a/src/main/java/net/minecraft/server/level/EntityPlayer.java b/src/main/java/net/minecraft/server/level/EntityPlayer.java
index 37c9b5fd712e30a9a0faccc840f738f4b2cfc723..f7e9a151ffbbb64cb8f8bc1d37516d7979277b17 100644
--- a/src/main/java/net/minecraft/server/level/EntityPlayer.java
+++ b/src/main/java/net/minecraft/server/level/EntityPlayer.java
@@ -370,7 +370,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
 
                 if (blockposition1 != null) {
                     this.setPositionRotation(blockposition1, 0.0F, 0.0F);
-                    if (worldserver.getCubes(this)) {
+                    if (!worldserver.collidesWithAnyBlockOrWorldBorder(this, this.getBoundingBox(), true, false)) { // Tuinity - make sure this is loaded
                         break;
                     }
                 }
@@ -378,7 +378,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
         } else {
             this.setPositionRotation(blockposition, 0.0F, 0.0F);
 
-            while (!worldserver.getCubes(this) && this.locY() < 255.0D) {
+            while (worldserver.collidesWithAnyBlockOrWorldBorder(this, this.getBoundingBox(), true, false) && this.locY() < 255.0D) { // Tuinity - make sure this is loaded
                 this.setPosition(this.locX(), this.locY() + 1.0D, this.locZ());
             }
         }
diff --git a/src/main/java/net/minecraft/server/level/WorldServer.java b/src/main/java/net/minecraft/server/level/WorldServer.java
index 1658c03b70b5984c87d069276efcc7b8c8878431..86d1ce955c88530c043128a043fe75534077c665 100644
--- a/src/main/java/net/minecraft/server/level/WorldServer.java
+++ b/src/main/java/net/minecraft/server/level/WorldServer.java
@@ -474,6 +474,251 @@ public class WorldServer extends World implements GeneratorAccessSeed {
         this.asyncChunkTaskManager = new com.destroystokyo.paper.io.chunk.ChunkTaskManager(this); // Paper
     }
 
+    // Tuinity start - optimise collision
+    public boolean collidesWithAnyBlockOrWorldBorder(@Nullable Entity entity, AxisAlignedBB axisalignedbb, boolean loadChunks,
+                                                     boolean collidesWithUnloaded) {
+        return this.getCollisionsForBlocksOrWorldBorder(entity, axisalignedbb, null, loadChunks, collidesWithUnloaded, true, null);
+    }
+
+    public boolean collidesWithAnyBlockOrWorldBorder(@Nullable Entity entity, AxisAlignedBB axisalignedbb,
+                                                     boolean loadChunks, boolean collidesWithUnloaded,
+                                                     java.util.function.BiPredicate<IBlockData, BlockPosition> predicate) {
+        return this.getCollisionsForBlocksOrWorldBorder(entity, axisalignedbb, null, loadChunks, collidesWithUnloaded, true, predicate);
+    }
+
+    public final boolean hardCollidesWithAnyEntities(@Nullable Entity entity, AxisAlignedBB axisalignedbb, @Nullable Predicate<Entity> predicate) {
+        if (axisalignedbb.isEmpty()) {
+            return false;
+        }
+
+        // to comply with vanilla intersection rules, expand by -epsilon so we only get stuff we definitely collide with.
+        // Vanilla for hard collisions has this backwards, and they expand by +epsilon but this causes terrible problems
+        // specifically with boat collisions.
+        axisalignedbb = axisalignedbb.grow(-MCUtil.COLLISION_EPSILON, -MCUtil.COLLISION_EPSILON, -MCUtil.COLLISION_EPSILON);
+        List<Entity> entities = com.tuinity.tuinity.util.CachedLists.getTempGetEntitiesList();
+        try {
+            if (entity != null && entity.hardCollides()) {
+                this.getEntities(entity, axisalignedbb, predicate, entities);
+            } else {
+                this.getHardCollidingEntities(entity, axisalignedbb, predicate, entities);
+            }
+
+            for (int i = 0, len = entities.size(); i < len; ++i) {
+                Entity otherEntity = entities.get(i);
+
+                if ((entity == null || otherEntity.collisionBoxIsHard()) || entity.hardCollidesWith(otherEntity)) {
+                    return true;
+                }
+            }
+
+            return false;
+        } finally {
+            com.tuinity.tuinity.util.CachedLists.returnTempGetEntitiesList(entities);
+        }
+    }
+
+    public final boolean hasAnyCollisions(@Nullable Entity entity, AxisAlignedBB axisalignedbb) {
+        return this.hasAnyCollisions(entity, axisalignedbb, true);
+    }
+
+    public final boolean hasAnyCollisions(@Nullable Entity entity, AxisAlignedBB axisalignedbb, boolean loadChunks) {
+        return this.collidesWithAnyBlockOrWorldBorder(entity, axisalignedbb, loadChunks, true)
+                || this.hardCollidesWithAnyEntities(entity, axisalignedbb, null);
+    }
+
+    // returns whether any collisions were detected
+    public boolean getCollisionsForBlocksOrWorldBorder(@Nullable Entity entity, AxisAlignedBB axisalignedbb, List<AxisAlignedBB> list,
+                                                    boolean loadChunks, boolean collidesWithUnloaded, boolean checkOnly,
+                                                    java.util.function.BiPredicate<IBlockData, BlockPosition> predicate) {
+        boolean ret = false;
+
+        if (entity != null) {
+            if (this.getWorldBorder().isAlmostCollidingOnBorder(axisalignedbb)) {
+                if (checkOnly) {
+                    return true;
+                } else {
+                    VoxelShapes.addBoxesTo(this.getWorldBorder().getCollisionShape(), list);
+                    ret = true;
+                }
+            }
+        }
+
+        int minBlockX = MathHelper.floor(axisalignedbb.minX - MCUtil.COLLISION_EPSILON) - 1;
+        int maxBlockX = MathHelper.floor(axisalignedbb.maxX + MCUtil.COLLISION_EPSILON) + 1;
+
+        int minBlockY = MathHelper.floor(axisalignedbb.minY - MCUtil.COLLISION_EPSILON) - 1;
+        int maxBlockY = MathHelper.floor(axisalignedbb.maxY + MCUtil.COLLISION_EPSILON) + 1;
+
+        int minBlockZ = MathHelper.floor(axisalignedbb.minZ - MCUtil.COLLISION_EPSILON) - 1;
+        int maxBlockZ = MathHelper.floor(axisalignedbb.maxZ + MCUtil.COLLISION_EPSILON) + 1;
+
+
+        BlockPosition.MutableBlockPosition mutablePos = new BlockPosition.MutableBlockPosition();
+        net.minecraft.world.phys.shapes.VoxelShapeCollision collisionShape = null;
+
+        // special cases:
+        if (minBlockY > 255 || maxBlockY < 0) {
+            // no point in checking
+            return ret;
+        }
+
+        int minYIterate = Math.max(0, minBlockY);
+        int maxYIterate = Math.min(255, maxBlockY);
+
+        int minChunkX = minBlockX >> 4;
+        int maxChunkX = maxBlockX >> 4;
+
+        int minChunkZ = minBlockZ >> 4;
+        int maxChunkZ = maxBlockZ >> 4;
+
+        ChunkProviderServer chunkProvider = (ChunkProviderServer)this.chunkProvider;
+        // TODO special case single chunk?
+
+        for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
+            int minZ = currChunkZ == minChunkZ ? minBlockZ & 15 : 0; // coordinate in chunk
+            int maxZ = currChunkZ == maxChunkZ ? maxBlockZ & 15 : 15; // coordinate in chunk
+
+            for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) {
+                int minX = currChunkX == minChunkX ? minBlockX & 15 : 0; // coordinate in chunk
+                int maxX = currChunkX == maxChunkX ? maxBlockX & 15 : 15; // coordinate in chunk
+
+                int chunkXGlobalPos = currChunkX << 4;
+                int chunkZGlobalPos = currChunkZ << 4;
+                Chunk chunk = loadChunks ? chunkProvider.getChunkAt(currChunkX, currChunkZ, true) : chunkProvider.getChunkAtIfLoadedImmediately(currChunkX, currChunkZ);
+
+                if (chunk == null) {
+                    if (collidesWithUnloaded) {
+                        if (checkOnly) {
+                            return true;
+                        } else {
+                            list.add(AxisAlignedBB.getBoxForChunk(currChunkX, currChunkZ));
+                            ret = true;
+                        }
+                    }
+                    continue;
+                }
+
+                ChunkSection[] sections = chunk.getSections();
+
+                // bound y
+
+                for (int currY = minYIterate; currY <= maxYIterate; ++currY) {
+                    ChunkSection section = sections[currY >>> 4];
+                    if (section == null || section.isFullOfAir()) {
+                        // empty
+                        // skip to next section
+                        currY = (currY & ~(15)) + 15; // increment by 15: iterator loop increments by the extra one
+                        continue;
+                    }
+
+                    net.minecraft.world.level.chunk.DataPaletteBlock<IBlockData> blocks = section.blockIds;
+
+                    for (int currZ = minZ; currZ <= maxZ; ++currZ) {
+                        for (int currX = minX; currX <= maxX; ++currX) {
+                            int localBlockIndex = (currX) | (currZ << 4) | ((currY & 15) << 8);
+                            int blockX = currX | chunkXGlobalPos;
+                            int blockY = currY;
+                            int blockZ = currZ | chunkZGlobalPos;
+
+                            int edgeCount = ((blockX == minBlockX || blockX == maxBlockX) ? 1 : 0) +
+                                    ((blockY == minBlockY || blockY == maxBlockY) ? 1 : 0) +
+                                    ((blockZ == minBlockZ || blockZ == maxBlockZ) ? 1 : 0);
+                            if (edgeCount == 3) {
+                                continue;
+                            }
+
+                            IBlockData blockData = blocks.rawGet(localBlockIndex);
+
+                            if ((edgeCount != 1 || blockData.shapeExceedsCube()) && (edgeCount != 2 || blockData.getBlock() == Blocks.MOVING_PISTON)) {
+                                mutablePos.setValues(blockX, blockY, blockZ);
+                                if (collisionShape == null) {
+                                    collisionShape = entity == null ? net.minecraft.world.phys.shapes.VoxelShapeCollision.a() : net.minecraft.world.phys.shapes.VoxelShapeCollision.a(entity);
+                                }
+                                VoxelShape voxelshape2 = blockData.getCollisionShape(this, mutablePos, collisionShape);
+                                if (voxelshape2 != VoxelShapes.getEmptyShape()) {
+                                    VoxelShape voxelshape3 = voxelshape2.offset((double)blockX, (double)blockY, (double)blockZ);
+
+                                    if (predicate != null && !predicate.test(blockData, mutablePos)) {
+                                        continue;
+                                    }
+
+                                    if (checkOnly) {
+                                        if (voxelshape3.intersects(axisalignedbb)) {
+                                            return true;
+                                        }
+                                    } else {
+                                        ret |= VoxelShapes.addBoxesToIfIntersects(voxelshape3, axisalignedbb, list);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        return ret;
+    }
+
+    public final void getEntityHardCollisions(@Nullable Entity entity, AxisAlignedBB axisalignedbb, @Nullable Predicate<Entity> predicate, List<AxisAlignedBB> list) {
+        if (axisalignedbb.isEmpty()) {
+            return;
+        }
+
+        // to comply with vanilla intersection rules, expand by -epsilon so we only get stuff we definitely collide with.
+        // Vanilla for hard collisions has this backwards, and they expand by +epsilon but this causes terrible problems
+        // specifically with boat collisions.
+        axisalignedbb = axisalignedbb.grow(-MCUtil.COLLISION_EPSILON, -MCUtil.COLLISION_EPSILON, -MCUtil.COLLISION_EPSILON);
+        List<Entity> entities = com.tuinity.tuinity.util.CachedLists.getTempGetEntitiesList();
+        try {
+            if (entity != null && entity.hardCollides()) {
+                this.getEntities(entity, axisalignedbb, predicate, entities);
+            } else {
+                this.getHardCollidingEntities(entity, axisalignedbb, predicate, entities);
+            }
+
+            for (int i = 0, len = entities.size(); i < len; ++i) {
+                Entity otherEntity = entities.get(i);
+
+                if ((entity == null || otherEntity.collisionBoxIsHard()) || entity.hardCollidesWith(otherEntity)) {
+                    if (!otherEntity.getBoundingBox().isEmpty()) {
+                        list.add(otherEntity.getBoundingBox());
+                    }
+                }
+            }
+        } finally {
+            com.tuinity.tuinity.util.CachedLists.returnTempGetEntitiesList(entities);
+        }
+    }
+
+    public final void getCollisions(@Nullable Entity entity, AxisAlignedBB axisalignedbb, List<AxisAlignedBB> list, boolean loadChunks) {
+        this.getCollisionsForBlocksOrWorldBorder(entity, axisalignedbb, list, loadChunks, true, false, null);
+        this.getEntityHardCollisions(entity, axisalignedbb, null, list);
+    }
+
+    @Override
+    public boolean getCubes(AxisAlignedBB axisalignedbb) {
+        return !this.hasAnyCollisions(null, axisalignedbb);
+    }
+
+    @Override
+    public boolean getCubes(Entity entity) {
+        return !this.hasAnyCollisions(entity, entity.getBoundingBox());
+    }
+
+    @Override
+    public boolean getCubes(@Nullable Entity entity, AxisAlignedBB axisalignedbb) {
+        if (entity instanceof net.minecraft.world.entity.decoration.EntityArmorStand && !entity.world.paperConfig.armorStandEntityLookups) return false;
+        return !this.hasAnyCollisions(entity, axisalignedbb);
+    }
+
+    @Override
+    public boolean getCubes(@Nullable Entity entity, AxisAlignedBB axisalignedbb, Predicate<Entity> predicate) {
+        if (entity instanceof net.minecraft.world.entity.decoration.EntityArmorStand && !entity.world.paperConfig.armorStandEntityLookups) return false;
+        return !this.collidesWithAnyBlockOrWorldBorder(entity, axisalignedbb, false, false) && !this.hardCollidesWithAnyEntities(entity, axisalignedbb, predicate);
+    }
+    // Tuinity end - optimise collision
+
     // CraftBukkit start
     @Override
     public TileEntity getTileEntity(BlockPosition pos, boolean validate) {
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
index 2df8e914f66176e22aeddf8b94a83af5ea921d88..306d5ab69f8c4ca1c69f081e17cf9a4a2bee0446 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -943,7 +943,7 @@ public abstract class PlayerList {
 
         worldserver1.getChunkProvider().addTicket(TicketType.POST_TELEPORT, new ChunkCoordIntPair(location.getBlockX() >> 4, location.getBlockZ() >> 4), 1, entityplayer.getId()); // Paper
         entityplayer1.forceCheckHighPriority(); // Player
-        while (avoidSuffocation && !worldserver1.getCubes(entityplayer1) && entityplayer1.locY() < 256.0D) {
+        while (avoidSuffocation && worldserver1.collidesWithAnyBlockOrWorldBorder(entityplayer1, entityplayer1.getBoundingBox(), true, false) && entityplayer1.locY() < 256.0D) { // Tuinity - make sure this is loaded
             entityplayer1.setPosition(entityplayer1.locX(), entityplayer1.locY() + 1.0D, entityplayer1.locZ());
         }
         // CraftBukkit start
diff --git a/src/main/java/net/minecraft/world/entity/Entity.java b/src/main/java/net/minecraft/world/entity/Entity.java
index b32b0ebf283a8face528ded0b845270953f536f1..c5ce10a55de6f611ec65f32382b0082f86e73fe3 100644
--- a/src/main/java/net/minecraft/world/entity/Entity.java
+++ b/src/main/java/net/minecraft/world/entity/Entity.java
@@ -228,7 +228,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, ne
     public double D;
     public double E;
     public double F;
-    public float G;
+    public float G; public final float getStepHeight() { return this.G; } // Tuinity - OBFHELPER
     public boolean noclip;
     public float I;
     protected final Random random;
@@ -786,7 +786,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, ne
             // Paper end
 
             vec3d = this.a(vec3d, enummovetype);
-            Vec3D vec3d1 = this.g(vec3d);
+            Vec3D vec3d1 = this.performCollision(vec3d); // Tuinity - optimise collisions
 
             if (vec3d1.g() > 1.0E-7D) {
                 this.a(this.getBoundingBox().c(vec3d1));
@@ -877,7 +877,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, ne
             }
 
             try {
-                this.checkBlockCollisions();
+                this.checkBlockCollisions(this.fireTicks <= 0); // Tuinity - move fire checking into method here
             } catch (Throwable throwable) {
                 CrashReport crashreport = CrashReport.a(throwable, "Checking entity block collision");
                 CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Entity being checked for collision");
@@ -889,11 +889,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, ne
             float f2 = this.getBlockSpeedFactor();
 
             this.setMot(this.getMot().d((double) f2, 1.0D, (double) f2));
-            if (this.world.c(this.getBoundingBox().shrink(0.001D)).noneMatch((iblockdata1) -> {
-                return iblockdata1.a((Tag) TagsBlock.FIRE) || iblockdata1.a(Blocks.LAVA);
-            }) && this.fireTicks <= 0) {
-                this.setFireTicks(-this.getMaxFireTicks());
-            }
+            // Tuinity - move into checkBlockCollisions
 
             if (this.aG() && this.isBurning()) {
                 this.playSound(SoundEffects.ENTITY_GENERIC_EXTINGUISH_FIRE, 0.7F, 1.6F + (this.random.nextFloat() - this.random.nextFloat()) * 0.4F);
@@ -989,6 +985,137 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, ne
         return d0;
     }
 
+    // Tuinity start - optimise entity movement
+    private static double performCollisionsX(AxisAlignedBB currentBoundingBox, double value, List<AxisAlignedBB> potentialCollisions) {
+        for (int i = 0, len = potentialCollisions.size(); i < len; ++i) {
+            AxisAlignedBB target = potentialCollisions.get(i);
+            value = AxisAlignedBB.collideX(target, currentBoundingBox, value);
+        }
+
+        return value;
+    }
+
+    private static double performCollisionsY(AxisAlignedBB currentBoundingBox, double value, List<AxisAlignedBB> potentialCollisions) {
+        for (int i = 0, len = potentialCollisions.size(); i < len; ++i) {
+            AxisAlignedBB target = potentialCollisions.get(i);
+            value = AxisAlignedBB.collideY(target, currentBoundingBox, value);
+        }
+
+        return value;
+    }
+
+    private static double performCollisionsZ(AxisAlignedBB currentBoundingBox, double value, List<AxisAlignedBB> potentialCollisions) {
+        for (int i = 0, len = potentialCollisions.size(); i < len; ++i) {
+            AxisAlignedBB target = potentialCollisions.get(i);
+            value = AxisAlignedBB.collideZ(target, currentBoundingBox, value);
+        }
+
+        return value;
+    }
+
+    private static Vec3D performCollisions(Vec3D moveVector, AxisAlignedBB axisalignedbb, List<AxisAlignedBB> potentialCollisions) {
+        double x = moveVector.x;
+        double y = moveVector.y;
+        double z = moveVector.z;
+
+        if (y != 0.0) {
+            y = Entity.performCollisionsY(axisalignedbb, y, potentialCollisions);
+            if (y != 0.0) {
+                axisalignedbb = axisalignedbb.offsetY(y);
+            }
+        }
+
+        boolean xSmaller = Math.abs(x) < Math.abs(z);
+
+        if (xSmaller && z != 0.0) {
+            z = Entity.performCollisionsZ(axisalignedbb, z, potentialCollisions);
+            if (z != 0.0) {
+                axisalignedbb = axisalignedbb.offsetZ(z);
+            }
+        }
+
+        if (x != 0.0) {
+            x = Entity.performCollisionsX(axisalignedbb, x, potentialCollisions);
+            if (!xSmaller && x != 0.0) {
+                axisalignedbb = axisalignedbb.offsetX(x);
+            }
+        }
+
+        if (!xSmaller && z != 0.0) {
+            z = Entity.performCollisionsZ(axisalignedbb, z, potentialCollisions);
+        }
+
+        return new Vec3D(x, y, z);
+    }
+
+    Vec3D performCollision(Vec3D moveVector) {
+        if (moveVector.getX() == 0.0 && moveVector.getY() == 0.0 && moveVector.getZ() == 0.0) {
+            return moveVector;
+        }
+
+        WorldServer world = ((WorldServer)this.world);
+        AxisAlignedBB currBoundingBox = this.getBoundingBox();
+
+        List<AxisAlignedBB> potentialCollisions = com.tuinity.tuinity.util.CachedLists.getTempCollisionList();
+        try {
+            AxisAlignedBB collisionBox;
+            double stepHeight = (double)this.getStepHeight();
+
+            if (moveVector.x == 0.0 && moveVector.z == 0.0 && moveVector.y != 0.0) {
+                // a lot of entities just stand still. optimise the search AABB
+                if (moveVector.y > 0.0) {
+                    collisionBox = currBoundingBox.cutUpwards(moveVector.y);
+                } else {
+                    collisionBox = currBoundingBox.cutDownwards(moveVector.y);
+                }
+            } else {
+                if (stepHeight > 0.0 && (this.onGround || (moveVector.y < 0.0)) && (moveVector.x != 0.0 || moveVector.z != 0.0)) {
+                    // don't bother getting the collisions if we don't need them.
+                    if (moveVector.y <= 0.0) {
+                        collisionBox = currBoundingBox.expand(moveVector.x, moveVector.y, moveVector.z).expandUpwards(stepHeight);
+                    } else {
+                        collisionBox = currBoundingBox.expand(moveVector.x, Math.max(stepHeight, moveVector.y), moveVector.z);
+                    }
+                } else {
+                    collisionBox = currBoundingBox.expand(moveVector.x, moveVector.y, moveVector.z);
+                }
+            }
+
+            world.getCollisions(this, collisionBox, potentialCollisions, this instanceof EntityPlayer && !this.world.paperConfig.preventMovingIntoUnloadedChunks);
+            if (world.getWorldBorder().isCollidingWithBorderEdge(collisionBox)) {
+                VoxelShapes.addBoxesToIfIntersects(world.getWorldBorder().getCollisionShape(), collisionBox, potentialCollisions);
+            }
+
+            Vec3D limitedMoveVector = Entity.performCollisions(moveVector, currBoundingBox, potentialCollisions);
+
+            if (stepHeight > 0.0
+                    && (this.onGround || (limitedMoveVector.y != moveVector.y && moveVector.y < 0.0))
+                    && (limitedMoveVector.x != moveVector.x || limitedMoveVector.z != moveVector.z)) {
+                Vec3D vec3d2 = Entity.performCollisions(new Vec3D(moveVector.x, stepHeight, moveVector.z), currBoundingBox, potentialCollisions);
+                Vec3D vec3d3 = Entity.performCollisions(new Vec3D(0.0, stepHeight, 0.0), currBoundingBox.expand(moveVector.x, 0.0, moveVector.z), potentialCollisions);
+
+                if (vec3d3.y < stepHeight) {
+                    Vec3D vec3d4 = Entity.performCollisions(new Vec3D(moveVector.x, 0.0D, moveVector.z), currBoundingBox.offset(vec3d3), potentialCollisions).add(vec3d3);
+
+                    if (Entity.getXZSquared(vec3d4) > Entity.getXZSquared(vec3d2)) {
+                        vec3d2 = vec3d4;
+                    }
+                }
+
+                if (Entity.getXZSquared(vec3d2) > Entity.getXZSquared(limitedMoveVector)) {
+                    return vec3d2.add(Entity.performCollisions(new Vec3D(0.0D, -vec3d2.y + moveVector.y, 0.0D), currBoundingBox.offset(vec3d2), potentialCollisions));
+                }
+
+                return limitedMoveVector;
+            } else {
+                return limitedMoveVector;
+            }
+        } finally {
+            com.tuinity.tuinity.util.CachedLists.returnTempCollisionList(potentialCollisions);
+        }
+    }
+    // Tuinity end - optimise entity movement
+
     private Vec3D g(Vec3D vec3d) {
         AxisAlignedBB axisalignedbb = this.getBoundingBox();
         VoxelShapeCollision voxelshapecollision = VoxelShapeCollision.a(this);
@@ -1024,6 +1151,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, ne
         return vec3d1;
     }
 
+    public static double getXZSquared(Vec3D vec3d) { return Entity.c(vec3d); } // Tuinity - OBFHELPER
     public static double c(Vec3D vec3d) {
         return vec3d.x * vec3d.x + vec3d.z * vec3d.z;
     }
@@ -1136,18 +1264,34 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, ne
     }
 
     protected void checkBlockCollisions() {
+        // Tuinity start
+        this.checkBlockCollisions(false);
+    }
+    protected void checkBlockCollisions(boolean checkFire) {
+        boolean inFire = false;
+        // Tuinity end
         AxisAlignedBB axisalignedbb = this.getBoundingBox();
         BlockPosition blockposition = new BlockPosition(axisalignedbb.minX + 0.001D, axisalignedbb.minY + 0.001D, axisalignedbb.minZ + 0.001D);
         BlockPosition blockposition1 = new BlockPosition(axisalignedbb.maxX - 0.001D, axisalignedbb.maxY - 0.001D, axisalignedbb.maxZ - 0.001D);
         BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition();
 
         if (this.world.areChunksLoadedBetween(blockposition, blockposition1)) {
-            for (int i = blockposition.getX(); i <= blockposition1.getX(); ++i) {
-                for (int j = blockposition.getY(); j <= blockposition1.getY(); ++j) {
-                    for (int k = blockposition.getZ(); k <= blockposition1.getZ(); ++k) {
+            // Tuinity start - reorder iteration to more cache aware
+            for (int j = blockposition.getY(); j <= blockposition1.getY(); ++j) {
+                for (int k = blockposition.getZ(); k <= blockposition1.getZ(); ++k) {
+                    for (int i = blockposition.getX(); i <= blockposition1.getX(); ++i) {
+                        // Tuinity end - reorder iteration to more cache aware
                         blockposition_mutableblockposition.d(i, j, k);
                         IBlockData iblockdata = this.world.getType(blockposition_mutableblockposition);
 
+                        // Tuinity start - move fire checking in here - reuse getType from this method
+                        if (checkFire) {
+                            if (!inFire && (iblockdata.a(TagsBlock.FIRE) || iblockdata.a(Blocks.LAVA))) {
+                                inFire = true;
+                            }
+                        }
+                        // Tuinity end - move fire checking in here - reuse getType from this method
+
                         try {
                             iblockdata.a(this.world, blockposition_mutableblockposition, this);
                             this.a(iblockdata);
@@ -1161,6 +1305,11 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, ne
                     }
                 }
             }
+            // Tuinity start - move fire checking in here - reuse getType from this method
+            if (checkFire & !inFire) {
+                this.setFireTicks(-this.getMaxFireTicks());
+            }
+            // Tuinity end - move fire checking in here - reuse getType from this method
         }
 
     }
@@ -2119,9 +2268,9 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, ne
             float f1 = this.size.width * 0.8F;
             AxisAlignedBB axisalignedbb = AxisAlignedBB.g((double) f1, 0.10000000149011612D, (double) f1).d(this.locX(), this.getHeadY(), this.locZ());
 
-            return this.world.b(this, axisalignedbb, (iblockdata, blockposition) -> {
+            return ((WorldServer)this.world).collidesWithAnyBlockOrWorldBorder(this, axisalignedbb, false, false, (iblockdata, blockposition) -> { // Tuinity - use optimised method
                 return iblockdata.o(this.world, blockposition);
-            }).findAny().isPresent();
+            }); // Tuinity - use optimised method
         }
     }
 
@@ -2129,10 +2278,12 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, ne
         return EnumInteractionResult.PASS;
     }
 
+    public final boolean hardCollidesWith(Entity other) { return this.j(other); } // Tuinity - OBFHELPER
     public boolean j(Entity entity) { // Tuinity - diff on change, hard colliding entities override this
         return entity.aZ() && !this.isSameVehicle(entity);
     }
 
+    public final boolean collisionBoxIsHard() { return this.aZ(); } // Tuinity - OBFHELPER
     public boolean aZ() {// Tuinity - diff on change, hard colliding entities override this
         return false;
     }
diff --git a/src/main/java/net/minecraft/world/level/ChunkCache.java b/src/main/java/net/minecraft/world/level/ChunkCache.java
index 7a760ef0264c9041c38bdfb8fd31333052c26139..b547eb352f90f68cf36ffb82e43ad7acb1892f6a 100644
--- a/src/main/java/net/minecraft/world/level/ChunkCache.java
+++ b/src/main/java/net/minecraft/world/level/ChunkCache.java
@@ -1,5 +1,6 @@
 package net.minecraft.world.level;
 
+import java.util.List;
 import java.util.function.Predicate;
 import java.util.stream.Stream;
 import javax.annotation.Nullable;
@@ -26,6 +27,156 @@ public class ChunkCache implements IBlockAccess, ICollisionAccess {
     protected boolean d;
     protected final World e; protected final World getWorld() { return e; } // Paper - OBFHELPER
 
+    // Tuinity start - optimise pathfinder collision detection
+    @Override
+    public boolean getCubes(Entity entity) {
+        return !this.getCollisionsForBlocksOrWorldBorder(entity, entity.getBoundingBox(), null, true, null);
+    }
+
+    @Override
+    public boolean getCubes(Entity entity, AxisAlignedBB axisalignedbb) {
+        return !this.getCollisionsForBlocksOrWorldBorder(entity, axisalignedbb, null, true, null);
+    }
+
+    @Override
+    public boolean getCubes(@Nullable Entity entity, AxisAlignedBB axisalignedbb, Predicate<Entity> predicate) {
+        return !this.getCollisionsForBlocksOrWorldBorder(entity, axisalignedbb, null, true, null);
+    }
+
+    public boolean getCollisionsForBlocksOrWorldBorder(@Nullable Entity entity, AxisAlignedBB axisalignedbb, List<AxisAlignedBB> list,
+                                                    boolean collidesWithUnloaded,
+                                                    java.util.function.BiPredicate<IBlockData, BlockPosition> predicate) {
+        boolean ret = false;
+        final boolean checkOnly = true;
+
+        if (entity != null) {
+            if (this.getWorldBorder().isAlmostCollidingOnBorder(axisalignedbb)) {
+                if (checkOnly) {
+                    return true;
+                } else {
+                    net.minecraft.world.phys.shapes.VoxelShapes.addBoxesTo(this.getWorldBorder().getCollisionShape(), list);
+                    ret = true;
+                }
+            }
+        }
+
+        int minBlockX = net.minecraft.util.MathHelper.floor(axisalignedbb.minX - net.minecraft.server.MCUtil.COLLISION_EPSILON) - 1;
+        int maxBlockX = net.minecraft.util.MathHelper.floor(axisalignedbb.maxX + net.minecraft.server.MCUtil.COLLISION_EPSILON) + 1;
+
+        int minBlockY = net.minecraft.util.MathHelper.floor(axisalignedbb.minY - net.minecraft.server.MCUtil.COLLISION_EPSILON) - 1;
+        int maxBlockY = net.minecraft.util.MathHelper.floor(axisalignedbb.maxY + net.minecraft.server.MCUtil.COLLISION_EPSILON) + 1;
+
+        int minBlockZ = net.minecraft.util.MathHelper.floor(axisalignedbb.minZ - net.minecraft.server.MCUtil.COLLISION_EPSILON) - 1;
+        int maxBlockZ = net.minecraft.util.MathHelper.floor(axisalignedbb.maxZ + net.minecraft.server.MCUtil.COLLISION_EPSILON) + 1;
+
+
+        BlockPosition.MutableBlockPosition mutablePos = new BlockPosition.MutableBlockPosition();
+        net.minecraft.world.phys.shapes.VoxelShapeCollision collisionShape = null;
+
+        // special cases:
+        if (minBlockY > 255 || maxBlockY < 0) {
+            // no point in checking
+            return ret;
+        }
+
+        int minYIterate = Math.max(0, minBlockY);
+        int maxYIterate = Math.min(255, maxBlockY);
+
+        int minChunkX = minBlockX >> 4;
+        int maxChunkX = maxBlockX >> 4;
+
+        int minChunkZ = minBlockZ >> 4;
+        int maxChunkZ = maxBlockZ >> 4;
+
+        // TODO special case single chunk?
+
+        for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) {
+            int minZ = currChunkZ == minChunkZ ? minBlockZ & 15 : 0; // coordinate in chunk
+            int maxZ = currChunkZ == maxChunkZ ? maxBlockZ & 15 : 15; // coordinate in chunk
+
+            for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) {
+                int minX = currChunkX == minChunkX ? minBlockX & 15 : 0; // coordinate in chunk
+                int maxX = currChunkX == maxChunkX ? maxBlockX & 15 : 15; // coordinate in chunk
+
+                int chunkXGlobalPos = currChunkX << 4;
+                int chunkZGlobalPos = currChunkZ << 4;
+                net.minecraft.world.level.chunk.Chunk chunk = (net.minecraft.world.level.chunk.Chunk)this.getChunkIfLoaded(currChunkX, currChunkZ);
+
+                if (chunk == null) {
+                    if (collidesWithUnloaded) {
+                        if (checkOnly) {
+                            return true;
+                        } else {
+                            list.add(AxisAlignedBB.getBoxForChunk(currChunkX, currChunkZ));
+                            ret = true;
+                        }
+                    }
+                    continue;
+                }
+
+                net.minecraft.world.level.chunk.ChunkSection[] sections = chunk.getSections();
+
+                // bound y
+
+                for (int currY = minYIterate; currY <= maxYIterate; ++currY) {
+                    net.minecraft.world.level.chunk.ChunkSection section = sections[currY >>> 4];
+                    if (section == null || section.isFullOfAir()) {
+                        // empty
+                        // skip to next section
+                        currY = (currY & ~(15)) + 15; // increment by 15: iterator loop increments by the extra one
+                        continue;
+                    }
+
+                    net.minecraft.world.level.chunk.DataPaletteBlock<IBlockData> blocks = section.blockIds;
+
+                    for (int currZ = minZ; currZ <= maxZ; ++currZ) {
+                        for (int currX = minX; currX <= maxX; ++currX) {
+                            int localBlockIndex = (currX) | (currZ << 4) | ((currY & 15) << 8);
+                            int blockX = currX | chunkXGlobalPos;
+                            int blockY = currY;
+                            int blockZ = currZ | chunkZGlobalPos;
+
+                            int edgeCount = ((blockX == minBlockX || blockX == maxBlockX) ? 1 : 0) +
+                                    ((blockY == minBlockY || blockY == maxBlockY) ? 1 : 0) +
+                                    ((blockZ == minBlockZ || blockZ == maxBlockZ) ? 1 : 0);
+                            if (edgeCount == 3) {
+                                continue;
+                            }
+
+                            IBlockData blockData = blocks.rawGet(localBlockIndex);
+
+                            if ((edgeCount != 1 || blockData.shapeExceedsCube()) && (edgeCount != 2 || blockData.getBlock() == Blocks.MOVING_PISTON)) {
+                                mutablePos.setValues(blockX, blockY, blockZ);
+                                if (collisionShape == null) {
+                                    collisionShape = entity == null ? net.minecraft.world.phys.shapes.VoxelShapeCollision.a() : net.minecraft.world.phys.shapes.VoxelShapeCollision.a(entity);
+                                }
+                                VoxelShape voxelshape2 = blockData.getCollisionShape(this, mutablePos, collisionShape);
+                                if (voxelshape2 != net.minecraft.world.phys.shapes.VoxelShapes.getEmptyShape()) {
+                                    VoxelShape voxelshape3 = voxelshape2.offset((double)blockX, (double)blockY, (double)blockZ);
+
+                                    if (predicate != null && !predicate.test(blockData, mutablePos)) {
+                                        continue;
+                                    }
+
+                                    if (checkOnly) {
+                                        if (voxelshape3.intersects(axisalignedbb)) {
+                                            return true;
+                                        }
+                                    } else {
+                                        ret |= net.minecraft.world.phys.shapes.VoxelShapes.addBoxesToIfIntersects(voxelshape3, axisalignedbb, list);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        return ret;
+    }
+    // Tuinity end - optimise pathfinder collision detection
+
     public ChunkCache(World world, BlockPosition blockposition, BlockPosition blockposition1) {
         this.e = world;
         this.a = blockposition.getX() >> 4;
diff --git a/src/main/java/net/minecraft/world/level/ICollisionAccess.java b/src/main/java/net/minecraft/world/level/ICollisionAccess.java
index fcb3e2f9dea97138e0fd4cd2eb11b54799e1d3b5..1c66d6c69148e6aa45641c0351d63c9b96ffb1d3 100644
--- a/src/main/java/net/minecraft/world/level/ICollisionAccess.java
+++ b/src/main/java/net/minecraft/world/level/ICollisionAccess.java
@@ -36,6 +36,11 @@ public interface ICollisionAccess extends IBlockAccess {
     }
 
     default boolean b(AxisAlignedBB axisalignedbb) {
+        // Tuinity start - allow overriding in WorldServer
+        return this.getCubes(axisalignedbb);
+    }
+    default boolean getCubes(AxisAlignedBB axisalignedbb) {
+        // Tuinity end - allow overriding in WorldServer
         return this.b((Entity) null, axisalignedbb, (entity) -> {
             return true;
         });
@@ -54,6 +59,11 @@ public interface ICollisionAccess extends IBlockAccess {
     }
 
     default boolean b(@Nullable Entity entity, AxisAlignedBB axisalignedbb, Predicate<Entity> predicate) {
+        // Tuinity start - allow overriding in WorldServer
+        return this.getCubes(entity, axisalignedbb, predicate);
+    }
+    default boolean getCubes(@Nullable Entity entity, AxisAlignedBB axisalignedbb, Predicate<Entity> predicate) {
+        // Tuinity end - allow overriding in WorldServer
         try { if (entity != null) entity.collisionLoadChunks = true; // Paper
         return this.d(entity, axisalignedbb, predicate).allMatch(VoxelShape::isEmpty);
         } finally { if (entity != null) entity.collisionLoadChunks = false; } // Paper
diff --git a/src/main/java/net/minecraft/world/level/IEntityAccess.java b/src/main/java/net/minecraft/world/level/IEntityAccess.java
index 4b0736d1f805b82e031e7cd7077b59365c196ebe..69cc7664089c505eb5cfdc437f16b91e9713eada 100644
--- a/src/main/java/net/minecraft/world/level/IEntityAccess.java
+++ b/src/main/java/net/minecraft/world/level/IEntityAccess.java
@@ -77,9 +77,10 @@ public interface IEntityAccess {
         if (axisalignedbb.a() < 1.0E-7D) {
             return Stream.empty();
         } else {
-            AxisAlignedBB axisalignedbb1 = axisalignedbb.g(1.0E-7D);
+            AxisAlignedBB axisalignedbb1 = axisalignedbb.g(-1.0E-7D); // Tuinity - to comply with vanilla intersection rules, expand by -epsilon so we only get stuff we definitely collide with. expanding by +epsilon gives us stuff we don't collide with, which will screw over callers in some cases.
 
-            predicate = predicate.and((entity1) -> { // Tuinity - optimise entity hard collisions
+            if (predicate == null) predicate = (e) -> true; // Tuinity - allow nullable
+            predicate = predicate.and((entity1) -> { // Tuinity - optimise entity hard collisions // Tuinity - allow nullable
                 boolean flag;
 
                 if (true || entity1.getBoundingBox().c(axisalignedbb1)) { // Tuinity - always true, wtf did they think this.getEntities(entity, axisalignedbb1) does?
diff --git a/src/main/java/net/minecraft/world/level/VoxelShapeSpliterator.java b/src/main/java/net/minecraft/world/level/VoxelShapeSpliterator.java
index 03584572fa5bf0d96fc4cecece573547f9c94cea..8bc965a3b3d0d4140c6b94636f0b33b2805c5867 100644
--- a/src/main/java/net/minecraft/world/level/VoxelShapeSpliterator.java
+++ b/src/main/java/net/minecraft/world/level/VoxelShapeSpliterator.java
@@ -106,7 +106,7 @@ public class VoxelShapeSpliterator extends AbstractSpliterator<VoxelShape> {
                 VoxelShape voxelshape = iblockdata.b((IBlockAccess) this.g, this.e, this.c);
 
                 if (voxelshape == VoxelShapes.b()) {
-                    if (!this.b.a((double) i, (double) j, (double) k, (double) i + 1.0D, (double) j + 1.0D, (double) k + 1.0D)) {
+                    if (!this.b.voxelShapeIntersect((double) i, (double) j, (double) k, (double) i + 1.0D, (double) j + 1.0D, (double) k + 1.0D)) { // Tuinity - keep vanilla behavior for voxelshape intersection - See comment in AxisAlignedBB
                         continue;
                     }
 
diff --git a/src/main/java/net/minecraft/world/level/World.java b/src/main/java/net/minecraft/world/level/World.java
index bf7ca5c956a2a3a9cd2bfde7a0e4e8805d618797..a0ff1e87224f418a387656c8793151af7929454a 100644
--- a/src/main/java/net/minecraft/world/level/World.java
+++ b/src/main/java/net/minecraft/world/level/World.java
@@ -1175,8 +1175,13 @@ public abstract class World implements GeneratorAccess, AutoCloseable {
 
     @Override
     public List<Entity> getEntities(@Nullable Entity entity, AxisAlignedBB axisalignedbb, @Nullable Predicate<? super Entity> predicate) {
-        this.getMethodProfiler().c("getEntities");
+        // Tuinity start - add list parameter
         List<Entity> list = Lists.newArrayList();
+        return this.getEntities(entity, axisalignedbb, predicate, list);
+    }
+    public List<Entity> getEntities(@Nullable Entity entity, AxisAlignedBB axisalignedbb, @Nullable Predicate<? super Entity> predicate, List<Entity> list) {
+        // Tuinity end - add list parameter
+        this.getMethodProfiler().c("getEntities");
         int i = MathHelper.floor((axisalignedbb.minX - 2.0D) / 16.0D);
         int j = MathHelper.floor((axisalignedbb.maxX + 2.0D) / 16.0D);
         int k = MathHelper.floor((axisalignedbb.minZ - 2.0D) / 16.0D);
diff --git a/src/main/java/net/minecraft/world/level/border/WorldBorder.java b/src/main/java/net/minecraft/world/level/border/WorldBorder.java
index 3c25021835d6d8fd112fc89636616bfd744e7f1a..aa49565cd364db3781a110ee138ee1a4edbfa288 100644
--- a/src/main/java/net/minecraft/world/level/border/WorldBorder.java
+++ b/src/main/java/net/minecraft/world/level/border/WorldBorder.java
@@ -60,11 +60,59 @@ public class WorldBorder {
         return axisalignedbb.maxX > this.e() && axisalignedbb.minX < this.g() && axisalignedbb.maxZ > this.f() && axisalignedbb.minZ < this.h();
     }
 
+    // Tuinity start - optimise collisions
+    // determines whether we are almost colliding with the world border
+    // for clear collisions, this rets false
+    public final boolean isAlmostCollidingOnBorder(AxisAlignedBB boundingBox) {
+        return this.isAlmostCollidingOnBorder(boundingBox.minX, boundingBox.maxX, boundingBox.minZ, boundingBox.maxZ);
+    }
+
+    public final boolean isAlmostCollidingOnBorder(double boxMinX, double boxMaxX, double boxMinZ, double boxMaxZ) {
+        double borderMinX = this.getMinX();
+        double borderMaxX = this.getMaxX();
+
+        double borderMinZ = this.getMinZ();
+        double borderMaxZ = this.getMaxZ();
+
+        return
+                // Not intersecting if we're smaller
+                !AxisAlignedBB.voxelShapeIntersect(
+                        boxMinX + net.minecraft.server.MCUtil.COLLISION_EPSILON, Double.NEGATIVE_INFINITY, boxMinZ + net.minecraft.server.MCUtil.COLLISION_EPSILON,
+                                boxMaxX - net.minecraft.server.MCUtil.COLLISION_EPSILON, Double.POSITIVE_INFINITY, boxMaxZ - net.minecraft.server.MCUtil.COLLISION_EPSILON,
+                        borderMinX, Double.NEGATIVE_INFINITY, borderMinZ, borderMaxX, Double.POSITIVE_INFINITY, borderMaxZ
+                )
+                &&
+
+                // Are intersecting if we're larger
+                AxisAlignedBB.voxelShapeIntersect(
+                        boxMinX - net.minecraft.server.MCUtil.COLLISION_EPSILON, Double.NEGATIVE_INFINITY, boxMinZ - net.minecraft.server.MCUtil.COLLISION_EPSILON,
+                                boxMaxX + net.minecraft.server.MCUtil.COLLISION_EPSILON, Double.POSITIVE_INFINITY, boxMaxZ + net.minecraft.server.MCUtil.COLLISION_EPSILON,
+                        borderMinX, Double.NEGATIVE_INFINITY, borderMinZ, borderMaxX, Double.POSITIVE_INFINITY, borderMaxZ
+                )
+                ;
+    }
+
+    public final boolean isCollidingWithBorderEdge(AxisAlignedBB boundingBox) {
+        return this.isCollidingWithBorderEdge(boundingBox.minX, boundingBox.maxX, boundingBox.minZ, boundingBox.maxZ);
+    }
+
+    public final boolean isCollidingWithBorderEdge(double boxMinX, double boxMaxX, double boxMinZ, double boxMaxZ) {
+        double borderMinX = this.getMinX() + net.minecraft.server.MCUtil.COLLISION_EPSILON;
+        double borderMaxX = this.getMaxX() - net.minecraft.server.MCUtil.COLLISION_EPSILON;
+
+        double borderMinZ = this.getMinZ() + net.minecraft.server.MCUtil.COLLISION_EPSILON;
+        double borderMaxZ = this.getMaxZ() - net.minecraft.server.MCUtil.COLLISION_EPSILON;
+
+        return boxMinX < borderMinX || boxMaxX > borderMaxX || boxMinZ < borderMinZ || boxMaxZ > borderMaxZ;
+    }
+    // Tuinity end - optimise collisions
+
     public double a(Entity entity) {
         return this.b(entity.locX(), entity.locZ());
     }
 
     public final VoxelShape asVoxelShape(){ return c();} // Paper - OBFHELPER
+    public final VoxelShape getCollisionShape() { return this.c(); } // Tuinity - OBFHELPER
     public VoxelShape c() {
         return this.j.m();
     }
@@ -80,18 +128,22 @@ public class WorldBorder {
         return Math.min(d6, d3);
     }
 
+    public final double getMinX() { return this.e(); } // Tuinity - OBFHELPER
     public double e() {
         return this.j.a();
     }
 
+    public final double getMinZ() { return this.f(); } // Tuinity - OBFHELPER
     public double f() {
         return this.j.c();
     }
 
+    public final double getMaxX() { return this.g(); } // Tuinity - OBFHELPER
     public double g() {
         return this.j.b();
     }
 
+    public final double getMaxZ() { return this.h(); } // Tuinity - OBFHELPER
     public double h() {
         return this.j.d();
     }
diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkSection.java b/src/main/java/net/minecraft/world/level/chunk/ChunkSection.java
index 8fe060c3b2ad0873f96218eb7d02cdff3279224e..b17bedec0fa10d81273119b04f05f1cb4d908111 100644
--- a/src/main/java/net/minecraft/world/level/chunk/ChunkSection.java
+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkSection.java
@@ -103,6 +103,7 @@ public class ChunkSection {
         return iblockdata1;
     }
 
+    public final boolean isFullOfAir() { return this.c(); } // Tuinity - OBFHELPER
     public boolean c() {
         return this.nonEmptyBlockCount == 0;
     }
diff --git a/src/main/java/net/minecraft/world/level/chunk/DataPaletteBlock.java b/src/main/java/net/minecraft/world/level/chunk/DataPaletteBlock.java
index 86dfab740883c138a0df8a3da9dfb4eb9acefaa3..a6937366cd9c9d708edb5cd1ab3ac096e7b2032e 100644
--- a/src/main/java/net/minecraft/world/level/chunk/DataPaletteBlock.java
+++ b/src/main/java/net/minecraft/world/level/chunk/DataPaletteBlock.java
@@ -172,6 +172,7 @@ public class DataPaletteBlock<T> implements DataPaletteExpandable<T> {
         return this.a(j << 8 | k << 4 | i); // Paper - inline
     }
 
+    public final T rawGet(int index) { return this.a(index); } // Tuinity - OBFHELPER
     protected T a(int i) {
         T t0 = this.h.a(this.a.a(i));
 
diff --git a/src/main/java/net/minecraft/world/phys/AxisAlignedBB.java b/src/main/java/net/minecraft/world/phys/AxisAlignedBB.java
index 62513c812b497bb9d8dafe1d9c2f574059aebf15..0248ff18bf3f2dede4d0dda90df5e0eea56b7708 100644
--- a/src/main/java/net/minecraft/world/phys/AxisAlignedBB.java
+++ b/src/main/java/net/minecraft/world/phys/AxisAlignedBB.java
@@ -17,6 +17,157 @@ public class AxisAlignedBB {
     public final double maxY;
     public final double maxZ;
 
+    // Tuinity start
+    public final boolean isEmpty() {
+        return (this.maxX - this.minX) < net.minecraft.server.MCUtil.COLLISION_EPSILON && (this.maxY - this.minY) < net.minecraft.server.MCUtil.COLLISION_EPSILON && (this.maxZ - this.minZ) < net.minecraft.server.MCUtil.COLLISION_EPSILON;
+    }
+
+    public static AxisAlignedBB getBoxForChunk(int chunkX, int chunkZ) {
+        double x = (double)(chunkX << 4);
+        double z = (double)(chunkZ << 4);
+        // use a bounding box bigger than the chunk to prevent entities from entering it on move
+        return new AxisAlignedBB(x - 3*net.minecraft.server.MCUtil.COLLISION_EPSILON, Double.NEGATIVE_INFINITY, z - 3*net.minecraft.server.MCUtil.COLLISION_EPSILON, x + (16.0 + 3*net.minecraft.server.MCUtil.COLLISION_EPSILON), Double.POSITIVE_INFINITY, z + (16.0 + 3*net.minecraft.server.MCUtil.COLLISION_EPSILON), false);
+    }
+
+    /*
+      A couple of rules for VoxelShape collisions:
+      Two shapes only intersect if they are actually more than EPSILON units into each other. This also applies to movement
+      checks.
+      If the two shapes strictly collide, then the return value of a collide call will return a value in the opposite
+      direction of the source move. However, this value will not be greater in magnitude than EPSILON. Collision code
+      will automatically round it to 0.
+     */
+
+    public final boolean voxelShapeIntersect(AxisAlignedBB other) {
+        return (this.minX - other.maxX) < -net.minecraft.server.MCUtil.COLLISION_EPSILON && (this.maxX - other.minX) > net.minecraft.server.MCUtil.COLLISION_EPSILON &&
+               (this.minY - other.maxY) < -net.minecraft.server.MCUtil.COLLISION_EPSILON && (this.maxY - other.minY) > net.minecraft.server.MCUtil.COLLISION_EPSILON &&
+               (this.minZ - other.maxZ) < -net.minecraft.server.MCUtil.COLLISION_EPSILON && (this.maxZ - other.minZ) > net.minecraft.server.MCUtil.COLLISION_EPSILON;
+    }
+
+    public final boolean voxelShapeIntersect(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
+        return (this.minX - maxX) < -net.minecraft.server.MCUtil.COLLISION_EPSILON && (this.maxX - minX) > net.minecraft.server.MCUtil.COLLISION_EPSILON &&
+               (this.minY - maxY) < -net.minecraft.server.MCUtil.COLLISION_EPSILON && (this.maxY - minY) > net.minecraft.server.MCUtil.COLLISION_EPSILON &&
+               (this.minZ - maxZ) < -net.minecraft.server.MCUtil.COLLISION_EPSILON && (this.maxZ - minZ) > net.minecraft.server.MCUtil.COLLISION_EPSILON;
+    }
+
+    public static boolean voxelShapeIntersect(double minX1, double minY1, double minZ1, double maxX1, double maxY1, double maxZ1,
+                                              double minX2, double minY2, double minZ2, double maxX2, double maxY2, double maxZ2) {
+        return (minX1 - maxX2) < -net.minecraft.server.MCUtil.COLLISION_EPSILON && (maxX1 - minX2) > net.minecraft.server.MCUtil.COLLISION_EPSILON &&
+                (minY1 - maxY2) < -net.minecraft.server.MCUtil.COLLISION_EPSILON && (maxY1 - minY2) > net.minecraft.server.MCUtil.COLLISION_EPSILON &&
+                (minZ1 - maxZ2) < -net.minecraft.server.MCUtil.COLLISION_EPSILON && (maxZ1 - minZ2) > net.minecraft.server.MCUtil.COLLISION_EPSILON;
+    }
+
+    public static double collideX(AxisAlignedBB target, AxisAlignedBB source, double source_move) {
+        if (source_move == 0.0) {
+            return 0.0;
+        }
+
+        if ((source.minY - target.maxY) < -net.minecraft.server.MCUtil.COLLISION_EPSILON && (source.maxY - target.minY) > net.minecraft.server.MCUtil.COLLISION_EPSILON &&
+                (source.minZ - target.maxZ) < -net.minecraft.server.MCUtil.COLLISION_EPSILON && (source.maxZ - target.minZ) > net.minecraft.server.MCUtil.COLLISION_EPSILON) {
+
+            if (source_move >= 0.0) {
+                double max_move = target.minX - source.maxX; // < 0.0 if no strict collision
+                if (max_move < -net.minecraft.server.MCUtil.COLLISION_EPSILON) {
+                    return source_move;
+                }
+                return Math.min(max_move, source_move);
+            } else {
+                double max_move = target.maxX - source.minX; // > 0.0 if no strict collision
+                if (max_move > net.minecraft.server.MCUtil.COLLISION_EPSILON) {
+                    return source_move;
+                }
+                return Math.max(max_move, source_move);
+            }
+        }
+        return source_move;
+    }
+
+    public static double collideY(AxisAlignedBB target, AxisAlignedBB source, double source_move) {
+        if (source_move == 0.0) {
+            return 0.0;
+        }
+
+        if ((source.minX - target.maxX) < -net.minecraft.server.MCUtil.COLLISION_EPSILON && (source.maxX - target.minX) > net.minecraft.server.MCUtil.COLLISION_EPSILON &&
+                (source.minZ - target.maxZ) < -net.minecraft.server.MCUtil.COLLISION_EPSILON && (source.maxZ - target.minZ) > net.minecraft.server.MCUtil.COLLISION_EPSILON) {
+            if (source_move >= 0.0) {
+                double max_move = target.minY - source.maxY; // < 0.0 if no strict collision
+                if (max_move < -net.minecraft.server.MCUtil.COLLISION_EPSILON) {
+                    return source_move;
+                }
+                return Math.min(max_move, source_move);
+            } else {
+                double max_move = target.maxY - source.minY; // > 0.0 if no strict collision
+                if (max_move > net.minecraft.server.MCUtil.COLLISION_EPSILON) {
+                    return source_move;
+                }
+                return Math.max(max_move, source_move);
+            }
+        }
+        return source_move;
+    }
+
+    public static double collideZ(AxisAlignedBB target, AxisAlignedBB source, double source_move) {
+        if (source_move == 0.0) {
+            return 0.0;
+        }
+
+        if ((source.minX - target.maxX) < -net.minecraft.server.MCUtil.COLLISION_EPSILON && (source.maxX - target.minX) > net.minecraft.server.MCUtil.COLLISION_EPSILON &&
+                (source.minY - target.maxY) < -net.minecraft.server.MCUtil.COLLISION_EPSILON && (source.maxY - target.minY) > net.minecraft.server.MCUtil.COLLISION_EPSILON) {
+            if (source_move >= 0.0) {
+                double max_move = target.minZ - source.maxZ; // < 0.0 if no strict collision
+                if (max_move < -net.minecraft.server.MCUtil.COLLISION_EPSILON) {
+                    return source_move;
+                }
+                return Math.min(max_move, source_move);
+            } else {
+                double max_move = target.maxZ - source.minZ; // > 0.0 if no strict collision
+                if (max_move > net.minecraft.server.MCUtil.COLLISION_EPSILON) {
+                    return source_move;
+                }
+                return Math.max(max_move, source_move);
+            }
+        }
+        return source_move;
+    }
+
+    public final AxisAlignedBB offsetX(double dx) {
+        return new AxisAlignedBB(this.minX + dx, this.minY, this.minZ, this.maxX + dx, this.maxY, this.maxZ, false);
+    }
+
+    public final AxisAlignedBB offsetY(double dy) {
+        return new AxisAlignedBB(this.minX, this.minY + dy, this.minZ, this.maxX, this.maxY + dy, this.maxZ, false);
+    }
+
+    public final AxisAlignedBB offsetZ(double dz) {
+        return new AxisAlignedBB(this.minX, this.minY, this.minZ + dz, this.maxX, this.maxY, this.maxZ + dz, false);
+    }
+
+    public AxisAlignedBB(double d0, double d1, double d2, double d3, double d4, double d5, boolean dummy) {
+        this.minX = d0;
+        this.minY = d1;
+        this.minZ = d2;
+        this.maxX = d3;
+        this.maxY = d4;
+        this.maxZ = d5;
+    }
+
+    public final AxisAlignedBB expandUpwards(double dy) {
+        return new AxisAlignedBB(this.minX, this.minY, this.minZ, this.maxX, this.maxY + dy, this.maxZ, false);
+    }
+
+    public final  AxisAlignedBB cutUpwards(final double dy) { // dy > 0.0
+        return new AxisAlignedBB(this.minX, this.maxY, this.minZ, this.maxX, this.maxY + dy, this.maxZ, false);
+    }
+
+    public final AxisAlignedBB cutDownwards(final double dy) { // dy < 0.0
+        return new AxisAlignedBB(this.minX, this.minY + dy, this.minZ, this.maxX, this.minY, this.maxZ, false);
+    }
+
+    public final AxisAlignedBB expandUpwardsAndCutBelow(double dy) {
+        return new AxisAlignedBB(this.minX, this.maxY, this.minZ, this.maxX, this.maxY + dy, this.maxZ, false);
+    }
+    // Tuinity end
+
     public AxisAlignedBB(double d0, double d1, double d2, double d3, double d4, double d5) {
         this.minX = Math.min(d0, d3);
         this.minY = Math.min(d1, d4);
@@ -189,6 +340,7 @@ public class AxisAlignedBB {
         return new AxisAlignedBB(d0, d1, d2, d3, d4, d5);
     }
 
+    public final AxisAlignedBB offset(double d0, double d1, double d2) { return this.d(d0, d1, d2); } // Tuinity - OBFHELPER
     public AxisAlignedBB d(double d0, double d1, double d2) {
         return new AxisAlignedBB(this.minX + d0, this.minY + d1, this.minZ + d2, this.maxX + d0, this.maxY + d1, this.maxZ + d2);
     }
@@ -197,6 +349,7 @@ public class AxisAlignedBB {
         return new AxisAlignedBB(this.minX + (double) blockposition.getX(), this.minY + (double) blockposition.getY(), this.minZ + (double) blockposition.getZ(), this.maxX + (double) blockposition.getX(), this.maxY + (double) blockposition.getY(), this.maxZ + (double) blockposition.getZ());
     }
 
+    public final AxisAlignedBB offset(Vec3D vec3d) { return this.b(vec3d); } // Tuinity - OBFHELPER
     public AxisAlignedBB c(Vec3D vec3d) {
         return this.d(vec3d.x, vec3d.y, vec3d.z);
     }
@@ -216,6 +369,7 @@ public class AxisAlignedBB {
         return this.e(vec3d.x, vec3d.y, vec3d.z);
     }
 
+    public final boolean contains(double d0, double d1, double d2) { return this.e(d0, d1, d2); } // Tuinity - OBFHELPER
     public boolean e(double d0, double d1, double d2) {
         return d0 >= this.minX && d0 < this.maxX && d1 >= this.minY && d1 < this.maxY && d2 >= this.minZ && d2 < this.maxZ;
     }
diff --git a/src/main/java/net/minecraft/world/phys/Vec3D.java b/src/main/java/net/minecraft/world/phys/Vec3D.java
index b71e119eed6fa283d99dc033144c8be7b336d9c4..3fe1508b091e1fd0325eae50138d02fa6445c9ff 100644
--- a/src/main/java/net/minecraft/world/phys/Vec3D.java
+++ b/src/main/java/net/minecraft/world/phys/Vec3D.java
@@ -9,7 +9,7 @@ import net.minecraft.util.MathHelper;
 
 public class Vec3D implements IPosition {
 
-    public static final Vec3D ORIGIN = new Vec3D(0.0D, 0.0D, 0.0D);
+    public static final Vec3D ORIGIN = new Vec3D(0.0D, 0.0D, 0.0D); public static Vec3D getZeroVector() { return Vec3D.ORIGIN; } // Tuinity - OBFHELPER
     public final double x;
     public final double y;
     public final double z;
@@ -66,6 +66,7 @@ public class Vec3D implements IPosition {
         return this.add(-d0, -d1, -d2);
     }
 
+    public final Vec3D add(Vec3D vec3d) { return this.e(vec3d); } // Tuinity - OBFHELPER
     public Vec3D e(Vec3D vec3d) {
         return this.add(vec3d.x, vec3d.y, vec3d.z);
     }
@@ -114,10 +115,12 @@ public class Vec3D implements IPosition {
         return new Vec3D(this.x * d0, this.y * d1, this.z * d2);
     }
 
+    public final double magnitude() { return this.f(); } // Tuinity - OBFHELPER
     public double f() {
         return (double) MathHelper.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
     }
 
+    public final double magnitudeSquared() { return this.g(); } // Tuinity - OBFHELPER
     public double g() {
         return this.x * this.x + this.y * this.y + this.z * this.z;
     }
diff --git a/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java b/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java
index 887016224c16f8a38c10a98eb0e2ae6cb353a153..8ecd4d38334872da8d7d05cdef1fb08cf0ff17d9 100644
--- a/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java
+++ b/src/main/java/net/minecraft/world/phys/shapes/VoxelShape.java
@@ -16,11 +16,11 @@ import net.minecraft.world.phys.Vec3D;
 
 public abstract class VoxelShape {
 
-    protected final VoxelShapeDiscrete a;
+    protected final VoxelShapeDiscrete a; public final VoxelShapeDiscrete getShape() { return this.a; } // Tuinity - OBFHELPER
     @Nullable
     private VoxelShape[] b;
 
-    VoxelShape(VoxelShapeDiscrete voxelshapediscrete) {
+    protected VoxelShape(VoxelShapeDiscrete voxelshapediscrete) { // Tuinity
         this.a = voxelshapediscrete;
     }
 
@@ -56,9 +56,15 @@ public abstract class VoxelShape {
 
     public final VoxelShape offset(double x, double y, double z) { return this.a(x, y, z); } // Paper - OBFHELPER
     public VoxelShape a(double d0, double d1, double d2) {
-        return (VoxelShape) (this.isEmpty() ? VoxelShapes.a() : new VoxelShapeArray(this.a, new DoubleListOffset(this.a(EnumDirection.EnumAxis.X), d0), new DoubleListOffset(this.a(EnumDirection.EnumAxis.Y), d1), new DoubleListOffset(this.a(EnumDirection.EnumAxis.Z), d2)));
+        return (VoxelShape) (this.isEmpty() ? VoxelShapes.a() : new VoxelShapeArray(this.a, new DoubleListOffset(this.a(EnumDirection.EnumAxis.X), d0), new DoubleListOffset(this.a(EnumDirection.EnumAxis.Y), d1), new DoubleListOffset(this.a(EnumDirection.EnumAxis.Z), d2))); // Tuinity - diff on change, copied into VoxelShapeArray override
     }
 
+    // Tuinity start - optimise multi-aabb shapes
+    public boolean intersects(final AxisAlignedBB axisalingedbb) {
+        return VoxelShapes.applyOperation(this, new com.tuinity.tuinity.voxel.AABBVoxelShape(axisalingedbb), OperatorBoolean.AND);
+    }
+    // Tuinity end - optimise multi-aabb shapes
+
     public VoxelShape c() {
         VoxelShape[] avoxelshape = new VoxelShape[]{VoxelShapes.a()};
 
@@ -78,6 +84,7 @@ public abstract class VoxelShape {
         }, true);
     }
 
+    public final List<AxisAlignedBB> getBoundingBoxesRepresentation() { return this.d(); } // Tuinity - OBFHELPER
     public List<AxisAlignedBB> d() {
         List<AxisAlignedBB> list = Lists.newArrayList();
 
diff --git a/src/main/java/net/minecraft/world/phys/shapes/VoxelShapeArray.java b/src/main/java/net/minecraft/world/phys/shapes/VoxelShapeArray.java
index 56f0b3a74f676d288d81671a4791337e169b9758..5a0f8a9f5bfb3f271ccf8293bbe9fe99dcb116a1 100644
--- a/src/main/java/net/minecraft/world/phys/shapes/VoxelShapeArray.java
+++ b/src/main/java/net/minecraft/world/phys/shapes/VoxelShapeArray.java
@@ -12,11 +12,25 @@ public final class VoxelShapeArray extends VoxelShape {
     private final DoubleList c;
     private final DoubleList d;
 
+    // Tuinity start - optimise multi-aabb shapes
+    static final net.minecraft.world.phys.AxisAlignedBB[] EMPTY = new net.minecraft.world.phys.AxisAlignedBB[0];
+    final net.minecraft.world.phys.AxisAlignedBB[] boundingBoxesRepresentation;
+
+    final double offsetX;
+    final double offsetY;
+    final double offsetZ;
+    // Tuinity end - optimise multi-aabb shapes
+
     protected VoxelShapeArray(VoxelShapeDiscrete voxelshapediscrete, double[] adouble, double[] adouble1, double[] adouble2) {
         this(voxelshapediscrete, (DoubleList) DoubleArrayList.wrap(Arrays.copyOf(adouble, voxelshapediscrete.b() + 1)), (DoubleList) DoubleArrayList.wrap(Arrays.copyOf(adouble1, voxelshapediscrete.c() + 1)), (DoubleList) DoubleArrayList.wrap(Arrays.copyOf(adouble2, voxelshapediscrete.d() + 1)));
     }
 
     VoxelShapeArray(VoxelShapeDiscrete voxelshapediscrete, DoubleList doublelist, DoubleList doublelist1, DoubleList doublelist2) {
+        // Tuinity start - optimise multi-aabb shapes
+        this(voxelshapediscrete, doublelist, doublelist1, doublelist2, null, null, 0.0, 0.0, 0.0);
+    }
+    VoxelShapeArray(VoxelShapeDiscrete voxelshapediscrete, DoubleList doublelist, DoubleList doublelist1, DoubleList doublelist2, VoxelShapeArray original, net.minecraft.world.phys.AxisAlignedBB[] boundingBoxesRepresentation, double offsetX, double offsetY, double offsetZ) {
+        // Tuinity end - optimise multi-aabb shapes
         super(voxelshapediscrete);
         int i = voxelshapediscrete.b() + 1;
         int j = voxelshapediscrete.c() + 1;
@@ -29,6 +43,18 @@ public final class VoxelShapeArray extends VoxelShape {
         } else {
             throw (IllegalArgumentException) SystemUtils.c((Throwable) (new IllegalArgumentException("Lengths of point arrays must be consistent with the size of the VoxelShape.")));
         }
+        // Tuinity start - optimise multi-aabb shapes
+        this.boundingBoxesRepresentation = boundingBoxesRepresentation == null ? this.getBoundingBoxesRepresentation().toArray(EMPTY) : boundingBoxesRepresentation; // Tuinity - optimise multi-aabb shapes
+        if (original == null) {
+            this.offsetX = offsetX;
+            this.offsetY = offsetY;
+            this.offsetZ = offsetZ;
+        } else {
+            this.offsetX = offsetX + original.offsetX;
+            this.offsetY = offsetY + original.offsetY;
+            this.offsetZ = offsetZ + original.offsetZ;
+        }
+        // Tuinity end - optimise multi-aabb shapes
     }
 
     @Override
@@ -44,4 +70,63 @@ public final class VoxelShapeArray extends VoxelShape {
                 throw new IllegalArgumentException();
         }
     }
+
+    // Tuinity start - optimise multi-aabb shapes
+    @Override
+    public VoxelShape a(double d0, double d1, double d2) {
+        if (this == VoxelShapes.getEmptyShape() || this.boundingBoxesRepresentation.length == 0) {
+            return this;
+        }
+        return new VoxelShapeArray(this.a, new DoubleListOffset(this.a(EnumDirection.EnumAxis.X), d0), new DoubleListOffset(this.a(EnumDirection.EnumAxis.Y), d1), new DoubleListOffset(this.a(EnumDirection.EnumAxis.Z), d2), this, this.boundingBoxesRepresentation, d0, d1, d2);
+    }
+
+    @Override
+    public java.util.List<net.minecraft.world.phys.AxisAlignedBB> d() { // getBoundingBoxesRepresentation
+        if (this.boundingBoxesRepresentation == null) {
+            return super.d();
+        }
+        java.util.List<net.minecraft.world.phys.AxisAlignedBB> ret = new java.util.ArrayList<>(this.boundingBoxesRepresentation.length);
+
+        double offX = this.offsetX;
+        double offY = this.offsetY;
+        double offZ = this.offsetZ;
+        for (net.minecraft.world.phys.AxisAlignedBB boundingBox : this.boundingBoxesRepresentation) {
+            ret.add(boundingBox.offset(offX, offY, offZ));
+        }
+
+        return ret;
+    }
+
+    public final net.minecraft.world.phys.AxisAlignedBB[] getBoundingBoxesRepresentationRaw() {
+        return this.boundingBoxesRepresentation;
+    }
+
+    public final double getOffsetX() {
+        return this.offsetX;
+    }
+
+    public final double getOffsetY() {
+        return this.offsetY;
+    }
+
+    public final double getOffsetZ() {
+        return this.offsetZ;
+    }
+
+    public final boolean intersects(net.minecraft.world.phys.AxisAlignedBB axisalingedbb) {
+        // this can be optimised by checking an "overall shape" first, but not needed
+        double offX = this.offsetX;
+        double offY = this.offsetY;
+        double offZ = this.offsetZ;
+
+        for (net.minecraft.world.phys.AxisAlignedBB boundingBox : this.boundingBoxesRepresentation) {
+            if (axisalingedbb.voxelShapeIntersect(boundingBox.minX + offX, boundingBox.minY + offY, boundingBox.minZ + offZ,
+                    boundingBox.maxX + offX, boundingBox.maxY + offY, boundingBox.maxZ + offZ)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+    // Tuinity end - optimise multi-aabb shapes
 }
diff --git a/src/main/java/net/minecraft/world/phys/shapes/VoxelShapes.java b/src/main/java/net/minecraft/world/phys/shapes/VoxelShapes.java
index 38fde2e10adf06bf5c5141d17d82ba125b372cd7..98e787e6383a39de1708428137fd7f9e057ff153 100644
--- a/src/main/java/net/minecraft/world/phys/shapes/VoxelShapes.java
+++ b/src/main/java/net/minecraft/world/phys/shapes/VoxelShapes.java
@@ -27,18 +27,101 @@ public final class VoxelShapes {
 
         voxelshapebitset.a(0, 0, 0, true, true);
         return new VoxelShapeCube(voxelshapebitset);
-    });
+    }); public static final VoxelShape getFullUnoptimisedCube() { return VoxelShapes.b; } // Tuinity - OBFHELPER
     public static final VoxelShape a = create(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
-    private static final VoxelShape c = new VoxelShapeArray(new VoxelShapeBitSet(0, 0, 0), new DoubleArrayList(new double[]{0.0D}), new DoubleArrayList(new double[]{0.0D}), new DoubleArrayList(new double[]{0.0D}));
+    private static final VoxelShape c = new VoxelShapeArray(new VoxelShapeBitSet(0, 0, 0), new DoubleArrayList(new double[]{0.0D}), new DoubleArrayList(new double[]{0.0D}), new DoubleArrayList(new double[]{0.0D})); public static final VoxelShape getEmptyShape() { return VoxelShapes.c; } // Tuinity - OBFHELPER
+
+    // Tuinity start - optimise voxelshapes
+    public static boolean isEmpty(VoxelShape voxelshape) {
+        // helper function for determining empty shapes fast
+        return voxelshape == getEmptyShape() || voxelshape.isEmpty();
+    }
+    // Tuinity end - optimise voxelshapes
 
     public static final VoxelShape empty() {return a();} // Paper - OBFHELPER
     public static VoxelShape a() {
         return VoxelShapes.c;
     }
 
+    public static final com.tuinity.tuinity.voxel.AABBVoxelShape optimisedFullCube = new com.tuinity.tuinity.voxel.AABBVoxelShape(new AxisAlignedBB(0, 0, 0, 1.0, 1.0, 1.0)); // Tuinity - optimise voxelshape
+
+    // Tuinity start - optimise voxelshapes
+    public static boolean addBoxesToIfIntersects(VoxelShape shape, AxisAlignedBB aabb, java.util.List<AxisAlignedBB> list) {
+        if (shape instanceof com.tuinity.tuinity.voxel.AABBVoxelShape) {
+            com.tuinity.tuinity.voxel.AABBVoxelShape shapeCasted = (com.tuinity.tuinity.voxel.AABBVoxelShape)shape;
+            if (!shapeCasted.aabb.isEmpty() && shapeCasted.aabb.voxelShapeIntersect(aabb)) {
+                list.add(shapeCasted.aabb);
+                return true;
+            }
+            return false;
+        } else if (shape instanceof VoxelShapeArray) {
+            VoxelShapeArray shapeCasted = (VoxelShapeArray)shape;
+            // this can be optimised by checking an "overall shape" first, but not needed
+
+            double offX = shapeCasted.offsetX;
+            double offY = shapeCasted.offsetY;
+            double offZ = shapeCasted.offsetZ;
+
+            boolean ret = false;
+
+            for (AxisAlignedBB boundingBox : shapeCasted.boundingBoxesRepresentation) {
+                double minX, minY, minZ, maxX, maxY, maxZ;
+                if (aabb.voxelShapeIntersect(minX = boundingBox.minX + offX, minY = boundingBox.minY + offY, minZ = boundingBox.minZ + offZ,
+                        maxX = boundingBox.maxX + offX, maxY = boundingBox.maxY + offY, maxZ = boundingBox.maxZ + offZ)) {
+                    AxisAlignedBB box = new AxisAlignedBB(minX, minY, minZ, maxX, maxY, maxZ, false);
+                    if (!box.isEmpty()) {
+                        list.add(box);
+                        ret = true;
+                    }
+                }
+            }
+
+            return ret;
+        } else {
+            boolean ret = false;
+
+            java.util.List<AxisAlignedBB> boxes = shape.getBoundingBoxesRepresentation();
+            for (int i = 0, len = boxes.size(); i < len; ++i) {
+                AxisAlignedBB box = boxes.get(i);
+                if (!box.isEmpty() && box.voxelShapeIntersect(aabb)) {
+                    list.add(box);
+                    ret = true;
+                }
+            }
+
+            return ret;
+        }
+    }
+
+    public static void addBoxesTo(VoxelShape shape, java.util.List<AxisAlignedBB> list) {
+        if (shape instanceof com.tuinity.tuinity.voxel.AABBVoxelShape) {
+            com.tuinity.tuinity.voxel.AABBVoxelShape shapeCasted = (com.tuinity.tuinity.voxel.AABBVoxelShape)shape;
+            if (!shapeCasted.isEmpty()) {
+                list.add(shapeCasted.aabb);
+            }
+        } else if (shape instanceof VoxelShapeArray) {
+            VoxelShapeArray shapeCasted = (VoxelShapeArray)shape;
+
+            for (AxisAlignedBB boundingBox : shapeCasted.boundingBoxesRepresentation) {
+                if (!boundingBox.isEmpty()) {
+                    list.add(boundingBox.offset(shapeCasted.offsetX, shapeCasted.offsetY, shapeCasted.offsetZ));
+                }
+            }
+        } else {
+            java.util.List<AxisAlignedBB> boxes = shape.getBoundingBoxesRepresentation();
+            for (int i = 0, len = boxes.size(); i < len; ++i) {
+                AxisAlignedBB box = boxes.get(i);
+                if (!box.isEmpty()) {
+                    list.add(box);
+                }
+            }
+        }
+    }
+    // Tuinity end - optimise voxelshapes
+
     public static final VoxelShape fullCube() {return b();} // Paper - OBFHELPER
     public static VoxelShape b() {
-        return VoxelShapes.b;
+        return VoxelShapes.optimisedFullCube; // Tuinity - optimise voxelshape
     }
 
     public static VoxelShape create(double d0, double d1, double d2, double d3, double d4, double d5) {
@@ -77,7 +160,7 @@ public final class VoxelShapes {
                 return new VoxelShapeCube(voxelshapebitset);
             }
         } else {
-            return new VoxelShapeArray(VoxelShapes.b.a, new double[]{axisalignedbb.minX, axisalignedbb.maxX}, new double[]{axisalignedbb.minY, axisalignedbb.maxY}, new double[]{axisalignedbb.minZ, axisalignedbb.maxZ});
+            return new com.tuinity.tuinity.voxel.AABBVoxelShape(axisalignedbb); // Tuinity - optimise VoxelShapes for single AABB shapes
         }
     }
 
@@ -142,6 +225,20 @@ public final class VoxelShapes {
 
     public static final boolean applyOperation(VoxelShape voxelshape, VoxelShape voxelshape1, OperatorBoolean operatorboolean) { return VoxelShapes.c(voxelshape, voxelshape1, operatorboolean); } // Paper - OBFHELPER
     public static boolean c(VoxelShape voxelshape, VoxelShape voxelshape1, OperatorBoolean operatorboolean) {
+        // Tuinity start - optimise voxelshape
+        if (operatorboolean == OperatorBoolean.AND) {
+            if (voxelshape instanceof com.tuinity.tuinity.voxel.AABBVoxelShape && voxelshape1 instanceof com.tuinity.tuinity.voxel.AABBVoxelShape) {
+                return ((com.tuinity.tuinity.voxel.AABBVoxelShape)voxelshape).aabb.voxelShapeIntersect(((com.tuinity.tuinity.voxel.AABBVoxelShape)voxelshape1).aabb);
+            } else if (voxelshape instanceof com.tuinity.tuinity.voxel.AABBVoxelShape && voxelshape1 instanceof VoxelShapeArray) {
+                return ((VoxelShapeArray)voxelshape1).intersects(((com.tuinity.tuinity.voxel.AABBVoxelShape)voxelshape).aabb);
+            } else if (voxelshape1 instanceof com.tuinity.tuinity.voxel.AABBVoxelShape && voxelshape instanceof VoxelShapeArray) {
+                return ((VoxelShapeArray)voxelshape).intersects(((com.tuinity.tuinity.voxel.AABBVoxelShape)voxelshape1).aabb);
+            }
+        }
+        return abstract_c(voxelshape, voxelshape1, operatorboolean);
+    }
+    public static boolean abstract_c(VoxelShape voxelshape, VoxelShape voxelshape1, OperatorBoolean operatorboolean) {
+        // Tuinity end - optimise voxelshape
         if (operatorboolean.apply(false, false)) {
             throw (IllegalArgumentException) SystemUtils.c((Throwable) (new IllegalArgumentException()));
         } else if (voxelshape == voxelshape1) {
@@ -326,7 +423,50 @@ public final class VoxelShapes {
 
     public static boolean combinationOccludes(VoxelShape voxelshape, VoxelShape voxelshape1) { return b(voxelshape, voxelshape1); } // Tuinity - OBFHELPER
     public static boolean b(VoxelShape voxelshape, VoxelShape voxelshape1) {
-        return voxelshape != b() && voxelshape1 != b() ? (voxelshape.isEmpty() && voxelshape1.isEmpty() ? false : !c(b(), b(voxelshape, voxelshape1, OperatorBoolean.OR), OperatorBoolean.ONLY_FIRST)) : true;
+        if (voxelshape == getFullUnoptimisedCube() || voxelshape == optimisedFullCube
+                || voxelshape1 == getFullUnoptimisedCube() || voxelshape1 == optimisedFullCube) {
+            return true;
+        }
+        boolean v1Empty = voxelshape == getEmptyShape();
+        boolean v2Empty = voxelshape1 == getEmptyShape();
+        if (v1Empty && v2Empty) {
+            return false;
+        }
+        if ((voxelshape instanceof com.tuinity.tuinity.voxel.AABBVoxelShape || v1Empty) && (voxelshape1 instanceof com.tuinity.tuinity.voxel.AABBVoxelShape || v2Empty)) {
+            if (!v1Empty && !v2Empty && (voxelshape != voxelshape1)) {
+                AxisAlignedBB boundingBox1 = ((com.tuinity.tuinity.voxel.AABBVoxelShape)voxelshape).aabb;
+                AxisAlignedBB boundingBox2 = ((com.tuinity.tuinity.voxel.AABBVoxelShape)voxelshape1).aabb;
+                // can call it here in some cases
+
+                // check overall bounding box
+                double minY = Math.min(boundingBox1.minY, boundingBox2.minY);
+                double maxY = Math.max(boundingBox1.maxY, boundingBox2.maxY);
+                if (minY > net.minecraft.server.MCUtil.COLLISION_EPSILON || maxY < (1 - net.minecraft.server.MCUtil.COLLISION_EPSILON)) {
+                    return false;
+                }
+                double minX = Math.min(boundingBox1.minX, boundingBox2.minX);
+                double maxX = Math.max(boundingBox1.maxX, boundingBox2.maxX);
+                if (minX > net.minecraft.server.MCUtil.COLLISION_EPSILON || maxX < (1 - net.minecraft.server.MCUtil.COLLISION_EPSILON)) {
+                    return false;
+                }
+                double minZ = Math.min(boundingBox1.minZ, boundingBox2.minZ);
+                double maxZ = Math.max(boundingBox1.maxZ, boundingBox2.maxZ);
+                if (minZ > net.minecraft.server.MCUtil.COLLISION_EPSILON || maxZ < (1 - net.minecraft.server.MCUtil.COLLISION_EPSILON)) {
+                    return false;
+                }
+                // fall through to full merge check
+            } else {
+                AxisAlignedBB boundingBox = v1Empty ? ((com.tuinity.tuinity.voxel.AABBVoxelShape)voxelshape1).aabb : ((com.tuinity.tuinity.voxel.AABBVoxelShape)voxelshape).aabb;
+                // check if the bounding box encloses the full cube
+                return (boundingBox.minY <= net.minecraft.server.MCUtil.COLLISION_EPSILON && boundingBox.maxY >= (1 - net.minecraft.server.MCUtil.COLLISION_EPSILON)) &&
+                       (boundingBox.minX <= net.minecraft.server.MCUtil.COLLISION_EPSILON && boundingBox.maxX >= (1 - net.minecraft.server.MCUtil.COLLISION_EPSILON)) &&
+                       (boundingBox.minZ <= net.minecraft.server.MCUtil.COLLISION_EPSILON && boundingBox.maxZ >= (1 - net.minecraft.server.MCUtil.COLLISION_EPSILON));
+            }
+        }
+        return b_rare(voxelshape, voxelshape1);
+    }
+    public static boolean b_rare(VoxelShape voxelshape, VoxelShape voxelshape1) {
+        return (voxelshape != b() || voxelshape != getFullUnoptimisedCube()) && (voxelshape1 != b() || voxelshape1 != getFullUnoptimisedCube()) ? ((voxelshape == VoxelShapes.getEmptyShape() || voxelshape.isEmpty()) && (voxelshape1 == VoxelShapes.getEmptyShape() || voxelshape1.isEmpty()) ? false : !c(b(), b(voxelshape, voxelshape1, OperatorBoolean.OR), OperatorBoolean.ONLY_FIRST)) : true; // Tuinity - optimise call by checking against more constant shapes
     }
 
     @VisibleForTesting
diff --git a/src/main/java/org/bukkit/craftbukkit/util/UnsafeList.java b/src/main/java/org/bukkit/craftbukkit/util/UnsafeList.java
index f72c13bedaa6fa45e26f5dcad564835bdd4af61f..50f855b931dba60754fff9c7cdf5e0e744f00fdd 100644
--- a/src/main/java/org/bukkit/craftbukkit/util/UnsafeList.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/UnsafeList.java
@@ -119,6 +119,32 @@ public class UnsafeList<E> extends AbstractList<E> implements List<E>, RandomAcc
         return indexOf(o) >= 0;
     }
 
+    // Tuinity start
+    protected transient int maxSize;
+    public void setSize(int size) {
+        if (this.maxSize < this.size) {
+            this.maxSize = this.size;
+        }
+        this.size = size;
+    }
+
+    public void completeReset() {
+        if (this.data != null) {
+            Arrays.fill(this.data, 0, Math.max(this.size, this.maxSize), null);
+        }
+        this.size = 0;
+        this.maxSize = 0;
+        if (this.iterPool != null) {
+            for (Iterator temp : this.iterPool) {
+                if (temp == null) {
+                    continue;
+                }
+                ((Itr)temp).valid = false;
+            }
+        }
+    }
+    // Tuinity end
+
     @Override
     public void clear() {
         // Create new array to reset memory usage to initial capacity
