diff --git a/fmlcore/src/main/java/net/minecraftforge/fml/config/ConfigTracker.java b/fmlcore/src/main/java/net/minecraftforge/fml/config/ConfigTracker.java index 53107720fc..02ce9623c5 100644 --- a/fmlcore/src/main/java/net/minecraftforge/fml/config/ConfigTracker.java +++ b/fmlcore/src/main/java/net/minecraftforge/fml/config/ConfigTracker.java @@ -66,8 +66,12 @@ public class ConfigTracker { private void closeConfig(final ModConfig config, final Path configBasePath) { if (config.getConfigData() != null) { LOGGER.trace(CONFIG, "Closing config file type {} at {} for {}", config.getType(), config.getFileName(), config.getModId()); - config.save(); + // stop the filewatcher before we save the file and close it, so reload doesn't fire config.getHandler().unload(configBasePath, config); + var unloading = IConfigEvent.unloading(config); + if (unloading != null) + config.fireEvent(unloading); + config.save(); config.setConfigData(null); } } diff --git a/fmlcore/src/main/java/net/minecraftforge/fml/config/IConfigEvent.java b/fmlcore/src/main/java/net/minecraftforge/fml/config/IConfigEvent.java index d0b1e6a940..124010fe1c 100644 --- a/fmlcore/src/main/java/net/minecraftforge/fml/config/IConfigEvent.java +++ b/fmlcore/src/main/java/net/minecraftforge/fml/config/IConfigEvent.java @@ -10,8 +10,17 @@ import net.minecraftforge.fml.Bindings; import java.util.function.Function; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + public interface IConfigEvent { - record ConfigConfig(Function loading, Function reloading) {} + record ConfigConfig(Function loading, Function reloading, @Nullable Function unloading) { + @Deprecated(since = "1.19.3", forRemoval = true) + @ApiStatus.Internal + ConfigConfig(Function loading, Function reloading) { + this(loading, reloading, null); + } + } ConfigConfig CONFIGCONFIG = Bindings.getConfigConfiguration().get(); static IConfigEvent reloading(ModConfig modConfig) { @@ -20,6 +29,9 @@ public interface IConfigEvent { static IConfigEvent loading(ModConfig modConfig) { return CONFIGCONFIG.loading().apply(modConfig); } + @Nullable static IConfigEvent unloading(ModConfig modConfig) { + return CONFIGCONFIG.unloading() == null ? null : CONFIGCONFIG.unloading().apply(modConfig); + } ModConfig getConfig(); @SuppressWarnings("unchecked") diff --git a/fmlonly/src/main/java/net/minecraftforge/fmlonly/FMLOnlyBindings.java b/fmlonly/src/main/java/net/minecraftforge/fmlonly/FMLOnlyBindings.java index 97f6c5eb22..49bf17aa08 100644 --- a/fmlonly/src/main/java/net/minecraftforge/fmlonly/FMLOnlyBindings.java +++ b/fmlonly/src/main/java/net/minecraftforge/fmlonly/FMLOnlyBindings.java @@ -42,6 +42,6 @@ public class FMLOnlyBindings implements IBindingsProvider { @Override public Supplier getConfigConfiguration() { - return ()->new IConfigEvent.ConfigConfig(ModConfigEvent.Loading::new, ModConfigEvent.Reloading::new); + return ()->new IConfigEvent.ConfigConfig(ModConfigEvent.Loading::new, ModConfigEvent.Reloading::new, ModConfigEvent.Unloading::new); } } diff --git a/src/fmlcommon/java/net/minecraftforge/fml/event/config/ModConfigEvent.java b/src/fmlcommon/java/net/minecraftforge/fml/event/config/ModConfigEvent.java index 2fb64ab936..60c44855cc 100644 --- a/src/fmlcommon/java/net/minecraftforge/fml/event/config/ModConfigEvent.java +++ b/src/fmlcommon/java/net/minecraftforge/fml/event/config/ModConfigEvent.java @@ -22,15 +22,37 @@ public class ModConfigEvent extends Event implements IModBusEvent, IConfigEvent return config; } + /** + * Fired during mod and server loading, depending on {@link ModConfig.Type} of config file. + * Any Config objects associated with this will be valid and can be queried directly. + */ public static class Loading extends ModConfigEvent { public Loading(final ModConfig config) { super(config); } } + /** + * Fired when the configuration is changed. This can be caused by a change to the config + * from a UI or from editing the file itself. IMPORTANT: this can fire at any time + * and may not even be on the server or client threads. Ensure you properly synchronize + * any resultant changes. + */ public static class Reloading extends ModConfigEvent { public Reloading(final ModConfig config) { super(config); } } + + /** + * Fired when a config is unloaded. This only happens when the server closes, which is + * probably only really relevant on the client, to reset internal mod state when the + * server goes away, though it will fire on the dedicated server as well. + * The config file will be saved after this event has fired. + */ + public static class Unloading extends ModConfigEvent { + public Unloading(final ModConfig config) { + super(config); + } + } } diff --git a/src/main/java/net/minecraftforge/internal/ForgeBindings.java b/src/main/java/net/minecraftforge/internal/ForgeBindings.java index 197376ecc2..716ca44161 100644 --- a/src/main/java/net/minecraftforge/internal/ForgeBindings.java +++ b/src/main/java/net/minecraftforge/internal/ForgeBindings.java @@ -38,6 +38,6 @@ public class ForgeBindings implements IBindingsProvider { @Override public Supplier getConfigConfiguration() { - return ()->new IConfigEvent.ConfigConfig(ModConfigEvent.Loading::new, ModConfigEvent.Reloading::new); + return ()->new IConfigEvent.ConfigConfig(ModConfigEvent.Loading::new, ModConfigEvent.Reloading::new, ModConfigEvent.Unloading::new); } }