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.
This commit is contained in:
Spottedleaf
2023-09-01 15:15:03 -07:00
parent 48043cd406
commit dc20d1a144
3 changed files with 106 additions and 38 deletions

View File

@@ -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<Entity> getEntities(final Entity entity, final AABB boundingBox, final Predicate<? super Entity> predicate) {
this.getProfiler().incrementCounter("getEntities");
final List<Entity> ret = new ArrayList<>();
@@ -171,7 +175,7 @@ public abstract class LevelMixin implements CollisionLevel, CollisionEntityGette
* @author Spottedleaf
*/
@Override
public <T extends Entity> List<T> getEntitiesOfClass(final Class<T> entityClass, final AABB boundingBox, final Predicate<? super T> predicate) {
public final <T extends Entity> List<T> getEntitiesOfClass(final Class<T> entityClass, final AABB boundingBox, final Predicate<? super T> predicate) {
this.getProfiler().incrementCounter("getEntities");
final List<T> ret = new ArrayList<>();
@@ -185,7 +189,7 @@ public abstract class LevelMixin implements CollisionLevel, CollisionEntityGette
* @author Spottedleaf
*/
@Override
public List<Entity> getHardCollidingEntities(final Entity entity, final AABB box, final Predicate<? super Entity> predicate) {
public final List<Entity> getHardCollidingEntities(final Entity entity, final AABB box, final Predicate<? super Entity> predicate) {
this.getProfiler().incrementCounter("getEntities");
final List<Entity> 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<Vec3> 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<AABB> aabbs = new ArrayList<>();
final List<VoxelShape> 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);
}
}

View File

@@ -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<Vec3> closestPointTo(final Vec3 point) {
if (this.isEmpty) {
return Optional.empty();
}
Vec3 ret = null;
double retDistance = Double.MAX_VALUE;
final List<AABB> 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);
}
}

View File

@@ -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() {}
}