diff --git a/src/main/java/gregtech/GregTechMod.java b/src/main/java/gregtech/GregTechMod.java index f76989a97d5..7e55e60b889 100644 --- a/src/main/java/gregtech/GregTechMod.java +++ b/src/main/java/gregtech/GregTechMod.java @@ -3,6 +3,7 @@ import gregtech.api.GTValues; import gregtech.api.GregTechAPI; import gregtech.api.modules.ModuleContainerRegistryEvent; +import gregtech.api.persistence.PersistentData; import gregtech.client.utils.BloomEffectUtil; import gregtech.modules.GregTechModules; import gregtech.modules.ModuleManager; @@ -50,6 +51,7 @@ public GregTechMod() { @EventHandler public void onConstruction(FMLConstructionEvent event) { + PersistentData.instance().init(); moduleManager = ModuleManager.getInstance(); GregTechAPI.moduleManager = moduleManager; moduleManager.registerContainer(new GregTechModules()); diff --git a/src/main/java/gregtech/api/persistence/PersistentData.java b/src/main/java/gregtech/api/persistence/PersistentData.java new file mode 100644 index 00000000000..d1fa1ed9b9b --- /dev/null +++ b/src/main/java/gregtech/api/persistence/PersistentData.java @@ -0,0 +1,56 @@ +package gregtech.api.persistence; + +import gregtech.api.GTValues; + +import net.minecraftforge.common.config.Configuration; +import net.minecraftforge.fml.common.Loader; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.nio.file.Path; + +public final class PersistentData { + + public static final String CATEGORY_NAME = "persistent data"; + + private static final PersistentData INSTANCE = new PersistentData(); + private static final String FILE_NAME = "persistent_data.cfg"; + + private @Nullable Configuration config; + + public static @NotNull PersistentData instance() { + return INSTANCE; + } + + private PersistentData() {} + + /** + * @return the persistent data storage + */ + public @NotNull Configuration getConfig() { + if (config == null) { + Path configFolderPath = Loader.instance().getConfigDir().toPath().resolve(GTValues.MODID); + File file = configFolderPath.resolve(FILE_NAME).toFile(); + config = new Configuration(file); + } + return config; + } + + @ApiStatus.Internal + public void init() { + Configuration configuration = getConfig(); + configuration.load(); + String comment = """ + GregTech Persistent Data. Items in this file will persist across game loads. + Modifications to this file may be overwritten by GT. + If you are a modpack author, you should ship this file in releases."""; + configuration.addCustomCategoryComment(CATEGORY_NAME, comment); + + if (configuration.hasChanged()) { + configuration.save(); + } + } +} diff --git a/src/main/java/gregtech/api/recipes/GTRecipeInputCache.java b/src/main/java/gregtech/api/recipes/GTRecipeInputCache.java index c70f328ed1e..5e63b6fd4da 100644 --- a/src/main/java/gregtech/api/recipes/GTRecipeInputCache.java +++ b/src/main/java/gregtech/api/recipes/GTRecipeInputCache.java @@ -1,16 +1,18 @@ package gregtech.api.recipes; import gregtech.api.GTValues; +import gregtech.api.persistence.PersistentData; import gregtech.api.recipes.ingredients.GTRecipeInput; import gregtech.api.util.GTLog; import gregtech.common.ConfigHolder; -import net.minecraftforge.common.config.Config; -import net.minecraftforge.common.config.ConfigManager; +import net.minecraftforge.common.config.Configuration; +import net.minecraftforge.common.config.Property; import it.unimi.dsi.fastutil.Hash; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.Collections; @@ -27,24 +29,27 @@ */ public final class GTRecipeInputCache { - /** - * The minimum size for the cache - */ - public static final int MINIMUM_CACHE_SIZE = 8192; + private static final int MINIMUM_CACHE_SIZE = 1 << 13; + private static final int MAXIMUM_CACHE_SIZE = 1 << 30; + + private static ObjectOpenHashSet instances; - private static ObjectOpenHashSet INSTANCES; + private static final String DATA_NAME = "expectedIngredientInstances"; + private static final String DATA_COMMENT = """ + The expected amount of unique GT recipe ingredients. + This setting improves memory allocation and garbage collection during game-load."""; private GTRecipeInputCache() {} public static boolean isCacheEnabled() { - return INSTANCES != null; + return instances != null; } @ApiStatus.Internal public static void enableCache() { if (!isCacheEnabled()) { int size = calculateOptimalExpectedSize(); - INSTANCES = new ObjectOpenHashSet<>(size); + instances = new ObjectOpenHashSet<>(size); if (ConfigHolder.misc.debug || GTValues.isDeobfEnvironment()) { GTLog.logger.info("GTRecipeInput cache enabled with expected size {}", size); @@ -55,19 +60,31 @@ public static void enableCache() { @ApiStatus.Internal public static void disableCache() { if (isCacheEnabled()) { - int size = INSTANCES.size(); + int size = instances.size(); if (ConfigHolder.misc.debug || GTValues.isDeobfEnvironment()) { GTLog.logger.info("GTRecipeInput cache disabled; releasing {} unique instances", size); } - INSTANCES = null; - - if (ConfigHolder.persistentData.expectedIngredientInstances != size && size > MINIMUM_CACHE_SIZE) { - ConfigHolder.persistentData.expectedIngredientInstances = size; - ConfigManager.sync(GTValues.MODID, Config.Type.INSTANCE); + instances = null; + + if (size >= MINIMUM_CACHE_SIZE && size < MAXIMUM_CACHE_SIZE) { + Configuration config = PersistentData.instance().getConfig(); + Property expected = getExpectedInstanceAmount(config); + + if (expected.getInt() != size) { + expected.set(size); + if (expected.hasChanged()) { + config.save(); + } + } } } } + private static @NotNull Property getExpectedInstanceAmount(@NotNull Configuration configuration) { + return configuration.get(PersistentData.CATEGORY_NAME, DATA_NAME, MINIMUM_CACHE_SIZE, + DATA_COMMENT, MINIMUM_CACHE_SIZE, MAXIMUM_CACHE_SIZE); + } + /** * Tries to deduplicate the instance with previously cached instances. If there is no identical GTRecipeInput * present in cache, the {@code recipeInput} will be put into cache, marked as cached, and returned subsequently. @@ -82,7 +99,7 @@ public static GTRecipeInput deduplicate(GTRecipeInput recipeInput) { if (!isCacheEnabled() || recipeInput.isCached()) { return recipeInput; } - GTRecipeInput cached = INSTANCES.addOrGet(recipeInput); + GTRecipeInput cached = instances.addOrGet(recipeInput); if (cached == recipeInput) { // If recipeInput is cached just now... cached.setCached(); } @@ -129,12 +146,14 @@ public static List deduplicateInputs(List inputs) * @return the optimal expected input cache size */ private static int calculateOptimalExpectedSize() { + int min = getExpectedInstanceAmount(PersistentData.instance().getConfig()) + .getInt(MINIMUM_CACHE_SIZE); for (int i = 13; i < 31; i++) { int sizeToTest = 1 << i; int arraySize = nextHighestPowerOf2((int) (sizeToTest / Hash.DEFAULT_LOAD_FACTOR)); int maxStoredBeforeRehash = (int) (arraySize * Hash.DEFAULT_LOAD_FACTOR); - if (maxStoredBeforeRehash >= ConfigHolder.persistentData.expectedIngredientInstances) { + if (maxStoredBeforeRehash >= min) { return sizeToTest; } } diff --git a/src/main/java/gregtech/common/ConfigHolder.java b/src/main/java/gregtech/common/ConfigHolder.java index 911417a5009..54863fecb57 100644 --- a/src/main/java/gregtech/common/ConfigHolder.java +++ b/src/main/java/gregtech/common/ConfigHolder.java @@ -2,7 +2,6 @@ import gregtech.api.GTValues; import gregtech.api.GregTechAPI; -import gregtech.api.recipes.GTRecipeInputCache; import net.minecraftforge.common.config.Config; @@ -44,10 +43,6 @@ public class ConfigHolder { @Config.RequiresMcRestart public static WorldGenOptions worldgen = new WorldGenOptions(); - @Config.Comment({ "Persistent Data for GT", "Do not modify the contents of this section." }) - @Config.Name("Persistent Data") - public static PersistentData persistentData = new PersistentData(); - public static class MachineOptions { @Config.Comment({ "Whether insufficient energy supply should reset Machine recipe progress to zero.", @@ -706,15 +701,4 @@ public static class NanoSaber { @Config.Comment({ "The EU/t consumption of the NanoSaber.", "Default: 64" }) public int energyConsumption = 64; } - - public static class PersistentData { - - @Config.Comment({ "The expected amount of unique GT recipe ingredients.", - "This setting improves memory allocation and garbage collection during game-load.", - "Do NOT modify this setting. This value is automatically adjusted by GT.", - "Manual changes are not preserved and WILL be overwritten." }) - @Config.RangeInt(min = GTRecipeInputCache.MINIMUM_CACHE_SIZE, max = 1 << 30) - @Config.RequiresMcRestart - public int expectedIngredientInstances = GTRecipeInputCache.MINIMUM_CACHE_SIZE; - } }