diff --git a/build.gradle b/build.gradle index 09646caef3..65338bb454 100644 --- a/build.gradle +++ b/build.gradle @@ -27,6 +27,8 @@ dependencies { minecraft "com.mojang:minecraft:${project.minecraft_version}" mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + modImplementation 'net.caffeinemc:CaffeineConfig:1.0.0' + include 'net.caffeinemc:CaffeineConfig:1.0.0' // Fabric API modIncludeImplementation(fabricApi.module("fabric-api-base", project.fabric_version)) @@ -35,6 +37,10 @@ dependencies { modIncludeImplementation(fabricApi.module("fabric-resource-loader-v0", project.fabric_version)) } +repositories { + mavenLocal() +} + if (project.use_third_party_mods) { repositories { maven { url = "https://maven.gegy.dev" } diff --git a/src/main/java/me/jellysquid/mods/sodium/common/config/Option.java b/src/main/java/me/jellysquid/mods/sodium/common/config/Option.java deleted file mode 100644 index 79e7ccc833..0000000000 --- a/src/main/java/me/jellysquid/mods/sodium/common/config/Option.java +++ /dev/null @@ -1,63 +0,0 @@ -package me.jellysquid.mods.sodium.common.config; - -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.Set; - -public class Option { - private final String name; - - private Set modDefined = null; - private boolean enabled; - private boolean userDefined; - - public Option(String name, boolean enabled, boolean userDefined) { - this.name = name; - this.enabled = enabled; - this.userDefined = userDefined; - } - - public void setEnabled(boolean enabled, boolean userDefined) { - this.enabled = enabled; - this.userDefined = userDefined; - } - - public void addModOverride(boolean enabled, String modId) { - this.enabled = enabled; - - if (this.modDefined == null) { - this.modDefined = new LinkedHashSet<>(); - } - - this.modDefined.add(modId); - } - - public boolean isEnabled() { - return this.enabled; - } - - public boolean isOverridden() { - return this.isUserDefined() || this.isModDefined(); - } - - public boolean isUserDefined() { - return this.userDefined; - } - - public boolean isModDefined() { - return this.modDefined != null; - } - - public String getName() { - return this.name; - } - - public void clearModsDefiningValue() { - this.modDefined = null; - } - - public Collection getDefiningMods() { - return this.modDefined != null ? Collections.unmodifiableCollection(this.modDefined) : Collections.emptyList(); - } -} diff --git a/src/main/java/me/jellysquid/mods/sodium/common/config/SodiumConfig.java b/src/main/java/me/jellysquid/mods/sodium/common/config/SodiumConfig.java deleted file mode 100644 index 99b9f8c3b9..0000000000 --- a/src/main/java/me/jellysquid/mods/sodium/common/config/SodiumConfig.java +++ /dev/null @@ -1,244 +0,0 @@ -package me.jellysquid.mods.sodium.common.config; - -import net.fabricmc.loader.api.FabricLoader; -import net.fabricmc.loader.api.ModContainer; -import net.fabricmc.loader.api.metadata.CustomValue; -import net.fabricmc.loader.api.metadata.ModMetadata; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.io.*; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; - -/** - * Documentation of these options: https://github.com/jellysquid3/sodium-fabric/wiki/Configuration-File - */ -@SuppressWarnings("CanBeFinal") -public class SodiumConfig { - private static final Logger LOGGER = LogManager.getLogger("SodiumConfig"); - - private static final String JSON_KEY_SODIUM_OPTIONS = "sodium:options"; - - private final Map options = new HashMap<>(); - - private SodiumConfig() { - // Defines the default rules which can be configured by the user or other mods. - // You must manually add a rule for any new mixins not covered by an existing package rule. - this.addMixinRule("core", true); // TODO: Don't actually allow the user to disable this - - this.addMixinRule("features.block", true); - this.addMixinRule("features.buffer_builder", true); - this.addMixinRule("features.buffer_builder.fast_advance", true); - this.addMixinRule("features.buffer_builder.fast_sort", true); - this.addMixinRule("features.buffer_builder.intrinsics", true); - this.addMixinRule("features.chunk_rendering", true); - this.addMixinRule("features.debug", true); - this.addMixinRule("features.entity", true); - this.addMixinRule("features.entity.fast_render", true); - this.addMixinRule("features.entity.smooth_lighting", true); - this.addMixinRule("features.gui", true); - this.addMixinRule("features.gui.fast_loading_screen", true); - this.addMixinRule("features.gui.font", true); - this.addMixinRule("features.item", true); - this.addMixinRule("features.matrix_stack", true); - this.addMixinRule("features.model", true); - this.addMixinRule("features.options", true); - this.addMixinRule("features.particle", true); - this.addMixinRule("features.particle.cull", true); - this.addMixinRule("features.particle.fast_render", true); - this.addMixinRule("features.render_layer", true); - this.addMixinRule("features.render_layer.leaves", true); - this.addMixinRule("features.sky", true); - this.addMixinRule("features.texture_tracking", true); - this.addMixinRule("features.texture_updates", true); - this.addMixinRule("features.world_ticking", true); - this.addMixinRule("features.fast_biome_colors", true); - this.addMixinRule("features.shader", true); - } - - /** - * Defines a Mixin rule which can be configured by users and other mods. - * @throws IllegalStateException If a rule with that name already exists - * @param mixin The name of the mixin package which will be controlled by this rule - * @param enabled True if the rule will be enabled by default, otherwise false - */ - private void addMixinRule(String mixin, boolean enabled) { - String name = getMixinRuleName(mixin); - - if (this.options.putIfAbsent(name, new Option(name, enabled, false)) != null) { - throw new IllegalStateException("Mixin rule already defined: " + mixin); - } - } - - private void readProperties(Properties props) { - for (Map.Entry entry : props.entrySet()) { - String key = (String) entry.getKey(); - String value = (String) entry.getValue(); - - Option option = this.options.get(key); - - if (option == null) { - LOGGER.warn("No configuration key exists with name '{}', ignoring", key); - continue; - } - - boolean enabled; - - if (value.equalsIgnoreCase("true")) { - enabled = true; - } else if (value.equalsIgnoreCase("false")) { - enabled = false; - } else { - LOGGER.warn("Invalid value '{}' encountered for configuration key '{}', ignoring", value, key); - continue; - } - - option.setEnabled(enabled, true); - } - } - - private void applyModOverrides() { - for (ModContainer container : FabricLoader.getInstance().getAllMods()) { - ModMetadata meta = container.getMetadata(); - - if (meta.containsCustomValue(JSON_KEY_SODIUM_OPTIONS)) { - CustomValue overrides = meta.getCustomValue(JSON_KEY_SODIUM_OPTIONS); - - if (overrides.getType() != CustomValue.CvType.OBJECT) { - LOGGER.warn("Mod '{}' contains invalid Sodium option overrides, ignoring", meta.getId()); - continue; - } - - for (Map.Entry entry : overrides.getAsObject()) { - this.applyModOverride(meta, entry.getKey(), entry.getValue()); - } - } - } - } - - private void applyModOverride(ModMetadata meta, String name, CustomValue value) { - Option option = this.options.get(name); - - if (option == null) { - LOGGER.warn("Mod '{}' attempted to override option '{}', which doesn't exist, ignoring", meta.getId(), name); - return; - } - - if (value.getType() != CustomValue.CvType.BOOLEAN) { - LOGGER.warn("Mod '{}' attempted to override option '{}' with an invalid value, ignoring", meta.getId(), name); - return; - } - - boolean enabled = value.getAsBoolean(); - - // disabling the option takes precedence over enabling - if (!enabled && option.isEnabled()) { - option.clearModsDefiningValue(); - } - - if (!enabled || option.isEnabled() || option.getDefiningMods().isEmpty()) { - option.addModOverride(enabled, meta.getId()); - } - } - - /** - * Returns the effective option for the specified class name. This traverses the package path of the given mixin - * and checks each root for configuration rules. If a configuration rule disables a package, all mixins located in - * that package and its children will be disabled. The effective option is that of the highest-priority rule, either - * a enable rule at the end of the chain or a disable rule at the earliest point in the chain. - * - * @return Null if no options matched the given mixin name, otherwise the effective option for this Mixin - */ - public Option getEffectiveOptionForMixin(String mixinClassName) { - int lastSplit = 0; - int nextSplit; - - Option rule = null; - - while ((nextSplit = mixinClassName.indexOf('.', lastSplit)) != -1) { - String key = getMixinRuleName(mixinClassName.substring(0, nextSplit)); - - Option candidate = this.options.get(key); - - if (candidate != null) { - rule = candidate; - - if (!rule.isEnabled()) { - return rule; - } - } - - lastSplit = nextSplit + 1; - } - - return rule; - } - - /** - * Loads the configuration file from the specified location. If it does not exist, a new configuration file will be - * created. The file on disk will then be updated to include any new options. - */ - public static SodiumConfig load(File file) { - if (!file.exists()) { - try { - writeDefaultConfig(file); - } catch (IOException e) { - LOGGER.warn("Could not write default configuration file", e); - } - - return new SodiumConfig(); - } - - Properties props = new Properties(); - - try (FileInputStream fin = new FileInputStream(file)){ - props.load(fin); - } catch (IOException e) { - throw new RuntimeException("Could not load config file", e); - } - - SodiumConfig config = new SodiumConfig(); - config.readProperties(props); - config.applyModOverrides(); - - return config; - } - - private static void writeDefaultConfig(File file) throws IOException { - File dir = file.getParentFile(); - - if (!dir.exists()) { - if (!dir.mkdirs()) { - throw new IOException("Could not create parent directories"); - } - } else if (!dir.isDirectory()) { - throw new IOException("The parent file is not a directory"); - } - - try (Writer writer = new FileWriter(file)) { - writer.write("# This is the configuration file for Sodium.\n"); - writer.write("#\n"); - writer.write("# You can find information on editing this file and all the available options here:\n"); - writer.write("# https://github.com/jellysquid3/sodium-fabric/wiki/Configuration-File\n"); - writer.write("#\n"); - writer.write("# By default, this file will be empty except for this notice.\n"); - } - } - - private static String getMixinRuleName(String name) { - return "mixin." + name; - } - - public int getOptionCount() { - return this.options.size(); - } - - public int getOptionOverrideCount() { - return (int) this.options.values() - .stream() - .filter(Option::isOverridden) - .count(); - } -} diff --git a/src/main/java/me/jellysquid/mods/sodium/mixin/SodiumMixinPlugin.java b/src/main/java/me/jellysquid/mods/sodium/mixin/SodiumMixinPlugin.java index 8c4bec2475..e9d2539a9b 100644 --- a/src/main/java/me/jellysquid/mods/sodium/mixin/SodiumMixinPlugin.java +++ b/src/main/java/me/jellysquid/mods/sodium/mixin/SodiumMixinPlugin.java @@ -1,96 +1,50 @@ package me.jellysquid.mods.sodium.mixin; -import me.jellysquid.mods.sodium.common.config.Option; -import me.jellysquid.mods.sodium.common.config.SodiumConfig; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.objectweb.asm.tree.ClassNode; -import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; -import org.spongepowered.asm.mixin.extensibility.IMixinInfo; +import net.caffeinemc.caffeineconfig.AbstractCaffeineConfigMixinPlugin; +import net.caffeinemc.caffeineconfig.CaffeineConfig; +import net.fabricmc.loader.api.FabricLoader; -import java.io.File; -import java.util.List; -import java.util.Set; - -@SuppressWarnings("unused") -public class SodiumMixinPlugin implements IMixinConfigPlugin { +public class SodiumMixinPlugin extends AbstractCaffeineConfigMixinPlugin { private static final String MIXIN_PACKAGE_ROOT = "me.jellysquid.mods.sodium.mixin."; - private final Logger logger = LogManager.getLogger("Sodium"); - private SodiumConfig config; - - @Override - public void onLoad(String mixinPackage) { - try { - this.config = SodiumConfig.load(new File("./config/sodium-mixins.properties")); - } catch (Exception e) { - throw new RuntimeException("Could not load configuration file for Sodium", e); - } - - this.logger.info("Loaded configuration file for Sodium: {} options available, {} override(s) found", - this.config.getOptionCount(), this.config.getOptionOverrideCount()); - } - - @Override - public String getRefMapperConfig() { - return null; - } - - @Override - public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { - if (!mixinClassName.startsWith(MIXIN_PACKAGE_ROOT)) { - this.logger.error("Expected mixin '{}' to start with package root '{}', treating as foreign and " + - "disabling!", mixinClassName, MIXIN_PACKAGE_ROOT); - - return false; - } - - String mixin = mixinClassName.substring(MIXIN_PACKAGE_ROOT.length()); - Option option = this.config.getEffectiveOptionForMixin(mixin); - - if (option == null) { - this.logger.error("No rules matched mixin '{}', treating as foreign and disabling!", mixin); - - return false; - } - - if (option.isOverridden()) { - String source = "[unknown]"; - - if (option.isUserDefined()) { - source = "user configuration"; - } else if (option.isModDefined()) { - source = "mods [" + String.join(", ", option.getDefiningMods()) + "]"; - } - - if (option.isEnabled()) { - this.logger.warn("Force-enabling mixin '{}' as rule '{}' (added by {}) enables it", mixin, - option.getName(), source); - } else { - this.logger.warn("Force-disabling mixin '{}' as rule '{}' (added by {}) disables it and children", mixin, - option.getName(), source); - } - } - - return option.isEnabled(); - } - @Override - public void acceptTargets(Set myTargets, Set otherTargets) { - - } - @Override - public List getMixins() { - return null; + protected CaffeineConfig createConfig() { + return CaffeineConfig.builder("Sodium") + .addMixinOption("core", true) // TODO: Don't actually allow the user to disable + + .addMixinOption("features.block", true) + .addMixinOption("features.buffer_builder", true) + .addMixinOption("features.buffer_builder.fast_advance", true) + .addMixinOption("features.buffer_builder.fast_sort", true) + .addMixinOption("features.buffer_builder.intrinsics", true) + .addMixinOption("features.chunk_rendering", true) + .addMixinOption("features.debug", true) + .addMixinOption("features.entity", true) + .addMixinOption("features.entity.fast_render", true) + .addMixinOption("features.entity.smooth_lighting", true) + .addMixinOption("features.gui", true) + .addMixinOption("features.gui.fast_loading_screen", true) + .addMixinOption("features.gui.font", true) + .addMixinOption("features.item", true) + .addMixinOption("features.matrix_stack", true) + .addMixinOption("features.model", true) + .addMixinOption("features.options", true) + .addMixinOption("features.particle", true) + .addMixinOption("features.particle.cull", true) + .addMixinOption("features.particle.fast_render", true) + .addMixinOption("features.render_layer", true) + .addMixinOption("features.render_layer.leaves", true) + .addMixinOption("features.sky", true) + .addMixinOption("features.texture_tracking", true) + .addMixinOption("features.texture_updates", true) + .addMixinOption("features.world_ticking", true) + .addMixinOption("features.fast_biome_colors", true) + .addMixinOption("features.shader", true) + .build(FabricLoader.getInstance().getConfigDir().resolve("sodium.properties")); } @Override - public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { - - } - - @Override - public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { - + protected String mixinPackageRoot() { + return MIXIN_PACKAGE_ROOT; } } diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index a42fc838df..08e897ed0e 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -32,7 +32,8 @@ "depends": { "fabricloader": ">=0.8.0", "fabric-rendering-data-attachment-v1": ">=0.1", - "fabric-rendering-fluids-v1": ">=0.1" + "fabric-rendering-fluids-v1": ">=0.1", + "caffeineconfig": ">=1.0.0" }, "breaks": { "optifabric": "*",