diff options
Diffstat (limited to 'src/main/java/com/anthonyhilyard/iceberg/mixin')
6 files changed, 376 insertions, 164 deletions
diff --git a/src/main/java/com/anthonyhilyard/iceberg/mixin/ConfigMenusForgeConfigScreenMixin.java b/src/main/java/com/anthonyhilyard/iceberg/mixin/ConfigMenusForgeConfigScreenMixin.java new file mode 100644 index 0000000..42c388d --- /dev/null +++ b/src/main/java/com/anthonyhilyard/iceberg/mixin/ConfigMenusForgeConfigScreenMixin.java @@ -0,0 +1,217 @@ +package com.anthonyhilyard.iceberg.mixin; + +import java.lang.reflect.Constructor; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import com.anthonyhilyard.iceberg.Loader; +import com.anthonyhilyard.iceberg.util.ConfigMenusForgeHelper; +import com.electronwill.nightconfig.core.UnmodifiableConfig; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; + +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import fuzs.configmenusforge.client.gui.data.IEntryData; +import fuzs.configmenusforge.client.gui.screens.ConfigScreen; +import fuzs.configmenusforge.client.util.ServerConfigUploader; +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.config.ModConfig; + + +@Mixin(ConfigScreen.class) +public abstract class ConfigMenusForgeConfigScreenMixin extends Screen +{ + protected ConfigMenusForgeConfigScreenMixin(Component p_96550_) { super(p_96550_); } + + @Unique + private UnmodifiableConfig mainConfig = null; + + @Inject(method = "create", at = @At("HEAD"), remap = false, cancellable = true) + private static void create(Screen lastScreen, Component title, ResourceLocation background, ModConfig config, Map<Object, IEntryData> valueToData, CallbackInfoReturnable<ConfigScreen> info) + { + try + { + Constructor<?> mainConstructor = Class.forName("fuzs.configmenusforge.client.gui.screens.ConfigScreen$Main").getDeclaredConstructor(Screen.class, Component.class, ResourceLocation.class, UnmodifiableConfig.class, Map.class, Runnable.class); + mainConstructor.setAccessible(true); + info.setReturnValue((ConfigScreen)mainConstructor.newInstance(lastScreen, title, background, ConfigMenusForgeHelper.getValues(config.getSpec()), valueToData, (Runnable)(() -> ServerConfigUploader.saveAndUpload(config)))); + info.cancel(); + return; + } + catch (Exception e) + { + Loader.LOGGER.warn(ExceptionUtils.getStackTrace(e.getCause())); + } + } + + @Redirect(method = "<init>(Lnet/minecraft/client/gui/screens/Screen;Lnet/minecraft/network/chat/Component;Lnet/minecraft/resources/ResourceLocation;Lcom/electronwill/nightconfig/core/UnmodifiableConfig;Ljava/util/Map;[I)V", + at = @At(value = "INVOKE", target = "Ljava/util/Collection;stream()Ljava/util/stream/Stream;", ordinal = 0, remap = false), remap = false) + Stream<Object> filteredEntries(Collection<Object> values) + { + return values.stream().map(value -> { + if (value instanceof ForgeConfigSpec.ConfigValue<?> configValue && configValue.get() instanceof UnmodifiableConfig config) + { + return config; + } + else + { + return value; + } + }); + } + + /// TODO: Add extended support for mutable subconfigs by adding an "Add new key" button and ability to delete keys. + + // @Shadow(remap = false) + // @Final + // @Mutable + // private List<IEntryData> searchEntries; + + // @Shadow(remap = false) + // @Final + // @Mutable + // private List<IEntryData> screenEntries; + + // @Shadow(remap = false) + // @Final + // @Mutable + // Map<Object, IEntryData> valueToData; + + // @Shadow(remap = false) + // EditBox searchTextField; + + // @Shadow(remap = false) + // @Final + // ResourceLocation background; + + // @Shadow(remap = false) + // List<ConfigScreen.Entry> getConfigListEntries(List<IEntryData> entries, final String searchHighlight) { return null; } + + // @Inject(method = "getConfigListEntries(Ljava/lang/String;)Ljava/util/List;", at = @At("HEAD"), remap = false, cancellable = true) + // private void getConfigListEntries(String query, CallbackInfoReturnable<List<ConfigScreen.Entry>> info) + // { + // query = query.toLowerCase(Locale.ROOT).trim(); + // if (query.isEmpty()) + // { + // List<ConfigScreen.Entry> entries = Lists.newArrayList(getConfigListEntries(screenEntries, query)); + + // // Add an "add new key" button if this is a dynamic subconfig. We can't be sure that's what this is, + // // since we don't have access to the spec here, so we're going to have to make an assumption... + // try + // { + // if (mainConfig != null && mainConfig.getClass().isAssignableFrom(Class.forName("com.electronwill.nightconfig.core.SimpleCommentedConfig"))) + // { + // Class<?> categoryEntryClass = Class.forName("fuzs.configmenusforge.client.gui.screens.ConfigScreen$CategoryEntry"); + + // Constructor<?> categoryEntryConstructor = categoryEntryClass.getDeclaredConstructor(ConfigScreen.class, CategoryEntryData.class, String.class); + // categoryEntryConstructor.setAccessible(true); + // ConfigScreen.Entry addNewKeyEntry = (ConfigScreen.Entry) categoryEntryConstructor.newInstance(this, new CategoryEntryData(null, null, null) { + // // TODO: Make translatable + // private static Component title = new TextComponent("Add new key"); + // @Override + // public String getPath() { return null; } + // @Override + // public String getComment() { return null; } + // @Override + // public Component getTitle() { return title; } + // @Override + // public boolean mayResetValue() { return false; } + // @Override + // public boolean mayDiscardChanges() { return false; } + // @Override + // public void resetCurrentValue() { } + // @Override + // public void discardCurrentValue() { } + // @Override + // public void saveConfigValue() { } + // @Override + // public boolean category() { return false; } + // }, null); + + // Field buttonField = categoryEntryClass.getDeclaredField("button"); + // UnsafeHacks.setField(buttonField, addNewKeyEntry, new Button(10, 5, 260, 20, new TextComponent("Add new key"), button -> { + // searchTextField.setValue(""); + // searchTextField.setFocus(false); + // Screen editScreen = new EditStringScreen((ConfigScreen)(Object)this, title, background, "", x -> true, currentValue -> { + // ((Config)mainConfig).set(currentValue, ""); + // // Update screen and search entries lists. + // List<IEntryData> newEntries = Lists.newArrayList(); + // ValueSpec newValueSpec = IcebergConfigSpec.createValueSpec(null, null, false, Object.class, () -> null, v -> v != null); + // final EntryData.ConfigEntryData<?> data = new DynamicConfigEntryData<>(List.of(currentValue), "", newValueSpec, mainConfig); + // valueToData = Maps.newLinkedHashMap(valueToData); + // valueToData.put(currentValue, data); + // gatherEntries(mainConfig, newEntries, valueToData); + // searchEntries = newEntries; + // screenEntries = mainConfig.valueMap().values().stream().map(valueToData::get).toList(); + // ((ConfigScreen)(Object)this).updateList(false); + // }); + // final Minecraft minecraft = Minecraft.getInstance(); + // minecraft.setScreen(editScreen); + // })); + + // entries.add(addNewKeyEntry); + // } + // } + // catch (Exception e) + // { + // Loader.LOGGER.info(ExceptionUtils.getStackTrace(e)); + // } + + // info.setReturnValue(entries); + // } + // else + // { + // info.setReturnValue(getConfigListEntries(searchEntries, query)); + // } + + // info.cancel(); + // } + + @Inject(method = "gatherEntriesRecursive(Lcom/electronwill/nightconfig/core/UnmodifiableConfig;Ljava/util/Map;)Ljava/util/List;", + at = @At("HEAD"), remap = false, cancellable = true) + private void gatherEntriesRecursiveSubconfigSupport(UnmodifiableConfig mainConfig, Map<Object, IEntryData> allEntries, CallbackInfoReturnable<List<IEntryData>> info) + { + // Store this config for later. + this.mainConfig = mainConfig; + + List<IEntryData> entries = Lists.newArrayList(); + gatherEntries(mainConfig, entries, allEntries); + info.setReturnValue(ImmutableList.copyOf(entries)); + info.cancel(); + } + + @Unique + private static void gatherEntries(UnmodifiableConfig mainConfig, List<IEntryData> entries, Map<Object, IEntryData> entryMap) + { + for (Object value : mainConfig.valueMap().values()) + { + if (entryMap.get(value) != null) + { + entries.add(entryMap.get(value)); + } + if (value instanceof UnmodifiableConfig config) + { + gatherEntries(config, entries, entryMap); + } + else if (value instanceof ForgeConfigSpec.ConfigValue<?> configValue && configValue.get() instanceof UnmodifiableConfig config) + { + if (entryMap.get(config) != null) + { + entries.add(entryMap.get(config)); + } + gatherEntries(config, entries, entryMap); + } + } + } +} diff --git a/src/main/java/com/anthonyhilyard/iceberg/mixin/ConfigMenusForgeIEntryDataMixin.java b/src/main/java/com/anthonyhilyard/iceberg/mixin/ConfigMenusForgeIEntryDataMixin.java new file mode 100644 index 0000000..9bd54f6 --- /dev/null +++ b/src/main/java/com/anthonyhilyard/iceberg/mixin/ConfigMenusForgeIEntryDataMixin.java @@ -0,0 +1,56 @@ +package com.anthonyhilyard.iceberg.mixin; + +import java.util.Map; + +import com.anthonyhilyard.iceberg.util.ConfigMenusForgeHelper; +import com.electronwill.nightconfig.core.UnmodifiableConfig; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +import fuzs.configmenusforge.client.gui.data.IEntryData; +import net.minecraftforge.fml.config.IConfigSpec; +import net.minecraftforge.fml.config.ModConfig; + +@Mixin(IEntryData.class) +public interface ConfigMenusForgeIEntryDataMixin +{ + /** + * @author Iceberg + * @reason Overwriting makeValueToDataMap to allow classes other than ForgeConfigSpec to be supported. + */ + @Overwrite(remap = false) + public static Map<Object, IEntryData> makeValueToDataMap(ModConfig config) + { + if (checkInvalid(config)) + { + return ImmutableMap.of(); + } + Map<Object, IEntryData> allData = Maps.newHashMap(); + UnmodifiableConfig spec = config.getSpec(); + ConfigMenusForgeHelper.makeValueToDataMap(spec, ConfigMenusForgeHelper.getValues(spec), config.getConfigData(), allData, ""); + return ImmutableMap.copyOf(allData); + } + + /** + * @author Iceberg + * @reason Overwriting checkInvalid to allow classes other than ForgeConfigSpec to be supported. + */ + @Overwrite(remap = false) + public static boolean checkInvalid(ModConfig config) + { + IConfigSpec<?> spec = config.getSpec(); + + // True / false means the config class has been cached, null means it's new. + Boolean cachedValue = ConfigMenusForgeHelper.cachedValidity(spec.getClass()); + if (cachedValue == null) + { + // It's not cached, so do the lookup via MethodHandles API and cache the results. + ConfigMenusForgeHelper.cacheClass(spec.getClass()); + } + + return config.getConfigData() == null || !ConfigMenusForgeHelper.cachedValidity(spec.getClass()) || !ConfigMenusForgeHelper.isLoaded(spec); + } +} diff --git a/src/main/java/com/anthonyhilyard/iceberg/mixin/ConfigMenusForgeServerConfigUploaderMixin.java b/src/main/java/com/anthonyhilyard/iceberg/mixin/ConfigMenusForgeServerConfigUploaderMixin.java new file mode 100644 index 0000000..003251f --- /dev/null +++ b/src/main/java/com/anthonyhilyard/iceberg/mixin/ConfigMenusForgeServerConfigUploaderMixin.java @@ -0,0 +1,40 @@ +package com.anthonyhilyard.iceberg.mixin; + +import java.io.ByteArrayOutputStream; + +import com.anthonyhilyard.iceberg.util.ConfigMenusForgeHelper; +import com.electronwill.nightconfig.toml.TomlFormat; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import fuzs.configmenusforge.ConfigMenusForge; +import fuzs.configmenusforge.client.util.ModConfigSync; +import fuzs.configmenusforge.client.util.ServerConfigUploader; +import fuzs.configmenusforge.network.client.message.C2SSendConfigMessage; +import net.minecraft.client.Minecraft; +import net.minecraftforge.fml.config.ModConfig; + +@Mixin(ServerConfigUploader.class) +public class ConfigMenusForgeServerConfigUploaderMixin +{ + @Inject(method = "saveAndUpload", at = @At("HEAD"), remap = false, cancellable = true) + private static void saveAndUpload(ModConfig config, CallbackInfo info) + { + ConfigMenusForgeHelper.save(config.getSpec()); + ModConfigSync.fireReloadingEvent(config); + if (config.getType() == ModConfig.Type.SERVER) + { + final Minecraft minecraft = Minecraft.getInstance(); + if (minecraft.getConnection() != null && !minecraft.isLocalServer()) + { + final ByteArrayOutputStream stream = new ByteArrayOutputStream(); + TomlFormat.instance().createWriter().write(config.getConfigData(), stream); + ConfigMenusForge.NETWORK.sendToServer(new C2SSendConfigMessage(config.getFileName(), stream.toByteArray())); + } + } + info.cancel(); + } +} diff --git a/src/main/java/com/anthonyhilyard/iceberg/mixin/ForgeConfigMenusPlugin.java b/src/main/java/com/anthonyhilyard/iceberg/mixin/ForgeConfigMenusPlugin.java new file mode 100644 index 0000000..23c70ad --- /dev/null +++ b/src/main/java/com/anthonyhilyard/iceberg/mixin/ForgeConfigMenusPlugin.java @@ -0,0 +1,59 @@ +package com.anthonyhilyard.iceberg.mixin; + +import java.util.List; +import java.util.Set; + +import org.objectweb.asm.tree.ClassNode; +import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; +import org.spongepowered.asm.mixin.extensibility.IMixinInfo; + +import net.minecraftforge.fml.loading.FMLLoader; +import net.minecraftforge.fml.loading.LoadingModList; +import net.minecraftforge.fml.loading.moddiscovery.ModInfo; + +public class ForgeConfigMenusPlugin implements IMixinConfigPlugin +{ + private LoadingModList loadingModList = null; + + @Override + public void onLoad(String mixinPackage) { } + + @Override + public String getRefMapperConfig() { return null; } + + @Override + public boolean shouldApplyMixin(String targetClassName, String mixinClassName) + { + if (mixinClassName.toLowerCase().contains("configmenusforge")) + { + if (loadingModList == null) + { + loadingModList = FMLLoader.getLoadingModList(); + } + + // Check if Config Menus for Forge is available. + for (ModInfo modInfo : loadingModList.getMods()) + { + if (modInfo.getModId().equals("configmenusforge")) + { + return true; + } + } + + return false; + } + return true; + } + + @Override + public void acceptTargets(Set<String> myTargets, Set<String> otherTargets) { } + + @Override + public List<String> getMixins() { return null; } + + @Override + public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { } + + @Override + public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { } +}
\ No newline at end of file diff --git a/src/main/java/com/anthonyhilyard/iceberg/mixin/ForgeConfigSpecMixin.java b/src/main/java/com/anthonyhilyard/iceberg/mixin/ForgeConfigSpecMixin.java deleted file mode 100644 index 288af26..0000000 --- a/src/main/java/com/anthonyhilyard/iceberg/mixin/ForgeConfigSpecMixin.java +++ /dev/null @@ -1,160 +0,0 @@ -package com.anthonyhilyard.iceberg.mixin; - -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -import javax.annotation.Nullable; - -import com.anthonyhilyard.iceberg.util.DynamicSubconfig; -import com.electronwill.nightconfig.core.CommentedConfig; -import com.electronwill.nightconfig.core.UnmodifiableConfig; -import com.electronwill.nightconfig.core.ConfigSpec.CorrectionAction; -import com.electronwill.nightconfig.core.ConfigSpec.CorrectionListener; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Overwrite; -import org.spongepowered.asm.mixin.Shadow; - -import net.minecraftforge.common.ForgeConfigSpec; -import net.minecraftforge.common.ForgeConfigSpec.ValueSpec; - - -@Mixin(value = ForgeConfigSpec.class, remap = false) -public class ForgeConfigSpecMixin -{ - @Shadow(remap = false) - private Map<List<String>, String> levelComments; - - @Shadow(remap = false) - private boolean stringsMatchIgnoringNewlines(@Nullable Object obj1, @Nullable Object obj2) { return false; } - - /** - * @author iceberg - * @reason Overwrite the correct method to fix subconfigs not being handled properly. - */ - @Overwrite(remap = false) - private int correct(UnmodifiableConfig spec, CommentedConfig config, LinkedList<String> parentPath, List<String> parentPathUnmodifiable, CorrectionListener listener, CorrectionListener commentListener, boolean dryRun) - { - int count = 0; - - Map<String, Object> specMap = spec.valueMap(); - Map<String, Object> configMap = config.valueMap(); - - for (Map.Entry<String, Object> specEntry : specMap.entrySet()) - { - final String key = specEntry.getKey(); - Object specValue = specEntry.getValue(); - final Object configValue = configMap.get(key); - final CorrectionAction action = configValue == null ? CorrectionAction.ADD : CorrectionAction.REPLACE; - - parentPath.addLast(key); - - String subConfigComment = null; - - // If this value is a config, use that as the spec value to support subconfigs. - if (specValue instanceof ValueSpec valueSpec && valueSpec.getDefault() instanceof UnmodifiableConfig) - { - subConfigComment = valueSpec.getComment(); - specValue = valueSpec.getDefault(); - } - - if (specValue instanceof UnmodifiableConfig) - { - if (configValue instanceof CommentedConfig) - { - count += correct((UnmodifiableConfig)specValue, (CommentedConfig)configValue, parentPath, parentPathUnmodifiable, listener, commentListener, dryRun); - if (count > 0 && dryRun) - { - return count; - } - } - else if (dryRun) - { - return 1; - } - else - { - CommentedConfig newValue = config.createSubConfig(); - configMap.put(key, newValue); - listener.onCorrect(action, parentPathUnmodifiable, configValue, newValue); - count++; - count += correct((UnmodifiableConfig)specValue, newValue, parentPath, parentPathUnmodifiable, listener, commentListener, dryRun); - } - - String newComment = subConfigComment == null ? levelComments.get(parentPath) : subConfigComment; - String oldComment = config.getComment(key); - if (!stringsMatchIgnoringNewlines(oldComment, newComment)) - { - if (commentListener != null) - { - commentListener.onCorrect(action, parentPathUnmodifiable, oldComment, newComment); - } - - if (dryRun) - { - return 1; - } - - config.setComment(key, newComment); - } - } - else - { - ValueSpec valueSpec = (ValueSpec)specValue; - if (!valueSpec.test(configValue)) - { - if (dryRun) - { - return 1; - } - - Object newValue = valueSpec.correct(configValue); - configMap.put(key, newValue); - listener.onCorrect(action, parentPathUnmodifiable, configValue, newValue); - count++; - } - String oldComment = config.getComment(key); - if (!stringsMatchIgnoringNewlines(oldComment, valueSpec.getComment())) - { - if (commentListener != null) - { - commentListener.onCorrect(action, parentPathUnmodifiable, oldComment, valueSpec.getComment()); - } - - if (dryRun) - { - return 1; - } - - config.setComment(key, valueSpec.getComment()); - } - } - - parentPath.removeLast(); - } - - // Second step: removes the unspecified values - for (Iterator<Map.Entry<String, Object>> ittr = configMap.entrySet().iterator(); ittr.hasNext();) - { - Map.Entry<String, Object> entry = ittr.next(); - - // If the spec is a dynamic subconfig, don't bother checking the spec since that's the point. - if (!(spec instanceof DynamicSubconfig) && !specMap.containsKey(entry.getKey())) - { - if (dryRun) - { - return 1; - } - - ittr.remove(); - parentPath.addLast(entry.getKey()); - listener.onCorrect(CorrectionAction.REMOVE, parentPathUnmodifiable, entry.getValue(), null); - parentPath.removeLast(); - count++; - } - } - return count; - } -} diff --git a/src/main/java/com/anthonyhilyard/iceberg/mixin/TextColorMixin.java b/src/main/java/com/anthonyhilyard/iceberg/mixin/TextColorMixin.java index 2bd40ba..9a87af8 100644 --- a/src/main/java/com/anthonyhilyard/iceberg/mixin/TextColorMixin.java +++ b/src/main/java/com/anthonyhilyard/iceberg/mixin/TextColorMixin.java @@ -14,23 +14,23 @@ public class TextColorMixin * Fix an issue in TextColor parsing that makes it so only alpha values up to 0x7F are supported. */ @Inject(method = "parseColor", at = @At("HEAD"), cancellable = true) - private static boolean parseColor(String colorString, CallbackInfoReturnable<TextColor> info) + private static void parseColor(String colorString, CallbackInfoReturnable<TextColor> info) { if (!colorString.startsWith("#")) { - return false; + return; } try { int i = Integer.parseUnsignedInt(colorString.substring(1), 16); info.setReturnValue(TextColor.fromRgb(i)); - return true; + info.cancel(); } catch (NumberFormatException numberformatexception) { info.setReturnValue(null); - return true; + info.cancel(); } } } |