Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
cd47649
CleanMix 0.2.9
Rongmario Jun 28, 2026
94a230e
addMixinMetadataToCrashReport
Rongmario Jun 28, 2026
d257f0d
Update javadocs + latest deprecations
Rongmario Jun 29, 2026
447d55c
Cleaned up late mixin loading
Rongmario Jun 29, 2026
b42d58d
More shims
Rongmario Jun 30, 2026
e984458
CleanMixService
Rongmario Jul 1, 2026
94f8c18
Update to CleanMix 0.2.11
Rongmario Jul 1, 2026
44fb93e
Stub for MixinBooterPlugin + mod container, added Cleanroom logger
Rongmario Jul 2, 2026
03c6701
Added mixins cl exclusions
Rongmario Jul 2, 2026
6867c76
MB 11.1, read meta from mixinbooter.info + logo
Rongmario Jul 2, 2026
8fe96f5
Don't shim mixin plugin and remapper classes
Rongmario Jul 3, 2026
85f0931
Rudimentary replacement for FML's coremod/mod discovery system
Rongmario Jul 3, 2026
bad3669
Fix logger name
Rongmario Jul 4, 2026
94ca7ec
Delay mixin initialization
Rongmario Jul 4, 2026
eb38d0d
Add cleanmix versioning to gradle props
Rongmario Jul 4, 2026
a58e6bc
Simplified attributes variable name
Rongmario Jul 4, 2026
389e37d
Load only coremods of course
Rongmario Jul 4, 2026
baa2624
Fix reading attributes from null manifest
Rongmario Jul 4, 2026
4352bd9
Make IdentifiedMods' list mutable for Loader
Rongmario Jul 4, 2026
983d6f7
Remove MixinBooterPlugin from rootPlugins
Rongmario Jul 4, 2026
7b06596
CleanMix mod container
Rongmario Jul 4, 2026
65ad971
Update CleanMix to 0.4.1
Rongmario Jul 5, 2026
b58e00f
Update MB marker to 11.2
Rongmario Jul 5, 2026
0f6bdc5
Merge remote-tracking branch 'origin/main' into feature/cleanmix-mb11…
Rongmario Jul 5, 2026
57e3fb8
Let's not be retarded here git
Rongmario Jul 5, 2026
338d1db
Use new discoverer to check if instance has forge mods
Rongmario Jul 5, 2026
befd32b
ForgeHooks => CleanMixHooks patch
Rongmario Jul 5, 2026
4426b96
Submit mixin containers
Rongmario Jul 6, 2026
6bdd4bb
Better cleanmix location resolver
Rongmario Jul 6, 2026
c386fa4
Load mixin containers
Rongmario Jul 6, 2026
29e95a6
Add getSideName to service
Rongmario Jul 6, 2026
f034727
Keep old handleCascadingTweak method as mods reflectively call it
Rongmario Jul 6, 2026
efa55d4
Cleanup and delay mixin init
Rongmario Jul 6, 2026
4141afa
Update CleanMix to 0.4.2
Rongmario Jul 6, 2026
a932ebf
When did I delete this?
Rongmario Jul 7, 2026
4c2a265
Enable audit log here
Rongmario Jul 7, 2026
7d7f5c4
Optimize discovery and move early mixin loading when injecting
Rongmario Jul 7, 2026
e10acca
More appropriate location for late mixins to load
Rongmario Jul 7, 2026
8e2cfb5
Update CleanMix to 0.4.5
Rongmario Jul 7, 2026
6d0e915
Extend ModDiscoverer for compat + fix some tweakers not being reparsed
Rongmario Jul 7, 2026
2a5a767
Update CleanMix to 0.4.6
Rongmario Jul 8, 2026
8d55508
Enforce ForceLoadAsMod definition even more
Rongmario Jul 8, 2026
4f59810
Derp
Rongmario Jul 8, 2026
d9d44d1
Bump FG version just to fix the version issue
Rongmario Jul 8, 2026
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 build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ buildscript {
gradlePluginPortal()
}
dependencies {
classpath 'top.outlands.gradle:ForgeGradle:6.0.55'
classpath 'top.outlands.gradle:ForgeGradle:6.0.57'
classpath('top.outlands:artifactural:3.0.4') {
transitive = false
}
Expand Down
1 change: 1 addition & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ asm_version = 9.10.1
asm_deprecated = 7.1
netty_version = 4.2.15.Final
lwjgl_version = 3.4.1
cleanmix_version = 0.4.6

# Nsight Graphics
# Check https://docs.nvidia.com/nsight-graphics/UserGuide/launch-application-overview.html#cli-arguments-details for details
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
}

- return s;
+ return s + net.minecraftforge.common.ForgeHooks.gatherMixinInfo(throwable);
+ return s + com.cleanroommc.cleanmix.CleanMixHooks.addMixinMetadataToCrashReport(throwable);
}

public String getCompleteReport()
Expand Down
9 changes: 8 additions & 1 deletion projects/cleanroom/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ dependencies {
installer 'io.github.classgraph:classgraph:4.8.184'

// Mixin
installer 'com.cleanroommc:sponge-mixin:0.20.12+mixin.0.8.7'
installer "com.cleanroommc:cleanmix:$props.cleanmix_version"
installer annotationProcessor('io.github.llamalad7:mixinextras-common:0.5.4')

testImplementation platform('org.junit:junit-bom:6.1.0')
Expand All @@ -390,6 +390,13 @@ dependencies {
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

processResources {
inputs.property 'cleanmix_version', props.cleanmix_version
filesMatching('cleanmix.info') {
expand cleanmix_version: props.cleanmix_version
}
}

test {
maxParallelForks = 1
useJUnitPlatform()
Expand Down
167 changes: 167 additions & 0 deletions src/main/java/com/cleanroommc/cleanmix/CleanMixHooks.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
package com.cleanroommc.cleanmix;

import com.cleanroommc.discovery.CleanroomModDiscoverer;
import net.minecraftforge.fml.common.discovery.ASMDataTable;
import net.minecraftforge.fml.relauncher.CoreModManager;
import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin;
import org.spongepowered.asm.logging.ILogger;
import org.spongepowered.asm.mixin.Mixins;
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
import org.spongepowered.asm.mixin.transformer.ClassInfo;
import org.spongepowered.asm.mixin.transformer.Config;
import org.spongepowered.asm.mixin.transformer.Proxy;
import org.spongepowered.asm.service.MixinService;
import zone.rong.mixinbooter.*;

import java.util.*;

public class CleanMixHooks {

public static String addMixinMetadataToCrashReport(Throwable throwable) {
Map<String, ClassInfo> classes = new LinkedHashMap<>();
while (throwable != null) {
if (throwable instanceof NoClassDefFoundError) {
ClassInfo classInfo = ClassInfo.fromCache(throwable.getMessage());
if (classInfo != null) {
classes.put(throwable.getMessage(), classInfo);
}
}
StackTraceElement[] stacktrace = throwable.getStackTrace();
for (StackTraceElement stackTraceElement : stacktrace) {
String className = stackTraceElement.getClassName().replace('.', '/');
if (classes.containsKey(className)) {
ClassInfo classInfo = ClassInfo.fromCache(className);
while (classInfo != null) {
classes.put(className, classInfo);
className = classInfo.getSuperName();
if (className == null || className.isEmpty() || "java/lang/Object".equals(className)) {
break;
}
classInfo = classInfo.getSuperClass();
}
}
}
throwable = throwable.getCause();
}
if (classes.isEmpty()) {
return "\nNo Mixin Metadata is found in the Stacktrace.\n";
} else {
StringBuilder mixinMetadataBuilder = new StringBuilder("\nMixins in Stacktrace:");
boolean addedMetadata = false;
for (Map.Entry<String, ClassInfo> entry : classes.entrySet()) {
addedMetadata |= findAndAddMixinMetadata(mixinMetadataBuilder, entry.getKey(), entry.getValue());
}
if (addedMetadata) {
return mixinMetadataBuilder.toString();
} else {
return "\nNo Mixin Metadata is found in the Stacktrace.\n";
}
}
}

public static void loadMixinBooterEarlyMixins(List<CoreModManager.FMLPluginWrapper> loadPlugins) {
ILogger logger = MixinService.getService().getLogger("CleanMix");
Set<String> presentMods = CleanroomModDiscoverer.instance().presentMods();
Set<IEarlyMixinLoader> queuedLoaders = new LinkedHashSet<>();
Context context = new Context(null, presentMods);
for (CoreModManager.FMLPluginWrapper plugin : loadPlugins) {
IFMLLoadingPlugin thePlugin = plugin.coreModInstance;
if (thePlugin instanceof IMixinConfigHijacker interceptor) {
logger.info("Loading config hijacker {}.", interceptor.getClass().getName());
for (String hijacked : interceptor.getHijackedMixinConfigs(context)) {
Config.blacklist(hijacked);
logger.info("{} will hijack the mixin config {}", interceptor.getClass().getName(), hijacked);
}
}
if (thePlugin instanceof IEarlyMixinLoader loader) {
queuedLoaders.add(loader);
}
}
for (IEarlyMixinLoader queuedLoader : queuedLoaders) {
logger.info("Loading early loader {} for its mixins.", queuedLoader.getClass().getName());
try {
for (String mixinConfig : queuedLoader.getMixinConfigs()) {
context = new Context(mixinConfig, presentMods);
if (queuedLoader.shouldMixinConfigQueue(context)) {
logger.info("Adding [{}] mixin configuration.", mixinConfig);
Mixins.addConfiguration(mixinConfig);
queuedLoader.onMixinConfigQueued(context);
}
}
} catch (Throwable t) {
logger.error("Failed to execute early loader [{}].", queuedLoader.getClass().getName(), t);
}
}
}

public static void loadMixinBooterLateMixins(ASMDataTable data) {
ILogger logger = MixinService.getService().getLogger("CleanMix");

// Gather ILateMixinLoaders
Set<ASMDataTable.ASMData> interfaceData = data.getAll(ILateMixinLoader.class.getName().replace('.', '/'));
Set<ILateMixinLoader> lateLoaders = new HashSet<>();

// Instantiate all @MixinLoader annotated classes
Set<ASMDataTable.ASMData> annotatedData = data.getAll(MixinLoader.class.getName());
for (ASMDataTable.ASMData annotated : annotatedData) {
try {
Class<?> clazz = Class.forName(annotated.getClassName());
logger.info("Loading annotated late loader [{}] for its mixins.", clazz.getName());
Object instance = clazz.getConstructor().newInstance();
if (instance instanceof ILateMixinLoader) {
lateLoaders.add((ILateMixinLoader) instance);
}
} catch (Throwable t) {
throw new RuntimeException("Unexpected error.", t);
}
}

// Instantiate all ILateMixinLoader implemented classes
for (ASMDataTable.ASMData itf : interfaceData) {
try {
Class<?> clazz = Class.forName(itf.getClassName().replace('/', '.'));
logger.info("Loading late loader [{}] for its mixins.", clazz.getName());
lateLoaders.add((ILateMixinLoader) clazz.getConstructor().newInstance());
} catch (Throwable t) {
throw new RuntimeException("Unexpected error.", t);
}
}

for (ILateMixinLoader lateLoader : lateLoaders) {
try {
for (String mixinConfig : lateLoader.getMixinConfigs()) {
Context context = new Context(mixinConfig, CleanroomModDiscoverer.instance().presentMods());
if (lateLoader.shouldMixinConfigQueue(context)) {
logger.info("Adding [{}] mixin configuration.", mixinConfig);
Mixins.addConfiguration(mixinConfig);
lateLoader.onMixinConfigQueued(context);
}
}
} catch (Throwable t) {
logger.error("Failed to execute late loader [{}].", lateLoader.getClass().getName(), t);
}
}
Proxy.refreshMixins();
}

private static boolean findAndAddMixinMetadata(StringBuilder mixinMetadataBuilder, String className, ClassInfo classInfo) {
Set<IMixinInfo> mixinInfos = classInfo.getMixins();
if (!mixinInfos.isEmpty()) {
mixinMetadataBuilder.append("\n\t");
mixinMetadataBuilder.append(className);
mixinMetadataBuilder.append(':');
for (IMixinInfo mixinInfo : mixinInfos) {
mixinMetadataBuilder.append("\n\t\t");
mixinMetadataBuilder.append(mixinInfo.getClassName());
mixinMetadataBuilder.append(" (");
mixinMetadataBuilder.append(mixinInfo.getConfig());
mixinMetadataBuilder.append(") [");
mixinMetadataBuilder.append(mixinInfo.getConfig().getCleanSourceId());
mixinMetadataBuilder.append("]");
}
return true;
}
return false;
}

}
62 changes: 62 additions & 0 deletions src/main/java/com/cleanroommc/cleanmix/CleanMixModContainer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.cleanroommc.cleanmix;

import com.google.common.eventbus.EventBus;
import net.minecraftforge.fml.common.DummyModContainer;
import net.minecraftforge.fml.common.LoadController;
import net.minecraftforge.fml.common.MetadataCollection;
import net.minecraftforge.fml.common.ModMetadata;
import net.minecraftforge.fml.common.asm.FMLSanityChecker;
import org.spongepowered.asm.mixin.Mixin;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

public class CleanMixModContainer extends DummyModContainer {

public static File location() {
try {
String resource = Mixin.class.getName().replace('.', '/') + ".class";
URL url = Mixin.class.getClassLoader().getResource(resource);
String path = url.getPath();
int index = path.indexOf('!');
if (index != -1) {
path = path.substring(0, index);
}
return new File(new URI(path)).getAbsoluteFile();
} catch (Exception ignore) { }
return FMLSanityChecker.fmlLocation;
}

public CleanMixModContainer() {
super(loadMetadata());
}

@Override
public boolean registerBus(EventBus bus, LoadController controller) {
bus.register(this);
return true;
}

private static ModMetadata loadMetadata() {
Map<String, Object> fallbackData = new HashMap<>();
fallbackData.put("name", "CleanMix");
fallbackData.put("version", "0.2.11");

InputStream inputStream = CleanMixModContainer.class.getClassLoader().getResourceAsStream("cleanmix.info");
try {
return MetadataCollection.from(inputStream, "cleanmix.info").getMetadataForId("cleanmix", fallbackData);
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException ignored) { }
}
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.cleanroommc.cleanmix.service;

import org.spongepowered.asm.service.IMixinServiceBootstrap;

public class CleanMixBootstrap implements IMixinServiceBootstrap {

private static final String OWN_SERVICE = "com.cleanroommc.cleanmix.service.CleanMixService";

@Override
public String getName() {
return "CleanMix";
}

@Override
public String getServiceClassName() {
return OWN_SERVICE;
}

@Override
public void bootstrap() { }

}
Loading