Yatopia/patches/Tuinity/patches/server/0025-Highly-optimise-single...

1779 lines
91 KiB
Diff

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