aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/com/anthonyhilyard/iceberg/compat/configured
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/anthonyhilyard/iceberg/compat/configured')
-rw-r--r--src/main/java/com/anthonyhilyard/iceberg/compat/configured/IcebergConfigPlugin.java199
-rw-r--r--src/main/java/com/anthonyhilyard/iceberg/compat/configured/IcebergConfigProvider.java45
-rw-r--r--src/main/java/com/anthonyhilyard/iceberg/compat/configured/IcebergFolderEntry.java160
3 files changed, 404 insertions, 0 deletions
diff --git a/src/main/java/com/anthonyhilyard/iceberg/compat/configured/IcebergConfigPlugin.java b/src/main/java/com/anthonyhilyard/iceberg/compat/configured/IcebergConfigPlugin.java
new file mode 100644
index 0000000..7f230de
--- /dev/null
+++ b/src/main/java/com/anthonyhilyard/iceberg/compat/configured/IcebergConfigPlugin.java
@@ -0,0 +1,199 @@
+package com.anthonyhilyard.iceberg.compat.configured;
+
+import com.anthonyhilyard.iceberg.config.IcebergConfigSpec;
+import com.electronwill.nightconfig.core.AbstractConfig;
+import com.electronwill.nightconfig.core.CommentedConfig;
+import com.electronwill.nightconfig.core.UnmodifiableConfig;
+import com.electronwill.nightconfig.core.file.CommentedFileConfig;
+import com.google.common.collect.ImmutableList;
+import com.mojang.datafixers.util.Pair;
+import com.mrcrayfish.configured.Configured;
+import com.mrcrayfish.configured.api.ConfigType;
+import com.mrcrayfish.configured.api.IConfigEntry;
+import com.mrcrayfish.configured.api.IConfigValue;
+import com.mrcrayfish.configured.api.IModConfig;
+import com.mrcrayfish.configured.impl.forge.ForgeListValue;
+import com.mrcrayfish.configured.impl.forge.ForgeValue;
+import com.mrcrayfish.configured.util.ConfigHelper;
+import net.minecraft.Util;
+import net.minecraftforge.common.ForgeConfigSpec;
+import net.minecraftforge.fml.config.ModConfig;
+import net.minecraftforge.fml.event.config.ModConfigEvent;
+
+import java.nio.file.Path;
+import java.util.EnumMap;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import org.apache.commons.compress.utils.Lists;
+
+public class IcebergConfigPlugin implements IModConfig
+{
+ protected static final EnumMap<ModConfig.Type, ConfigType> TYPE_RESOLVER = Util.make(new EnumMap<>(ModConfig.Type.class), (map) -> {
+ map.put(ModConfig.Type.CLIENT, ConfigType.CLIENT);
+ map.put(ModConfig.Type.COMMON, ConfigType.UNIVERSAL);
+ map.put(ModConfig.Type.SERVER, ConfigType.WORLD_SYNC);
+ });
+
+ protected final ModConfig config;
+ protected final List<IcebergValueEntry> allConfigValues;
+
+ public IcebergConfigPlugin(ModConfig config)
+ {
+ this.config = config;
+ this.allConfigValues = getAllConfigValues(config);
+ }
+
+ @Override
+ public void update(IConfigEntry entry)
+ {
+ Set<IConfigValue<?>> changedValues = ConfigHelper.getChangedValues(entry);
+ if (!changedValues.isEmpty())
+ {
+ CommentedConfig newConfig = CommentedConfig.copy(this.config.getConfigData());
+ changedValues.forEach(value ->
+ {
+ if (value instanceof ForgeValue<?> forge)
+ {
+ if (forge instanceof ForgeListValue forgeList)
+ {
+ Function<List<?>, List<?>> converter = forgeList.getConverter();
+ if (converter != null)
+ {
+ newConfig.set(forge.configValue.getPath(), converter.apply(forgeList.get()));
+ return;
+ }
+ }
+ newConfig.set(forge.configValue.getPath(), value.get());
+ }
+ });
+ this.config.getConfigData().putAll(newConfig);
+ }
+
+ if (this.getType() == ConfigType.WORLD_SYNC)
+ {
+ if (!ConfigHelper.isPlayingGame())
+ {
+ // Unload server configs since still in main menu
+ this.config.getHandler().unload(this.config.getFullPath().getParent(), this.config);
+ ConfigHelper.setForgeConfigData(this.config, null);
+ }
+ }
+ else if (!changedValues.isEmpty())
+ {
+ Configured.LOGGER.info("Sending config reloading event for {}", this.config.getFileName());
+ this.config.getSpec().afterReload();
+ ConfigHelper.fireForgeConfigEvent(this.config, new ModConfigEvent.Reloading(this.config));
+ }
+ }
+
+ @Override
+ public IConfigEntry getRoot()
+ {
+ return new IcebergFolderEntry(((IcebergConfigSpec) this.config.getSpec()).getValues(), (IcebergConfigSpec) this.config.getSpec());
+ }
+
+ @Override
+ public ConfigType getType()
+ {
+ return TYPE_RESOLVER.get(this.config.getType());
+ }
+
+ @Override
+ public String getFileName()
+ {
+ return this.config.getFileName();
+ }
+
+ @Override
+ public String getModId()
+ {
+ return this.config.getModId();
+ }
+
+ @Override
+ public void loadWorldConfig(Path path, Consumer<IModConfig> result)
+ {
+ final CommentedFileConfig data = this.config.getHandler().reader(path).apply(this.config);
+ ConfigHelper.setForgeConfigData(this.config, data);
+ result.accept(this);
+ }
+
+ @Override
+ public void stopEditing()
+ {
+ // Attempts to unload the server config if player simply just went back
+ if (this.config != null && this.getType() == ConfigType.WORLD)
+ {
+ if (!ConfigHelper.isPlayingGame())
+ {
+ // Unload server configs since still in main menu
+ this.config.getHandler().unload(this.config.getFullPath().getParent(), this.config);
+ ConfigHelper.setForgeConfigData(this.config, null);
+ }
+ }
+ }
+
+ @Override
+ public boolean isChanged()
+ {
+ // Block world configs since the path is dynamic
+ if (ConfigHelper.isWorldConfig(this) && this.config.getConfigData() == null)
+ {
+ return false;
+ }
+
+ // Check if any config value doesn't equal it's default
+ return this.allConfigValues.stream().anyMatch(entry -> {
+ return !Objects.equals(entry.value.get(), entry.spec.getDefault());
+ });
+ }
+
+ @Override
+ public void restoreDefaults()
+ {
+ // Block world configs since the path is dynamic
+ if (ConfigHelper.isWorldConfig(this) && this.config.getConfigData() == null)
+ {
+ return;
+ }
+
+ // Creates a copy of the config data then pushes all at once to avoid multiple IO ops
+ CommentedConfig newConfig = CommentedConfig.copy(this.config.getConfigData());
+ this.allConfigValues.forEach(entry -> newConfig.set(entry.value.getPath(), entry.spec.getDefault()));
+ this.config.getConfigData().putAll(newConfig);
+
+ // Finally clear cache of all config values
+ this.allConfigValues.forEach(pair -> pair.value.clearCache());
+ }
+
+ private static List<IcebergValueEntry> getAllConfigValues(ModConfig config)
+ {
+ IcebergConfigSpec icebergConfigSpec = (IcebergConfigSpec) config.getSpec();
+ List<Pair<ForgeConfigSpec.ConfigValue<?>, ForgeConfigSpec.ValueSpec>> values = Lists.newArrayList();
+ gatherValuesFromIcebergConfig(icebergConfigSpec.getValues(), icebergConfigSpec, values);
+ return ImmutableList.copyOf(values).stream().map(pair -> new IcebergValueEntry(pair.getFirst(), pair.getSecond())).collect(Collectors.toList());
+ }
+
+ private static void gatherValuesFromIcebergConfig(UnmodifiableConfig config, IcebergConfigSpec spec, List<Pair<ForgeConfigSpec.ConfigValue<?>, ForgeConfigSpec.ValueSpec>> values)
+ {
+ config.valueMap().forEach((key, value) ->
+ {
+ if (value instanceof AbstractConfig)
+ {
+ gatherValuesFromIcebergConfig((UnmodifiableConfig) value, spec, values);
+ }
+ else if (value instanceof ForgeConfigSpec.ConfigValue<?> configValue)
+ {
+ ForgeConfigSpec.ValueSpec valueSpec = spec.getRaw(configValue.getPath());
+ values.add(Pair.of(configValue, valueSpec));
+ }
+ });
+ }
+
+ private record IcebergValueEntry(ForgeConfigSpec.ConfigValue<?> value, ForgeConfigSpec.ValueSpec spec) {}
+}
diff --git a/src/main/java/com/anthonyhilyard/iceberg/compat/configured/IcebergConfigProvider.java b/src/main/java/com/anthonyhilyard/iceberg/compat/configured/IcebergConfigProvider.java
new file mode 100644
index 0000000..6eda2ee
--- /dev/null
+++ b/src/main/java/com/anthonyhilyard/iceberg/compat/configured/IcebergConfigProvider.java
@@ -0,0 +1,45 @@
+package com.anthonyhilyard.iceberg.compat.configured;
+
+import java.util.EnumMap;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+import com.anthonyhilyard.iceberg.config.IcebergConfigSpec;
+import com.mrcrayfish.configured.api.IConfigProvider;
+import com.mrcrayfish.configured.api.IModConfig;
+
+import net.minecraftforge.fml.ModContainer;
+import net.minecraftforge.fml.config.ConfigTracker;
+import net.minecraftforge.fml.config.ModConfig;
+import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
+
+public class IcebergConfigProvider implements IConfigProvider
+{
+ @Override
+ public Set<IModConfig> getConfigurationsForMod(ModContainer container)
+ {
+ // Add Iceberg configurations
+ Set<IModConfig> configs = new HashSet<>();
+ addIcebergConfigsToMap(container, ModConfig.Type.CLIENT, configs::add);
+ addIcebergConfigsToMap(container, ModConfig.Type.COMMON, configs::add);
+ addIcebergConfigsToMap(container, ModConfig.Type.SERVER, configs::add);
+ return configs;
+ }
+
+ private static void addIcebergConfigsToMap(ModContainer container, ModConfig.Type type, Consumer<IModConfig> consumer)
+ {
+ getForgeTrackedConfigs().get(type).stream()
+ .filter(config -> config.getModId().equals(container.getModId()) && config.getSpec() instanceof IcebergConfigSpec)
+ .map(IcebergConfigPlugin::new)
+ .collect(Collectors.toSet())
+ .forEach(consumer);
+ }
+
+ private static EnumMap<ModConfig.Type, Set<ModConfig>> getForgeTrackedConfigs()
+ {
+ return ObfuscationReflectionHelper.getPrivateValue(ConfigTracker.class, ConfigTracker.INSTANCE, "configSets");
+ }
+
+}
diff --git a/src/main/java/com/anthonyhilyard/iceberg/compat/configured/IcebergFolderEntry.java b/src/main/java/com/anthonyhilyard/iceberg/compat/configured/IcebergFolderEntry.java
new file mode 100644
index 0000000..c9d3612
--- /dev/null
+++ b/src/main/java/com/anthonyhilyard/iceberg/compat/configured/IcebergFolderEntry.java
@@ -0,0 +1,160 @@
+package com.anthonyhilyard.iceberg.compat.configured;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nullable;
+
+import com.anthonyhilyard.iceberg.config.IcebergConfigSpec;
+import com.electronwill.nightconfig.core.UnmodifiableConfig;
+import com.google.common.collect.ImmutableList;
+import com.mrcrayfish.configured.api.IConfigEntry;
+import com.mrcrayfish.configured.api.IConfigValue;
+import com.mrcrayfish.configured.api.ValueEntry;
+import com.mrcrayfish.configured.impl.forge.ForgeEnumValue;
+import com.mrcrayfish.configured.impl.forge.ForgeListValue;
+import com.mrcrayfish.configured.impl.forge.ForgeValue;
+
+import net.minecraft.client.resources.language.I18n;
+import net.minecraft.network.chat.Component;
+import net.minecraftforge.common.ForgeConfigSpec;
+
+public class IcebergFolderEntry implements IConfigEntry
+{
+ protected final List<String> path;
+ protected final UnmodifiableConfig config;
+ protected final IcebergConfigSpec spec;
+ private final String customComment;
+ protected List<IConfigEntry> entries;
+
+ public IcebergFolderEntry(UnmodifiableConfig config, IcebergConfigSpec spec)
+ {
+ this(new ArrayList<>(), config, spec);
+ }
+
+ public IcebergFolderEntry(List<String> path, UnmodifiableConfig config, IcebergConfigSpec spec)
+ {
+ this(path, config, spec, null);
+ }
+
+ public IcebergFolderEntry(List<String> path, UnmodifiableConfig config, IcebergConfigSpec spec, String customComment)
+ {
+ this.path = path;
+ this.config = config;
+ this.spec = spec;
+ this.customComment = customComment;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public List<IConfigEntry> getChildren()
+ {
+ if (this.entries == null)
+ {
+ ImmutableList.Builder<IConfigEntry> builder = ImmutableList.builder();
+
+ this.config.valueMap().forEach((key, value) ->
+ {
+ if (value instanceof ForgeConfigSpec.ValueSpec valueSpec && valueSpec.getDefault() instanceof UnmodifiableConfig)
+ {
+ value = valueSpec.getDefault();
+ }
+
+ if (value instanceof UnmodifiableConfig)
+ {
+ List<String> path = new ArrayList<>(this.path);
+ path.add(key);
+ builder.add(new IcebergFolderEntry(path, (UnmodifiableConfig) value, this.spec));
+ }
+ else if (value instanceof ForgeConfigSpec.ConfigValue<?> configValue)
+ {
+ ForgeConfigSpec.ValueSpec valueSpec = this.spec.getRaw(configValue.getPath());
+ if (valueSpec != null)
+ {
+ if (configValue.get() instanceof List<?>)
+ {
+ builder.add(new ValueEntry(new ForgeListValue((ForgeConfigSpec.ConfigValue<List<?>>) configValue, valueSpec)));
+ }
+ else if (configValue.get() instanceof Enum<?>)
+ {
+ builder.add(new ValueEntry(new ForgeEnumValue<>((ForgeConfigSpec.EnumValue<?>) configValue, valueSpec)));
+ }
+ // Configured doesn't currently support map configuration values, so leave a message for users.
+ else if (configValue.get() instanceof UnmodifiableConfig)
+ {
+ List<String> path = new ArrayList<>(this.path);
+ path.add(key);
+ builder.add(new IcebergFolderEntry(path, (UnmodifiableConfig) configValue.get(), this.spec, "This is a map configuration value, which is not currently supported by Configured. To edit this value, please modify the configuration file directly."));
+ }
+ else
+ {
+ builder.add(new ValueEntry(new ForgeValue<>(configValue, valueSpec)));
+ }
+ }
+ }
+ });
+ this.entries = builder.build();
+ }
+ return this.entries;
+ }
+
+ @Override
+ public boolean isRoot()
+ {
+ return this.path.isEmpty();
+ }
+
+ @Override
+ public boolean isLeaf()
+ {
+ return false;
+ }
+
+ @Override
+ public IConfigValue<?> getValue()
+ {
+ return null;
+ }
+
+ @Override
+ public String getEntryName()
+ {
+ return ForgeValue.lastValue(this.path, "Root");
+ }
+
+ @Nullable
+ @Override
+ public Component getTooltip()
+ {
+ if (customComment != null)
+ {
+ return Component.translatable(customComment);
+ }
+ else
+ {
+ String translationKey = this.getTranslationKey();
+ if (translationKey != null)
+ {
+ String tooltipKey = translationKey + ".tooltip";
+ if (I18n.exists(tooltipKey))
+ {
+ return Component.translatable(tooltipKey);
+ }
+ }
+ String comment = this.spec.getLevelComment(this.path);
+ if (comment != null)
+ {
+ return Component.literal(comment);
+ }
+ return null;
+ }
+ }
+
+ @Nullable
+ @Override
+ public String getTranslationKey()
+ {
+ return this.spec.getLevelTranslationKey(this.path);
+ }
+
+}