From 68b84ade831925bf60528334b83d8efe079a9aae Mon Sep 17 00:00:00 2001 From: Anastasia Chrysanthemum Date: Wed, 25 Feb 2026 09:41:41 +1100 Subject: [PATCH] feat: Delegate config saving to after the game loads. --- CHANGELOG.md | 8 +- .../client/GreenhouseConfigClientFabric.java | 6 +- .../GreenhouseConfigDedicatedFabric.java | 6 +- .../test/GreenhouseConfigTestFabric.java | 6 +- .../GreenhouseConfigClientNeoForge.java | 4 +- .../GreenhouseConfigDedicatedNeoForge.java | 4 +- .../test/GreenhouseConfigTestNeoForge.java | 8 + .../GreenhouseConfigTestClientNeoForge.java | 7 +- templates/v0/greenhouseconfig_test_split.json | 8 + .../v1/greenhouseconfig_test_split.jsonc | 12 ++ .../api/v3/GreenhouseConfigEventPhases.java | 9 +- .../config/impl/GreenhouseConfigImpl.java | 182 ++++++++---------- .../config/test/GreenhouseConfigTest.java | 30 +++ .../config/test/config/SplitConfig.java | 4 +- 14 files changed, 172 insertions(+), 122 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8b1312..87b4271 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,9 @@ ### Build Specific Changes -- Configs now load immediately when registered on Fabric. -- Added getLocalRoot method to GreenhouseInheritedConfigHolder, a shortcut for getLocal().root() -- Removed GreenhouseConfigHolder#loadEarly from public facing API. +- Configs now load immediately upon registration, no matter the loader. +- Config changes on initial load now save after the client/server has initialized, rather than immediately. +- Removed GreenhouseConfigEvents#CONFIG_LOAD_PHASE and replaced it with GreenhouseConfigEvents#CONFIG_SAVE_PHASE. Which now runs after the default phase on Fabric or at lowest priority on NeoForge. +- Fixed a bug introduced within the previous version where data fixes were incorrectly handled when inherited configs are in play. +- Fixed an API oversight that caused an inability to load configs after registration on NeoForge. # Full Changelog This is the v3.0.0 Release of Greenhouse Config, which is additionally the first release to be hosted on the Greenhouse Forgejo instead of GitHub. diff --git a/fabric/src/main/java/lgbt/greenhouse/config/impl/fabric/client/GreenhouseConfigClientFabric.java b/fabric/src/main/java/lgbt/greenhouse/config/impl/fabric/client/GreenhouseConfigClientFabric.java index 6a806f2..c14cf63 100644 --- a/fabric/src/main/java/lgbt/greenhouse/config/impl/fabric/client/GreenhouseConfigClientFabric.java +++ b/fabric/src/main/java/lgbt/greenhouse/config/impl/fabric/client/GreenhouseConfigClientFabric.java @@ -20,9 +20,9 @@ public class GreenhouseConfigClientFabric implements ClientModInitializer { } private static void registerEvents() { - ClientLifecycleEvents.CLIENT_STARTED.addPhaseOrdering(GreenhouseConfigEventPhases.CONFIG_LOAD_PHASE, Event.DEFAULT_PHASE); - ClientLifecycleEvents.CLIENT_STARTED.register(GreenhouseConfigEventPhases.CONFIG_LOAD_PHASE, server -> - GreenhouseConfigImpl.loadConfigs()); + ClientLifecycleEvents.CLIENT_STARTED.addPhaseOrdering(Event.DEFAULT_PHASE, GreenhouseConfigEventPhases.CONFIG_SAVE_PHASE); + ClientLifecycleEvents.CLIENT_STARTED.register(GreenhouseConfigEventPhases.CONFIG_SAVE_PHASE, server -> + GreenhouseConfigImpl.onMinecraftLoad()); ClientPlayConnectionEvents.JOIN.addPhaseOrdering(GreenhouseConfigEventPhases.CONFIG_REGISTRIES_PHASE, Event.DEFAULT_PHASE); ClientPlayConnectionEvents.JOIN.register(GreenhouseConfigEventPhases.CONFIG_REGISTRIES_PHASE, (listener, sender, client) -> { diff --git a/fabric/src/main/java/lgbt/greenhouse/config/impl/fabric/server/dedicated/GreenhouseConfigDedicatedFabric.java b/fabric/src/main/java/lgbt/greenhouse/config/impl/fabric/server/dedicated/GreenhouseConfigDedicatedFabric.java index 8cee217..f8ec007 100644 --- a/fabric/src/main/java/lgbt/greenhouse/config/impl/fabric/server/dedicated/GreenhouseConfigDedicatedFabric.java +++ b/fabric/src/main/java/lgbt/greenhouse/config/impl/fabric/server/dedicated/GreenhouseConfigDedicatedFabric.java @@ -13,9 +13,9 @@ public class GreenhouseConfigDedicatedFabric implements DedicatedServerModInitia } private static void registerEvents() { - ServerLifecycleEvents.SERVER_STARTING.addPhaseOrdering(GreenhouseConfigEventPhases.CONFIG_LOAD_PHASE, Event.DEFAULT_PHASE); - ServerLifecycleEvents.SERVER_STARTING.register(GreenhouseConfigEventPhases.CONFIG_LOAD_PHASE, server -> - GreenhouseConfigImpl.loadConfigs()); + ServerLifecycleEvents.SERVER_STARTING.addPhaseOrdering(Event.DEFAULT_PHASE, GreenhouseConfigEventPhases.CONFIG_SAVE_PHASE); + ServerLifecycleEvents.SERVER_STARTING.register(GreenhouseConfigEventPhases.CONFIG_SAVE_PHASE, server -> + GreenhouseConfigImpl.onMinecraftLoad()); ServerLifecycleEvents.SERVER_STARTED.addPhaseOrdering(GreenhouseConfigEventPhases.CONFIG_REGISTRIES_PHASE, Event.DEFAULT_PHASE); ServerLifecycleEvents.SERVER_STARTED.register(GreenhouseConfigEventPhases.CONFIG_REGISTRIES_PHASE, server -> diff --git a/fabric/src/test/java/lgbt/greenhouse/config/test/GreenhouseConfigTestFabric.java b/fabric/src/test/java/lgbt/greenhouse/config/test/GreenhouseConfigTestFabric.java index d61e071..01aa131 100644 --- a/fabric/src/test/java/lgbt/greenhouse/config/test/GreenhouseConfigTestFabric.java +++ b/fabric/src/test/java/lgbt/greenhouse/config/test/GreenhouseConfigTestFabric.java @@ -7,9 +7,9 @@ public class GreenhouseConfigTestFabric implements ModInitializer { @Override public void onInitialize() { GreenhouseConfigTest.init(); + GreenhouseConfigTest.ITEM_REGISTRY.registerContents(); GreenhouseConfigTest.SOUND_EVENT_REGISTRY.registerContents(); - CommandRegistrationCallback.EVENT.register((dispatcher, ctx, selection) -> { - GreenhouseConfigTest.registerServerCommands(dispatcher); - }); + CommandRegistrationCallback.EVENT.register((dispatcher, ctx, selection) -> + GreenhouseConfigTest.registerServerCommands(dispatcher)); } } diff --git a/neoforge/src/main/java/lgbt/greenhouse/config/impl/neoforge/client/GreenhouseConfigClientNeoForge.java b/neoforge/src/main/java/lgbt/greenhouse/config/impl/neoforge/client/GreenhouseConfigClientNeoForge.java index bc971ed..9686abd 100644 --- a/neoforge/src/main/java/lgbt/greenhouse/config/impl/neoforge/client/GreenhouseConfigClientNeoForge.java +++ b/neoforge/src/main/java/lgbt/greenhouse/config/impl/neoforge/client/GreenhouseConfigClientNeoForge.java @@ -20,9 +20,9 @@ public class GreenhouseConfigClientNeoForge { event.register(SyncGreenhouseConfigClientboundPacket.TYPE, (packet, ctx) -> GreenhouseConfigClientPacketHandlers.handleConfigSync(packet)); } - @SubscribeEvent(priority = EventPriority.HIGHEST) + @SubscribeEvent(priority = EventPriority.LOWEST) public static void onFmlClientSetup(FMLClientSetupEvent event) { - GreenhouseConfigImpl.loadConfigs(); + event.enqueueWork(GreenhouseConfigImpl::onMinecraftLoad); } @SubscribeEvent(priority = EventPriority.HIGHEST) diff --git a/neoforge/src/main/java/lgbt/greenhouse/config/impl/neoforge/server/dedicated/GreenhouseConfigDedicatedNeoForge.java b/neoforge/src/main/java/lgbt/greenhouse/config/impl/neoforge/server/dedicated/GreenhouseConfigDedicatedNeoForge.java index 9ea9f8a..31daaf8 100644 --- a/neoforge/src/main/java/lgbt/greenhouse/config/impl/neoforge/server/dedicated/GreenhouseConfigDedicatedNeoForge.java +++ b/neoforge/src/main/java/lgbt/greenhouse/config/impl/neoforge/server/dedicated/GreenhouseConfigDedicatedNeoForge.java @@ -12,9 +12,9 @@ import net.neoforged.neoforge.event.server.ServerStoppedEvent; @EventBusSubscriber(modid = GreenhouseConfigConstants.MOD_ID, value = Dist.DEDICATED_SERVER) public class GreenhouseConfigDedicatedNeoForge { - @SubscribeEvent(priority = EventPriority.HIGHEST) + @SubscribeEvent(priority = EventPriority.LOWEST) public static void onFmlDedicatedServerSetup(FMLDedicatedServerSetupEvent event) { - GreenhouseConfigImpl.loadConfigs(); + event.enqueueWork(GreenhouseConfigImpl::onMinecraftLoad); } @SubscribeEvent(priority = EventPriority.HIGHEST) diff --git a/neoforge/src/test/java/lgbt/greenhouse/config/test/GreenhouseConfigTestNeoForge.java b/neoforge/src/test/java/lgbt/greenhouse/config/test/GreenhouseConfigTestNeoForge.java index a24a762..0978df2 100644 --- a/neoforge/src/test/java/lgbt/greenhouse/config/test/GreenhouseConfigTestNeoForge.java +++ b/neoforge/src/test/java/lgbt/greenhouse/config/test/GreenhouseConfigTestNeoForge.java @@ -5,6 +5,7 @@ import net.neoforged.bus.api.IEventBus; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.fml.common.Mod; +import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; import net.neoforged.neoforge.event.RegisterCommandsEvent; import net.neoforged.neoforge.registries.RegisterEvent; @@ -12,6 +13,10 @@ import net.neoforged.neoforge.registries.RegisterEvent; @EventBusSubscriber(modid = GreenhouseConfigTest.MOD_ID) public class GreenhouseConfigTestNeoForge { public GreenhouseConfigTestNeoForge(IEventBus bus) { + } + + @SubscribeEvent + public static void onCommonSetup(FMLCommonSetupEvent event) { GreenhouseConfigTest.init(); } @@ -22,6 +27,9 @@ public class GreenhouseConfigTestNeoForge { @SubscribeEvent public static void registerContents(RegisterEvent event) { + if (event.getRegistryKey() == Registries.ITEM) { + GreenhouseConfigTest.ITEM_REGISTRY.registerContents(); + } if (event.getRegistryKey() == Registries.SOUND_EVENT) { GreenhouseConfigTest.SOUND_EVENT_REGISTRY.registerContents(); } diff --git a/neoforge/src/test/java/lgbt/greenhouse/config/test/client/GreenhouseConfigTestClientNeoForge.java b/neoforge/src/test/java/lgbt/greenhouse/config/test/client/GreenhouseConfigTestClientNeoForge.java index 0e15ea7..1abece9 100644 --- a/neoforge/src/test/java/lgbt/greenhouse/config/test/client/GreenhouseConfigTestClientNeoForge.java +++ b/neoforge/src/test/java/lgbt/greenhouse/config/test/client/GreenhouseConfigTestClientNeoForge.java @@ -7,6 +7,7 @@ import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.ModContainer; import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.fml.common.Mod; +import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; import net.neoforged.neoforge.client.event.RegisterClientCommandsEvent; import net.neoforged.neoforge.client.gui.IConfigScreenFactory; @@ -14,10 +15,14 @@ import net.neoforged.neoforge.client.gui.IConfigScreenFactory; @EventBusSubscriber(modid = GreenhouseConfigTest.MOD_ID, value = Dist.CLIENT) public class GreenhouseConfigTestClientNeoForge { public GreenhouseConfigTestClientNeoForge(ModContainer container) { - GreenhouseConfigTestClient.init(); container.registerExtensionPoint(IConfigScreenFactory.class, (modContainer, screen) -> new GreenhouseConfigTestScreen(screen)); } + @SubscribeEvent + public static void onClientSetup(FMLClientSetupEvent event) { + GreenhouseConfigTestClient.init(); + } + @SubscribeEvent public static void registerClientCommands(RegisterClientCommandsEvent event) { GreenhouseConfigTestClient.registerClientCommands(event.getDispatcher()); diff --git a/templates/v0/greenhouseconfig_test_split.json b/templates/v0/greenhouseconfig_test_split.json index df74309..0e88889 100644 --- a/templates/v0/greenhouseconfig_test_split.json +++ b/templates/v0/greenhouseconfig_test_split.json @@ -9,6 +9,14 @@ "f": "f", "g": "g" }, + "vanilla_item_stack": { + "id": "minecraft:enchanted_golden_apple", + "count": 1 + }, + "modded_item_stack": { + "id": "greenhouseconfig_test:test", + "count": 1 + }, "play_cow_sounds_constantly": false, "play_cat_sounds_constantly": false } \ No newline at end of file diff --git a/templates/v1/greenhouseconfig_test_split.jsonc b/templates/v1/greenhouseconfig_test_split.jsonc index a8f60c9..6a82e11 100644 --- a/templates/v1/greenhouseconfig_test_split.jsonc +++ b/templates/v1/greenhouseconfig_test_split.jsonc @@ -20,6 +20,18 @@ // Default Value: "g" "g": "g" }, + // An item stack of an item from the vanilla game. + // Useful for testing static registries! + "vanilla_item_stack": { + "id": "minecraft:enchanted_golden_apple", + "count": 1 + }, + // An item stack of an item from the test mod. + // Useful for testing static registries! + "modded_item_stack": { + "id": "greenhouseconfig_test:test", + "count": 1 + }, // Whether to play cow sounds locally at the player's position constantly // Default Value: false "play_cow_sounds_constantly": false, diff --git a/xplat/src/main/java/lgbt/greenhouse/config/api/v3/GreenhouseConfigEventPhases.java b/xplat/src/main/java/lgbt/greenhouse/config/api/v3/GreenhouseConfigEventPhases.java index cfb0b04..887d823 100644 --- a/xplat/src/main/java/lgbt/greenhouse/config/api/v3/GreenhouseConfigEventPhases.java +++ b/xplat/src/main/java/lgbt/greenhouse/config/api/v3/GreenhouseConfigEventPhases.java @@ -1,7 +1,6 @@ package lgbt.greenhouse.config.api.v3; import lgbt.greenhouse.config.impl.GreenhouseConfigConstants; -import net.minecraft.core.RegistryAccess; import net.minecraft.resources.Identifier; public class GreenhouseConfigEventPhases { @@ -15,13 +14,13 @@ public class GreenhouseConfigEventPhases { public static final Identifier LATE_REGISTRY_DEPOPULATION_PHASE = GreenhouseConfigConstants.id("late_registry_depopulation"); /** - * An event phase used on Fabric that runs before the default event phase. + * An event phase used on Fabric that runs after the default event phase. *

- * This is used when loading configs initially and when reloading configs via {@link GreenhouseConfigHolder#reload(RegistryAccess)} or {@link GreenhouseConfigHolder#save(Object, RegistryAccess)}. + * This is used when saving changes within configs upon client/server load. *

- * The NeoForge equivalent instead runs at HIGHEST priority. + * The NeoForge equivalent instead runs at LOWEST priority. */ - public static final Identifier CONFIG_LOAD_PHASE = GreenhouseConfigConstants.id("config_load"); + public static final Identifier CONFIG_SAVE_PHASE = GreenhouseConfigConstants.id("close_registration"); /** * An event phase used on Fabric that runs before the default event phase. diff --git a/xplat/src/main/java/lgbt/greenhouse/config/impl/GreenhouseConfigImpl.java b/xplat/src/main/java/lgbt/greenhouse/config/impl/GreenhouseConfigImpl.java index 31a19b2..a5eb369 100644 --- a/xplat/src/main/java/lgbt/greenhouse/config/impl/GreenhouseConfigImpl.java +++ b/xplat/src/main/java/lgbt/greenhouse/config/impl/GreenhouseConfigImpl.java @@ -23,7 +23,6 @@ import lgbt.greenhouse.config.impl.config.AbstractGreenhouseConfigHolderImpl; import lgbt.greenhouse.config.impl.config.builder.AbstractGreenhouseConfigHolderBuilderImpl; import lgbt.greenhouse.config.impl.config.builder.ConfigRecordBuilderImpl; import lgbt.greenhouse.config.impl.network.clientbound.SyncGreenhouseConfigClientboundPacket; -import lgbt.greenhouse.config.impl.platform.Platform; import lgbt.greenhouse.config.impl.platform.side.Side; import lgbt.greenhouse.config.impl.util.GreenhouseConfigLangExecutor; import lgbt.greenhouse.config.impl.util.MetaFileData; @@ -33,6 +32,8 @@ import net.minecraft.core.Holder; import net.minecraft.core.HolderSet; import net.minecraft.core.RegistryAccess; import org.jspecify.annotations.Nullable; +import org.slf4j.event.Level; +import org.slf4j.spi.LoggingEventBuilder; import java.io.*; import java.nio.ByteBuffer; @@ -53,13 +54,14 @@ public class GreenhouseConfigImpl { private static final Map> CONFIG_HOLDER_REGISTRATION = new HashMap<>(); private static final Map> CONFIG_HOLDERS = new HashMap<>(); + private static final Set> CONFIGS_TO_SAVE = new HashSet<>(); + private static final Map, ExceptionEntry> CONFIG_EXCEPTIONS = new HashMap<>(); + private static final Map, Object> LOCAL_CONFIGS = new HashMap<>(); private static final Map, Object> NETWORKABLE_CONFIGS = new HashMap<>(); private static final Map, GreenhouseConfigLangExecutor> EXECUTORS = new HashMap<>(); - private static final String CONFIG_LANG_ERROR = "This is not a fault of the developer implementing the config, Please report this to https://git.greenhouse.lgbt/Modding/polyamory or wherever the repository of the config language they used is instead"; - public static > H registerConfig(H configHolder, GreenhouseConfigSide side) { if (!registrationOpen) throw new RuntimeException("Unable to register a new config at this point in time"); @@ -70,9 +72,7 @@ public class GreenhouseConfigImpl { return configList; }); - if (GreenhouseConfigConstants.getPlatformHelper().getPlatform() == Platform.FABRIC) { - loadEarly(configHolder.getModId()); - } + register(configHolder.getModId()); return configHolder; } @@ -201,7 +201,7 @@ public class GreenhouseConfigImpl { } } - public static void loadEarly(String configKey) { + public static void register(String configKey) { Side side = GreenhouseConfigConstants.getPlatformHelper().getSide(); var registeredConfig = CONFIG_HOLDER_REGISTRATION.get(configKey); @@ -241,66 +241,74 @@ public class GreenhouseConfigImpl { } GreenhouseConfigEvents.invokeConfigLoadedEvents(holderImpl); - - writeSchemaVersion(GreenhouseConfigConstants.getPlatformHelper() - .getConfigDir() - .resolve(holderImpl.getModId() + "." + holderImpl.getConfigLang().getFileExtension()), - holderImpl.getSchemaVersion() - ); }); - CONFIG_HOLDER_REGISTRATION.remove(configKey); } - public static void loadConfigs() { - Side side = GreenhouseConfigConstants.getPlatformHelper().getSide(); - for (String configKey : CONFIG_HOLDER_REGISTRATION.keySet()) { - var registeredConfig = CONFIG_HOLDER_REGISTRATION.get(configKey); - registeredConfig.removeIf(configEntry -> - configEntry.side == GreenhouseConfigSide.DEDICATED && side == Side.CLIENT || configEntry.side == GreenhouseConfigSide.CLIENT && side == Side.DEDICATED - ); - Optional configEntry = registeredConfig.stream().max((entry, otherEntry) -> { // Sorted provides an easy way to compare values. - if (entry.side == otherEntry.side) { - throw new IllegalStateException("Config with ID '" + configKey + "' has been registered more than once for the same side: '" + side.name() + "'"); - } + public static void onMinecraftLoad() { + saveChanges(); + logExceptions(); + CONFIG_HOLDER_REGISTRATION.clear(); + registrationOpen = false; + } - if (!(entry.configHolder instanceof GreenhouseInheritedConfigHolder) && !(otherEntry.configHolder instanceof GreenhouseInheritedConfigHolder)) { - throw new IllegalStateException("Config with ID '" + configKey + "' has been registered more than once"); - } + private static void saveChanges() { + for (var holder : CONFIG_HOLDERS.values()) { + //noinspection DuplicatedCode + AbstractGreenhouseConfigHolderImpl impl = (AbstractGreenhouseConfigHolderImpl) holder; + File file = GreenhouseConfigConstants.getPlatformHelper() + .getConfigDir() + .resolve(impl.getModId() + "." + impl.getConfigLang().getFileExtension()) + .toFile(); + + if (!CONFIGS_TO_SAVE.contains(holder)) { + continue; + } - if ((entry.side == GreenhouseConfigSide.CLIENT || entry.side == GreenhouseConfigSide.DEDICATED) && otherEntry.side == GreenhouseConfigSide.COMMON) { - return 1; - } - if (entry.side == GreenhouseConfigSide.COMMON && (otherEntry.side == GreenhouseConfigSide.CLIENT || otherEntry.side == GreenhouseConfigSide.DEDICATED)) { - return -1; - } - return 0; - }); + try { + saveNewConfig(impl, file, impl.getConfigLang()); + writeSchemaVersion(GreenhouseConfigConstants.getPlatformHelper() + .getConfigDir() + .resolve(impl.getModId() + "." + impl.getConfigLang().getFileExtension()), + impl.getSchemaVersion() + ); + } catch (IOException e) { + CONFIG_SAVE_LOG.error("Could not save config '{}'", holder.getModId(), e); + } + } + CONFIGS_TO_SAVE.clear(); + } - configEntry.ifPresent(entry -> { - CONFIG_HOLDERS.put(configKey, entry.configHolder); - AbstractGreenhouseConfigHolderImpl holderImpl = (AbstractGreenhouseConfigHolderImpl) entry.configHolder; - var config = loadConfigInternal(holderImpl); + private static void logExceptions() { + for (var entry : CONFIG_EXCEPTIONS.entrySet()) { + if (!CONFIG_HOLDERS.containsValue(entry.getKey())) + continue; - var localData = holderImpl.getLocalData(config); - var networkData = holderImpl.getNetworkData(config); + ExceptionEntry exceptionEntry = entry.getValue(); - LOCAL_CONFIGS.put(holderImpl, localData); - if (localData != networkData) { - NETWORKABLE_CONFIGS.put(holderImpl, networkData); - } + LoggingEventBuilder builder = CONFIG_LOAD_LOG + .atLevel(exceptionEntry.logLevel) + .setMessage(exceptionEntry.message); - GreenhouseConfigEvents.invokeConfigLoadedEvents(holderImpl); + if (exceptionEntry.exception != null) { + builder = builder.setCause(exceptionEntry.exception); + } - writeSchemaVersion(GreenhouseConfigConstants.getPlatformHelper() - .getConfigDir() - .resolve(holderImpl.getModId() + "." + holderImpl.getConfigLang().getFileExtension()), - holderImpl.getSchemaVersion() - ); - }); + builder.log(); } - CONFIG_HOLDER_REGISTRATION.clear(); - registrationOpen = false; } + + private static void saveNewConfig(AbstractGreenhouseConfigHolderImpl impl, File file, GreenhouseConfigLang lang) throws IOException { + DataResult encoded; + if (impl.getCommentedCodec() != null) { + impl.getCommentedCodec().encodeDefaultComments(); + encoded = impl.getCommentedCodec().encodeStart(lang.getOps(), new CommentedValueWithoutInternal<>(Collections.emptyList(), impl.getDefaultValue().get())); + } else { + encoded = impl.getCodec().encodeStart(lang.getOps(), impl.getDefaultValue().get()); + } + + saveConfigInternal(impl, lang, encoded.getPartialOrThrow(), file); + } + public static void lateHolderRegistryCallback(AbstractGreenhouseConfigHolderBuilder config, Function> getter) { AbstractGreenhouseConfigHolderBuilderImpl impl = (AbstractGreenhouseConfigHolderBuilderImpl)config; @@ -363,7 +371,7 @@ public class GreenhouseConfigImpl { .toFile(); File file = latestLangFile; - int schemaVersion = readSchemaVersion(holder.getModId(), file.toPath()); + int schemaVersion = readSchemaVersion(holder, file.toPath()); if (!latestLangFile.exists()) { schemaVersion = 0; for (var entry : holderImpl.getPreviousLangs().int2ObjectEntrySet()) { @@ -371,7 +379,7 @@ public class GreenhouseConfigImpl { .getConfigDir() .resolve(holderImpl.getModId() + "." + entry.getValue().getFileExtension()) .toFile(); - int potentialSchemaVersion = readSchemaVersion(holder.getModId(), oldFile.toPath()); + int potentialSchemaVersion = readSchemaVersion(holder, oldFile.toPath()); if (oldFile.exists() && schemaVersion <= entry.getIntKey()) { file = oldFile; schemaVersion = potentialSchemaVersion; @@ -400,7 +408,7 @@ public class GreenhouseConfigImpl { if (oldLang != null) { if (oldLang == lang) { - CONFIG_LOAD_LOG.warn("Config file '{}' has an unnecessary previous lang fix for schema version {}", configFileName, schemaVersion); + CONFIG_EXCEPTIONS.put(holder, new ExceptionEntry("Config file '" + configFileName + "' has an unnecessary previous lang fix for schema version " + schemaVersion, Level.ERROR)); } OldLang oldContents; @@ -411,10 +419,9 @@ public class GreenhouseConfigImpl { try { Files.deleteIfExists(file.toPath()); } catch (FileSystemException e) { - CONFIG_LOAD_LOG.error("Failed to delete old config at path {}", holderImpl.getModId() + "." + oldLang.getFileExtension(), e); + CONFIG_EXCEPTIONS.put(holder, new ExceptionEntry("Failed to delete old config at path " + holderImpl.getModId() + "." + oldLang.getFileExtension(), Level.ERROR, e)); } contents = oldLang.getOps().convertTo(lang.getOps(), oldContents); - file = latestLangFile; } else { try (FileReader fileReader = new FileReader(file)) { GreenhouseConfigLangExecutor executor = (GreenhouseConfigLangExecutor)EXECUTORS @@ -434,25 +441,17 @@ public class GreenhouseConfigImpl { DataResult> commentedParsed = holderImpl.getCommentedCodec() .parse(lang.getOps(), lang.mergeComments(fixed, contents)); - holderImpl.getCommentedCodec().encodeDefaultComments(); - Lang reEncoded = holderImpl.getCommentedCodec().encodeStart(lang.getOps(), commentedParsed.getPartialOrThrow()) - .getOrThrow(s -> new IllegalStateException("Failed to re-encode broken fields in data-fixed config. " + CONFIG_LANG_ERROR + " " + s)); - saveConfigInternal(holder, lang, reEncoded, file); - parsed = commentedParsed.map(CommentedValue::value); } else { parsed = holderImpl.getCodec().parse(lang.getOps(), fixed); - - Lang reEncoded = holderImpl.getCodec() - .encodeStart(lang.getOps(), parsed.getPartialOrThrow()) - .getOrThrow(s -> new IllegalStateException("Failed to re-encode broken fields in data-fixed config. " + CONFIG_LANG_ERROR + " " + s)); - saveConfigInternal(holder, lang, reEncoded, file); } - if (parsed.resultOrPartial(s -> CONFIG_LOAD_LOG.warn("Could not completely fix config file '{}'. {}", configFileName, s)).isEmpty()) { + if (parsed.resultOrPartial(error -> CONFIG_EXCEPTIONS.put(holder, new ExceptionEntry("Could not completely fix config file '" + configFileName + "' " + error, Level.WARN))).isEmpty()) { throw new IllegalStateException("Could not fix old config file. " + parsed.error().orElseThrow().message()); } + CONFIGS_TO_SAVE.add(holder); + return parsed.resultOrPartial() .orElse(holderImpl.getDefaultValue().get()); } @@ -471,11 +470,7 @@ public class GreenhouseConfigImpl { .parse(lang.getOps(), contents); if (!commentedParsed.isSuccess()) { - holderImpl.getCommentedCodec().encodeDefaultComments(); - Lang reEncoded = holderImpl.getCommentedCodec() - .encodeStart(lang.getOps(), commentedParsed.getPartialOrThrow()) - .getOrThrow(s -> new IllegalStateException("Failed to re-encode broken config fields. " + CONFIG_LANG_ERROR + " " + s)); - saveConfigInternal(holder, lang, reEncoded, file); + CONFIGS_TO_SAVE.add(holder); } parsed = commentedParsed.map(CommentedValue::value); @@ -484,10 +479,7 @@ public class GreenhouseConfigImpl { .parse(lang.getOps(), contents); if (!parsed.isSuccess()) { - Lang reEncoded = holderImpl.getCodec() - .encodeStart(lang.getOps(), parsed.getPartialOrThrow()) - .getOrThrow(s -> new IllegalStateException("Failed to re-encode broken config fields. " + CONFIG_LANG_ERROR + " " + s)); - saveConfigInternal(holder, lang, reEncoded, file); + CONFIGS_TO_SAVE.add(holder); } } @@ -496,28 +488,14 @@ public class GreenhouseConfigImpl { } return parsed - .resultOrPartial(error -> CONFIG_LOAD_LOG.warn("Could not completely load config file '{}'. {}", configFileName, error)) + .resultOrPartial(error -> CONFIG_EXCEPTIONS.put(holder, new ExceptionEntry("Could not completely load config file '" + configFileName + "' " + error, Level.WARN))) .orElse(holderImpl.getDefaultValue().get()); } } catch (Exception e) { - CONFIG_LOAD_LOG.error("Failed to load config from file '{}'. Falling back to the default values...", configFileName, e); + CONFIG_EXCEPTIONS.put(holder, new ExceptionEntry("Failed to load config from file '" + configFileName + "'. Falling back to the default values...", Level.ERROR, e)); } - try { - DataResult encoded; - if (holderImpl.getCommentedCodec() != null) { - holderImpl.getCommentedCodec().encodeDefaultComments(); - encoded = holderImpl.getCommentedCodec().encodeStart(lang.getOps(), new CommentedValueWithoutInternal<>(Collections.emptyList(), holderImpl.getDefaultValue().get())); - } else { - encoded = holderImpl.getCodec().encodeStart(lang.getOps(), holderImpl.getDefaultValue().get()); - } - - saveConfigInternal(holder, lang, encoded.getPartialOrThrow(), file); - - return holderImpl.getDefaultValue().get(); - } catch (Exception e) { - CONFIG_LOAD_LOG.error("Failed to encode default values to file {}. {}", configFileName, CONFIG_LANG_ERROR, e); - } + CONFIGS_TO_SAVE.add(holder); return holderImpl.getDefaultValue().get(); } @@ -542,7 +520,7 @@ public class GreenhouseConfigImpl { } } - private static int readSchemaVersion(String configModId, Path path) { + private static int readSchemaVersion(GreenhouseConfigHolder configHolder, Path path) { try { Path metaFilePath = GreenhouseConfigConstants.getPlatformHelper().getConfigDir() .resolve(".greenhouse_config_meta/" + path.getFileName() + ".meta"); @@ -568,7 +546,7 @@ public class GreenhouseConfigImpl { } } } catch (IOException e) { - CONFIG_LOAD_LOG.warn("Failed to load schema version for config '{}', falling back to its default value.", configModId, e); + CONFIG_EXCEPTIONS.put(configHolder, new ExceptionEntry("Failed to load schema version for config '" + configHolder + "', falling back to its default value.", Level.ERROR, e)); } return 0; } @@ -669,4 +647,10 @@ public class GreenhouseConfigImpl { private record ConfigEntry(GreenhouseConfigHolder configHolder, GreenhouseConfigSide side) { } + + private record ExceptionEntry(String message, Level logLevel, @Nullable Exception exception) { + private ExceptionEntry(String message, Level logLevel) { + this(message, logLevel, null); + } + } } diff --git a/xplat/src/test/java/lgbt/greenhouse/config/test/GreenhouseConfigTest.java b/xplat/src/test/java/lgbt/greenhouse/config/test/GreenhouseConfigTest.java index fa3baad..778ee39 100644 --- a/xplat/src/test/java/lgbt/greenhouse/config/test/GreenhouseConfigTest.java +++ b/xplat/src/test/java/lgbt/greenhouse/config/test/GreenhouseConfigTest.java @@ -31,6 +31,9 @@ import net.minecraft.resources.Identifier; import net.minecraft.resources.ResourceKey; import net.minecraft.sounds.SoundEvent; import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.block.Block; import org.slf4j.Logger; @@ -39,6 +42,13 @@ public class GreenhouseConfigTest { public static final String MOD_ID = "greenhouseconfig_test"; public static final Logger LOG = GreenhouseConfigConstants.getLogger("Test"); + public static final DeferredRegistry ITEM_REGISTRY = DeferredRegistry.create(BuiltInRegistries.ITEM); + public static final DeferredValue TEST_ITEM = ITEM_REGISTRY.register("test", () -> + new Item(new Item.Properties() + .setId(ResourceKey.create(Registries.ITEM, Identifier.fromNamespaceAndPath(MOD_ID, "test"))) + ) + ); + public static final DeferredRegistry SOUND_EVENT_REGISTRY = DeferredRegistry.create(BuiltInRegistries.SOUND_EVENT); public static final DeferredValue SYLV_FAN = SOUND_EVENT_REGISTRY.register("sylv_fan", () -> SoundEvent.createVariableRangeEvent(id("sylv_fan"))); @@ -122,6 +132,26 @@ public class GreenhouseConfigTest { SplitConfig.Alphabet::h, DefaultValueCommentSettings.DISABLE ) + ).withValue( + "vanilla_item_stack", + """ + An item stack of an item from the vanilla game. + Useful for testing static registries! + """, + ItemStack.CODEC, + new ItemStack(Items.ENCHANTED_GOLDEN_APPLE), + SplitConfig::vanillaItemStack, + DefaultValueCommentSettings.DISABLE + ).withValue( + "modded_item_stack", + """ + An item stack of an item from the test mod. + Useful for testing static registries! + """, + ItemStack.CODEC, + new ItemStack(TEST_ITEM.get()), + SplitConfig::moddedItemStack, + DefaultValueCommentSettings.DISABLE ), fixerBuilder -> fixerBuilder .withPreviousLang(0, GreenhouseConfigJsonLang.INSTANCE) diff --git a/xplat/src/test/java/lgbt/greenhouse/config/test/config/SplitConfig.java b/xplat/src/test/java/lgbt/greenhouse/config/test/config/SplitConfig.java index a9e155b..5931bfd 100644 --- a/xplat/src/test/java/lgbt/greenhouse/config/test/config/SplitConfig.java +++ b/xplat/src/test/java/lgbt/greenhouse/config/test/config/SplitConfig.java @@ -1,6 +1,8 @@ package lgbt.greenhouse.config.test.config; -public record SplitConfig(double volumeOfSylvsFan, Alphabet alphabet) { +import net.minecraft.world.item.ItemStack; + +public record SplitConfig(double volumeOfSylvsFan, Alphabet alphabet, ItemStack vanillaItemStack, ItemStack moddedItemStack) { public record Alphabet(String a, String b, String c, String d, String e, String f, String h) { } -- 2.51.2