From dc20d1a144d607d0cf5db94e3731b5c464780128 Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Fri, 1 Sep 2023 15:15:03 -0700 Subject: [PATCH] Optimise findFreePosition Ghasts teleporting can perform many voxelshape operations. We can reduce the number of voxelshape operations and use more efficient ones at the same time. --- .../moonrise/mixin/collisions/LevelMixin.java | 74 ++++++++++++++++++- .../mixin/collisions/VoxelShapeMixin.java | 32 ++++++++ .../ThreadingDetectorMixin.java | 38 +--------- 3 files changed, 106 insertions(+), 38 deletions(-) diff --git a/src/main/java/ca/spottedleaf/moonrise/mixin/collisions/LevelMixin.java b/src/main/java/ca/spottedleaf/moonrise/mixin/collisions/LevelMixin.java index 697de97..be681ec 100644 --- a/src/main/java/ca/spottedleaf/moonrise/mixin/collisions/LevelMixin.java +++ b/src/main/java/ca/spottedleaf/moonrise/mixin/collisions/LevelMixin.java @@ -24,6 +24,8 @@ import net.minecraft.world.level.material.Fluids; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.Vec3; +import net.minecraft.world.phys.shapes.BooleanOp; +import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Overwrite; @@ -34,6 +36,7 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.function.Predicate; @Mixin(Level.class) @@ -91,6 +94,7 @@ public abstract class LevelMixin implements CollisionLevel, CollisionEntityGette * @author Spottedleaf */ @Overwrite + @Override public List getEntities(final Entity entity, final AABB boundingBox, final Predicate predicate) { this.getProfiler().incrementCounter("getEntities"); final List ret = new ArrayList<>(); @@ -171,7 +175,7 @@ public abstract class LevelMixin implements CollisionLevel, CollisionEntityGette * @author Spottedleaf */ @Override - public List getEntitiesOfClass(final Class entityClass, final AABB boundingBox, final Predicate predicate) { + public final List getEntitiesOfClass(final Class entityClass, final AABB boundingBox, final Predicate predicate) { this.getProfiler().incrementCounter("getEntities"); final List ret = new ArrayList<>(); @@ -185,7 +189,7 @@ public abstract class LevelMixin implements CollisionLevel, CollisionEntityGette * @author Spottedleaf */ @Override - public List getHardCollidingEntities(final Entity entity, final AABB box, final Predicate predicate) { + public final List getHardCollidingEntities(final Entity entity, final AABB box, final Predicate predicate) { this.getProfiler().incrementCounter("getEntities"); final List ret = new ArrayList<>(); @@ -200,7 +204,7 @@ public abstract class LevelMixin implements CollisionLevel, CollisionEntityGette * @author Spottedleaf */ @Override - public boolean isUnobstructed(final Entity entity) { + public final boolean isUnobstructed(final Entity entity) { final AABB boundingBox = entity.getBoundingBox(); if (CollisionUtil.isEmpty(boundingBox)) { return false; @@ -370,7 +374,7 @@ public abstract class LevelMixin implements CollisionLevel, CollisionEntityGette * @author Spottedleaf */ @Override - public BlockHitResult clip(final ClipContext clipContext) { + public final BlockHitResult clip(final ClipContext clipContext) { // can only do this in this class, as not everything that implements BlockGetter can retrieve chunks return fastClip(clipContext.getFrom(), clipContext.getTo(), (Level)(Object)this, clipContext); } @@ -402,4 +406,66 @@ public abstract class LevelMixin implements CollisionLevel, CollisionEntityGette } ); } + + @Unique + private static VoxelShape inflateAABBToVoxel(final AABB aabb, final double x, final double y, final double z) { + return Shapes.create( + aabb.minX - x, + aabb.minY - y, + aabb.minZ - z, + + aabb.maxX + x, + aabb.maxY + y, + aabb.maxZ + z + ); + } + + /** + * @reason Use optimised merge strategy, avoid streams + * @author Spottedleaf + */ + @Override + public final Optional findFreePosition(final Entity entity, final VoxelShape boundsShape, final Vec3 fromPosition, + final double rangeX, final double rangeY, final double rangeZ) { + if (boundsShape.isEmpty()) { + return Optional.empty(); + } + + final double expandByX = rangeX * 0.5; + final double expandByY = rangeY * 0.5; + final double expandByZ = rangeZ * 0.5; + + // note: it is useless to look at shapes outside of range / 2.0 + final AABB collectionVolume = boundsShape.bounds().inflate(expandByX, expandByY, expandByZ); + + final List aabbs = new ArrayList<>(); + final List voxels = new ArrayList<>(); + + CollisionUtil.getCollisionsForBlocksOrWorldBorder( + (Level)(Object)this, entity, collectionVolume, voxels, aabbs, + CollisionUtil.COLLISION_FLAG_CHECK_BORDER, + null + ); + + // push voxels into aabbs + for (int i = 0, len = voxels.size(); i < len; ++i) { + aabbs.addAll(voxels.get(i).toAabbs()); + } + + // expand AABBs + final VoxelShape first = aabbs.isEmpty() ? Shapes.empty() : inflateAABBToVoxel(aabbs.get(0), expandByX, expandByY, expandByZ); + final VoxelShape[] rest = new VoxelShape[Math.max(0, aabbs.size() - 1)]; + + for (int i = 1, len = aabbs.size(); i < len; ++i) { + rest[i - 1] = inflateAABBToVoxel(aabbs.get(i), expandByX, expandByY, expandByZ); + } + + // use optimized join + final VoxelShape joined = Shapes.or(first, rest); + + // find free space + final VoxelShape freeSpace = Shapes.join(boundsShape, joined, BooleanOp.ONLY_FIRST); + + return freeSpace.closestPointTo(fromPosition); + } } diff --git a/src/main/java/ca/spottedleaf/moonrise/mixin/collisions/VoxelShapeMixin.java b/src/main/java/ca/spottedleaf/moonrise/mixin/collisions/VoxelShapeMixin.java index de6feb8..dfa6c74 100644 --- a/src/main/java/ca/spottedleaf/moonrise/mixin/collisions/VoxelShapeMixin.java +++ b/src/main/java/ca/spottedleaf/moonrise/mixin/collisions/VoxelShapeMixin.java @@ -13,6 +13,7 @@ import it.unimi.dsi.fastutil.doubles.DoubleList; import net.minecraft.Util; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.util.Mth; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.Vec3; @@ -31,6 +32,7 @@ import org.spongepowered.asm.mixin.Unique; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Optional; @Mixin(VoxelShape.class) public abstract class VoxelShapeMixin implements CollisionVoxelShape { @@ -711,4 +713,34 @@ public abstract class VoxelShapeMixin implements CollisionVoxelShape { return ret; } } + + /** + * @reason Use AABBs cache + * @author Spottedleaf + */ + @Overwrite + public Optional closestPointTo(final Vec3 point) { + if (this.isEmpty) { + return Optional.empty(); + } + + Vec3 ret = null; + double retDistance = Double.MAX_VALUE; + + final List aabbs = this.toAabbs(); + for (int i = 0, len = aabbs.size(); i < len; ++i) { + final AABB aabb = aabbs.get(i); + final double x = Mth.clamp(point.x, aabb.minX, aabb.maxX); + final double y = Mth.clamp(point.y, aabb.minY, aabb.maxY); + final double z = Mth.clamp(point.z, aabb.minZ, aabb.maxZ); + + double dist = point.distanceToSqr(x, y, z); + if (dist < retDistance) { + ret = new Vec3(x, y, z); + retDistance = dist; + } + } + + return Optional.ofNullable(ret); + } } diff --git a/src/main/java/ca/spottedleaf/moonrise/mixin/util_threading_detector/ThreadingDetectorMixin.java b/src/main/java/ca/spottedleaf/moonrise/mixin/util_threading_detector/ThreadingDetectorMixin.java index 962e88f..4730648 100644 --- a/src/main/java/ca/spottedleaf/moonrise/mixin/util_threading_detector/ThreadingDetectorMixin.java +++ b/src/main/java/ca/spottedleaf/moonrise/mixin/util_threading_detector/ThreadingDetectorMixin.java @@ -1,26 +1,17 @@ package ca.spottedleaf.moonrise.mixin.util_threading_detector; -import ca.spottedleaf.concurrentutil.util.ConcurrentUtil; import net.minecraft.util.ThreadingDetector; -import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Overwrite; -import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; -import java.lang.invoke.VarHandle; import java.util.concurrent.Semaphore; import java.util.concurrent.locks.ReentrantLock; @Mixin(ThreadingDetector.class) public abstract class ThreadingDetectorMixin { - @Shadow - @Final - private String name; - - @Unique private static final ReentrantLock CANT_USE_NULL_IN_NEW_REDIRECT_MIXIN_WHAT_THE_FUCK_REENTRANTLOCK = new ReentrantLock(); @@ -57,38 +48,17 @@ public abstract class ThreadingDetectorMixin { return CANT_USE_NULL_IN_NEW_REDIRECT_MIXIN_WHAT_THE_FUCK_SEMAPHORE; } - // why bother with all of that locking crap when a single CAS works? - - // no unique to prevent renames - private volatile Thread moonriseCurrThread; - - @Unique - private static final VarHandle CURR_THREAD_HANDLE = ConcurrentUtil.getVarHandle(ThreadingDetector.class, "moonriseCurrThread", Thread.class); - /** - * @reason Replace with optimised version + * @reason Remove * @author Spottedleaf */ @Overwrite - public void checkAndLock() { - final Thread prev = (Thread)CURR_THREAD_HANDLE.compareAndExchange((ThreadingDetector)(Object)this, (Thread)null, (Thread)Thread.currentThread()); - if (prev != null) { - throw ThreadingDetector.makeThreadingException(this.name, prev); - } - } + public void checkAndLock() {} /** - * @reason Replace with optimised version + * @reason Remove * @author Spottedleaf */ @Overwrite - public void checkAndUnlock() { - final Thread expect = Thread.currentThread(); - - final Thread prev = (Thread)CURR_THREAD_HANDLE.compareAndExchange((ThreadingDetector)(Object)this, expect, (Thread)null); - - if (prev != expect) { - throw ThreadingDetector.makeThreadingException(this.name, prev); - } - } + public void checkAndUnlock() {} }