@@ -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