From cda06cf996cb8a0df2340b14b2a43d5a2a6ab13a Mon Sep 17 00:00:00 2001 From: Anthony Hilyard Date: Mon, 26 Sep 2022 00:29:41 -0700 Subject: Added support for Configured 2.x. --- build.gradle | 26 +-- gradle.properties | 6 +- lib/configured-2.0.0-1.19.2.jar | Bin 0 -> 279079 bytes .../compat/configured/IcebergConfigPlugin.java | 199 +++++++++++++++++++ .../compat/configured/IcebergConfigProvider.java | 45 +++++ .../compat/configured/IcebergFolderEntry.java | 160 +++++++++++++++ .../iceberg/mixin/ConfiguredConfigHelperMixin.java | 55 ------ .../ConfiguredModConfigSelectionScreenMixin.java | 216 --------------------- .../anthonyhilyard/iceberg/mixin/MixinConfig.java | 22 +-- .../com/anthonyhilyard/iceberg/util/Tooltips.java | 20 +- src/main/resources/META-INF/mods.toml | 11 +- src/main/resources/iceberg.mixins.json | 4 +- 12 files changed, 439 insertions(+), 325 deletions(-) create mode 100644 lib/configured-2.0.0-1.19.2.jar create mode 100644 src/main/java/com/anthonyhilyard/iceberg/compat/configured/IcebergConfigPlugin.java create mode 100644 src/main/java/com/anthonyhilyard/iceberg/compat/configured/IcebergConfigProvider.java create mode 100644 src/main/java/com/anthonyhilyard/iceberg/compat/configured/IcebergFolderEntry.java delete mode 100644 src/main/java/com/anthonyhilyard/iceberg/mixin/ConfiguredConfigHelperMixin.java delete mode 100644 src/main/java/com/anthonyhilyard/iceberg/mixin/ConfiguredModConfigSelectionScreenMixin.java diff --git a/build.gradle b/build.gradle index 2d142c6..6a8004c 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ apply plugin: 'net.minecraftforge.gradle' apply plugin: 'org.spongepowered.mixin' apply plugin: 'eclipse' -archivesBaseName = project.name + '-' + project.mcVersion +archivesBaseName = project.name + '-' + project.mcVersion + '-forge' java.toolchain.languageVersion = JavaLanguageVersion.of(17) @@ -41,14 +41,6 @@ minecraft { } } -sourceSets { - main { - resources { - srcDir 'src/generated/resources' - } - } -} - repositories { maven { name "CurseMaven" @@ -60,18 +52,18 @@ dependencies { minecraft "net.minecraftforge:forge:${project.mcVersion}-${project.forgeVersion}" annotationProcessor 'org.spongepowered:mixin:0.8.5:processor' compileClasspath fg.deobf('curse.maven:configmenusforge-544048:3570070') - compileClasspath fg.deobf('curse.maven:configured-457570:3872190') + compileClasspath fg.deobf(files('lib/configured-2.0.0-1.19.2.jar')) } jar { manifest { - attributes(["Specification-Title": project.name, - "Specification-Vendor": project.author, - "Specification-Version": project.version, - "Implementation-Title": project.name, - "Implementation-Version": project.version, - "Implementation-Vendor" : project.author, + attributes(["Specification-Title": project.name, + "Specification-Vendor": project.author, + "Specification-Version": project.version, + "Implementation-Title": project.name, + "Implementation-Version": project.version, + "Implementation-Vendor" : project.author, "Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"), - "MixinConfigs": "${project.name.toLowerCase()}.mixins.json"],) + "MixinConfigs": "${project.name.toLowerCase()}.mixins.json"],) } } diff --git a/gradle.properties b/gradle.properties index 3de623b..99bfe24 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,6 +6,6 @@ org.gradle.daemon=false name=${rootProject.name} group=com.anthonyhilyard.${name.toLowerCase()} author=anthonyhilyard -version=1.0.46 -mcVersion=1.19 -forgeVersion=41.0.110 \ No newline at end of file +version=1.0.47 +mcVersion=1.19.2 +forgeVersion=43.1.27 \ No newline at end of file diff --git a/lib/configured-2.0.0-1.19.2.jar b/lib/configured-2.0.0-1.19.2.jar new file mode 100644 index 0000000..5c13814 Binary files /dev/null and b/lib/configured-2.0.0-1.19.2.jar differ 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 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 allConfigValues; + + public IcebergConfigPlugin(ModConfig config) + { + this.config = config; + this.allConfigValues = getAllConfigValues(config); + } + + @Override + public void update(IConfigEntry entry) + { + Set> 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> 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 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 getAllConfigValues(ModConfig config) + { + IcebergConfigSpec icebergConfigSpec = (IcebergConfigSpec) config.getSpec(); + List, 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, 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 getConfigurationsForMod(ModContainer container) + { + // Add Iceberg configurations + Set 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 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> 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 path; + protected final UnmodifiableConfig config; + protected final IcebergConfigSpec spec; + private final String customComment; + protected List entries; + + public IcebergFolderEntry(UnmodifiableConfig config, IcebergConfigSpec spec) + { + this(new ArrayList<>(), config, spec); + } + + public IcebergFolderEntry(List path, UnmodifiableConfig config, IcebergConfigSpec spec) + { + this(path, config, spec, null); + } + + public IcebergFolderEntry(List path, UnmodifiableConfig config, IcebergConfigSpec spec, String customComment) + { + this.path = path; + this.config = config; + this.spec = spec; + this.customComment = customComment; + } + + @Override + @SuppressWarnings("unchecked") + public List getChildren() + { + if (this.entries == null) + { + ImmutableList.Builder 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 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>) 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 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); + } + +} diff --git a/src/main/java/com/anthonyhilyard/iceberg/mixin/ConfiguredConfigHelperMixin.java b/src/main/java/com/anthonyhilyard/iceberg/mixin/ConfiguredConfigHelperMixin.java deleted file mode 100644 index 9918740..0000000 --- a/src/main/java/com/anthonyhilyard/iceberg/mixin/ConfiguredConfigHelperMixin.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.anthonyhilyard.iceberg.mixin; - -import java.util.ArrayList; -import java.util.List; - -import com.anthonyhilyard.iceberg.config.IcebergConfigSpec; -import com.electronwill.nightconfig.core.AbstractConfig; -import com.electronwill.nightconfig.core.UnmodifiableConfig; - - -import org.apache.commons.lang3.tuple.Pair; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -import net.minecraftforge.common.ForgeConfigSpec; -import net.minecraftforge.fml.config.ModConfig; - - -import com.mrcrayfish.configured.util.ConfigHelper; - - -@Mixin(ConfigHelper.class) -public class ConfiguredConfigHelperMixin -{ - @Inject(method = "gatherAllConfigValues(Lnet/minecraftforge/fml/config/ModConfig;)Ljava/util/List;", - at = @At(value = "HEAD"), cancellable = true, remap = false, require = 0) - private static void gatherAllConfigValuesIcebergSupport(ModConfig config, CallbackInfoReturnable> info) - { - if (config.getSpec() instanceof IcebergConfigSpec icebergConfigSpec) - { - List, ForgeConfigSpec.ValueSpec>> values = new ArrayList<>(); - gatherValuesFromIcebergConfig(icebergConfigSpec.getValues(), icebergConfigSpec, values); - info.setReturnValue(values); - } - } - - private static void gatherValuesFromIcebergConfig(UnmodifiableConfig config, IcebergConfigSpec spec, List, ForgeConfigSpec.ValueSpec>> values) - { - config.valueMap().forEach((s, o) -> - { - if (o instanceof AbstractConfig) - { - gatherValuesFromIcebergConfig((UnmodifiableConfig) o, spec, values); - } - else if (o instanceof ForgeConfigSpec.ConfigValue) - { - ForgeConfigSpec.ConfigValue configValue = (ForgeConfigSpec.ConfigValue) o; - ForgeConfigSpec.ValueSpec valueSpec = spec.getRaw(configValue.getPath()); - values.add(Pair.of(configValue, valueSpec)); - } - }); - } -} \ No newline at end of file diff --git a/src/main/java/com/anthonyhilyard/iceberg/mixin/ConfiguredModConfigSelectionScreenMixin.java b/src/main/java/com/anthonyhilyard/iceberg/mixin/ConfiguredModConfigSelectionScreenMixin.java deleted file mode 100644 index 1d0882a..0000000 --- a/src/main/java/com/anthonyhilyard/iceberg/mixin/ConfiguredModConfigSelectionScreenMixin.java +++ /dev/null @@ -1,216 +0,0 @@ -package com.anthonyhilyard.iceberg.mixin; - -import java.lang.reflect.Field; -import java.util.List; -import java.util.ArrayList; - -import com.anthonyhilyard.iceberg.Loader; -import com.anthonyhilyard.iceberg.config.IcebergConfigSpec; -import com.electronwill.nightconfig.core.UnmodifiableConfig; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Lists; -import com.mrcrayfish.configured.client.screen.ConfigScreen; -import com.mrcrayfish.configured.client.screen.ListMenuScreen; -import com.mrcrayfish.configured.client.screen.ModConfigSelectionScreen; -import com.mrcrayfish.configured.client.screen.ModConfigSelectionScreen.FileItem; -import com.mrcrayfish.configured.client.screen.WorldSelectionScreen; -import com.mrcrayfish.configured.client.screen.ConfigScreen.FolderEntry; -import com.mrcrayfish.configured.client.screen.ConfigScreen.IEntry; -import com.mrcrayfish.configured.client.screen.widget.IconButton; -import com.mrcrayfish.configured.util.ConfigHelper; - -import org.apache.commons.lang3.exception.ExceptionUtils; -import org.apache.commons.lang3.reflect.FieldUtils; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import org.spongepowered.asm.mixin.injection.At; - -import net.minecraft.ChatFormatting; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.components.Button; -import net.minecraft.client.gui.narration.ScreenNarrationCollector; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.network.chat.Component; -import net.minecraft.resources.ResourceLocation; -import net.minecraftforge.common.ForgeConfigSpec; -import net.minecraftforge.fml.ModList; -import net.minecraftforge.fml.config.ModConfig; -import net.minecraftforge.fml.unsafe.UnsafeHacks; - -@Mixin(FileItem.class) -public class ConfiguredModConfigSelectionScreenMixin -{ - @Shadow(aliases = "this$0", remap = false) - @Final - ModConfigSelectionScreen this$0; - - @Shadow(remap = false) - @Final - protected ModConfig config; - - @Shadow(remap = false) - @Final - protected Component title; - - @Shadow(remap = false) - private boolean hasRequiredPermission() { return true; } - - @Inject(method = "createModifyButton", at = @At(value = "HEAD"), remap = false, cancellable = true, require = 0) - private void createModifyButton(ModConfig config, CallbackInfoReturnable