Skip to content

Commit b300222

Browse files
authored
stage2/lw: Fix crash when Minecraft and temp files are on separate drives
The way we used to pass extra jars which may contain mods to Forge, via its `--mods` argument, only accepts relative paths, and on Windows it is impossible to construct a relative path when the two paths are on different drive letters. This commit now instead uses our existing RelaunchTransformer to modify the code in Forge which parses the `--mods` argument, to manually inject the absolute paths to any such extra mods right after the regular `--mods` parsing. GitHub: #34
1 parent 1685fd1 commit b300222

File tree

5 files changed

+75
-20
lines changed

5 files changed

+75
-20
lines changed

stage2/launchwrapper/src/main/java/gg/essential/loader/stage2/Loader.java

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,16 @@ private void relaunchViaNewerLoader(Path loaderJar) {
117117
}
118118

119119
private void relaunch(List<JarInfo> jars) {
120+
List<URL> loadedByForge = Launch.classLoader.getSources();
121+
120122
RelaunchInfo relaunchInfo = new RelaunchInfo();
121123
relaunchInfo.loadedIds = jars.stream()
122124
.map(it -> it.id)
123125
.collect(Collectors.toSet());
126+
relaunchInfo.extraMods = jars.stream()
127+
.filter(it -> !loadedByForge.contains(it.url()))
128+
.map(it -> it.path.toAbsolutePath().toString())
129+
.collect(Collectors.toList());
124130
RelaunchInfo.put(relaunchInfo);
125131

126132
Set<URL> priorityClassPath = new LinkedHashSet<>();
@@ -132,23 +138,7 @@ private void relaunch(List<JarInfo> jars) {
132138
priorityClassPath.add(jarInfo.url());
133139
}
134140

135-
List<URL> loadedByForge = Launch.classLoader.getSources();
136-
String extraMods = jars.stream()
137-
.filter(it -> !loadedByForge.contains(it.url()))
138-
.map(it -> minecraftHome.relativize(it.path).toString())
139-
.collect(Collectors.joining(","));
140-
141-
Consumer<List<String>> modifyArgsForExtraMods = extraMods.isEmpty() ? arg -> {} : args -> {
142-
int index = args.indexOf("--mods");
143-
if (index == -1) {
144-
args.add("--mods");
145-
args.add(extraMods);
146-
} else {
147-
args.set(index + 1, extraMods + "," + args.get(index + 1));
148-
}
149-
};
150-
151-
Relaunch.relaunch(priorityClassPath, modifyArgsForExtraMods);
141+
Relaunch.relaunch(priorityClassPath);
152142
throw new AssertionError("relaunch should not return");
153143
}
154144

stage2/launchwrapper/src/main/java/gg/essential/loader/stage2/RelaunchInfo.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22

33
import com.google.gson.Gson;
44

5+
import java.util.List;
56
import java.util.Set;
67

78
class RelaunchInfo {
89
public Set<String> loadedIds;
10+
public List<String> extraMods;
911

1012
private static String PROPERTY = "gg.essential.loader.stage2.relaunch-info";
1113

stage2/launchwrapper/src/main/java/gg/essential/loader/stage2/RelaunchedLoader.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.util.ArrayList;
2525
import java.util.Collection;
2626
import java.util.List;
27+
import java.util.Map;
2728
import java.util.jar.Attributes;
2829
import java.util.jar.JarFile;
2930

@@ -188,6 +189,24 @@ private static void addMixinTransformerExclusionImpl(String name) {
188189
}
189190
}
190191

192+
@SuppressWarnings("unused") // called via ASM, see RelaunchTransformer
193+
public static void injectExtraMods(Map<String, File> forgeExtraModsMap) {
194+
RelaunchInfo relaunchInfo = RelaunchInfo.get();
195+
assert(relaunchInfo != null);
196+
for (String extraMod : relaunchInfo.extraMods) {
197+
forgeExtraModsMap.put(extraMod, new File(extraMod));
198+
}
199+
}
200+
201+
@SuppressWarnings("unused") // called via ASM, see RelaunchTransformer
202+
public static void injectExtraMods(List<File> forgeExtraModsList) {
203+
RelaunchInfo relaunchInfo = RelaunchInfo.get();
204+
assert(relaunchInfo != null);
205+
for (String extraMod : relaunchInfo.extraMods) {
206+
forgeExtraModsList.add(new File(extraMod));
207+
}
208+
}
209+
191210
private static class SourceFile {
192211
final File file;
193212
final String tweaker;

stage2/launchwrapper/src/main/java/gg/essential/loader/stage2/relaunch/Relaunch.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import java.util.List;
2121
import java.util.Map;
2222
import java.util.Set;
23-
import java.util.function.Consumer;
2423
import java.util.jar.JarFile;
2524
import java.util.jar.Manifest;
2625

@@ -32,7 +31,7 @@ public class Relaunch {
3231

3332
private static final String HAPPENED_PROPERTY = "essential.loader.relaunched";
3433

35-
public static void relaunch(Set<URL> prioritizedUrls, Consumer<List<String>> modifyArgs) {
34+
public static void relaunch(Set<URL> prioritizedUrls) {
3635
LOGGER.warn("");
3736
LOGGER.warn("");
3837
LOGGER.warn("");
@@ -79,7 +78,6 @@ public static void relaunch(Set<URL> prioritizedUrls, Consumer<List<String>> mod
7978
Launch.blackboard.put("gg.essential.loader.stage2.relaunchClassLoader", relaunchClassLoader);
8079

8180
List<String> args = new ArrayList<>(LaunchArgs.guessLaunchArgs());
82-
modifyArgs.accept(args);
8381
String main = args.remove(0);
8482

8583
Class<?> innerLaunch = Class.forName(main, false, relaunchClassLoader);

stage2/launchwrapper/src/main/java/gg/essential/loader/stage2/relaunch/RelaunchTransformer.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import org.objectweb.asm.Opcodes;
66
import org.objectweb.asm.tree.AbstractInsnNode;
77
import org.objectweb.asm.tree.ClassNode;
8+
import org.objectweb.asm.tree.FieldInsnNode;
89
import org.objectweb.asm.tree.InsnList;
910
import org.objectweb.asm.tree.InsnNode;
1011
import org.objectweb.asm.tree.MethodInsnNode;
@@ -66,6 +67,51 @@ public byte[] apply(String name, byte[] bytes) {
6667
return classWriter.toByteArray();
6768
}
6869

70+
// Inject extra mods which EssentialLoader has discovered outside the mod directory (e.g. Loader extracts
71+
// nested jars into the temporary files folder). If such jars contain mods/tweakers, Forge needs to explicitly
72+
// be told about them so it can load those mods/tweakers.
73+
// This should behave exactly like the `--mods` program argument which Forge adds (except that one is limited
74+
// to paths relative to the game directory, and such relative paths cannot be created on Windows when temp files
75+
// and game folder are on different drives).
76+
// ModListHelper is used on 1.8.9 and older 1.12.2 versions
77+
// LibraryManager is used on newer 1.12.2 versions
78+
if (name.equals("net.minecraftforge.fml.relauncher.ModListHelper")) {
79+
ClassNode classNode = new ClassNode(Opcodes.ASM5);
80+
new ClassReader(bytes).accept(classNode, 0);
81+
for (MethodNode method : classNode.methods) {
82+
if ("parseModList".equals(method.name)) {
83+
InsnList instructions = method.instructions;
84+
AbstractInsnNode ret = instructions.getLast();
85+
while (ret.getOpcode() != Opcodes.RETURN) {
86+
ret = ret.getPrevious();
87+
}
88+
instructions.insertBefore(ret, new FieldInsnNode(Opcodes.GETSTATIC, classNode.name, "additionalMods", "Ljava/util/Map;"));
89+
instructions.insertBefore(ret, new MethodInsnNode(Opcodes.INVOKESTATIC, "gg/essential/loader/stage2/RelaunchedLoader", "injectExtraMods", "(Ljava/util/Map;)V", false));
90+
}
91+
}
92+
ClassWriter classWriter = new ClassWriter(Opcodes.ASM5);
93+
classNode.accept(classWriter);
94+
return classWriter.toByteArray();
95+
}
96+
if (name.equals("net.minecraftforge.fml.relauncher.libraries.LibraryManager")) {
97+
ClassNode classNode = new ClassNode(Opcodes.ASM5);
98+
new ClassReader(bytes).accept(classNode, 0);
99+
for (MethodNode method : classNode.methods) {
100+
if ("gatherLegacyCanidates"/*sic*/.equals(method.name)) {
101+
InsnList instructions = method.instructions;
102+
AbstractInsnNode ret = instructions.getLast();
103+
while (ret.getOpcode() != Opcodes.ARETURN) {
104+
ret = ret.getPrevious();
105+
}
106+
instructions.insertBefore(ret, new InsnNode(Opcodes.DUP));
107+
instructions.insertBefore(ret, new MethodInsnNode(Opcodes.INVOKESTATIC, "gg/essential/loader/stage2/RelaunchedLoader", "injectExtraMods", "(Ljava/util/List;)V", false));
108+
}
109+
}
110+
ClassWriter classWriter = new ClassWriter(Opcodes.ASM5);
111+
classNode.accept(classWriter);
112+
return classWriter.toByteArray();
113+
}
114+
69115
return bytes;
70116
}
71117
}

0 commit comments

Comments
 (0)