Skip to content

Commit 4795a94

Browse files
committed
mixin skeleton for use in mods depending on fpl.
1 parent 4be841e commit 4795a94

File tree

6 files changed

+293
-1
lines changed

6 files changed

+293
-1
lines changed

dependencies.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,8 @@ dependencies {
22
compileOnly("org.projectlombok:lombok:1.18.22") {
33
transitive = false
44
}
5+
compileOnly("org.spongepowered:mixin:0.7.11-SNAPSHOT") {
6+
transitive = false
7+
}
58
annotationProcessor("org.projectlombok:lombok:1.18.22")
69
}

repositories.gradle

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,6 @@
1-
repositories {}
1+
repositories {
2+
maven {
3+
name = "sponge"
4+
url = "https://sponge.falsepattern.com/"
5+
}
6+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package com.falsepattern.lib.mixin;
2+
3+
import cpw.mods.fml.relauncher.FMLLaunchHandler;
4+
import lombok.val;
5+
6+
import java.util.List;
7+
import java.util.function.Predicate;
8+
import java.util.function.Supplier;
9+
10+
public interface IMixin {
11+
12+
Side getSide();
13+
String getMixin();
14+
Predicate<List<ITargetedMod>> getFilter();
15+
16+
default boolean shouldLoad(List<ITargetedMod> loadedMods) {
17+
val side = getSide();
18+
return (side == Side.COMMON
19+
|| side == Side.SERVER && FMLLaunchHandler.side().isServer()
20+
|| side == Side.CLIENT && FMLLaunchHandler.side().isClient())
21+
&& getFilter().test(loadedMods);
22+
}
23+
24+
static Predicate<List<ITargetedMod>> never() {
25+
return (list) -> false;
26+
}
27+
28+
static Predicate<List<ITargetedMod>> condition(Supplier<Boolean> condition) {
29+
return (list) -> condition.get();
30+
}
31+
32+
static Predicate<List<ITargetedMod>> always() {
33+
return (list) -> true;
34+
}
35+
36+
static Predicate<List<ITargetedMod>> require(ITargetedMod mod) {
37+
return (list) -> list.contains(mod);
38+
}
39+
40+
static Predicate<List<ITargetedMod>> avoid(ITargetedMod mod) {
41+
return (list) -> !list.contains(mod);
42+
}
43+
44+
45+
enum Side {
46+
COMMON,
47+
CLIENT,
48+
SERVER
49+
}
50+
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package com.falsepattern.lib.mixin;
2+
3+
import lombok.val;
4+
import net.minecraft.launchwrapper.Launch;
5+
import org.apache.logging.log4j.Logger;
6+
import org.spongepowered.asm.lib.tree.ClassNode;
7+
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
8+
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
9+
10+
import java.io.File;
11+
import java.io.FileNotFoundException;
12+
import java.io.IOException;
13+
import java.nio.file.Path;
14+
import java.util.ArrayList;
15+
import java.util.Arrays;
16+
import java.util.List;
17+
import java.util.Set;
18+
import java.util.stream.Collectors;
19+
20+
import static java.nio.file.Files.walk;
21+
22+
public interface IMixinPlugin extends IMixinConfigPlugin {
23+
Path MODS_DIRECTORY_PATH = new File(Launch.minecraftHome, "mods/").toPath();
24+
25+
Logger getLogger();
26+
ITargetedMod[] targetedModEnumValues();
27+
IMixin[] mixinEnumValues();
28+
29+
@Override
30+
default void onLoad(String mixinPackage) {
31+
32+
}
33+
34+
@Override
35+
default String getRefMapperConfig() {
36+
return null;
37+
}
38+
39+
@Override
40+
default boolean shouldApplyMixin(String targetClassName, String mixinClassName) {
41+
return true;
42+
}
43+
44+
@Override
45+
default void acceptTargets(Set<String> myTargets, Set<String> otherTargets) {
46+
47+
}
48+
49+
@Override
50+
default List<String> getMixins() {
51+
val isDevelopmentEnvironment = (boolean) Launch.blackboard.get("fml.deobfuscatedEnvironment");
52+
val targetedMods = targetedModEnumValues();
53+
val loadedMods = Arrays.stream(targetedMods)
54+
.filter(mod -> (mod.getLoadInDevelopment() && isDevelopmentEnvironment)
55+
|| loadJarOf(mod))
56+
.collect(Collectors.toList());
57+
58+
for (val mod : targetedMods) {
59+
if(loadedMods.contains(mod)) {
60+
getLogger().info("Found " + mod.getModName() + "! Integrating now...");
61+
}
62+
else {
63+
getLogger().info("Could not find " + mod.getModName() + "! Skipping integration....");
64+
}
65+
}
66+
67+
List<String> mixins = new ArrayList<>();
68+
for (val mixin : mixinEnumValues()) {
69+
if (mixin.shouldLoad(loadedMods)) {
70+
String mixinClass = mixin.getMixin();
71+
mixins.add(mixinClass);
72+
getLogger().info("Loading mixin: " + mixinClass);
73+
}
74+
}
75+
return mixins;
76+
}
77+
78+
@Override
79+
default void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
80+
81+
}
82+
83+
@Override
84+
default void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
85+
86+
}
87+
88+
default boolean loadJarOf(final ITargetedMod mod) {
89+
boolean success = false;
90+
try {
91+
File jar = findJarOf(mod);
92+
if(jar == null) {
93+
getLogger().info("Jar not found for " + mod);
94+
return false;
95+
}
96+
getLogger().info("Attempting to add " + jar + " to the URL Class Path");
97+
success = true;
98+
if(!jar.exists()) {
99+
success = false;
100+
throw new FileNotFoundException(jar.toString());
101+
}
102+
MinecraftURLClassPath.addJar(jar);
103+
} catch (Throwable e) {
104+
e.printStackTrace();
105+
}
106+
return success;
107+
}
108+
109+
static File findJarOf(final ITargetedMod mod) {
110+
try {
111+
return walk(MODS_DIRECTORY_PATH)
112+
.filter(mod::isMatchingJar)
113+
.map(Path::toFile)
114+
.findFirst()
115+
.orElse(null);
116+
}
117+
catch (IOException e) {
118+
e.printStackTrace();
119+
return null;
120+
}
121+
}
122+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.falsepattern.lib.mixin;
2+
3+
import com.google.common.io.Files;
4+
import lombok.val;
5+
import net.minecraft.launchwrapper.Launch;
6+
import org.apache.logging.log4j.Logger;
7+
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
8+
9+
import java.nio.file.Path;
10+
import java.util.function.Predicate;
11+
12+
public interface ITargetedMod {
13+
static Predicate<String> startsWith(String subString) {
14+
return (name) -> name.startsWith(subString);
15+
}
16+
static Predicate<String> contains(String subString) {
17+
return (name) -> name.contains(subString);
18+
}
19+
static Predicate<String> matches(String regex) {
20+
return (name) -> name.matches(regex);
21+
}
22+
23+
String getModName();
24+
Predicate<String> getCondition();
25+
boolean getLoadInDevelopment();
26+
27+
default boolean isMatchingJar(Path path) {
28+
String pathString = path.toString();
29+
String nameLowerCase = Files.getNameWithoutExtension(pathString).toLowerCase();
30+
String fileExtension = Files.getFileExtension(pathString);
31+
32+
return "jar".equals(fileExtension) && getCondition().test(nameLowerCase);
33+
}
34+
35+
default String toStringDefault() {
36+
return String.format("TargetedMod{%s}", getModName());
37+
}
38+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package com.falsepattern.lib.mixin;
2+
3+
import cpw.mods.fml.common.Loader;
4+
import cpw.mods.fml.common.ModClassLoader;
5+
import lombok.val;
6+
import net.minecraft.launchwrapper.LaunchClassLoader;
7+
import sun.misc.URLClassPath;
8+
9+
import java.io.File;
10+
import java.lang.reflect.Field;
11+
12+
/**
13+
* Backport from spongemixins 1.3 for compat with the curseforge 1.2.0 version.
14+
*
15+
* Also added Grimoire protection.
16+
*/
17+
public final class MinecraftURLClassPath {
18+
/**
19+
* Utility to manipulate the minecraft URL ClassPath
20+
*/
21+
22+
private static final URLClassPath ucp;
23+
private static final boolean GRIMOIRE;
24+
25+
static {
26+
boolean grimoire;
27+
try {
28+
Class.forName("io.github.crucible.grimoire.Grimoire", false, MinecraftURLClassPath.class.getClassLoader());
29+
grimoire = true;
30+
} catch (ClassNotFoundException ignored) {
31+
grimoire = false;
32+
}
33+
GRIMOIRE = grimoire;
34+
if (!GRIMOIRE) {
35+
try {
36+
val modClassLoaderField = Loader.class.getDeclaredField("modClassLoader");
37+
modClassLoaderField.setAccessible(true);
38+
39+
val loaderinstanceField = Loader.class.getDeclaredField("instance");
40+
loaderinstanceField.setAccessible(true);
41+
42+
val mainClassLoaderField = ModClassLoader.class.getDeclaredField("mainClassLoader");
43+
mainClassLoaderField.setAccessible(true);
44+
45+
val ucpField = LaunchClassLoader.class.getSuperclass().getDeclaredField("ucp");
46+
ucpField.setAccessible(true);
47+
48+
Object loader = loaderinstanceField.get(null);
49+
val modClassLoader = (ModClassLoader) modClassLoaderField.get(loader);
50+
val mainClassLoader = (LaunchClassLoader) mainClassLoaderField.get(modClassLoader);
51+
ucp = (URLClassPath) ucpField.get(mainClassLoader);
52+
} catch (NoSuchFieldException | IllegalAccessException e) {
53+
throw new RuntimeException(e.getMessage());
54+
}
55+
} else {
56+
ucp = null;
57+
System.err.println("Grimoire detected, disabling jar loading utility");
58+
}
59+
}
60+
61+
/**
62+
* Adds a Jar to the Minecraft URL ClassPath
63+
* - Needed when using mixins on classes outside of Minecraft or other coremods
64+
*/
65+
public static void addJar(File pathToJar) throws Exception {
66+
if (!GRIMOIRE)
67+
ucp.addURL(pathToJar.toURI().toURL());
68+
}
69+
70+
private MinecraftURLClassPath() {
71+
}
72+
73+
74+
}

0 commit comments

Comments
 (0)