diff --git a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java index 0ccb3991..7320ec90 100644 --- a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java +++ b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java @@ -26,6 +26,7 @@ public class ModernFixEarlyConfig { this.addMixinRule("perf.reduce_blockstate_cache_rebuilds", true); this.addMixinRule("perf.parallelize_model_loading", true); this.addMixinRule("perf.parallelize_model_loading.multipart", false); + this.addMixinRule("perf.cache_strongholds", true); this.addMixinRule("bugfix.concurrency", true); this.addMixinRule("bugfix.edge_chunk_not_saved", true); this.addMixinRule("perf.thread_priorities", true); diff --git a/src/main/java/org/embeddedt/modernfix/duck/IServerLevel.java b/src/main/java/org/embeddedt/modernfix/duck/IServerLevel.java new file mode 100644 index 00000000..34c6b0c8 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/duck/IServerLevel.java @@ -0,0 +1,7 @@ +package org.embeddedt.modernfix.duck; + +import org.embeddedt.modernfix.world.StrongholdLocationCache; + +public interface IServerLevel { + StrongholdLocationCache mfix$getStrongholdCache(); +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/cache_strongholds/ChunkGeneratorMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/cache_strongholds/ChunkGeneratorMixin.java new file mode 100644 index 00000000..a72df465 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/cache_strongholds/ChunkGeneratorMixin.java @@ -0,0 +1,67 @@ +package org.embeddedt.modernfix.mixin.perf.cache_strongholds; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.chunk.ChunkGenerator; +import net.minecraftforge.fml.server.ServerLifecycleHooks; +import org.embeddedt.modernfix.ModernFix; +import org.embeddedt.modernfix.duck.IServerLevel; +import org.embeddedt.modernfix.world.StrongholdLocationCache; +import org.spongepowered.asm.mixin.Final; +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.callback.CallbackInfo; + +import java.util.List; +import java.util.concurrent.ExecutionException; + +@Mixin(ChunkGenerator.class) +public class ChunkGeneratorMixin { + @Shadow @Final private List strongholdPositions; + + @Inject(method = "generateStrongholds", at = @At(value = "INVOKE", target = "Lcom/google/common/collect/Lists;newArrayList()Ljava/util/ArrayList;", ordinal = 0), cancellable = true) + private void useCachedDataIfAvailable(CallbackInfo ci) { + ServerLevel level = searchLevel(); + if(level == null) { + ModernFix.LOGGER.error("Can't find server level for " + this); + return; + } + StrongholdLocationCache cache = ((IServerLevel)level).mfix$getStrongholdCache(); + List positions = cache.getChunkPosList(); + if(positions.isEmpty()) + return; + ModernFix.LOGGER.debug("Loaded stronghold cache for dimension {} with {} positions", level.dimension().location(), positions.size()); + this.strongholdPositions.addAll(positions); + ci.cancel(); + } + + private ServerLevel searchLevel() { + MinecraftServer server = ServerLifecycleHooks.getCurrentServer(); + if(server != null) { + ServerLevel ourLevel = null; + for (ServerLevel level : server.getAllLevels()) { + if (level.getChunkSource().getGenerator() == ((ChunkGenerator) (Object) this)) { + ourLevel = level; + break; + } + } + return ourLevel; + } else + return null; + } + + @Inject(method = "generateStrongholds", at = @At("TAIL")) + private void saveCachedData(CallbackInfo ci) { + if(this.strongholdPositions.size() > 0) { + ServerLevel level = searchLevel(); + if(level != null) { + StrongholdLocationCache cache = ((IServerLevel)level).mfix$getStrongholdCache(); + cache.setChunkPosList(this.strongholdPositions); + ModernFix.LOGGER.debug("Saved stronghold cache for dimension {}", level.dimension().location()); + } + } + } +} diff --git a/src/main/java/org/embeddedt/modernfix/mixin/perf/cache_strongholds/ServerLevelMixin.java b/src/main/java/org/embeddedt/modernfix/mixin/perf/cache_strongholds/ServerLevelMixin.java new file mode 100644 index 00000000..57332c5e --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/mixin/perf/cache_strongholds/ServerLevelMixin.java @@ -0,0 +1,49 @@ +package org.embeddedt.modernfix.mixin.perf.cache_strongholds; + +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.progress.ChunkProgressListener; +import net.minecraft.util.profiling.ProfilerFiller; +import net.minecraft.world.level.CustomSpawner; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.chunk.ChunkGenerator; +import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.storage.DimensionDataStorage; +import net.minecraft.world.level.storage.LevelStorageSource; +import net.minecraft.world.level.storage.ServerLevelData; +import net.minecraft.world.level.storage.WritableLevelData; +import org.embeddedt.modernfix.duck.IServerLevel; +import org.embeddedt.modernfix.world.StrongholdLocationCache; +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.callback.CallbackInfo; + +import java.util.List; +import java.util.concurrent.Executor; +import java.util.function.Supplier; + +@Mixin(ServerLevel.class) +public abstract class ServerLevelMixin extends Level implements IServerLevel { + protected ServerLevelMixin(WritableLevelData arg, ResourceKey arg2, DimensionType arg3, Supplier supplier, boolean bl, boolean bl2, long l) { + super(arg, arg2, arg3, supplier, bl, bl2, l); + } + + @Shadow public abstract DimensionDataStorage getDataStorage(); + + private StrongholdLocationCache mfix$strongholdCache; + + @Inject(method = "", at = @At("RETURN")) + private void addStrongholdCache(MinecraftServer minecraftServer, Executor executor, LevelStorageSource.LevelStorageAccess arg, + ServerLevelData arg2, ResourceKey arg3, DimensionType arg4, ChunkProgressListener arg5, + ChunkGenerator arg6, boolean bl, long l, List list, boolean bl2, CallbackInfo ci) { + mfix$strongholdCache = this.getDataStorage().computeIfAbsent(() -> new StrongholdLocationCache((ServerLevel)(Object)this), StrongholdLocationCache.getFileId(this.dimensionType())); + } + + @Override + public StrongholdLocationCache mfix$getStrongholdCache() { + return mfix$strongholdCache; + } +} diff --git a/src/main/java/org/embeddedt/modernfix/world/StrongholdLocationCache.java b/src/main/java/org/embeddedt/modernfix/world/StrongholdLocationCache.java new file mode 100644 index 00000000..cb408664 --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/world/StrongholdLocationCache.java @@ -0,0 +1,53 @@ +package org.embeddedt.modernfix.world; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.saveddata.SavedData; +import net.minecraftforge.common.util.Constants; + +import java.util.ArrayList; +import java.util.List; + +public class StrongholdLocationCache extends SavedData { + private List chunkPosList; + public StrongholdLocationCache(ServerLevel level) { + super(getFileId(level.dimensionType())); + chunkPosList = new ArrayList<>(); + } + + public List getChunkPosList() { + return new ArrayList<>(chunkPosList); + } + + public void setChunkPosList(List positions) { + this.chunkPosList = new ArrayList<>(positions); + this.setDirty(); + } + + @Override + public void load(CompoundTag arg) { + if(arg.contains("Positions", Constants.NBT.TAG_LONG_ARRAY)) { + long[] positions = arg.getLongArray("Positions"); + for(long position : positions) { + chunkPosList.add(new ChunkPos(position)); + } + } + } + + @Override + public CompoundTag save(CompoundTag compoundTag) { + long[] serialized = new long[chunkPosList.size()]; + for(int i = 0; i < chunkPosList.size(); i++) { + ChunkPos thePos = chunkPosList.get(i); + serialized[i] = thePos.toLong(); + } + compoundTag.putLongArray("Positions", serialized); + return compoundTag; + } + + public static String getFileId(DimensionType dimensionType) { + return "mfix_strongholds" + dimensionType.getFileSuffix(); + } +} diff --git a/src/main/resources/modernfix.mixins.json b/src/main/resources/modernfix.mixins.json index 9f79110d..d2585a16 100644 --- a/src/main/resources/modernfix.mixins.json +++ b/src/main/resources/modernfix.mixins.json @@ -26,7 +26,9 @@ "feature.measure_time.ProfiledReloadInstanceMixin", "feature.measure_time.ReloadableServerResourcesMixin", "feature.branding.BrandingControlMixin", - "perf.fast_registry_validation.ForgeRegistryMixin" + "perf.fast_registry_validation.ForgeRegistryMixin", + "perf.cache_strongholds.ChunkGeneratorMixin", + "perf.cache_strongholds.ServerLevelMixin" ], "client": [ "core.MinecraftMixin",