Faster Filtering, Faster Voxel Shapes
This commit is contained in:
@@ -0,0 +1,34 @@
|
|||||||
|
package net.gensokyoreimagined.nitori.access;
|
||||||
|
|
||||||
|
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
|
||||||
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.server.level.ChunkHolder;
|
||||||
|
import net.minecraft.server.level.PlayerMap;
|
||||||
|
import net.minecraft.server.level.ServerChunkCache;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.level.ChunkPos;
|
||||||
|
import net.minecraft.util.thread.BlockableEventLoop;
|
||||||
|
import org.apache.commons.lang3.mutable.MutableObject;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||||
|
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||||
|
|
||||||
|
@Mixin(ServerChunkCache.class)
|
||||||
|
public interface IThreadedAnvilChunkStorage {
|
||||||
|
|
||||||
|
@Invoker
|
||||||
|
ChunkHolder invokeGetCurrentChunkHolder(long pos);
|
||||||
|
|
||||||
|
@Invoker
|
||||||
|
ChunkHolder invokeGetChunkHolder(long pos);
|
||||||
|
|
||||||
|
@Accessor
|
||||||
|
ServerLevel getLevel();
|
||||||
|
|
||||||
|
@Accessor
|
||||||
|
PlayerMap getPlayerChunkWatchingManager();
|
||||||
|
|
||||||
|
@Accessor
|
||||||
|
BlockableEventLoop<Runnable> getMainThreadExecutor();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package net.gensokyoreimagined.nitori.access;
|
||||||
|
|
||||||
|
public interface ITypeFilterableList {
|
||||||
|
|
||||||
|
Object[] getBackingArray();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package net.gensokyoreimagined.nitori.mixin.shapes.precompute_shape_arrays;
|
||||||
|
|
||||||
|
import net.minecraft.world.phys.shapes.CubePointRange;
|
||||||
|
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.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
|
@Mixin(CubePointRange.class)
|
||||||
|
public class FractionalDoubleListMixin {
|
||||||
|
@Shadow
|
||||||
|
@Final
|
||||||
|
private int parts;
|
||||||
|
|
||||||
|
private double scale;
|
||||||
|
|
||||||
|
@Inject(method = "<init>(I)V", at = @At("RETURN"))
|
||||||
|
public void initScale(int sectionCount, CallbackInfo ci) {
|
||||||
|
this.scale = 1.0D / this.parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author JellySquid
|
||||||
|
* @reason Replace division with multiplication
|
||||||
|
*/
|
||||||
|
@Overwrite
|
||||||
|
public double getDouble(int position) {
|
||||||
|
return position * this.scale;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,38 +1,58 @@
|
|||||||
package net.gensokyoreimagined.nitori.mixin.shapes.precompute_shape_arrays;
|
package net.gensokyoreimagined.nitori.mixin.shapes.precompute_shape_arrays;
|
||||||
|
|
||||||
//import it.unimi.dsi.fastutil.doubles.DoubleList;
|
import it.unimi.dsi.fastutil.doubles.DoubleList;
|
||||||
//import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
//import net.minecraft.world.phys.shapes.CubePointRange;
|
import net.minecraft.world.phys.shapes.CubePointRange;
|
||||||
//import net.minecraft.world.phys.shapes.CubeVoxelShape;
|
import net.minecraft.world.phys.shapes.CubeVoxelShape;
|
||||||
//import net.minecraft.world.phys.shapes.DiscreteVoxelShape;
|
import net.minecraft.world.phys.shapes.DiscreteVoxelShape;
|
||||||
//import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
//import org.spongepowered.asm.mixin.Overwrite;
|
import org.spongepowered.asm.mixin.Overwrite;
|
||||||
//import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
//import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
//import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
//
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
//@Mixin(CubeVoxelShape.class)
|
|
||||||
//public class SimpleVoxelShapeMixin {
|
import java.lang.reflect.Constructor;
|
||||||
// private static final Direction.Axis[] AXIS = Direction.Axis.values();
|
|
||||||
//
|
@Mixin(CubeVoxelShape.class)
|
||||||
// private DoubleList[] parts;
|
public class SimpleVoxelShapeMixin {
|
||||||
//
|
private static final Direction.Axis[] AXIS = Direction.Axis.values();
|
||||||
// @Inject(method = "<init>", at = @At("RETURN"))
|
|
||||||
// private void CubeVoxelShape(DiscreteVoxelShape voxels, CallbackInfo ci) {
|
private DoubleList[] list;
|
||||||
// this.parts = new DoubleList[AXIS.length];
|
|
||||||
//
|
@Unique
|
||||||
// for (Direction.Axis axis : AXIS) {
|
private static Constructor<CubePointRange> nitori$cubePointRangeConstructor;
|
||||||
// this.parts[axis.ordinal()] = new CubePointRange(voxels.getSize(axis));
|
|
||||||
// }
|
@Unique
|
||||||
// }
|
private CubePointRange nitori$cubePointRange(int sectionCount) {
|
||||||
//
|
try {
|
||||||
// /**
|
if (nitori$cubePointRangeConstructor == null) {
|
||||||
// * @author JellySquid
|
nitori$cubePointRangeConstructor = CubePointRange.class.getDeclaredConstructor(int.class);
|
||||||
// * @reason Use the cached array
|
nitori$cubePointRangeConstructor.setAccessible(true);
|
||||||
// */
|
}
|
||||||
// @Overwrite
|
|
||||||
// public DoubleList getCoords(Direction.Axis axis) {
|
return nitori$cubePointRangeConstructor.newInstance(sectionCount);
|
||||||
// return this.parts[axis.ordinal()];
|
} catch (Exception ex) {
|
||||||
// }
|
throw new AssertionError("Failed to find CubePointRange constructor - " + ex.getMessage(), ex);
|
||||||
//
|
}
|
||||||
//}
|
}
|
||||||
|
|
||||||
|
@Inject(method = "<init>", at = @At("RETURN"))
|
||||||
|
private void onConstructed(DiscreteVoxelShape voxels, CallbackInfo ci) {
|
||||||
|
this.list = new DoubleList[AXIS.length];
|
||||||
|
|
||||||
|
for (Direction.Axis axis : AXIS) {
|
||||||
|
this.list[axis.ordinal()] = nitori$cubePointRange(voxels.getSize(axis));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author JellySquid
|
||||||
|
* @reason Use the cached array
|
||||||
|
*/
|
||||||
|
@Overwrite
|
||||||
|
public DoubleList getCoords(Direction.Axis axis) {
|
||||||
|
return this.list[axis.ordinal()];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
package net.gensokyoreimagined.nitori.mixin.shapes.specialized_shapes;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.doubles.DoubleList;
|
||||||
|
import net.minecraft.core.AxisCycle;
|
||||||
|
import net.minecraft.world.phys.AABB;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.world.phys.shapes.DiscreteVoxelShape;
|
||||||
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Overwrite;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implement faster methods for determining penetration during collision resolution.
|
||||||
|
*/
|
||||||
|
@Mixin(VoxelShape.class)
|
||||||
|
public abstract class VoxelShapeMixin {
|
||||||
|
private static final double POSITIVE_EPSILON = +1.0E-7D;
|
||||||
|
private static final double NEGATIVE_EPSILON = -1.0E-7D;
|
||||||
|
|
||||||
|
@Shadow
|
||||||
|
@Final
|
||||||
|
public DiscreteVoxelShape shape;
|
||||||
|
|
||||||
|
@Shadow
|
||||||
|
public abstract boolean isEmpty();
|
||||||
|
|
||||||
|
@Shadow
|
||||||
|
protected abstract double get(Direction.Axis axis, int index);
|
||||||
|
|
||||||
|
@Shadow
|
||||||
|
public abstract DoubleList getCoords(Direction.Axis axis);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @reason Use optimized implementation which delays searching for coordinates as long as possible
|
||||||
|
* @author JellySquid
|
||||||
|
*/
|
||||||
|
@Overwrite
|
||||||
|
public double collideX(AxisCycle cycleDirection, AABB box, double maxDist) {
|
||||||
|
if (this.isEmpty()) {
|
||||||
|
return maxDist;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Math.abs(maxDist) < POSITIVE_EPSILON) {
|
||||||
|
return 0.0D;
|
||||||
|
}
|
||||||
|
|
||||||
|
AxisCycle cycle = cycleDirection.inverse();
|
||||||
|
|
||||||
|
Direction.Axis axisX = cycle.cycle(Direction.Axis.X);
|
||||||
|
Direction.Axis axisY = cycle.cycle(Direction.Axis.Y);
|
||||||
|
Direction.Axis axisZ = cycle.cycle(Direction.Axis.Z);
|
||||||
|
|
||||||
|
int minY = Integer.MIN_VALUE;
|
||||||
|
int maxY = Integer.MIN_VALUE;
|
||||||
|
int minZ = Integer.MIN_VALUE;
|
||||||
|
int maxZ = Integer.MIN_VALUE;
|
||||||
|
|
||||||
|
int x, y, z;
|
||||||
|
|
||||||
|
double dist;
|
||||||
|
|
||||||
|
if (maxDist > 0.0D) {
|
||||||
|
double max = box.max(axisX);
|
||||||
|
int maxIdx = this.findIndex(axisX, max - POSITIVE_EPSILON);
|
||||||
|
|
||||||
|
int maxX = this.shape.getSize(axisX);
|
||||||
|
|
||||||
|
for (x = maxIdx + 1; x < maxX; ++x) {
|
||||||
|
minY = minY == Integer.MIN_VALUE ? Math.max(0, this.findIndex(axisY, box.min(axisY) + POSITIVE_EPSILON)) : minY;
|
||||||
|
maxY = maxY == Integer.MIN_VALUE ? Math.min(this.shape.getSize(axisY), this.findIndex(axisY, box.max(axisY) - POSITIVE_EPSILON) + 1) : maxY;
|
||||||
|
|
||||||
|
for (y = minY; y < maxY; ++y) {
|
||||||
|
minZ = minZ == Integer.MIN_VALUE ? Math.max(0, this.findIndex(axisZ, box.min(axisZ) + POSITIVE_EPSILON)) : minZ;
|
||||||
|
maxZ = maxZ == Integer.MIN_VALUE ? Math.min(this.shape.getSize(axisZ), this.findIndex(axisZ, box.max(axisZ) - POSITIVE_EPSILON) + 1) : maxZ;
|
||||||
|
|
||||||
|
for (z = minZ; z < maxZ; ++z) {
|
||||||
|
if (this.shape.isFullWide(cycle, x, y, z)) {
|
||||||
|
dist = this.get(axisX, x) - max;
|
||||||
|
|
||||||
|
if (dist >= NEGATIVE_EPSILON) {
|
||||||
|
maxDist = Math.min(maxDist, dist);
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxDist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (maxDist < 0.0D) {
|
||||||
|
double min = box.min(axisX);
|
||||||
|
int minIdx = this.findIndex(axisX, min + POSITIVE_EPSILON);
|
||||||
|
|
||||||
|
for (x = minIdx - 1; x >= 0; --x) {
|
||||||
|
minY = minY == Integer.MIN_VALUE ? Math.max(0, this.findIndex(axisY, box.min(axisY) + POSITIVE_EPSILON)) : minY;
|
||||||
|
maxY = maxY == Integer.MIN_VALUE ? Math.min(this.shape.getSize(axisY), this.findIndex(axisY, box.max(axisY) - POSITIVE_EPSILON) + 1) : maxY;
|
||||||
|
|
||||||
|
for (y = minY; y < maxY; ++y) {
|
||||||
|
minZ = minZ == Integer.MIN_VALUE ? Math.max(0, this.findIndex(axisZ, box.min(axisZ) + POSITIVE_EPSILON)) : minZ;
|
||||||
|
maxZ = maxZ == Integer.MIN_VALUE ? Math.min(this.shape.getSize(axisZ), this.findIndex(axisZ, box.max(axisZ) - POSITIVE_EPSILON) + 1) : maxZ;
|
||||||
|
|
||||||
|
for (z = minZ; z < maxZ; ++z) {
|
||||||
|
if (this.shape.isFullWide(cycle, x, y, z)) {
|
||||||
|
dist = this.get(axisX, x + 1) - min;
|
||||||
|
|
||||||
|
if (dist <= POSITIVE_EPSILON) {
|
||||||
|
maxDist = Math.max(maxDist, dist);
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxDist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxDist;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inlines the lambda passed to MathHelper#binarySearch. Simplifies the implementation very slightly for additional
|
||||||
|
* speed.
|
||||||
|
*
|
||||||
|
* @reason Use faster implementation
|
||||||
|
* @author JellySquid
|
||||||
|
*/
|
||||||
|
@Overwrite
|
||||||
|
public int findIndex(Direction.Axis axis, double coord) {
|
||||||
|
DoubleList list = this.getCoords(axis);
|
||||||
|
|
||||||
|
int size = this.shape.getSize(axis);
|
||||||
|
|
||||||
|
int start = 0;
|
||||||
|
int end = size + 1 - start;
|
||||||
|
|
||||||
|
while (end > 0) {
|
||||||
|
int middle = end / 2;
|
||||||
|
int idx = start + middle;
|
||||||
|
|
||||||
|
if (idx >= 0 && (idx > size || coord < list.getDouble(idx))) {
|
||||||
|
end = middle;
|
||||||
|
} else {
|
||||||
|
start = idx + 1;
|
||||||
|
end -= middle + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return start - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
package net.gensokyoreimagined.nitori.mixin.shapes.specialized_shapes;
|
||||||
|
|
||||||
|
//TODO: Later
|
||||||
|
|
||||||
|
//import me.jellysquid.mods.lithium.common.shapes.VoxelShapeAlignedCuboid;
|
||||||
|
//import me.jellysquid.mods.lithium.common.shapes.VoxelShapeEmpty;
|
||||||
|
//import me.jellysquid.mods.lithium.common.shapes.VoxelShapeSimpleCube;
|
||||||
|
//import net.minecraft.world.phys.shapes.BitSetDiscreteVoxelShape;
|
||||||
|
//import net.minecraft.world.phys.shapes.DiscreteVoxelShape;
|
||||||
|
//import net.minecraft.world.phys.shapes.VoxelShape;
|
||||||
|
//import net.minecraft.world.phys.shapes.Shapes;
|
||||||
|
//import org.spongepowered.asm.mixin.*;
|
||||||
|
//
|
||||||
|
///**
|
||||||
|
// * Shape specialization allows us to optimize comparison logic by guaranteeing certain constraints about the
|
||||||
|
// * configuration of vertices in a given shape. For example, most block shapes consist of only one cuboid and by
|
||||||
|
// * nature, only one voxel. This fact can be taken advantage of to create an optimized implementation which avoids
|
||||||
|
// * scanning over voxels as there are only ever two given vertices in the shape, allowing simple math operations to be
|
||||||
|
// * used for determining intersection and penetration.
|
||||||
|
// * <p>
|
||||||
|
// * In most cases, comparison logic is rather simple as the game often only deals with empty shapes or simple cubes.
|
||||||
|
// * Specialization provides a significant speed-up to entity collision resolution and various other parts of the game
|
||||||
|
// * without needing invasive patches, as we can simply replace the types returned by this class. Modern processors
|
||||||
|
// * (along with the help of the potent JVM) make the cost of dynamic dispatch negligible when compared to the execution
|
||||||
|
// * times of shape comparison methods.
|
||||||
|
// */
|
||||||
|
//@Mixin(Shapes.class)
|
||||||
|
//public abstract class VoxelShapesMixin {
|
||||||
|
// @Mutable
|
||||||
|
// @Shadow
|
||||||
|
// @Final
|
||||||
|
// public static final VoxelShape INFINITY;
|
||||||
|
//
|
||||||
|
// @Mutable
|
||||||
|
// @Shadow
|
||||||
|
// @Final
|
||||||
|
// private static final VoxelShape BLOCK;
|
||||||
|
//
|
||||||
|
// @Mutable
|
||||||
|
// @Shadow
|
||||||
|
// @Final
|
||||||
|
// private static final VoxelShape EMPTY;
|
||||||
|
//
|
||||||
|
// private static final DiscreteVoxelShape FULL_CUBE_VOXELS;
|
||||||
|
//
|
||||||
|
// // Re-initialize the global cached shapes with our specialized ones. This will happen right after all the static
|
||||||
|
// // state has been initialized and before any external classes access it.
|
||||||
|
// static {
|
||||||
|
// // [VanillaCopy] The FULL_CUBE and UNBOUNDED shape is initialized with a single 1x1x1 voxel as neither will
|
||||||
|
// // contain multiple inner cuboids.
|
||||||
|
// FULL_CUBE_VOXELS = new BitSetDiscreteVoxelShape(1, 1, 1);
|
||||||
|
// FULL_CUBE_VOXELS.fill(0, 0, 0);
|
||||||
|
//
|
||||||
|
// // Used in some rare cases to indicate a shape which encompasses the entire world (such as a moving world border)
|
||||||
|
// INFINITY = new VoxelShapeSimpleCube(FULL_CUBE_VOXELS, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY,
|
||||||
|
// Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
|
||||||
|
//
|
||||||
|
// // Represents a full-block cube shape, such as that for a dirt block.
|
||||||
|
// BLOCK = new VoxelShapeSimpleCube(FULL_CUBE_VOXELS, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0);
|
||||||
|
//
|
||||||
|
// // Represents an empty cube shape with no vertices that cannot be collided with.
|
||||||
|
// EMPTY = new VoxelShapeEmpty(new BitSetDiscreteVoxelShape(0, 0, 0));
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * Vanilla implements some very complex logic in this function in order to allow entity boxes to be used in
|
||||||
|
// * collision resolution the same way as block shapes. The specialized simple cube shape however can trivially
|
||||||
|
// * represent these cases with nothing more than the two vertexes. This provides a modest speed up for entity
|
||||||
|
// * collision code by allowing them to also use our optimized shapes.
|
||||||
|
// * <p>
|
||||||
|
// * Vanilla uses different kinds of VoxelShapes depending on the size and position of the box.
|
||||||
|
// * A box that isn't aligned with 1/8th of a block will become a very simple ArrayVoxelShape, while others
|
||||||
|
// * will become a "SimpleVoxelShape" with a BitSetVoxelSet that possibly has a higher resolution (1-3 bits) per axis.
|
||||||
|
// * <p>
|
||||||
|
// * Shapes that have a high resolution (e.g. extended piston base has 2 bits on one axis) have collision
|
||||||
|
// * layers inside them. An upwards extended piston base has extra collision boxes at 0.25 and 0.5 height.
|
||||||
|
// * Slabs don't have extra collision boxes, because they are only as high as the smallest height that is possible
|
||||||
|
// * with their bit resolution (1, so half a block).
|
||||||
|
// *
|
||||||
|
// * @reason Use our optimized shape types
|
||||||
|
// * @author JellySquid, 2No2Name
|
||||||
|
// */
|
||||||
|
// @Overwrite
|
||||||
|
// public static VoxelShape cuboidUnchecked(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
|
||||||
|
// if (maxX - minX < 1.0E-7D || maxY - minY < 1.0E-7D || maxZ - minZ < 1.0E-7D) {
|
||||||
|
// return EMPTY;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// int xRes;
|
||||||
|
// int yRes;
|
||||||
|
// int zRes;
|
||||||
|
// //findRequiredBitResolution(...) looks unnecessarily slow, and it seems to unintentionally return -1 on inputs like -1e-8,
|
||||||
|
// //A faster implementation is not in the scope of this mixin.
|
||||||
|
//
|
||||||
|
// //Description of what vanilla does:
|
||||||
|
// //If the VoxelShape cannot be represented by a BitSet with 3 bit resolution on any axis (BitSetVoxelSet),
|
||||||
|
// //a shape without boxes inside will be used in vanilla (ArrayVoxelShape with only 2 PointPositions on each axis)
|
||||||
|
//
|
||||||
|
// if ((xRes = Shapes.findBits(minX, maxX)) < 0 ||
|
||||||
|
// (yRes = Shapes.findBits(minY, maxY)) < 0 ||
|
||||||
|
// (zRes = Shapes.findBits(minZ, maxZ)) < 0) {
|
||||||
|
// //vanilla uses ArrayVoxelShape here without any rounding of the coordinates
|
||||||
|
// return new VoxelShapeSimpleCube(FULL_CUBE_VOXELS, minX, minY, minZ, maxX, maxY, maxZ);
|
||||||
|
// } else {
|
||||||
|
// if (xRes == 0 && yRes == 0 && zRes == 0) {
|
||||||
|
// return BLOCK;
|
||||||
|
// }
|
||||||
|
// // vanilla would use a SimpleVoxelShape with a BitSetVoxelSet of resolution of xRes, yRes, zRes here, we match its behavior
|
||||||
|
// return new VoxelShapeAlignedCuboid(Math.round(minX * 8D) / 8D, Math.round(minY * 8D) / 8D, Math.round(minZ * 8D) / 8D,
|
||||||
|
// Math.round(maxX * 8D) / 8D, Math.round(maxY * 8D) / 8D, Math.round(maxZ * 8D) / 8D, xRes, yRes, zRes);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package net.gensokyoreimagined.nitori.mixin.vmp.entity.move_zero_velocity;
|
||||||
|
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.entity.MoverType;
|
||||||
|
import net.minecraft.world.phys.AABB;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
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.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
|
@Mixin(Entity.class)
|
||||||
|
public class MixinEntity {
|
||||||
|
|
||||||
|
@Shadow private AABB bb;
|
||||||
|
@Unique
|
||||||
|
private boolean boundingBoxChanged = false;
|
||||||
|
|
||||||
|
@Inject(method = "move", at = @At("HEAD"), cancellable = true)
|
||||||
|
private void onMove(MoverType movementType, Vec3 movement, CallbackInfo ci) {
|
||||||
|
if (!boundingBoxChanged && movement.equals(Vec3.ZERO)) {
|
||||||
|
ci.cancel();
|
||||||
|
boundingBoxChanged = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "setBoundingBox", at = @At("HEAD"))
|
||||||
|
private void onBoundingBoxChanged(AABB boundingBox, CallbackInfo ci) {
|
||||||
|
if (!this.bb.equals(boundingBox)) boundingBoxChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package net.gensokyoreimagined.nitori.mixin.vmp.general.collections;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||||
|
import net.gensokyoreimagined.nitori.access.ITypeFilterableList;
|
||||||
|
import net.minecraft.util.ClassInstanceMultiMap;
|
||||||
|
import org.objectweb.asm.Opcodes;
|
||||||
|
import org.spongepowered.asm.mixin.*;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
@Mixin(value = ClassInstanceMultiMap.class, priority = 1005) // priority compatibility hack for lithium
|
||||||
|
public abstract class MixinTypeFilterableList<T> extends AbstractCollection<T> implements ITypeFilterableList {
|
||||||
|
|
||||||
|
@Mutable
|
||||||
|
@Shadow @Final private Map<Class<?>, List<T>> byClass;
|
||||||
|
|
||||||
|
@Mutable
|
||||||
|
@Shadow @Final private List<T> allInstances;
|
||||||
|
|
||||||
|
@Shadow @Final private Class<T> baseClass;
|
||||||
|
|
||||||
|
@Redirect(method = "<init>", at = @At(value = "FIELD", target = "Lnet/minecraft/util/ClassInstanceMultiMap;byClass:Ljava/util/Map;", opcode = Opcodes.PUTFIELD))
|
||||||
|
private void redirectSetElementsByType(ClassInstanceMultiMap<T> instance, Map<Class<?>, List<T>> value) {
|
||||||
|
this.byClass = new Object2ObjectLinkedOpenHashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Redirect(method = "<init>", at = @At(value = "FIELD", target = "Lnet/minecraft/util/ClassInstanceMultiMap;allInstances:Ljava/util/List;", opcode = Opcodes.PUTFIELD))
|
||||||
|
private void redirectSetAllElements(ClassInstanceMultiMap<T> instance, List<T> value) {
|
||||||
|
this.allInstances = new ObjectArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/Maps;newHashMap()Ljava/util/HashMap;", remap = false))
|
||||||
|
private HashMap<?, ?> redirectNewHashMap() {
|
||||||
|
return null; // avoid unnecessary alloc
|
||||||
|
}
|
||||||
|
|
||||||
|
@Redirect(method = "<init>", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/Lists;newArrayList()Ljava/util/ArrayList;", remap = false))
|
||||||
|
private ArrayList<?> redirectNewArrayList() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object[] getBackingArray() {
|
||||||
|
return ((ObjectArrayList<T>) this.allInstances).elements();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ishland
|
||||||
|
* @reason use fastutil array list for faster iteration & use array for filtering iteration
|
||||||
|
*/
|
||||||
|
@Overwrite
|
||||||
|
public <S> Collection<S> find(Class<S> type) {
|
||||||
|
List<T> cached = this.byClass.get(type);
|
||||||
|
if (cached != null) return (Collection<S>) cached;
|
||||||
|
|
||||||
|
if (!this.baseClass.isAssignableFrom(type)) {
|
||||||
|
throw new IllegalArgumentException("Don't know how to search for " + type);
|
||||||
|
} else {
|
||||||
|
List<? extends T> list = this.byClass.computeIfAbsent(type,
|
||||||
|
typeClass -> {
|
||||||
|
ObjectArrayList<T> ts = new ObjectArrayList<>(this.allInstances.size());
|
||||||
|
for (Object _allElement : ((ObjectArrayList<T>) this.allInstances).elements()) {
|
||||||
|
if (typeClass.isInstance(_allElement)) {
|
||||||
|
ts.add((T) _allElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ts;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return (Collection<S>) list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package net.gensokyoreimagined.nitori.mixin.playerwatching;
|
package net.gensokyoreimagined.nitori.mixin.vmp.playerwatching;
|
||||||
|
|
||||||
import net.minecraft.server.level.ChunkTrackingView;
|
import net.minecraft.server.level.ChunkTrackingView;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package net.gensokyoreimagined.nitori.mixin.playerwatching;
|
package net.gensokyoreimagined.nitori.mixin.vmp.playerwatching;
|
||||||
|
|
||||||
import net.gensokyoreimagined.nitori.common.chunkwatching.PlayerClientVDTracking;
|
import net.gensokyoreimagined.nitori.common.chunkwatching.PlayerClientVDTracking;
|
||||||
import net.minecraft.server.level.ClientInformation;
|
import net.minecraft.server.level.ClientInformation;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package net.gensokyoreimagined.nitori.mixin.playerwatching.optimize_nearby_player_lookups;
|
package net.gensokyoreimagined.nitori.mixin.vmp.playerwatching.optimize_nearby_player_lookups;
|
||||||
|
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.entity.EntityType;
|
import net.minecraft.world.entity.EntityType;
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package net.gensokyoreimagined.nitori.mixin.world.portal_checks;
|
||||||
|
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.level.portal.PortalShape;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.entity.EntityDimensions;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
|
@Mixin({PortalShape.class})
|
||||||
|
public class DisablePortalChecksMixin {
|
||||||
|
@Inject(
|
||||||
|
at = {@At("HEAD")},
|
||||||
|
method = {"findCollisionFreePosition"},
|
||||||
|
cancellable = true
|
||||||
|
)
|
||||||
|
private static void init(Vec3 fallback, ServerLevel world, Entity entity, EntityDimensions dimensions, CallbackInfoReturnable<Vec3> cir) {
|
||||||
|
cir.setReturnValue(fallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -58,11 +58,16 @@
|
|||||||
"math.vec.FastMathVec3DMixin",
|
"math.vec.FastMathVec3DMixin",
|
||||||
"network.microopt.VarIntsMixin",
|
"network.microopt.VarIntsMixin",
|
||||||
"network.microopt.StringEncodingMixin",
|
"network.microopt.StringEncodingMixin",
|
||||||
"playerwatching.MixinChunkFilter",
|
"vmp.playerwatching.MixinChunkFilter",
|
||||||
"playerwatching.MixinServerPlayerEntity",
|
"vmp.playerwatching.MixinServerPlayerEntity",
|
||||||
"playerwatching.optimize_nearby_player_lookups.MixinMobEntity",
|
"vmp.playerwatching.optimize_nearby_player_lookups.MixinMobEntity",
|
||||||
|
"vmp.general.collections.MixinTypeFilterableList",
|
||||||
|
"vmp.entity.move_zero_velocity.MixinEntity",
|
||||||
"shapes.blockstate_cache.BlockMixin",
|
"shapes.blockstate_cache.BlockMixin",
|
||||||
"shapes.lazy_shape_context.EntityShapeContextMixin",
|
"shapes.lazy_shape_context.EntityShapeContextMixin",
|
||||||
|
"shapes.precompute_shape_arrays.FractionalDoubleListMixin",
|
||||||
|
"shapes.precompute_shape_arrays.SimpleVoxelShapeMixin",
|
||||||
|
"shapes.specialized_shapes.VoxelShapeMixin",
|
||||||
"util.MixinLevelBlockEntityRetrieval",
|
"util.MixinLevelBlockEntityRetrieval",
|
||||||
"util.accessors.ClientEntityManagerAccessor",
|
"util.accessors.ClientEntityManagerAccessor",
|
||||||
"util.accessors.EntityTrackingSectionAccessor",
|
"util.accessors.EntityTrackingSectionAccessor",
|
||||||
@@ -75,6 +80,7 @@
|
|||||||
"world.block_entity_ticking.sleeping.furnace.AbstractFurnaceBlockEntityMixin",
|
"world.block_entity_ticking.sleeping.furnace.AbstractFurnaceBlockEntityMixin",
|
||||||
"world.block_entity_ticking.support_cache.BlockEntityMixin",
|
"world.block_entity_ticking.support_cache.BlockEntityMixin",
|
||||||
"world.block_entity_ticking.support_cache.DirectBlockEntityTickInvokerMixin",
|
"world.block_entity_ticking.support_cache.DirectBlockEntityTickInvokerMixin",
|
||||||
"world.block_entity_ticking.support_cache.WorldChunkMixin"
|
"world.block_entity_ticking.support_cache.WorldChunkMixin",
|
||||||
|
"world.portal_checks.DisablePortalChecksMixin"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user