diff --git a/src/main/java/org/embeddedt/modernfix/blockstate/BlockStateCacheHandler.java b/src/main/java/org/embeddedt/modernfix/blockstate/BlockStateCacheHandler.java index 4c439b5d..2cab74b6 100644 --- a/src/main/java/org/embeddedt/modernfix/blockstate/BlockStateCacheHandler.java +++ b/src/main/java/org/embeddedt/modernfix/blockstate/BlockStateCacheHandler.java @@ -2,11 +2,13 @@ package org.embeddedt.modernfix.blockstate; import com.google.common.base.Stopwatch; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.fml.loading.FMLLoader; import org.embeddedt.modernfix.ModernFix; import org.embeddedt.modernfix.core.config.ModernFixConfig; +import org.embeddedt.modernfix.duck.IBlockState; import org.embeddedt.modernfix.util.BakeReason; import java.util.ArrayList; @@ -15,8 +17,6 @@ import java.util.List; import java.util.concurrent.TimeUnit; public class BlockStateCacheHandler { - private static RebuildThread currentRebuildThread = null; - private static boolean needToBake() { BakeReason reason = BakeReason.getCurrentBakeReason(); return !(reason == BakeReason.FREEZE /* startup */ @@ -26,73 +26,11 @@ public class BlockStateCacheHandler { } public static void rebuildParallel(boolean force) { - if(currentRebuildThread != null) { - if(currentRebuildThread.isAlive()) - ModernFix.LOGGER.warn("Interrupting previous blockstate cache rebuild"); - currentRebuildThread.stopRebuild(); - try { - currentRebuildThread.join(10000); - if(currentRebuildThread.isAlive()) - throw new IllegalStateException("Blockstate cache rebuild thread has hung"); - } catch(InterruptedException e) { - throw new RuntimeException("Don't interrupt Minecraft threads", e); - } - ModernFix.LOGGER.debug("Rebuild thread exited"); - currentRebuildThread = null; - } - if(force || needToBake()) { - ArrayList stateList = new ArrayList<>(Block.BLOCK_STATE_REGISTRY.size()); + ModernFix.LOGGER.warn("Clearing blockstate cache"); + synchronized (BlockBehaviour.BlockStateBase.Cache.class) { for (BlockState blockState : Block.BLOCK_STATE_REGISTRY) { - stateList.add(blockState); + ((IBlockState)blockState).clearCache(); } - currentRebuildThread = new RebuildThread(stateList); - if(ModernFixConfig.REBUILD_BLOCKSTATES_ASYNC.get()) - currentRebuildThread.start(); - else { - currentRebuildThread.run(); - currentRebuildThread = null; - } - } else { - ModernFix.LOGGER.debug("Deferred blockstate cache rebuild"); - } - } - - private static class RebuildThread extends Thread { - private boolean stopRebuild = false; - private final List blockStateList; - - public RebuildThread(List statesToInit) { - this.setName("ModernFix blockstate cache rebuild thread"); - this.setPriority(Thread.MIN_PRIORITY + 1); - this.blockStateList = statesToInit; - } - - public void stopRebuild() { - this.stopRebuild = true; - } - - private void rebuildCache() { - Iterator stateIterator = blockStateList.iterator(); - while(!stopRebuild && stateIterator.hasNext()) { - BlockState state = stateIterator.next(); - try { - state.initCache(); - } catch(Exception e) { - ModernFix.LOGGER.warn("Exception encountered while initializing cache", e); - } - } - } - - @Override - public void run() { - ModernFix.waitForWorldLoad(() -> stopRebuild); - if(stopRebuild) - return; - Stopwatch realtimeStopwatch = Stopwatch.createStarted(); - rebuildCache(); - realtimeStopwatch.stop(); - if(!stopRebuild) - ModernFix.LOGGER.info("Blockstate cache rebuilt in " + realtimeStopwatch.elapsed(TimeUnit.MILLISECONDS)/1000f + " seconds"); } } } diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/reduce_blockstate_cache_rebuilds/BlockStateBaseMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/reduce_blockstate_cache_rebuilds/BlockStateBaseMixin.java new file mode 100644 index 00000000..38ea9532 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/reduce_blockstate_cache_rebuilds/BlockStateBaseMixin.java @@ -0,0 +1,58 @@ +package org.embeddedt.modernfix.mixin.perf.reduce_blockstate_cache_rebuilds; + +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockBehaviour; +import org.embeddedt.modernfix.ModernFix; +import org.embeddedt.modernfix.duck.IBlockState; +import org.objectweb.asm.Opcodes; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.concurrent.atomic.AtomicInteger; + +@Mixin(BlockBehaviour.BlockStateBase.class) +public abstract class BlockStateBaseMixin implements IBlockState { + @Shadow public abstract void initCache(); + + @Shadow private BlockBehaviour.BlockStateBase.Cache cache; + + private volatile boolean cacheInvalid = false; + private static boolean buildingCache = false; + private static final ThreadLocal isMakingCache = ThreadLocal.withInitial(() -> false); + @Override + public void clearCache() { + cacheInvalid = true; + } + + @Redirect(method = "*", at = @At( + value = "FIELD", + opcode = Opcodes.GETFIELD, + target = "Lnet/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase;cache:Lnet/minecraft/world/level/block/state/BlockBehaviour$BlockStateBase$Cache;", + ordinal = 0 + )) + private BlockBehaviour.BlockStateBase.Cache initCacheIfNeeded(BlockBehaviour.BlockStateBase base) { + if(cacheInvalid) { + // Ensure that only one block's cache is built at a time + synchronized (BlockBehaviour.BlockStateBase.Cache.class) { + if(cacheInvalid) { + // Ensure that if we end up in here recursively, we just use the original cache + if(!buildingCache) { + buildingCache = true; + try { + this.initCache(); + cacheInvalid = false; + } finally { + buildingCache = false; + } + } + } + + } + } + return this.cache; + } +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/reduce_blockstate_cache_rebuilds/ShapeCacheMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/reduce_blockstate_cache_rebuilds/ShapeCacheMixin.java deleted file mode 100644 index cebb4ac6..00000000 --- a/src/main/java/org/embeddedt/modernfix/mixin/perf/reduce_blockstate_cache_rebuilds/ShapeCacheMixin.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.embeddedt.modernfix.mixin.perf.reduce_blockstate_cache_rebuilds; - -import com.refinedmods.refinedstorage.block.shape.ShapeCache; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.phys.shapes.VoxelShape; -import org.embeddedt.modernfix.ModernFix; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Mutable; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -@Mixin(ShapeCache.class) -public class ShapeCacheMixin { - @Final - @Mutable - @Shadow(remap = false) private static Map CACHE; - - @Inject(method = "", at = @At("TAIL")) - private static void useConcurrentMap(CallbackInfo ci) { - CACHE = new ConcurrentHashMap<>(); - ModernFix.LOGGER.info("Successfully replaced ShapeCache with concurrent map"); - } -} diff --git a/src/main/resources/modernfix.mixins.json b/src/main/resources/modernfix.mixins.json index 34c4569b..d6a7f953 100644 --- a/src/main/resources/modernfix.mixins.json +++ b/src/main/resources/modernfix.mixins.json @@ -14,14 +14,13 @@ "perf.resourcepacks.VanillaPackMixin", "perf.skip_first_datapack_reload.LevelSaveMixin", "perf.skip_first_datapack_reload.SaveFormatAccessor", - "perf.reduce_blockstate_cache_rebuilds.GameDataMixin", - "perf.reduce_blockstate_cache_rebuilds.BlockCallbacksMixin", "perf.boost_worker_count.UtilMixin", "perf.thread_priorities.UtilMixin", "perf.preload_block_classes.GameDataMixin", - "perf.reduce_blockstate_cache_rebuilds.BlocksMixin", + "perf.reduce_blockstate_cache_rebuilds.GameDataMixin", "perf.reduce_blockstate_cache_rebuilds.BlockCallbacksMixin", - "perf.reduce_blockstate_cache_rebuilds.ShapeCacheMixin", + "perf.reduce_blockstate_cache_rebuilds.BlocksMixin", + "perf.reduce_blockstate_cache_rebuilds.BlockStateBaseMixin", "perf.deduplicate_location.MixinResourceLocation", "perf.sync_executor_sleep.SyncExecutorMixin", "perf.compress_biome_container.MixinBiomeContainer",