diff options
author | Anthony Hilyard <anthony.hilyard@gmail.com> | 2022-01-13 15:13:04 -0800 |
---|---|---|
committer | Anthony Hilyard <anthony.hilyard@gmail.com> | 2022-01-13 15:13:04 -0800 |
commit | 7a3df325b5bb294c2836082f58b719c70792ed61 (patch) | |
tree | 9d7596152c590f0be111141173702661d2309d48 | |
parent | bd85cc8ebd856605b458555a967647987b09945e (diff) | |
download | Iceberg-7a3df325b5bb294c2836082f58b719c70792ed61.tar.gz Iceberg-7a3df325b5bb294c2836082f58b719c70792ed61.tar.bz2 Iceberg-7a3df325b5bb294c2836082f58b719c70792ed61.zip |
Added subconfig capability to Forge's config system.
-rw-r--r-- | CHANGELOG.md | 136 | ||||
-rw-r--r-- | gradle.properties | 2 | ||||
-rw-r--r-- | src/main/java/com/anthonyhilyard/iceberg/mixin/ForgeConfigSpecMixin.java | 160 | ||||
-rw-r--r-- | src/main/java/com/anthonyhilyard/iceberg/util/DynamicSubconfig.java | 138 | ||||
-rw-r--r-- | src/main/resources/iceberg.mixins.json | 5 |
5 files changed, 438 insertions, 3 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..4ab1705 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,136 @@ +# Changelog + +### 1.0.39 +- Fixed an issue in Forge's configuration system that prevented dynamic subconfigs from working properly. + +### 1.0.38 +- Added support for tooltip components that use tooltip component generation event. + +### 1.0.37 +- Reverted first change from 1.0.35, as it was causing conflicts with other mod's tooltips. + +### 1.0.36 +- Fixed a crash issue with modded tooltip components that are not properly added to tooltip component factory. + +### 1.0.35 +- Fixed an issue that could cause dependent client-side mods to crash when run on a dedicated server. +- Fixed various warnings in latest.log file. +- Fixed bug causing incorrect tooltip background alpha. + +### 1.0.34 +- Improved item color detection for mods that do not properly implement item name colors. + +### 1.0.33 +- Improved extended tooltip events to better support multiple simultaneous tooltip rendering. +- Fixed bug in Minecraft's text color handling to fully support alpha values. + +### 1.0.32 +- Fixed a crash bug caused by invalid color codes in item names. +- Improved NBT selector to recognize tags in lists. ("&id=minecraft:sharpness" works for anything enchanted with Sharpness, for example) +- Bumped required Forge version. + +### 1.0.31 +- Fixed a rare issue that could prevent the game from loading. + +### 1.0.30 +- Added support for ItemEdit item name colors. +- Fixed tooltip rect calculation issues. +- Added constrained tooltip component gathering helper. + +### 1.0.29 +- Overhauled tooltip handling to support 1.18 tooltip components. +- First Forge 1.18 release. + +### 1.0.28 +- Added support for color-code specified item name colors. + +### 1.0.27 +- Fixed a bug with dynamic resource packs that prevented dynamic resources from properly overriding. +- Added minimum width constrain option for tooltip rendering. + +### 1.0.26 +- Added NBT selector. +- Added selector syntax validator. + +### 1.0.25 +- Added dynamic resource pack helper. +- First Fabric 1.18 release. + +### 1.0.24 +- Rewrote client-side item pickup event so Iceberg is no longer required on servers (for that event). + +### 1.0.23 +- Fixed an issue with tooltip events causing inaccurate tooltip bounding box for tooltips near the edge of the screen. + +### 1.0.22 +- Consolidated configuration item selector logic for dependent mods, so new selectors can be added without requiring mod updates. + +### 1.0.21 +- Fixed a crash bug caused by an incompatibility with Architectury. + +### 1.0.20 +- Added rendertick event, added color results for color event. + +### 1.0.19 +- Fixed a crash bug when rendering custom gradients. + +### 1.0.18 +- Network protocol rewrite. +- Fixed final warning. + +### 1.0.17 +- Fixed jar versioning for Mod Menu (Fabric). + +### 1.0.16 +- Fixed a crash bug due to missing fonts on tooltips. +- First Fabric 1.17 release. + +### 1.0.15 +- Added StringRecomposer helper. + +### 1.0.14 +- Readded a post-tooltip rendering event, since RenderTooltipEvent.PostText was removed from Forge. + +### 1.0.13 +- Fixed an access issue on the custom ItemRenderer class. + +### 1.0.12 +- Fixed a mod compatibility issue for mods that interacted with advancements. (Such as Clickable Advancements) +- First Forge 1.17 release. + +### 1.0.11 +- Fixed a rare null-pointer exception when calculating tooltips. + +### 1.0.10 +- Added extended tooltip rendering events to better facilitate tooltip customization. + +### 1.0.9 +- Added GuiHelper class with generic rendering helper methods. +- Added Color easing helper function to Easing class. + +### 1.0.8 +- Fixed a bug that was preventing auto-registered sounds from working in multiplayer. + +### 1.0.7 +- Added helper entity registry methods. + +### 1.0.6 +- Added renderer support for auto registration system. + +### 1.0.5 +- Added network protocol and new event for remote pre-item pickup events. + +### 1.0.4 +- Added new helper method to render item tooltips without vanilla positioning restrictions. + +### 1.0.3 +- Added helper functions to auto registration class for easy registration of entities and sounds. + +### 1.0.2 +- Added a simple automatic registration utility class. + +### 1.0.1 +- Added tooltip helper. + +### 1.0.0 +- Initial release.
\ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 0c29540..3e4c4b9 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.38 +version=1.0.39 mcVersion=1.18.1 forgeVersion=39.0.9 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 diff --git a/src/main/resources/iceberg.mixins.json b/src/main/resources/iceberg.mixins.json index 6ee1aca..f167048 100644 --- a/src/main/resources/iceberg.mixins.json +++ b/src/main/resources/iceberg.mixins.json @@ -1,11 +1,12 @@ { "required": true, "package": "com.anthonyhilyard.iceberg.mixin", - "compatibilityLevel": "JAVA_16", + "compatibilityLevel": "JAVA_17", "refmap": "iceberg.refmap.json", "mixins": [ "EntityMixin", - "PlayerAdvancementsMixin" + "PlayerAdvancementsMixin", + "ForgeConfigSpecMixin" ], "client": [ "ScreenMixin", |