From 3b0c1d01308b5deb92489207caae252428b6084e Mon Sep 17 00:00:00 2001 From: MicrocontrollersDev Date: Mon, 28 Sep 2026 18:55:47 -0700 Subject: [PATCH 1/3] fix MC-117075 --- .../mixin/bugfix/world/WorldChunkMixin.java | 18 +++++++ .../sarcio/mixin/bugfix/world/WorldMixin.java | 49 +++++++++++++++++-- .../java/dev/rdh/sarcio/util/WorldExt.java | 7 +++ 3 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 src/main/java/dev/rdh/sarcio/util/WorldExt.java diff --git a/src/main/java/dev/rdh/sarcio/mixin/bugfix/world/WorldChunkMixin.java b/src/main/java/dev/rdh/sarcio/mixin/bugfix/world/WorldChunkMixin.java index cda6809..5a58f0d 100644 --- a/src/main/java/dev/rdh/sarcio/mixin/bugfix/world/WorldChunkMixin.java +++ b/src/main/java/dev/rdh/sarcio/mixin/bugfix/world/WorldChunkMixin.java @@ -1,6 +1,14 @@ package dev.rdh.sarcio.mixin.bugfix.world; import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; + +import com.google.common.collect.Iterators; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import dev.rdh.sarcio.util.WorldExt; +import net.minecraft.world.World; import net.minecraft.world.chunk.WorldChunk; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -18,8 +26,18 @@ public class WorldChunkMixin { @Shadow private int lowestHeight; + @Shadow + @Final + private World world; + @Inject(method = "setHeightMap", at = @At("TAIL")) private void sarcio$updateHeightMapMinimum(int[] newHeightMap, CallbackInfo ci) { this.lowestHeight = Arrays.stream(this.heightMap).min().getAsInt(); } + + @WrapOperation(method = "unload", at = @At(value = "INVOKE", target = "Ljava/util/Collection;iterator()Ljava/util/Iterator;", ordinal = 0)) + private Iterator sarcio$unloadTileEntity(Collection instance, Operation> original) { + ((WorldExt) this.world).sarcio$markTileEntitiesInChunkForRemoval((WorldChunk) (Object) this); + return Iterators.emptyIterator(); + } } diff --git a/src/main/java/dev/rdh/sarcio/mixin/bugfix/world/WorldMixin.java b/src/main/java/dev/rdh/sarcio/mixin/bugfix/world/WorldMixin.java index 05c6a28..5eb1485 100644 --- a/src/main/java/dev/rdh/sarcio/mixin/bugfix/world/WorldMixin.java +++ b/src/main/java/dev/rdh/sarcio/mixin/bugfix/world/WorldMixin.java @@ -1,20 +1,42 @@ package dev.rdh.sarcio.mixin.bugfix.world; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import dev.rdh.sarcio.util.WorldExt; +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +import net.minecraft.block.entity.BlockEntity; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkPos; import net.minecraft.world.LightType; import net.minecraft.world.World; +import net.minecraft.world.chunk.WorldChunk; +import org.objectweb.asm.Opcodes; +import org.spongepowered.asm.mixin.Final; 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; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import java.util.List; +import java.util.function.Predicate; + @Mixin(World.class) -public abstract class WorldMixin { - @Shadow public abstract boolean isAreaLoaded(BlockPos center, int radius, boolean allowEmpty); - @Unique private int sarcio$range = 17; +public abstract class WorldMixin implements WorldExt { + @Shadow + public abstract boolean isAreaLoaded(BlockPos center, int radius, boolean allowEmpty); + @Unique + private int sarcio$range = 17; + + @Shadow + @Final + public List tickingBlockEntities; + @Shadow + @Final + public List blockEntities; + @Unique + private LongOpenHashSet sarcio$tileEntitiesChunkToBeRemoved = new LongOpenHashSet(); @Inject(method = "updateLight", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/profiler/Profiler;push(Ljava/lang/String;)V", ordinal = 0)) private void sarcio$updateRange(LightType lightType, BlockPos pos, CallbackInfoReturnable cir) { @@ -30,4 +52,25 @@ public abstract class WorldMixin { private int setVariableRange(int original) { return this.sarcio$range; } + + @Override + public void sarcio$markTileEntitiesInChunkForRemoval(WorldChunk chunk) { + if (!chunk.getBlockEntities().isEmpty()) { + long pos = ChunkPos.toLong(chunk.chunkX, chunk.chunkZ); + this.sarcio$tileEntitiesChunkToBeRemoved.add(pos); + } + } + + @Inject(method = "tickEntities", at = @At(value = "FIELD", target = "Lnet/minecraft/world/World;isTickingBlockEntities:Z", opcode = Opcodes.PUTFIELD, ordinal = 1)) + private void removeInUnloaded(CallbackInfo ci) { + if (!this.sarcio$tileEntitiesChunkToBeRemoved.isEmpty()) { + Predicate isInChunk = (blockEntity) -> { + long tileChunkPos = ChunkPos.toLong(blockEntity.getPos().getX() >> 4, blockEntity.getPos().getZ() >> 4); + return this.sarcio$tileEntitiesChunkToBeRemoved.contains(tileChunkPos); + }; + this.tickingBlockEntities.removeIf(isInChunk); + this.blockEntities.removeIf(isInChunk); + this.sarcio$tileEntitiesChunkToBeRemoved.clear(); + } + } } diff --git a/src/main/java/dev/rdh/sarcio/util/WorldExt.java b/src/main/java/dev/rdh/sarcio/util/WorldExt.java new file mode 100644 index 0000000..d10d779 --- /dev/null +++ b/src/main/java/dev/rdh/sarcio/util/WorldExt.java @@ -0,0 +1,7 @@ +package dev.rdh.sarcio.util; + +import net.minecraft.world.chunk.WorldChunk; + +public interface WorldExt { + void sarcio$markTileEntitiesInChunkForRemoval(WorldChunk chunk); +} From b029a2e6636257aca63bf240fdb576d6b22f7944 Mon Sep 17 00:00:00 2001 From: MicrocontrollersDev Date: Mon, 28 Sep 2026 19:54:06 -0700 Subject: [PATCH 2/3] greatly simplify --- .../mixin/bugfix/world/WorldChunkMixin.java | 12 ----- .../sarcio/mixin/bugfix/world/WorldMixin.java | 46 ++++--------------- .../java/dev/rdh/sarcio/util/WorldExt.java | 7 --- 3 files changed, 8 insertions(+), 57 deletions(-) delete mode 100644 src/main/java/dev/rdh/sarcio/util/WorldExt.java diff --git a/src/main/java/dev/rdh/sarcio/mixin/bugfix/world/WorldChunkMixin.java b/src/main/java/dev/rdh/sarcio/mixin/bugfix/world/WorldChunkMixin.java index 5a58f0d..6c911cd 100644 --- a/src/main/java/dev/rdh/sarcio/mixin/bugfix/world/WorldChunkMixin.java +++ b/src/main/java/dev/rdh/sarcio/mixin/bugfix/world/WorldChunkMixin.java @@ -1,13 +1,7 @@ package dev.rdh.sarcio.mixin.bugfix.world; import java.util.Arrays; -import java.util.Collection; -import java.util.Iterator; -import com.google.common.collect.Iterators; -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import dev.rdh.sarcio.util.WorldExt; import net.minecraft.world.World; import net.minecraft.world.chunk.WorldChunk; import org.spongepowered.asm.mixin.Final; @@ -34,10 +28,4 @@ public class WorldChunkMixin { private void sarcio$updateHeightMapMinimum(int[] newHeightMap, CallbackInfo ci) { this.lowestHeight = Arrays.stream(this.heightMap).min().getAsInt(); } - - @WrapOperation(method = "unload", at = @At(value = "INVOKE", target = "Ljava/util/Collection;iterator()Ljava/util/Iterator;", ordinal = 0)) - private Iterator sarcio$unloadTileEntity(Collection instance, Operation> original) { - ((WorldExt) this.world).sarcio$markTileEntitiesInChunkForRemoval((WorldChunk) (Object) this); - return Iterators.emptyIterator(); - } } diff --git a/src/main/java/dev/rdh/sarcio/mixin/bugfix/world/WorldMixin.java b/src/main/java/dev/rdh/sarcio/mixin/bugfix/world/WorldMixin.java index 5eb1485..fac6cb2 100644 --- a/src/main/java/dev/rdh/sarcio/mixin/bugfix/world/WorldMixin.java +++ b/src/main/java/dev/rdh/sarcio/mixin/bugfix/world/WorldMixin.java @@ -1,43 +1,29 @@ package dev.rdh.sarcio.mixin.bugfix.world; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import dev.rdh.sarcio.util.WorldExt; -import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import net.minecraft.block.entity.BlockEntity; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.ChunkPos; import net.minecraft.world.LightType; import net.minecraft.world.World; -import net.minecraft.world.chunk.WorldChunk; -import org.objectweb.asm.Opcodes; -import org.spongepowered.asm.mixin.Final; +import org.jetbrains.annotations.NotNull; 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; +import org.spongepowered.asm.mixin.injection.ModifyArg; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import java.util.List; -import java.util.function.Predicate; +import java.util.Collection; @Mixin(World.class) -public abstract class WorldMixin implements WorldExt { +public abstract class WorldMixin { @Shadow public abstract boolean isAreaLoaded(BlockPos center, int radius, boolean allowEmpty); @Unique private int sarcio$range = 17; - @Shadow - @Final - public List tickingBlockEntities; - @Shadow - @Final - public List blockEntities; - @Unique - private LongOpenHashSet sarcio$tileEntitiesChunkToBeRemoved = new LongOpenHashSet(); - @Inject(method = "updateLight", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/profiler/Profiler;push(Ljava/lang/String;)V", ordinal = 0)) private void sarcio$updateRange(LightType lightType, BlockPos pos, CallbackInfoReturnable cir) { this.sarcio$range = this.isAreaLoaded(pos, 18, false) ? 17 : 15; @@ -53,24 +39,8 @@ private int setVariableRange(int original) { return this.sarcio$range; } - @Override - public void sarcio$markTileEntitiesInChunkForRemoval(WorldChunk chunk) { - if (!chunk.getBlockEntities().isEmpty()) { - long pos = ChunkPos.toLong(chunk.chunkX, chunk.chunkZ); - this.sarcio$tileEntitiesChunkToBeRemoved.add(pos); - } - } - - @Inject(method = "tickEntities", at = @At(value = "FIELD", target = "Lnet/minecraft/world/World;isTickingBlockEntities:Z", opcode = Opcodes.PUTFIELD, ordinal = 1)) - private void removeInUnloaded(CallbackInfo ci) { - if (!this.sarcio$tileEntitiesChunkToBeRemoved.isEmpty()) { - Predicate isInChunk = (blockEntity) -> { - long tileChunkPos = ChunkPos.toLong(blockEntity.getPos().getX() >> 4, blockEntity.getPos().getZ() >> 4); - return this.sarcio$tileEntitiesChunkToBeRemoved.contains(tileChunkPos); - }; - this.tickingBlockEntities.removeIf(isInChunk); - this.blockEntities.removeIf(isInChunk); - this.sarcio$tileEntitiesChunkToBeRemoved.clear(); - } + @ModifyArg(method = "tickEntities", at = @At(value = "INVOKE", target = "Ljava/util/List;removeAll(Ljava/util/Collection;)Z")) + private Collection sarcio$hashRemovedBlockEntities(@NotNull Collection removed) { + return new ObjectOpenHashSet<>(removed); } } diff --git a/src/main/java/dev/rdh/sarcio/util/WorldExt.java b/src/main/java/dev/rdh/sarcio/util/WorldExt.java deleted file mode 100644 index d10d779..0000000 --- a/src/main/java/dev/rdh/sarcio/util/WorldExt.java +++ /dev/null @@ -1,7 +0,0 @@ -package dev.rdh.sarcio.util; - -import net.minecraft.world.chunk.WorldChunk; - -public interface WorldExt { - void sarcio$markTileEntitiesInChunkForRemoval(WorldChunk chunk); -} From b0cd37b8430de1b8553c84260f76e83089bc0881 Mon Sep 17 00:00:00 2001 From: MicrocontrollersDev Date: Mon, 28 Sep 2026 20:25:58 -0700 Subject: [PATCH 3/3] cleanup diff --- .../dev/rdh/sarcio/mixin/bugfix/world/WorldChunkMixin.java | 6 ------ .../java/dev/rdh/sarcio/mixin/bugfix/world/WorldMixin.java | 6 ++---- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/main/java/dev/rdh/sarcio/mixin/bugfix/world/WorldChunkMixin.java b/src/main/java/dev/rdh/sarcio/mixin/bugfix/world/WorldChunkMixin.java index 6c911cd..cda6809 100644 --- a/src/main/java/dev/rdh/sarcio/mixin/bugfix/world/WorldChunkMixin.java +++ b/src/main/java/dev/rdh/sarcio/mixin/bugfix/world/WorldChunkMixin.java @@ -1,8 +1,6 @@ package dev.rdh.sarcio.mixin.bugfix.world; import java.util.Arrays; - -import net.minecraft.world.World; import net.minecraft.world.chunk.WorldChunk; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -20,10 +18,6 @@ public class WorldChunkMixin { @Shadow private int lowestHeight; - @Shadow - @Final - private World world; - @Inject(method = "setHeightMap", at = @At("TAIL")) private void sarcio$updateHeightMapMinimum(int[] newHeightMap, CallbackInfo ci) { this.lowestHeight = Arrays.stream(this.heightMap).min().getAsInt(); diff --git a/src/main/java/dev/rdh/sarcio/mixin/bugfix/world/WorldMixin.java b/src/main/java/dev/rdh/sarcio/mixin/bugfix/world/WorldMixin.java index fac6cb2..45bd5c6 100644 --- a/src/main/java/dev/rdh/sarcio/mixin/bugfix/world/WorldMixin.java +++ b/src/main/java/dev/rdh/sarcio/mixin/bugfix/world/WorldMixin.java @@ -19,10 +19,8 @@ @Mixin(World.class) public abstract class WorldMixin { - @Shadow - public abstract boolean isAreaLoaded(BlockPos center, int radius, boolean allowEmpty); - @Unique - private int sarcio$range = 17; + @Shadow public abstract boolean isAreaLoaded(BlockPos center, int radius, boolean allowEmpty); + @Unique private int sarcio$range = 17; @Inject(method = "updateLight", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/profiler/Profiler;push(Ljava/lang/String;)V", ordinal = 0)) private void sarcio$updateRange(LightType lightType, BlockPos pos, CallbackInfoReturnable cir) {