From 716342c922695e95e7f2a548fbcfa804e6cdcdc3 Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Sun, 20 Aug 2023 15:56:24 -0700 Subject: [PATCH] Fix incorrect specialCollidingBlocks calculation clientside Need to add a hook to the section read function that only the client calls to actually count, as the client does not invoke recalcBlockCounts on its own. Can't be bothered to document the other changes in this commit. --- .../bitstorage/SimpleBitStorageMixin.java | 16 +- .../chunk_getblock/ChunkAccessMixin.java | 73 +++++ .../mixin/collisions/BlockStateBaseMixin.java | 7 +- .../mixin/collisions/DirectionMixin.java | 4 +- .../collisions/DiscreteVoxelShapeMixin.java | 5 +- .../collisions/LevelChunkSectionMixin.java | 62 +++- .../mixin/collisions/VoxelShapeMixin.java | 31 -- .../mixin/fluid/FlowingFluidMixin.java | 7 +- .../moonrise/mixin/fluid/FluidStateMixin.java | 13 + .../mixin/util_thread_counts/UtilMixin.java | 73 ++++- .../ThreadingDetectorMixin.java | 94 +++++++ .../patches/collisions/CollisionUtil.java | 62 +--- .../collisions/block/CollisionBlockState.java | 1 - .../collisions/shape/CollisionVoxelShape.java | 4 - .../collisions/world/BlockCounter.java | 38 --- .../world/CollisionLevelChunkSection.java | 2 + .../patches/render/VisibilityGraph.java | 265 ++++++++++++++++++ src/main/resources/moonrise.accesswidener | 15 +- src/main/resources/moonrise.mixins.json | 2 + 19 files changed, 615 insertions(+), 159 deletions(-) create mode 100644 src/main/java/ca/spottedleaf/moonrise/mixin/chunk_getblock/ChunkAccessMixin.java create mode 100644 src/main/java/ca/spottedleaf/moonrise/mixin/util_threading_detector/ThreadingDetectorMixin.java delete mode 100644 src/main/java/ca/spottedleaf/moonrise/patches/collisions/world/BlockCounter.java create mode 100644 src/main/java/ca/spottedleaf/moonrise/patches/render/VisibilityGraph.java diff --git a/src/main/java/ca/spottedleaf/moonrise/mixin/bitstorage/SimpleBitStorageMixin.java b/src/main/java/ca/spottedleaf/moonrise/mixin/bitstorage/SimpleBitStorageMixin.java index 90cf697..57c92ce 100644 --- a/src/main/java/ca/spottedleaf/moonrise/mixin/bitstorage/SimpleBitStorageMixin.java +++ b/src/main/java/ca/spottedleaf/moonrise/mixin/bitstorage/SimpleBitStorageMixin.java @@ -11,6 +11,8 @@ 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; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; @Mixin(SimpleBitStorage.class) public abstract class SimpleBitStorageMixin implements BitStorage { @@ -27,6 +29,8 @@ public abstract class SimpleBitStorageMixin implements BitStorage { @Final private int valuesPerLong; + @Unique + private static final VarHandle LONG_ARRAY_HANDLE = MethodHandles.arrayElementVarHandle(long[].class); /* @@ -73,6 +77,7 @@ public abstract class SimpleBitStorageMixin implements BitStorage { @Override public int getAndSet(final int index, final int value) { // assume index/value in range + // note: enforce atomic writes final long magic = this.magic; final int bits = this.bits; final long mul = magic >>> 32; @@ -86,8 +91,9 @@ public abstract class SimpleBitStorageMixin implements BitStorage { final int bitIndex = (index - (dataIndex * this.valuesPerLong)) * bits; final int prev = (int)(data >> bitIndex & mask); + final long write = data & ~(mask << bitIndex) | ((long)value & mask) << bitIndex; - dataArray[dataIndex] = data & ~(mask << bitIndex) | ((long)value & mask) << bitIndex; + LONG_ARRAY_HANDLE.setOpaque(dataArray, dataIndex, write); return prev; } @@ -100,6 +106,8 @@ public abstract class SimpleBitStorageMixin implements BitStorage { @Override public void set(final int index, final int value) { // assume index/value in range + // note: enforce atomic writes + final long magic = this.magic; final int bits = this.bits; final long mul = magic >>> 32; @@ -111,8 +119,9 @@ public abstract class SimpleBitStorageMixin implements BitStorage { final long mask = (1L << bits) - 1; // avoid extra memory read final int bitIndex = (index - (dataIndex * this.valuesPerLong)) * bits; + final long write = data & ~(mask << bitIndex) | ((long)value & mask) << bitIndex; - dataArray[dataIndex] = data & ~(mask << bitIndex) | ((long)value & mask) << bitIndex; + LONG_ARRAY_HANDLE.setOpaque(dataArray, dataIndex, write); } /** @@ -123,13 +132,14 @@ public abstract class SimpleBitStorageMixin implements BitStorage { @Override public int get(final int index) { // assume index in range + // note: enforce atomic reads final long magic = this.magic; final int bits = this.bits; final long mul = magic >>> 32; final int dataIndex = (int)(((long)index * mul) >>> magic); final long mask = (1L << bits) - 1; // avoid extra memory read - final long data = this.data[dataIndex]; + final long data = (long)LONG_ARRAY_HANDLE.getOpaque(this.data, dataIndex); final int bitIndex = (index - (dataIndex * this.valuesPerLong)) * bits; diff --git a/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_getblock/ChunkAccessMixin.java b/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_getblock/ChunkAccessMixin.java new file mode 100644 index 0000000..0f85f8a --- /dev/null +++ b/src/main/java/ca/spottedleaf/moonrise/mixin/chunk_getblock/ChunkAccessMixin.java @@ -0,0 +1,73 @@ +package ca.spottedleaf.moonrise.mixin.chunk_getblock; + +import ca.spottedleaf.moonrise.common.util.WorldUtil; +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; +import net.minecraft.util.Mth; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.LevelHeightAccessor; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeManager; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.level.chunk.LightChunk; +import net.minecraft.world.level.chunk.StructureAccess; +import net.minecraft.world.level.chunk.UpgradeData; +import net.minecraft.world.level.levelgen.blending.BlendingData; +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.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ChunkAccess.class) +public abstract class ChunkAccessMixin implements BlockGetter, BiomeManager.NoiseBiomeSource, LightChunk, StructureAccess { + + @Shadow + @Final + protected LevelChunkSection[] sections; + + + @Unique + private int minSection; + + @Unique + private int maxSection; + + /** + * Initialises the min/max section + */ + @Inject( + method = "", + at = @At("TAIL") + ) + public void onConstruct(ChunkPos chunkPos, UpgradeData upgradeData, LevelHeightAccessor levelHeightAccessor, Registry registry, long l, LevelChunkSection[] levelChunkSections, BlendingData blendingData, CallbackInfo ci) { + this.minSection = WorldUtil.getMinSection(levelHeightAccessor); + this.maxSection = WorldUtil.getMaxSection(levelHeightAccessor); + } + + /** + * @reason Optimise implementation + * @author Spottedleaf + */ + @Override + @Overwrite + public Holder getNoiseBiome(final int biomeX, final int biomeY, final int biomeZ) { + int sectionY = (biomeY >> 2) - this.minSection; + int rel = biomeY & 3; + + if (sectionY < 0) { + sectionY = 0; + rel = 0; + } else if (sectionY >= this.sections.length) { + sectionY = this.sections.length - 1; + rel = 3; + } + + return this.sections[sectionY].getNoiseBiome(biomeX & 3, rel, biomeZ & 3); + } +} diff --git a/src/main/java/ca/spottedleaf/moonrise/mixin/collisions/BlockStateBaseMixin.java b/src/main/java/ca/spottedleaf/moonrise/mixin/collisions/BlockStateBaseMixin.java index e43c085..b998e9d 100644 --- a/src/main/java/ca/spottedleaf/moonrise/mixin/collisions/BlockStateBaseMixin.java +++ b/src/main/java/ca/spottedleaf/moonrise/mixin/collisions/BlockStateBaseMixin.java @@ -1,5 +1,6 @@ package ca.spottedleaf.moonrise.mixin.collisions; +import ca.spottedleaf.moonrise.patches.collisions.CollisionUtil; import ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState; import ca.spottedleaf.moonrise.patches.collisions.shape.CollisionVoxelShape; import com.google.common.collect.ImmutableMap; @@ -35,7 +36,7 @@ public abstract class BlockStateBaseMixin extends StateHolder private static final Direction[] DIRECTIONS_CACHED = Direction.values(); @Unique - private static final AtomicInteger ID_GENERATOR = new AtomicInteger(RANDOM_OFFSET); + private static final AtomicInteger ID_GENERATOR = new AtomicInteger(); @Unique private int id1, id2; @@ -83,8 +84,8 @@ public abstract class BlockStateBaseMixin extends StateHolder ) private void init(final CallbackInfo ci) { // note: murmurHash3 has an inverse, so the field is still unique - this.id1 = HashCommon.murmurHash3(ID_GENERATOR.getAndIncrement()); - this.id2 = HashCommon.murmurHash3(ID_GENERATOR.getAndIncrement()); + this.id1 = HashCommon.murmurHash3(HashCommon.murmurHash3(ID_GENERATOR.getAndIncrement()) + RANDOM_OFFSET); + this.id2 = HashCommon.murmurHash3(HashCommon.murmurHash3(ID_GENERATOR.getAndIncrement()) + RANDOM_OFFSET); } /** diff --git a/src/main/java/ca/spottedleaf/moonrise/mixin/collisions/DirectionMixin.java b/src/main/java/ca/spottedleaf/moonrise/mixin/collisions/DirectionMixin.java index c876d19..0363fdd 100644 --- a/src/main/java/ca/spottedleaf/moonrise/mixin/collisions/DirectionMixin.java +++ b/src/main/java/ca/spottedleaf/moonrise/mixin/collisions/DirectionMixin.java @@ -44,7 +44,7 @@ public abstract class DirectionMixin implements CollisionDirection { private Quaternionf rotation; @Unique - private int id = HashCommon.murmurHash3(((Enum)(Object)this).ordinal() + 1); + private int id; @Unique private int stepX; @@ -69,7 +69,7 @@ public abstract class DirectionMixin implements CollisionDirection { for (final Direction direction : VALUES) { ((DirectionMixin)(Object)direction).opposite = from3DDataValue(((DirectionMixin)(Object)direction).oppositeIndex); ((DirectionMixin)(Object)direction).rotation = ((DirectionMixin)(Object)direction).getRotationUncached(); - ((DirectionMixin)(Object)direction).id = HashCommon.murmurHash3(direction.ordinal() + RANDOM_OFFSET); + ((DirectionMixin)(Object)direction).id = HashCommon.murmurHash3(HashCommon.murmurHash3(direction.ordinal()) + RANDOM_OFFSET); ((DirectionMixin)(Object)direction).stepX = ((DirectionMixin)(Object)direction).normal.getX(); ((DirectionMixin)(Object)direction).stepY = ((DirectionMixin)(Object)direction).normal.getY(); ((DirectionMixin)(Object)direction).stepZ = ((DirectionMixin)(Object)direction).normal.getZ(); diff --git a/src/main/java/ca/spottedleaf/moonrise/mixin/collisions/DiscreteVoxelShapeMixin.java b/src/main/java/ca/spottedleaf/moonrise/mixin/collisions/DiscreteVoxelShapeMixin.java index 3df8aac..6834f9e 100644 --- a/src/main/java/ca/spottedleaf/moonrise/mixin/collisions/DiscreteVoxelShapeMixin.java +++ b/src/main/java/ca/spottedleaf/moonrise/mixin/collisions/DiscreteVoxelShapeMixin.java @@ -5,11 +5,13 @@ import ca.spottedleaf.moonrise.patches.collisions.shape.CollisionDiscreteVoxelSh import net.minecraft.core.Direction; import net.minecraft.world.phys.shapes.DiscreteVoxelShape; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; @Mixin(DiscreteVoxelShape.class) public abstract class DiscreteVoxelShapeMixin implements CollisionDiscreteVoxelShape { // ignore race conditions here: the shape is static, so it doesn't matter + @Unique private CachedShapeData cachedShapeData; @Override @@ -31,12 +33,13 @@ public abstract class DiscreteVoxelShapeMixin implements CollisionDiscreteVoxelS final boolean isEmpty = discreteVoxelShape.isEmpty(); if (!isEmpty) { + final int mulX = sizeZ * sizeY; for (int x = 0; x < sizeX; ++x) { for (int y = 0; y < sizeY; ++y) { for (int z = 0; z < sizeZ; ++z) { if (discreteVoxelShape.isFull(x, y, z)) { // index = z + y*size_z + x*(size_z*size_y) - final int index = z + y * sizeZ + x * sizeZ * sizeY; + final int index = z + y*sizeZ + x*mulX; voxelSet[index >>> 6] |= 1L << index; } diff --git a/src/main/java/ca/spottedleaf/moonrise/mixin/collisions/LevelChunkSectionMixin.java b/src/main/java/ca/spottedleaf/moonrise/mixin/collisions/LevelChunkSectionMixin.java index 1ff9ccb..0f12c82 100644 --- a/src/main/java/ca/spottedleaf/moonrise/mixin/collisions/LevelChunkSectionMixin.java +++ b/src/main/java/ca/spottedleaf/moonrise/mixin/collisions/LevelChunkSectionMixin.java @@ -1,11 +1,12 @@ package ca.spottedleaf.moonrise.mixin.collisions; import ca.spottedleaf.moonrise.patches.collisions.CollisionUtil; -import ca.spottedleaf.moonrise.patches.collisions.world.BlockCounter; import ca.spottedleaf.moonrise.patches.collisions.world.CollisionLevelChunkSection; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.level.chunk.Palette; import net.minecraft.world.level.chunk.PalettedContainer; +import net.minecraft.world.level.material.FluidState; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Overwrite; @@ -13,7 +14,9 @@ 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; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import java.util.function.Predicate; @Mixin(LevelChunkSection.class) public abstract class LevelChunkSectionMixin implements CollisionLevelChunkSection { @@ -31,6 +34,9 @@ public abstract class LevelChunkSectionMixin implements CollisionLevelChunkSecti @Shadow private short tickingFluidCount; + @Shadow + public abstract boolean maybeHas(Predicate predicate); + @Unique private int specialCollidingBlocks; @@ -59,14 +65,56 @@ public abstract class LevelChunkSectionMixin implements CollisionLevelChunkSecti */ @Overwrite public void recalcBlockCounts() { - final BlockCounter counter = new BlockCounter(); + // reset, then recalculate + this.nonEmptyBlockCount = (short)0; + this.tickingBlockCount = (short)0; + this.tickingFluidCount = (short)0; + this.specialCollidingBlocks = (short)0; - this.states.count(counter); + if (this.maybeHas((final BlockState state) -> !state.isAir())) { + final PalettedContainer.Data data = this.states.data; + final Palette palette = data.palette; - this.nonEmptyBlockCount = (short)counter.nonEmptyBlockCount; - this.tickingBlockCount = (short)counter.tickingBlockCount; - this.tickingFluidCount = (short)counter.tickingFluidCount; - this.specialCollidingBlocks = (short)counter.specialCollidingBlocks; + data.storage.getAll((final int paletteIdx) -> { + final BlockState state = palette.valueFor(paletteIdx); + + if (state.isAir()) { + return; + } + + if (CollisionUtil.isSpecialCollidingBlock(state)) { + ++this.specialCollidingBlocks; + } + this.nonEmptyBlockCount += 1; + if (state.isRandomlyTicking()) { + this.tickingBlockCount += 1; + } + + final FluidState fluid = state.getFluidState(); + + if (!fluid.isEmpty()) { + //this.nonEmptyBlockCount += count; // fix vanilla bug: make non empty block count correct + if (fluid.isRandomlyTicking()) { + this.tickingFluidCount += 1; + } + } + }); + } + } + + /** + * @reason recalcBlockCounts is not called on the client, so we need to insert the call somewhere for our collision + * state caches + * @author Spottedleaf + */ + @Inject( + method = "read", + at = @At( + value = "RETURN" + ) + ) + private void callRecalcBlocksClient(final CallbackInfo ci) { + this.recalcBlockCounts(); } @Override 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 b406bd6..de6feb8 100644 --- a/src/main/java/ca/spottedleaf/moonrise/mixin/collisions/VoxelShapeMixin.java +++ b/src/main/java/ca/spottedleaf/moonrise/mixin/collisions/VoxelShapeMixin.java @@ -549,37 +549,6 @@ public abstract class VoxelShapeMixin implements CollisionVoxelShape { return AABB.clip(((VoxelShape)(Object)this).toAabbs(), from, to, offset); } - @Unique - private boolean multiAABBClips(final double fromX, final double fromY, final double fromZ, - final double directionInvX, final double directionInvY, final double directionInvZ, - final double tMax) { - final List aabbs = this.toAabbs(); - for (int i = 0, len = aabbs.size(); i < len; ++i) { - final AABB box = aabbs.get(i); - if (CollisionUtil.clips(box, fromX, fromY, fromZ, directionInvX, directionInvY, directionInvZ, tMax)) { - return true; - } - } - - return false; - } - - @Override - public boolean doesClip(final double fromX, final double fromY, final double fromZ, - final double directionInvX, final double directionInvY, final double directionInvZ, - final double tMax) { - if (this.isEmpty) { - return false; - } - - final AABB singleAABB = this.singleAABBRepresentation; - if (singleAABB != null) { - return CollisionUtil.clips(singleAABB, fromX, fromY, fromZ, directionInvX, directionInvY, directionInvZ, tMax); - } - - return this.multiAABBClips(fromX, fromY, fromZ, directionInvX, directionInvY, directionInvZ, tMax); - } - /** * @reason Cache bounds * @author Spottedleaf diff --git a/src/main/java/ca/spottedleaf/moonrise/mixin/fluid/FlowingFluidMixin.java b/src/main/java/ca/spottedleaf/moonrise/mixin/fluid/FlowingFluidMixin.java index dd8922e..94dd21b 100644 --- a/src/main/java/ca/spottedleaf/moonrise/mixin/fluid/FlowingFluidMixin.java +++ b/src/main/java/ca/spottedleaf/moonrise/mixin/fluid/FlowingFluidMixin.java @@ -70,7 +70,7 @@ public abstract class FlowingFluidMixin extends Fluid { @Shadow - public static short getCacheKey(BlockPos blockPos, BlockPos blockPos2) { + private static short getCacheKey(BlockPos blockPos, BlockPos blockPos2) { return (short)0; } @@ -453,6 +453,11 @@ public abstract class FlowingFluidMixin extends Fluid { directionsWithout.remove(direction); except[direction.ordinal()] = directionsWithout.toArray(new Direction[0]); } + for (int i = 0; i < except.length; ++i) { + if (except[i] == null) { + except[i] = HORIZONTAL_ARRAY; + } + } HORIZONTAL_EXCEPT = except; } diff --git a/src/main/java/ca/spottedleaf/moonrise/mixin/fluid/FluidStateMixin.java b/src/main/java/ca/spottedleaf/moonrise/mixin/fluid/FluidStateMixin.java index 9a01b45..48b28d3 100644 --- a/src/main/java/ca/spottedleaf/moonrise/mixin/fluid/FluidStateMixin.java +++ b/src/main/java/ca/spottedleaf/moonrise/mixin/fluid/FluidStateMixin.java @@ -47,6 +47,9 @@ public abstract class FluidStateMixin extends StateHolder imp @Unique private float ownHeight; + @Unique + private boolean isRandomlyTicking; + @Unique private BlockState legacyBlock; @@ -68,6 +71,7 @@ public abstract class FluidStateMixin extends StateHolder imp this.isEmpty = this.getType().isEmpty(); this.isSource = this.getType().isSource((FluidState)(Object)this); this.ownHeight = this.getType().getOwnHeight((FluidState)(Object)this); + this.isRandomlyTicking = this.getType().isRandomlyTicking(); if (this.getType() instanceof EmptyFluid) { this.classification = FluidClassification.EMPTY; @@ -140,6 +144,15 @@ public abstract class FluidStateMixin extends StateHolder imp return this.legacyBlock = this.getType().createLegacyBlock((FluidState)(Object)this); } + /** + * @reason Use cached result, avoiding indirection + * @author Spottedleaf + */ + @Overwrite + public boolean isRandomlyTicking() { + return this.isRandomlyTicking; + } + @Override public final FluidClassification getClassification() { return this.classification; diff --git a/src/main/java/ca/spottedleaf/moonrise/mixin/util_thread_counts/UtilMixin.java b/src/main/java/ca/spottedleaf/moonrise/mixin/util_thread_counts/UtilMixin.java index 5ad1f33..b41a6f6 100644 --- a/src/main/java/ca/spottedleaf/moonrise/mixin/util_thread_counts/UtilMixin.java +++ b/src/main/java/ca/spottedleaf/moonrise/mixin/util_thread_counts/UtilMixin.java @@ -1,27 +1,40 @@ package ca.spottedleaf.moonrise.mixin.util_thread_counts; +import com.google.common.util.concurrent.MoreExecutors; import net.minecraft.Util; import net.minecraft.util.Mth; +import org.slf4j.Logger; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; @Mixin(Util.class) public abstract class UtilMixin { - /** - * @reason Don't over-allocate executor threads, they may choke the rest of the game - * @author Spottedleaf - */ - @Redirect( - method = "makeExecutor", - at = @At( - value = "INVOKE", - target = "Lnet/minecraft/util/Mth;clamp(III)I" - ) - ) - private static int correctThreadCounts(int value, final int min, final int max) { + @Shadow + @Final + static Logger LOGGER; + + @Shadow + private static int getMaxThreads() { + return 0; + } + + @Shadow + private static void onThreadException(Thread thread, Throwable throwable) { + + } + + @Unique + private static int getThreadCounts(final int min, final int max) { final int cpus = Runtime.getRuntime().availableProcessors() / 2; + + final int value; if (cpus <= 4) { value = cpus <= 2 ? 1 : 2; } else if (cpus <= 8) { @@ -33,4 +46,36 @@ public abstract class UtilMixin { return Mth.clamp(value, min, max); } + + /** + * @reason Don't over-allocate executor threads, they may choke the rest of the game + * Additionally, use a thread pool with a fixed core pool count. This is so that thread-locals are + * not lost due to some timeout, as G1GC has some issues cleaning up those. + * @author Spottedleaf + */ + @Overwrite + private static ExecutorService makeExecutor(final String name) { + final int threads = getThreadCounts(1, getMaxThreads()); + if (threads <= 0) { + return MoreExecutors.newDirectExecutorService(); + } + + final AtomicInteger workerCount = new AtomicInteger(); + + return Executors.newFixedThreadPool(threads, (final Runnable run) -> { + final Thread ret = new Thread(run); + ret.setName("Worker-" + name + "-" + workerCount.getAndIncrement()); + + ret.setUncaughtExceptionHandler((final Thread thread, final Throwable thr) -> { + LOGGER.error(thread.getName() + "died", thr); + + onThreadException(thread, thr); + }); + + ret.setDaemon(true); // forkjoin workers are daemon + ret.setPriority(Thread.NORM_PRIORITY - 1); // de-prioritise over main threads (render/server) + + return 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 new file mode 100644 index 0000000..962e88f --- /dev/null +++ b/src/main/java/ca/spottedleaf/moonrise/mixin/util_threading_detector/ThreadingDetectorMixin.java @@ -0,0 +1,94 @@ +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(); + + @Unique + private static final Semaphore CANT_USE_NULL_IN_NEW_REDIRECT_MIXIN_WHAT_THE_FUCK_SEMAPHORE = new Semaphore(1); + + /** + * @reason The lock is unused after changes in this mixin + * @author Spottedleaf + */ + @Redirect( + method = "", + at = @At( + value = "NEW", + target = "()Ljava/util/concurrent/locks/ReentrantLock;" + ) + ) + private static ReentrantLock nullLock() { + return CANT_USE_NULL_IN_NEW_REDIRECT_MIXIN_WHAT_THE_FUCK_REENTRANTLOCK; + } + + /** + * @reason The semaphore is unused after changes in this mixin + * @author Spottedleaf + */ + @Redirect( + method = "", + at = @At( + value = "NEW", + target = "(I)Ljava/util/concurrent/Semaphore;" + ) + ) + private static Semaphore nullSemaphore(final int p1) { + 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 + * @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); + } + } + + /** + * @reason Replace with optimised version + * @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); + } + } +} diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java b/src/main/java/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java index ea44fe9..529a6eb 100644 --- a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java +++ b/src/main/java/ca/spottedleaf/moonrise/patches/collisions/CollisionUtil.java @@ -1,6 +1,5 @@ package ca.spottedleaf.moonrise.patches.collisions; -import ca.spottedleaf.moonrise.common.util.WorldUtil; import ca.spottedleaf.moonrise.patches.collisions.block.CollisionBlockState; import ca.spottedleaf.moonrise.patches.collisions.entity.CollisionEntity; import ca.spottedleaf.moonrise.patches.collisions.shape.CachedShapeData; @@ -13,9 +12,6 @@ import it.unimi.dsi.fastutil.doubles.DoubleArrayList; import it.unimi.dsi.fastutil.doubles.DoubleList; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; -import net.minecraft.server.level.ServerChunkCache; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.WorldGenRegion; import net.minecraft.util.Mth; import net.minecraft.world.entity.Entity; import net.minecraft.world.item.Item; @@ -26,12 +22,12 @@ import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.border.WorldBorder; import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkSource; import net.minecraft.world.level.chunk.ChunkStatus; import net.minecraft.world.level.chunk.LevelChunkSection; import net.minecraft.world.level.chunk.PalettedContainer; import net.minecraft.world.level.material.FluidState; import net.minecraft.world.phys.AABB; -import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.ArrayVoxelShape; import net.minecraft.world.phys.shapes.BitSetDiscreteVoxelShape; @@ -1643,47 +1639,6 @@ public final class CollisionUtil { return x > y ? x : y; } - /** - * @param box Bounding volume to check - * @param fromX Starting x-coordinate of ray - * @param fromY Starting y-coordinate of ray - * @param fromZ Starting z-coordinate of ray - * @param directionInvX 1.0 / ray x-direction - * @param directionInvY 1.0 / ray y-direction - * @param directionInvZ 1.0 / ray z-direction - * @param tMax Maximum distance along ray direction that the ray can clip - */ - public static boolean clips(final AABB box, - final double fromX, final double fromY, final double fromZ, - final double directionInvX, final double directionInvY, final double directionInvZ, - double tMax) { - /* https://tavianator.com/2022/ray_box_boundary.html */ - - double tMin = 0.0; - - double t1, t2; - - t1 = (box.minX - fromX) * directionInvX; - t2 = (box.maxX - fromX) * directionInvX; - - tMin = min(max(t1, tMin), max(t2, tMin)); - tMax = max(min(t1, tMax), min(t2, tMax)); - - t1 = (box.minY - fromY) * directionInvY; - t2 = (box.maxY - fromY) * directionInvY; - - tMin = min(max(t1, tMin), max(t2, tMin)); - tMax = max(min(t1, tMax), min(t2, tMax)); - - t1 = (box.minZ - fromZ) * directionInvZ; - t2 = (box.maxZ - fromZ) * directionInvZ; - - tMin = min(max(t1, tMin), max(t2, tMin)); - tMax = max(min(t1, tMax), min(t2, tMax)); - - return tMin <= tMax; - } - public static final int COLLISION_FLAG_LOAD_CHUNKS = 1 << 0; public static final int COLLISION_FLAG_COLLIDE_WITH_UNLOADED_CHUNKS = 1 << 1; public static final int COLLISION_FLAG_CHECK_BORDER = 1 << 2; @@ -1700,7 +1655,6 @@ public final class CollisionUtil { if (checkOnly) { return true; } else { - // the collision shape is basically always voxel, don't check it final VoxelShape borderShape = world.getWorldBorder().getCollisionShape(); intoVoxel.add(borderShape); ret = true; @@ -1738,10 +1692,11 @@ public final class CollisionUtil { final int maxChunkZ = maxBlockZ >> 4; final boolean loadChunks = (collisionFlags & COLLISION_FLAG_LOAD_CHUNKS) != 0; + final ChunkSource chunkSource = world.getChunkSource(); for (int currChunkZ = minChunkZ; currChunkZ <= maxChunkZ; ++currChunkZ) { for (int currChunkX = minChunkX; currChunkX <= maxChunkX; ++currChunkX) { - final ChunkAccess chunk = (ChunkAccess)world.getChunk(currChunkX, currChunkZ, ChunkStatus.FULL, loadChunks); + final ChunkAccess chunk = chunkSource.getChunk(currChunkX, currChunkZ, ChunkStatus.FULL, loadChunks); if (chunk == null) { if ((collisionFlags & COLLISION_FLAG_COLLIDE_WITH_UNLOADED_CHUNKS) != 0) { @@ -1768,11 +1723,12 @@ public final class CollisionUtil { // empty continue; } - final PalettedContainer blocks = section.states; final boolean hasSpecial = ((CollisionLevelChunkSection)section).getSpecialCollidingBlocks() != 0; final int sectionAdjust = !hasSpecial ? 1 : 0; + final PalettedContainer blocks = section.states; + final int minXIterate = currChunkX == minChunkX ? (minBlockX & 15) + sectionAdjust : 0; final int maxXIterate = currChunkX == maxChunkX ? (maxBlockX & 15) - sectionAdjust : 15; final int minZIterate = currChunkZ == minChunkZ ? (minBlockZ & 15) + sectionAdjust : 0; @@ -1809,10 +1765,6 @@ public final class CollisionUtil { blockCollision = blockData.getCollisionShape(world, mutablePos, collisionShape); } - if (blockCollision.isEmpty()) { - continue; - } - AABB singleAABB = ((CollisionVoxelShape)blockCollision).getSingleAABBRepresentation(); if (singleAABB != null) { singleAABB = singleAABB.move((double)blockX, (double)blockY, (double)blockZ); @@ -1836,6 +1788,10 @@ public final class CollisionUtil { } } + if (blockCollision.isEmpty()) { + continue; + } + final VoxelShape blockCollisionOffset = blockCollision.move((double)blockX, (double)blockY, (double)blockZ); if (!voxelShapeIntersectNoEmpty(blockCollisionOffset, aabb)) { diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/block/CollisionBlockState.java b/src/main/java/ca/spottedleaf/moonrise/patches/collisions/block/CollisionBlockState.java index e7b60ec..eccb837 100644 --- a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/block/CollisionBlockState.java +++ b/src/main/java/ca/spottedleaf/moonrise/patches/collisions/block/CollisionBlockState.java @@ -26,5 +26,4 @@ public interface CollisionBlockState { public VoxelShape getConstantCollisionShape(); public AABB getConstantCollisionAABB(); - } diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/shape/CollisionVoxelShape.java b/src/main/java/ca/spottedleaf/moonrise/patches/collisions/shape/CollisionVoxelShape.java index 626cb5b..5071305 100644 --- a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/shape/CollisionVoxelShape.java +++ b/src/main/java/ca/spottedleaf/moonrise/patches/collisions/shape/CollisionVoxelShape.java @@ -33,10 +33,6 @@ public interface CollisionVoxelShape { public boolean isFullBlock(); - public boolean doesClip(final double fromX, final double fromY, final double fromZ, - final double directionInvX, final double directionInvY, final double directionInvZ, - final double tMax); - public boolean occludesFullBlock(); public boolean occludesFullBlockIfCached(); diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/world/BlockCounter.java b/src/main/java/ca/spottedleaf/moonrise/patches/collisions/world/BlockCounter.java deleted file mode 100644 index a8e6e61..0000000 --- a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/world/BlockCounter.java +++ /dev/null @@ -1,38 +0,0 @@ -package ca.spottedleaf.moonrise.patches.collisions.world; - -import ca.spottedleaf.moonrise.patches.collisions.CollisionUtil; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.PalettedContainer; -import net.minecraft.world.level.material.FluidState; - -public final class BlockCounter implements PalettedContainer.CountConsumer { - public int nonEmptyBlockCount; - public int tickingBlockCount; - public int tickingFluidCount; - public int specialCollidingBlocks; - - @Override - public void accept(final BlockState state, final int count) { - // our logic - if (CollisionUtil.isSpecialCollidingBlock(state)) { - this.specialCollidingBlocks += count; - } - - // Vanilla logic - if (!state.isAir()) { - this.nonEmptyBlockCount += count; - if (state.isRandomlyTicking()) { - this.tickingBlockCount += count; - } - } - - final FluidState fluid = state.getFluidState(); - - if (!fluid.isEmpty()) { - //this.nonEmptyBlockCount += i; // fix vanilla bug: make non empty block count correct - if (fluid.isRandomlyTicking()) { - this.tickingFluidCount += count; - } - } - } -} \ No newline at end of file diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/world/CollisionLevelChunkSection.java b/src/main/java/ca/spottedleaf/moonrise/patches/collisions/world/CollisionLevelChunkSection.java index 09e79c0..d1f6a7b 100644 --- a/src/main/java/ca/spottedleaf/moonrise/patches/collisions/world/CollisionLevelChunkSection.java +++ b/src/main/java/ca/spottedleaf/moonrise/patches/collisions/world/CollisionLevelChunkSection.java @@ -1,5 +1,7 @@ package ca.spottedleaf.moonrise.patches.collisions.world; +import net.minecraft.world.level.block.state.BlockState; + public interface CollisionLevelChunkSection { public int getSpecialCollidingBlocks(); diff --git a/src/main/java/ca/spottedleaf/moonrise/patches/render/VisibilityGraph.java b/src/main/java/ca/spottedleaf/moonrise/patches/render/VisibilityGraph.java new file mode 100644 index 0000000..f16bcb6 --- /dev/null +++ b/src/main/java/ca/spottedleaf/moonrise/patches/render/VisibilityGraph.java @@ -0,0 +1,265 @@ +package ca.spottedleaf.moonrise.patches.render; + +import net.minecraft.client.renderer.chunk.VisibilitySet; +import net.minecraft.core.Direction; +import java.util.Arrays; + +public final class VisibilityGraph { + + private static final int SECTION_WIDTH = 16; + private static final int LOG2_SECTION_WIDTH = 4; + private static final long LOG2_LONG = 6; + + private static final long[] X_SET_FACES = new long[(SECTION_WIDTH*SECTION_WIDTH*SECTION_WIDTH + (Long.SIZE - 1)) >>> LOG2_LONG]; + + private static final int[] DIRECTIONS_BITSET_BY_INDEX = new int[SECTION_WIDTH*SECTION_WIDTH*SECTION_WIDTH]; + private static final int ALL_DIRECTIONS_BITSET = 0b111_111; + private static final int TOTAL_BLOCKS_FACES; + static { + int set = 0; + for (int y = 0; y < SECTION_WIDTH; ++y) { + for (int z = 0; z < SECTION_WIDTH; ++z) { + for (int x = 0; x < SECTION_WIDTH; ++x) { + if (x != 0 && x != (SECTION_WIDTH - 1) && y != 0 && y != (SECTION_WIDTH - 1) && z != 0 && z != (SECTION_WIDTH - 1)) { + continue; + } + final int idx = x | (z << LOG2_SECTION_WIDTH) | (y << (LOG2_SECTION_WIDTH+LOG2_SECTION_WIDTH)); + final int bitsetIndex = idx >>> LOG2_LONG; + + X_SET_FACES[bitsetIndex] |= (1L << idx); + ++set; + + int bitset = 0; + if (x == 0) { + bitset |= (1 << Direction.WEST.ordinal()); + } + if (x == 15) { + bitset |= (1 << Direction.EAST.ordinal()); + } + + if (y == 0) { + bitset |= (1 << Direction.DOWN.ordinal()); + } + if (y == 15) { + bitset |= (1 << Direction.UP.ordinal()); + } + + if (z == 0) { + bitset |= (1 << Direction.NORTH.ordinal()); + } + if (z == 15) { + bitset |= (1 << Direction.SOUTH.ordinal()); + } + + DIRECTIONS_BITSET_BY_INDEX[idx] = bitset; + } + } + } + + TOTAL_BLOCKS_FACES = set; + } + + private static final int[] INDEX_ADD_BY_DIRECTION_ORDINAL = new int[Direction.values().length]; + private static final int[] OPPOSITE_BITSET_BY_ORIDINAL = new int[Direction.values().length]; + static { + for (final Direction direction : Direction.values()) { + final int x = Math.abs(direction.getStepX()); + final int y = Math.abs(direction.getStepY()); + final int z = Math.abs(direction.getStepZ()); + + int value = x | (z << LOG2_SECTION_WIDTH) | (y << (LOG2_SECTION_WIDTH+LOG2_SECTION_WIDTH)); + if (direction.getAxisDirection() == Direction.AxisDirection.NEGATIVE) { + value = -value; + } + INDEX_ADD_BY_DIRECTION_ORDINAL[direction.ordinal()] = value; + + OPPOSITE_BITSET_BY_ORIDINAL[direction.ordinal()] = 1 << direction.getOpposite().ordinal(); + } + } + + // idx = (axis1 & 15) | ((axis2 & 15) << 4) | ((axis3 & 15) << (4+4)) + + private static final long BLOCKS_PER_LONG = Long.SIZE / SECTION_WIDTH; + + private static final int BITSET_SIZE = (SECTION_WIDTH*SECTION_WIDTH*SECTION_WIDTH + (Long.SIZE - 1)) >>> LOG2_LONG; + + // x z y + private final long[] opaque = new long[BITSET_SIZE]; + + private static final int TOTAL_DIRECTIONS = 6; + + private int opaqueEdgeCount = 0; + + // lower 12 bits: block index + // next 6 bits: propagation direction bitset + private int[] bfsQueue = new int[BITSET_SIZE * 4]; + + // assume (x,y,z) in [0,SECTION_WIDTH-1],[0,SECTION_WIDTH-1],[0,SECTION_WIDTH-1] + // assume that setOpaque for (x,y,z) has not been called since last reset() + public void setOpaque(final int x, final int y, final int z) { + final int idx = x | (z << LOG2_SECTION_WIDTH) | (y << (LOG2_SECTION_WIDTH+LOG2_SECTION_WIDTH));; + + final int bitsetIdx = idx >>> LOG2_LONG; + final long bitsetMask = 1L << idx; + + this.opaque[bitsetIdx] |= bitsetMask; + + this.opaqueEdgeCount += Long.bitCount(X_SET_FACES[bitsetIdx] & bitsetMask); + } + + public void reset() { + if (this.opaqueEdgeCount != 0L) { + Arrays.fill(this.opaque, 0L); + + this.opaqueEdgeCount = 0; + } + } + + private static final long EXCLUDING_EDGES = -1L ^ + ( + (1L << (0L * 16L)) | + (1L << (1L * 16L)) | + (1L << (2L * 16L)) | + (1L << (3L * 16L)) | + + (1L << (15L + 0L * 16L)) | + (1L << (15L + 1L * 16L)) | + (1L << (15L + 2L * 16L)) | + (1L << (15L + 3L * 16L)) + ); + + private static final long EXCLUDING_LEFT_EDGES = -1L ^ + ( + (1L << (0L * 16L)) | + (1L << (1L * 16L)) | + (1L << (2L * 16L)) | + (1L << (3L * 16L)) + ); + + private static final long EXCLUDING_RIGHT_EDGES = -1L ^ + ( + (1L << (15L + 0L * 16L)) | + (1L << (15L + 1L * 16L)) | + (1L << (15L + 2L * 16L)) | + (1L << (15L + 3L * 16L)) + ); + + private static long maskMoveNeighbours(long from, long neighbour) { + from = ~from; + neighbour = ~neighbour; + + // from/neighbour is a bitset where 1 is transparent + + final long leftMask = (EXCLUDING_LEFT_EDGES << 1) & neighbour; + final long rightMask = (EXCLUDING_RIGHT_EDGES >>> 1) & neighbour; + + long ret = from & neighbour; + for (int i = 0; i < (16 - 1); ++i) { + final long left = (ret << 1) & leftMask; + final long right = (ret >>> 1) & rightMask; + + ret = left | right; + } + + return ~ret; + } + + private final int[] resizeBFSQueue() { + return this.bfsQueue = Arrays.copyOf(this.bfsQueue, this.bfsQueue.length * 2); + } + + public VisibilitySet findFaces() { + final VisibilitySet ret = new VisibilitySet(); + if (this.opaqueEdgeCount < (SECTION_WIDTH*SECTION_WIDTH)) { + // impossible for one face to be fully opaque + ret.setAll(true); + return ret; + } + if (this.opaqueEdgeCount >= (TOTAL_BLOCKS_FACES - 1)) { + // impossible for there to be any visibility from one edge to another + ret.setAll(false); + return ret; + } + + final long[] opaque = this.opaque; + int[] queue = this.bfsQueue; + for (int faceBitsetIndex = 0; faceBitsetIndex < 64; ++faceBitsetIndex) { + final long faceMask = X_SET_FACES[faceBitsetIndex]; + for (;;) { + final long value = opaque[faceBitsetIndex]; + final long valueMask = ~value & faceMask; // bitset of transparent blocks on the edge + if (valueMask == 0L) { + break; + } + + int setIndex = Long.numberOfTrailingZeros(valueMask); + // mark visited + opaque[faceBitsetIndex] = value | (1L << setIndex); + + setIndex |= (faceBitsetIndex << 6); + + int directionBitset = DIRECTIONS_BITSET_BY_INDEX[setIndex]; + + int bfsIndex = 0; + int queued = 1; + + queue[0] = setIndex | (directionBitset << (LOG2_SECTION_WIDTH+LOG2_SECTION_WIDTH+LOG2_SECTION_WIDTH)); + + while (bfsIndex < queued) { + final int queuedValue = queue[bfsIndex++]; + // retrieve bits NOT set in the bitset, as this is where we want to propagate (AND it prevents us from propagating out of bounds) + int negatedBitset = (~queuedValue & (ALL_DIRECTIONS_BITSET << (LOG2_SECTION_WIDTH+LOG2_SECTION_WIDTH+LOG2_SECTION_WIDTH))) >>> (LOG2_SECTION_WIDTH+LOG2_SECTION_WIDTH+LOG2_SECTION_WIDTH); + + final int queuedIndex = queuedValue & (SECTION_WIDTH*SECTION_WIDTH*SECTION_WIDTH - 1); + + // propagate to neighbours + // improve branch prediction by using bitCount compared to while (bitset != 0) + for (int i = 0, len = Integer.bitCount(negatedBitset); i < len; ++i) { + final int directionOrdinal = Integer.numberOfTrailingZeros(negatedBitset); + final int add = INDEX_ADD_BY_DIRECTION_ORDINAL[directionOrdinal]; + + negatedBitset ^= -negatedBitset & negatedBitset; // remove trailing bit + + final int neighbourIndex = add + queuedIndex; + + final long bitsetValue = opaque[neighbourIndex >>> LOG2_LONG]; + final int directionBitsetForNeighbour = DIRECTIONS_BITSET_BY_INDEX[neighbourIndex]; + final long neighbourMask = 1L << neighbourIndex; + + final long newBitsetValue = bitsetValue | neighbourMask; + if (newBitsetValue == bitsetValue) { + continue; + } + + final int opposite = OPPOSITE_BITSET_BY_ORIDINAL[directionOrdinal]; + + directionBitset |= directionBitsetForNeighbour; + + opaque[neighbourIndex >>> LOG2_LONG] = newBitsetValue; + if (queued >= queue.length) { + queue = this.resizeBFSQueue(); + } + queue[queued++] = neighbourIndex | ((directionBitsetForNeighbour | opposite) << (LOG2_SECTION_WIDTH+LOG2_SECTION_WIDTH+LOG2_SECTION_WIDTH)); + } + } + + int iter1 = directionBitset; + for (int i = 0, ilen = Integer.bitCount(directionBitset); i < ilen; ++i) { + final int first = Integer.numberOfTrailingZeros(iter1); + iter1 ^= (-iter1 & iter1); + + int iter2 = directionBitset; + for (int k = 0, klen = Integer.bitCount(directionBitset); k < klen; ++k) { + final int second = Integer.numberOfTrailingZeros(iter2); + iter2 ^= (-iter2 & iter2); + + ret.data.set(first + (second * VisibilitySet.FACINGS)); + ret.data.set(second + (first * VisibilitySet.FACINGS)); + } + } + } + } + + return ret; + } +} diff --git a/src/main/resources/moonrise.accesswidener b/src/main/resources/moonrise.accesswidener index c4b3e1a..f8af3d2 100644 --- a/src/main/resources/moonrise.accesswidener +++ b/src/main/resources/moonrise.accesswidener @@ -12,6 +12,13 @@ accessible field net/minecraft/world/level/chunk/LevelChunkSection states Lnet/m # PalettedContainer accessible method net/minecraft/world/level/chunk/PalettedContainer get (I)Ljava/lang/Object; +accessible field net/minecraft/world/level/chunk/PalettedContainer data Lnet/minecraft/world/level/chunk/PalettedContainer$Data; + + +# PalettedContainer.Data +accessible class net/minecraft/world/level/chunk/PalettedContainer$Data +accessible field net/minecraft/world/level/chunk/PalettedContainer$Data storage Lnet/minecraft/util/BitStorage; +accessible field net/minecraft/world/level/chunk/PalettedContainer$Data palette Lnet/minecraft/world/level/chunk/Palette; # ChunkMap @@ -107,4 +114,10 @@ accessible field net/minecraft/world/level/ClipContext fluid Lnet/minecraft/worl # Fluid accessible method net/minecraft/world/level/material/Fluid isEmpty ()Z -accessible method net/minecraft/world/level/material/Fluid createLegacyBlock (Lnet/minecraft/world/level/material/FluidState;)Lnet/minecraft/world/level/block/state/BlockState; \ No newline at end of file +accessible method net/minecraft/world/level/material/Fluid createLegacyBlock (Lnet/minecraft/world/level/material/FluidState;)Lnet/minecraft/world/level/block/state/BlockState; +accessible method net/minecraft/world/level/material/Fluid isRandomlyTicking ()Z + + +# VisibilitySet +accessible field net/minecraft/client/renderer/chunk/VisibilitySet FACINGS I +accessible field net/minecraft/client/renderer/chunk/VisibilitySet data Ljava/util/BitSet; \ No newline at end of file diff --git a/src/main/resources/moonrise.mixins.json b/src/main/resources/moonrise.mixins.json index 6d300fb..16de736 100644 --- a/src/main/resources/moonrise.mixins.json +++ b/src/main/resources/moonrise.mixins.json @@ -6,6 +6,7 @@ "mixins": [ "bitstorage.SimpleBitStorageMixin", "bitstorage.ZeroBitStorageMixin", + "chunk_getblock.ChunkAccessMixin", "chunk_getblock.LevelChunkMixin", "collisions.ArmorStandMixin", "collisions.ArrayVoxelShapeMixin", @@ -51,6 +52,7 @@ "starlight.world.LevelMixin", "starlight.world.ServerWorldMixin", "util_thread_counts.UtilMixin", + "util_threading_detector.ThreadingDetectorMixin", "util_time_source.UtilMixin" ], "client": [