diff --git a/src/main/java/fermiummixins/config/VanillaConfig.java b/src/main/java/fermiummixins/config/VanillaConfig.java index 46f91a7..420b0e5 100644 --- a/src/main/java/fermiummixins/config/VanillaConfig.java +++ b/src/main/java/fermiummixins/config/VanillaConfig.java @@ -528,6 +528,12 @@ public class VanillaConfig { @Config.RequiresMcRestart @MixinConfig.MixinToggle(earlyMixin = "mixins.fermiummixins.early.vanilla.blockstorageoptifine.json", defaultValue = false) public boolean blockStorageOptifineLagFix = false; + + @Config.Comment("A modern version of \"fixWorldEntityCleanup\" by FoamFix. Made because the one by FoamFix was rarely creating ConcurrentModificationException crashes due to being older than the forge code it fixed.") + @Config.Name("Unload Entities on empty world (Vanilla)") + @Config.RequiresMcRestart + @MixinConfig.MixinToggle(earlyMixin = "mixins.fermiummixins.early.vanilla.unloadentities.json", defaultValue = false) + public boolean patchUpdateEntitiesPatch = false; private Set tippedArrowBlacklistedPotions = null; private List particleRetainCollisionClasses = null; diff --git a/src/main/java/fermiummixins/mixin/vanilla/WorldAccessor.java b/src/main/java/fermiummixins/mixin/vanilla/WorldAccessor.java new file mode 100644 index 0000000..67a71fd --- /dev/null +++ b/src/main/java/fermiummixins/mixin/vanilla/WorldAccessor.java @@ -0,0 +1,17 @@ +package fermiummixins.mixin.vanilla; + +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.List; + +@Mixin(World.class) +public interface WorldAccessor { + @Accessor("processingLoadedTiles") + void setProcessingLoadedTiles(boolean newValue); + + @Accessor("tileEntitiesToBeRemoved") + List getTileEntitiesToBeRemoved(); +} diff --git a/src/main/java/fermiummixins/mixin/vanilla/WorldEntitySpawner_LazyChunksMixin.java b/src/main/java/fermiummixins/mixin/vanilla/WorldEntitySpawner_LazyChunksMixin.java index 40d1dbb..846205b 100644 --- a/src/main/java/fermiummixins/mixin/vanilla/WorldEntitySpawner_LazyChunksMixin.java +++ b/src/main/java/fermiummixins/mixin/vanilla/WorldEntitySpawner_LazyChunksMixin.java @@ -1,11 +1,15 @@ package fermiummixins.mixin.vanilla; -import net.minecraft.util.math.MathHelper; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; +import net.minecraft.util.math.ChunkPos; import net.minecraft.world.WorldEntitySpawner; import net.minecraft.world.WorldServer; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Redirect; + +import java.util.Set; /** * Fix by Nischhelm @@ -13,16 +17,17 @@ @Mixin(WorldEntitySpawner.class) public abstract class WorldEntitySpawner_LazyChunksMixin { - @Redirect( + @WrapOperation( method = "findChunksForSpawning", - at = @At(value="INVOKE",target = "Lnet/minecraft/world/WorldServer;isAnyPlayerWithinRangeAt(DDDD)Z") + at = @At(value="INVOKE",target = "Ljava/util/Set;add(Ljava/lang/Object;)Z") ) - private boolean fermiummixins_vanillaWorldEntitySpawner_findChunksForSpawning(WorldServer instance, double x, double y, double z, double range) { - int x1 = MathHelper.floor(x); - int z1 = MathHelper.floor(z); - - if(!((IWorldInvoker)instance).invokeIsAreaLoaded(x1 - 32, 0, z1 - 32, x1 + 32, 0, z1 + 32, true)) return true; + private boolean fermiummixins_vanillaWorldEntitySpawner_findChunksForSpawning(Set instance, Object obj, Operation original, @Local(argsOnly = true) WorldServer world) { + ChunkPos cpos = (ChunkPos) obj; + int x1 = cpos.x << 4 + 8; + int z1 = cpos.z << 4 + 8; - return instance.isAnyPlayerWithinRangeAt(x, y, z, range); + if(!((IWorldInvoker)world).invokeIsAreaLoaded(x1 - 32, 0, z1 - 32, x1 + 32, 0, z1 + 32, true)) + return false; + return original.call(instance, obj); } } \ No newline at end of file diff --git a/src/main/java/fermiummixins/mixin/vanilla/WorldServer_UnloadEntitiesInEmptyWorld.java b/src/main/java/fermiummixins/mixin/vanilla/WorldServer_UnloadEntitiesInEmptyWorld.java new file mode 100644 index 0000000..dbfa73f --- /dev/null +++ b/src/main/java/fermiummixins/mixin/vanilla/WorldServer_UnloadEntitiesInEmptyWorld.java @@ -0,0 +1,66 @@ +package fermiummixins.mixin.vanilla; + +import net.minecraft.entity.Entity; +import net.minecraft.profiler.Profiler; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.World; +import net.minecraft.world.WorldProvider; +import net.minecraft.world.WorldServer; +import net.minecraft.world.storage.ISaveHandler; +import net.minecraft.world.storage.WorldInfo; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.Collections; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Set; + +@Mixin(WorldServer.class) +public abstract class WorldServer_UnloadEntitiesInEmptyWorld extends World { + protected WorldServer_UnloadEntitiesInEmptyWorld(ISaveHandler saveHandlerIn, WorldInfo info, WorldProvider providerIn, Profiler profilerIn, boolean client) { + super(saveHandlerIn, info, providerIn, profilerIn, client); + } + + //Copied from FoamFix (patch from 2017), modified to not create rare Concurrent Modification Exceptions + @Inject( + method = "updateEntities", + at = @At(value = "RETURN", ordinal = 0) + ) + public void fermiumMixins_vanillaWorldServer_updateEntities(CallbackInfo ci) { + this.profiler.startSection("entities"); + this.profiler.endStartSection("remove"); + this.loadedEntityList.removeAll(this.unloadedEntityList); + + for (Entity entityToUnload : this.unloadedEntityList) + if (entityToUnload.addedToChunk && this.isChunkLoaded(entityToUnload.chunkCoordX, entityToUnload.chunkCoordZ, true)) + this.getChunk(entityToUnload.chunkCoordX, entityToUnload.chunkCoordZ).removeEntity(entityToUnload); + + this.unloadedEntityList.forEach(this::onEntityRemoved); + + this.unloadedEntityList.clear(); + + this.profiler.endStartSection("blockEntities"); + + ((WorldAccessor) this).setProcessingLoadedTiles(true); //FML Move above remove to prevent CMEs + + List tileEntitiesToBeRemoved = ((WorldAccessor) this).getTileEntitiesToBeRemoved(); + + if (!tileEntitiesToBeRemoved.isEmpty()) { + tileEntitiesToBeRemoved.forEach(TileEntity::onChunkUnload); + + // forge: faster "contains" makes this removal much more efficient + Set remove = Collections.newSetFromMap(new IdentityHashMap<>()); + remove.addAll(tileEntitiesToBeRemoved); + + this.tickableTileEntities.removeAll(remove); + this.loadedTileEntityList.removeAll(remove); + tileEntitiesToBeRemoved.clear(); + } + + this.profiler.endSection(); + this.profiler.endSection(); + } +} diff --git a/src/main/java/fermiummixins/util/ModLoadedUtil.java b/src/main/java/fermiummixins/util/ModLoadedUtil.java index 9eca9e0..67a4d9e 100644 --- a/src/main/java/fermiummixins/util/ModLoadedUtil.java +++ b/src/main/java/fermiummixins/util/ModLoadedUtil.java @@ -35,6 +35,7 @@ public abstract class ModLoadedUtil { public static final String ElenaiDodge_MODID = "elenaidodge"; public static final String FancyMenu_MODID = "fancymenu"; public static final String FirstAid_MODID = "firstaid"; + public static final String FoamFix_MODID = "foamfix"; public static final String FoodExpansion_MODID = "foodexpansion"; public static final String ForgottenItems_MODID = "forgottenitems"; public static final String InfernalMobs_MODID = "infernalmobs"; diff --git a/src/main/resources/mixins.fermiummixins.early.vanilla.unloadentities.json b/src/main/resources/mixins.fermiummixins.early.vanilla.unloadentities.json new file mode 100644 index 0000000..da86b3f --- /dev/null +++ b/src/main/resources/mixins.fermiummixins.early.vanilla.unloadentities.json @@ -0,0 +1,12 @@ +{ + "required": true, + "package": "fermiummixins.mixin", + "refmap": "mixins.fermiummixins.refmap.json", + "compatibilityLevel": "JAVA_8", + "target": "@env(DEFAULT)", + "minVersion": "0.8", + "mixins": [ + "vanilla.WorldAccessor", + "vanilla.WorldServer_UnloadEntitiesInEmptyWorld" + ] +} \ No newline at end of file