diff options
author | Anthony Hilyard <anthony.hilyard@gmail.com> | 2022-09-26 00:29:41 -0700 |
---|---|---|
committer | Anthony Hilyard <anthony.hilyard@gmail.com> | 2022-09-26 00:29:41 -0700 |
commit | cda06cf996cb8a0df2340b14b2a43d5a2a6ab13a (patch) | |
tree | 10b53d9f4f94e7b37253ee635c7942ce5360f656 /src/main/java/com/anthonyhilyard/iceberg/compat | |
parent | 6d01463c32ea14f48ce273e9daf753905f101946 (diff) | |
download | Iceberg-cda06cf996cb8a0df2340b14b2a43d5a2a6ab13a.tar.gz Iceberg-cda06cf996cb8a0df2340b14b2a43d5a2a6ab13a.tar.bz2 Iceberg-cda06cf996cb8a0df2340b14b2a43d5a2a6ab13a.zip |
Added support for Configured 2.x.
Diffstat (limited to 'src/main/java/com/anthonyhilyard/iceberg/compat')
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); + } + +} |