Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ accesstransformers_version=11.0.2
eventbus_version=8.0.5
bootstraplauncher_version=1.1.8
asm_version=9.7
mixin_version=0.14.0+mixin.0.8.6
mixin_version=0.16.4+mixin.0.8.7
terminalconsoleappender_version=1.3.0
nightconfig_version=3.8.0
jetbrains_annotations_version=26.0.2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public void addMixinConfigs() {
final String modId = file.getModInfos().get(0).getModId();
for (ModFileParser.MixinConfig potential : file.getMixinConfigs()) {
if (potential.requiredMods().stream().allMatch(id -> this.getModFileById(id) != null)) {
DeferredMixinConfigRegistration.addMixinConfig(potential.config(), modId);
DeferredMixinConfigRegistration.addMixinConfig(potential.config(), modId, potential.behaviorVersion());
} else {
LOGGER.debug("Mixin config {} for mod {} not applied as required mods are missing", potential.config(), modId);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -22,7 +24,7 @@ public class DeferredMixinConfigRegistration {

private static boolean added = false;

record ConfigInfo(String fileName, @Nullable String modId) {}
record ConfigInfo(String fileName, @Nullable String modId, int behaviorVersion) {}

private static final List<ConfigInfo> mixinConfigs = new ArrayList<>();

Expand All @@ -39,11 +41,30 @@ public static void addMixinConfig(String config) {
}

public static void addMixinConfig(String config, @Nullable String modId) {
addMixinConfig(config, modId, null);
}

@ApiStatus.Internal
public static void addMixinConfig(String config, @Nullable String modId, @Nullable ArtifactVersion behaviorVersion) {
if (added) {
throw new IllegalStateException("Too late to add mixin configs!");
}

mixinConfigs.add(new ConfigInfo(config, modId));
mixinConfigs.add(new ConfigInfo(config, modId, calculateBehaviorVersion(behaviorVersion)));
}

// Increment to break compatibility; during a BC window, this should be set to the latest version. This is _not_ set
// to COMPATIBILITY_LATEST, so that if mixin is bumped past a BC it does not break mods.
@ApiStatus.Internal
public static final int DEFAULT_BEHAVIOUR_VERSION = FabricUtil.COMPATIBILITY_0_14_0;

private static int calculateBehaviorVersion(@Nullable ArtifactVersion behaviorVersion) {
if (behaviorVersion == null) {
return DEFAULT_BEHAVIOUR_VERSION;
}
return behaviorVersion.getMajorVersion() * (1000 * 1000) +
behaviorVersion.getMinorVersion() * 1000 +
behaviorVersion.getIncrementalVersion();
}

static void registerConfigs() {
Expand All @@ -59,6 +80,7 @@ static void registerConfigs() {
LOG.warn("Config file {} was not registered!", cfg.fileName());
} else {
config.decorate(FabricUtil.KEY_MOD_ID, cfg.modId());
config.decorate(FabricUtil.KEY_COMPATIBILITY, cfg.behaviorVersion());
}
});
mixinConfigs.clear();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,25 @@
import com.electronwill.nightconfig.core.file.FileConfig;
import com.electronwill.nightconfig.toml.TomlFormat;
import com.mojang.logging.LogUtils;
import java.lang.module.ModuleDescriptor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import net.neoforged.fml.loading.LogMarkers;
import net.neoforged.fml.loading.mixin.DeferredMixinConfigRegistration;
import net.neoforged.fml.loading.moddiscovery.readers.JarModsDotTomlModFileReader;
import net.neoforged.neoforgespi.language.IConfigurable;
import net.neoforged.neoforgespi.language.IModFileInfo;
import net.neoforged.neoforgespi.locating.IModFile;
import net.neoforged.neoforgespi.locating.InvalidModFileException;
import net.neoforged.neoforgespi.locating.ModFileInfoParser;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.spongepowered.asm.mixin.FabricUtil;

public class ModFileParser {
private static final Logger LOGGER = LogUtils.getLogger();
Expand Down Expand Up @@ -63,10 +69,32 @@ private static UnmodifiableConfig copyConfig(ConcurrentConfig config) {
/**
* Represents a potential mixin configuration.
*
* @param config The name of the mixin configuration.
* @param requiredMods The mod ids that are required for this mixin configuration to be loaded. If empty, will be loaded regardless.
* @param config The name of the mixin configuration.
* @param requiredMods The mod ids that are required for this mixin configuration to be loaded. If empty, will be loaded regardless.
* @param behaviorVersion The mixin version whose behavior this configuration requests; if unspecified, the default is provided by FML.
*/
public record MixinConfig(String config, List<String> requiredMods) {}
public record MixinConfig(String config, List<String> requiredMods, @Nullable ArtifactVersion behaviorVersion) {
public MixinConfig(String config, List<String> requiredMods) {
this(config, requiredMods, null);
}
}

private static final ArtifactVersion HIGHEST_MIXIN_VERSION;
private static final ArtifactVersion LOWEST_MIXIN_VERSION;

static {
HIGHEST_MIXIN_VERSION = new DefaultArtifactVersion(Optional.ofNullable(FabricUtil.class.getModule().getDescriptor())
.flatMap(ModuleDescriptor::version).map(ModuleDescriptor.Version::toString)
.or(() -> Optional.ofNullable(FabricUtil.class.getPackage().getImplementationVersion()))
.orElseThrow(() -> new IllegalStateException("Cannot determine version of currently running mixin")));
int defaultMixinVersion = DeferredMixinConfigRegistration.DEFAULT_BEHAVIOUR_VERSION;
int patch = defaultMixinVersion % 1000;
defaultMixinVersion /= 1000;
int minor = defaultMixinVersion % 1000;
defaultMixinVersion /= 1000;
int major = defaultMixinVersion;
LOWEST_MIXIN_VERSION = new DefaultArtifactVersion(major + "." + minor + "." + patch);
}

protected static List<MixinConfig> getMixinConfigs(IModFileInfo modFileInfo) {
try {
Expand All @@ -78,7 +106,21 @@ protected static List<MixinConfig> getMixinConfigs(IModFileInfo modFileInfo) {
var name = mixinsEntry.<String>getConfigElement("config")
.orElseThrow(() -> new InvalidModFileException("Missing \"config\" in [[mixins]] entry", modFileInfo));
var requiredModIds = mixinsEntry.<List<String>>getConfigElement("requiredMods").orElse(List.of());
potentialMixins.add(new MixinConfig(name, requiredModIds));
var behaviorVersion = mixinsEntry.<String>getConfigElement("behaviorVersion")
.map(DefaultArtifactVersion::new)
.orElse(null);
if (behaviorVersion != null) {
if (behaviorVersion.compareTo(HIGHEST_MIXIN_VERSION) > 0) {
throw new InvalidModFileException("Specified mixin behavior version " + behaviorVersion
+ " is higher than the current mixin version " + HIGHEST_MIXIN_VERSION + "; this may be fixable by updating neoforge",
modFileInfo);
} else if (behaviorVersion.compareTo(LOWEST_MIXIN_VERSION) < 0) {
throw new InvalidModFileException("Specified mixin behavior version " + behaviorVersion
+ " is lower than the minimum supported behavior version " + LOWEST_MIXIN_VERSION,
modFileInfo);
}
}
potentialMixins.add(new MixinConfig(name, requiredModIds, behaviorVersion));
}

return potentialMixins;
Expand Down
Loading