aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/com/anthonyhilyard
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/anthonyhilyard')
-rw-r--r--src/main/java/com/anthonyhilyard/iceberg/mixin/ForgeConfigSpecMixin.java160
-rw-r--r--src/main/java/com/anthonyhilyard/iceberg/util/DynamicSubconfig.java138
2 files changed, 298 insertions, 0 deletions
diff --git a/src/main/java/com/anthonyhilyard/iceberg/mixin/ForgeConfigSpecMixin.java b/src/main/java/com/anthonyhilyard/iceberg/mixin/ForgeConfigSpecMixin.java
new file mode 100644
index 0000000..288af26
--- /dev/null
+++ b/src/main/java/com/anthonyhilyard/iceberg/mixin/ForgeConfigSpecMixin.java
@@ -0,0 +1,160 @@
+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/util/DynamicSubconfig.java b/src/main/java/com/anthonyhilyard/iceberg/util/DynamicSubconfig.java
new file mode 100644
index 0000000..3ed17f3
--- /dev/null
+++ b/src/main/java/com/anthonyhilyard/iceberg/util/DynamicSubconfig.java
@@ -0,0 +1,138 @@
+package com.anthonyhilyard.iceberg.util;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Supplier;
+
+import com.electronwill.nightconfig.core.AbstractCommentedConfig;
+import com.electronwill.nightconfig.core.ConfigFormat;
+import com.electronwill.nightconfig.core.UnmodifiableCommentedConfig;
+import com.electronwill.nightconfig.core.UnmodifiableConfig;
+
+/**
+ * An exact copy of SimpleCommentedConfig, but this class is specifically meant for subconfigs.
+ * That being said--the class of a config is checked during config validation, and subconfigs are allowed
+ * extra leniency in config keys.
+ */
+final public class DynamicSubconfig extends AbstractCommentedConfig
+{
+ private final ConfigFormat<?> configFormat;
+
+ /**
+ * Creates a Subconfig with the specified format.
+ *
+ * @param configFormat the config's format
+ */
+ DynamicSubconfig(ConfigFormat<?> configFormat, boolean concurrent)
+ {
+ super(concurrent ? new ConcurrentHashMap<>() : new HashMap<>());
+ this.configFormat = configFormat;
+ }
+
+ /**
+ * Creates a Subconfig with the specified data and format. The map is used as it is and
+ * isn't copied.
+ */
+ DynamicSubconfig(Map<String, Object> valueMap, ConfigFormat<?> configFormat)
+ {
+ super(valueMap);
+ this.configFormat = configFormat;
+ }
+
+ /**
+ * Creates a Subconfig with the specified backing map supplier and format.
+ *
+ * @param mapCreator the supplier for backing maps
+ * @param configFormat the config's format
+ */
+ DynamicSubconfig(Supplier<Map<String, Object>> mapCreator, ConfigFormat<?> configFormat)
+ {
+ super(mapCreator);
+ this.configFormat = configFormat;
+ }
+
+ /**
+ * Creates a Subconfig by copying a config and with the specified format.
+ *
+ * @param toCopy the config to copy
+ * @param configFormat the config's format
+ */
+ DynamicSubconfig(UnmodifiableConfig toCopy, ConfigFormat<?> configFormat,
+ boolean concurrent)
+ {
+ super(toCopy, concurrent);
+ this.configFormat = configFormat;
+ }
+
+ /**
+ * Creates a Subconfig by copying a config, with the specified backing map creator and format.
+ *
+ * @param toCopy the config to copy
+ * @param mapCreator the supplier for backing maps
+ * @param configFormat the config's format
+ */
+ public DynamicSubconfig(UnmodifiableConfig toCopy, Supplier<Map<String, Object>> mapCreator,
+ ConfigFormat<?> configFormat)
+ {
+ super(toCopy, mapCreator);
+ this.configFormat = configFormat;
+ }
+
+ /**
+ * Creates a Subconfig by copying a config and with the specified format.
+ *
+ * @param toCopy the config to copy
+ * @param configFormat the config's format
+ */
+ DynamicSubconfig(UnmodifiableCommentedConfig toCopy, ConfigFormat<?> configFormat,
+ boolean concurrent)
+ {
+ super(toCopy, concurrent);
+ this.configFormat = configFormat;
+ }
+
+ /**
+ * Creates a Subconfig by copying a config, with the specified backing map creator and format.
+ *
+ * @param toCopy the config to copy
+ * @param mapCreator the supplier for backing maps
+ * @param configFormat the config's format
+ */
+ public DynamicSubconfig(UnmodifiableCommentedConfig toCopy, Supplier<Map<String, Object>> mapCreator,
+ ConfigFormat<?> configFormat)
+ {
+ super(toCopy, mapCreator);
+ this.configFormat = configFormat;
+ }
+
+ @Override
+ public ConfigFormat<?> configFormat()
+ {
+ return configFormat;
+ }
+
+ @Override
+ public DynamicSubconfig createSubConfig()
+ {
+ return new DynamicSubconfig(mapCreator, configFormat);
+ }
+
+ @Override
+ public AbstractCommentedConfig clone()
+ {
+ return new DynamicSubconfig(this, mapCreator, configFormat);
+ }
+
+ /**
+ * Creates a new Subconfig with the content of the given config. The returned config will have
+ * the same format as the copied config.
+ *
+ * @param config the config to copy
+ * @return a copy of the config
+ */
+ static DynamicSubconfig copy(UnmodifiableConfig config)
+ {
+ return new DynamicSubconfig(config, config.configFormat(), false);
+ }
+} \ No newline at end of file