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": [