From afe3e09a27df65553e2feda427abfa04074ae09a Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Sat, 23 May 2026 11:51:11 -0400 Subject: [PATCH] Add feature level system for mixins --- .../modernfix/annotation/FeatureLevel.java | 9 +++++++ .../annotation/RequiresFeatureLevel.java | 12 +++++++++ .../modernfix/core/ModernFixMixinPlugin.java | 6 +++++ .../core/config/ModernFixEarlyConfig.java | 26 +++++++++++++++++++ 4 files changed, 53 insertions(+) create mode 100644 annotations/src/main/java/org/embeddedt/modernfix/annotation/FeatureLevel.java create mode 100644 annotations/src/main/java/org/embeddedt/modernfix/annotation/RequiresFeatureLevel.java diff --git a/annotations/src/main/java/org/embeddedt/modernfix/annotation/FeatureLevel.java b/annotations/src/main/java/org/embeddedt/modernfix/annotation/FeatureLevel.java new file mode 100644 index 00000000..06aa3098 --- /dev/null +++ b/annotations/src/main/java/org/embeddedt/modernfix/annotation/FeatureLevel.java @@ -0,0 +1,9 @@ +package org.embeddedt.modernfix.annotation; + +public enum FeatureLevel { + GA, BETA; + + public boolean isAtLeast(FeatureLevel required) { + return this.ordinal() >= required.ordinal(); + } +} diff --git a/annotations/src/main/java/org/embeddedt/modernfix/annotation/RequiresFeatureLevel.java b/annotations/src/main/java/org/embeddedt/modernfix/annotation/RequiresFeatureLevel.java new file mode 100644 index 00000000..7ebd7787 --- /dev/null +++ b/annotations/src/main/java/org/embeddedt/modernfix/annotation/RequiresFeatureLevel.java @@ -0,0 +1,12 @@ +package org.embeddedt.modernfix.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.CLASS) +@Target(ElementType.TYPE) +public @interface RequiresFeatureLevel { + FeatureLevel value() default FeatureLevel.GA; +} diff --git a/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java b/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java index 05bf9147..aeda65ed 100644 --- a/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java +++ b/src/main/java/org/embeddedt/modernfix/core/ModernFixMixinPlugin.java @@ -3,6 +3,7 @@ package org.embeddedt.modernfix.core; import com.google.common.collect.ImmutableSet; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.embeddedt.modernfix.annotation.FeatureLevel; import org.embeddedt.modernfix.core.config.ModernFixEarlyConfig; import org.embeddedt.modernfix.core.config.Option; import org.embeddedt.modernfix.core.launchplugin.CoreLaunchPluginService; @@ -40,6 +41,11 @@ public class ModernFixMixinPlugin implements IMixinConfigPlugin { this.logger.info("Loaded configuration file for ModernFix {}: {} options available, {} override(s) found", ModernFixPlatformHooks.INSTANCE.getVersionString(), config.getOptionCount(), config.getOptionOverrideCount()); + if(ModernFixEarlyConfig.ACTIVE_FEATURE_LEVEL != FeatureLevel.GA) { + this.logger.warn("ModernFix stability level is set to {}. Features at this level may be unstable or cause crashes.", + ModernFixEarlyConfig.ACTIVE_FEATURE_LEVEL); + } + config.getOptionMap().values().forEach(option -> { if (option.isOverridden()) { String source = "[unknown]"; diff --git a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java index bebe4c6b..6e67bd7f 100644 --- a/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java +++ b/src/main/java/org/embeddedt/modernfix/core/config/ModernFixEarlyConfig.java @@ -9,7 +9,9 @@ import org.apache.commons.lang3.SystemUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.embeddedt.modernfix.annotation.ClientOnlyMixin; +import org.embeddedt.modernfix.annotation.FeatureLevel; import org.embeddedt.modernfix.annotation.IgnoreOutsideDev; +import org.embeddedt.modernfix.annotation.RequiresFeatureLevel; import org.embeddedt.modernfix.annotation.RequiresMod; import org.embeddedt.modernfix.core.ModernFixMixinPlugin; import org.embeddedt.modernfix.platform.ModernFixPlatformHooks; @@ -65,6 +67,18 @@ public class ModernFixEarlyConfig { private static final String MIXIN_CLIENT_ONLY_DESC = Type.getDescriptor(ClientOnlyMixin.class); private static final String MIXIN_REQUIRES_MOD_DESC = Type.getDescriptor(RequiresMod.class); private static final String MIXIN_DEV_ONLY_DESC = Type.getDescriptor(IgnoreOutsideDev.class); + private static final String FEATURE_LEVEL_ANNOTATION_DESC = Type.getDescriptor(RequiresFeatureLevel.class); + + public static final FeatureLevel ACTIVE_FEATURE_LEVEL = resolveFeatureLevel(); + + private static FeatureLevel resolveFeatureLevel() { + String prop = System.getProperty("modernfix.stabilityLevel", "ga").toLowerCase(Locale.ROOT); + try { + return FeatureLevel.valueOf(prop); + } catch (IllegalArgumentException e) { + return FeatureLevel.GA; + } + } private static final Pattern PLATFORM_PREFIX = Pattern.compile("(forge|fabric|common)\\."); @@ -112,6 +126,7 @@ public class ModernFixEarlyConfig { return; boolean isMixin = false, isClientOnly = false, requiredModPresent = true, isDevOnly = false; String requiredModId = ""; + FeatureLevel requiredLevel = FeatureLevel.GA; for(AnnotationNode annotation : node.invisibleAnnotations) { if(Objects.equals(annotation.desc, MIXIN_DESC)) { isMixin = true; @@ -130,6 +145,15 @@ public class ModernFixEarlyConfig { } } else if(Objects.equals(annotation.desc, MIXIN_DEV_ONLY_DESC)) { isDevOnly = true; + } else if(Objects.equals(annotation.desc, FEATURE_LEVEL_ANNOTATION_DESC)) { + for(int i = 0; i < annotation.values.size(); i += 2) { + if(annotation.values.get(i).equals("value")) { + // ASM stores enum annotation values as String[]{typeDescriptor, constantName} + String[] enumVal = (String[]) annotation.values.get(i + 1); + requiredLevel = FeatureLevel.valueOf(enumVal[1]); + break; + } + } } } if(isMixin && (!isDevOnly || ModernFixPlatformHooks.INSTANCE.isDevEnv())) { @@ -138,6 +162,8 @@ public class ModernFixEarlyConfig { mixinsMissingMods.put(mixinClassName, requiredModId); else if(isClientOnly && !ModernFixPlatformHooks.INSTANCE.isClient()) mixinsMissingMods.put(mixinClassName, "[not client]"); + else if(!ACTIVE_FEATURE_LEVEL.isAtLeast(requiredLevel)) + mixinsMissingMods.put(mixinClassName, "[feature level: requires " + requiredLevel + "]"); String mixinCategoryName = "mixin." + mixinClassName.substring(0, mixinClassName.lastIndexOf('.')); mixinOptions.add(mixinCategoryName); }