Skip to content

Commit b4b0a7e

Browse files
committed
stage2/ml9: Support KotlinForForge 5
1 parent b3f2b61 commit b4b0a7e

File tree

2 files changed

+52
-1
lines changed

2 files changed

+52
-1
lines changed

stage2/modlauncher9/src/main/java/gg/essential/loader/stage2/SelfRenamingJarMetadata.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
import java.util.List;
1818
import java.util.Set;
1919

20+
import static gg.essential.loader.stage2.util.KFFMerger.isJarJarKff;
21+
2022
/**
2123
* A jar metadata which takes the name of another jar with the same packages in the same layer.
2224
*
@@ -57,10 +59,19 @@ public String name() {
5759
} catch (SelfRenamingReEntranceException ignored) {
5860
continue;
5961
}
60-
if (compatibilityLayer.getPackages(otherJar).stream().anyMatch(ourPackages::contains)) {
62+
Set<String> otherPackages = compatibilityLayer.getPackages(otherJar);
63+
if (otherPackages.stream().anyMatch(ourPackages::contains)) {
6164
LOGGER.debug("Found existing module with name {}, renaming {} to match.", otherModuleName, defaultName);
6265
return otherModuleName;
6366
}
67+
// Special case for fully-JarJar-reliant KFF which no longer contains any code itself and doesn't
68+
// declare its module name in its manifest either.
69+
// We still need to make the KFF we ship to use the same module name though, because otherwise it'll
70+
// be loaded and we'll end up with two modules exporting Kotlin.
71+
if (defaultName.equals("thedarkcolour.kotlinforforge") && otherPackages.isEmpty() && isJarJarKff(otherJar)) {
72+
LOGGER.debug("Found existing JarJar KFF with name {}, renaming {} to match.", otherModuleName, defaultName);
73+
return otherModuleName;
74+
}
6475
}
6576
} catch (Throwable e) {
6677
LOGGER.error("Exception occurred while trying to self-rename module " + defaultName + ": ", e);

stage2/modlauncher9/src/main/java/gg/essential/loader/stage2/util/KFFMerger.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,19 @@ public List<SecureJar> maybeMergeInto(SecureJar secureJar) {
109109
return null;
110110
}
111111

112+
// If this is a KotlinForForge version which uses JarJar to bundle Kotlin (KFF 5+, potentially 4.12),
113+
// we no longer need to merge the Kotlin jars into it, we merely need to add our Kotlin jars to the to-be-loaded
114+
// list too.
115+
if (isJarJarKff(secureJar)) {
116+
LOGGER.info("Looks like KotlinForForge is using JarJar now, all good to go: {}", secureJar);
117+
List<SecureJar> allJars = new ArrayList<>();
118+
allJars.add(secureJar); // keep user-installed jar
119+
allJars.addAll(ourCoreJars.jars);
120+
allJars.addAll(ourCoroutinesJars.jars);
121+
allJars.addAll(ourSerializationJars.jars);
122+
return allJars;
123+
}
124+
112125
// Only care about a jar if it contains a Kotlin we can overwrite
113126
if (!compatibilityLayer.getPackages(secureJar).contains("kotlin")) {
114127
return null;
@@ -126,6 +139,13 @@ public List<SecureJar> maybeMergeInto(SecureJar secureJar) {
126139
boolean updateCoroutines = theirCoroutinesVersion < ourCoroutinesJars.version;
127140
int theirSerializationVersion = updateCore || updateCoroutines ? 0 : ourSerializationJars.version;
128141

142+
// If the jar contains only core but not coroutine libs, then it's not the fat KFF jar but rather KFF is
143+
// using JarJar, and we should be able to find that one later.
144+
if (theirCoreVersion != 0 && theirCoroutinesVersion == 0) {
145+
LOGGER.info("Looks like a standalone Kotlin jar. Keeping as is, we should be finding a JarJar KFF jar.");
146+
return null;
147+
}
148+
129149
List<Path> injectedJars = new ArrayList<>();
130150
ourCoreJars.maybeUpgrade(injectedJars, theirCoreVersion);
131151
ourCoroutinesJars.maybeUpgrade(injectedJars, theirCoroutinesVersion);
@@ -188,6 +208,26 @@ public String name() {
188208
}
189209
}
190210

211+
public static boolean isJarJarKff(SecureJar jar) {
212+
try {
213+
Path jarjarPath = jar.getRootPath().resolve("META-INF").resolve("jarjar");
214+
if (!Files.exists(jarjarPath)) return false;
215+
try (Stream<Path> stream = Files.list(jarjarPath)) {
216+
List<String> files = stream
217+
.map(it -> it.getFileName().toString())
218+
.filter(it -> it.endsWith(".jar"))
219+
.toList();
220+
// A JarJar-using KotlinForForge jar can be recognized by the fact that it bundles both the KFF mod as
221+
// well as the Kotlin Standard Library
222+
return files.stream().anyMatch(it -> it.startsWith("kffmod-"))
223+
&& files.stream().anyMatch(it -> it.startsWith("kotlin-stdlib-"));
224+
}
225+
} catch (Throwable t) {
226+
LOGGER.error("Failed to determine version of potential KFF jar at " + jar + ":", t);
227+
return false;
228+
}
229+
}
230+
191231
private int detectKotlinCoreVersion(SecureJar jar, Path root) {
192232
try {
193233
if (Files.notExists(root.resolve("kotlin").resolve("KotlinVersion.class"))) {

0 commit comments

Comments
 (0)