Skip to content

Fix compatibility with mods using JarJar to include Kotlin without KotlinForForge - #36

Merged
Johni0702 merged 6 commits into
masterfrom
feature/modlauncher-kotlin-merging-refactor
Jul 27, 2026
Merged

Fix compatibility with mods using JarJar to include Kotlin without KotlinForForge#36
Johni0702 merged 6 commits into
masterfrom
feature/modlauncher-kotlin-merging-refactor

Conversation

@Johni0702

Copy link
Copy Markdown
Contributor

This PR fixes Essential crashing when another mod uses (Neo)Forge's JarJar feature to bundle Kotlin libraries (so they do not need users to separately also install KotlinForForge) and KotlinForForge is not installed separately. See e.g. the slack thread linked in the linear issue.

See the description of the two main commit (59abdb0, f28181f) for details.

This PR also includes two relatively simple refactoring commits prior to the main commits and two small related fixes (see the description of those commits for details) that were required to get everything fully working afterwards.
Would highly recommend consuming commit-by-commit.

Linear: EM-3721

We augment the classpaths constructed by ModLauncher in three ways:
1. We sort them by version, so newer versions reliably get loaded over
older ones (see `SortedJarOrPathList`)
2. We merge our Kotlin libraries into KotlinForForge, or add them to the
classpath directly if KFF is recent enough (see `KFFMerger`)
3. We rename our bundled KFF module to match the name of any externally
istalled ones, since the name of the latter depends on their file name
(see `SelfRenamingJarMetadata`)

This commit changes the way we accomplish the first two.
Previously, we replaced the ModLauncher-internal list which holds the
in-progress classpath, with a custom one that does the sorting, and also
calls `KFFMerger` on every entry as it is `add`ed.

The only reason we originally did `KFFMerger` via `add`, is because
`SortedJarOrPathList` existed before `KFFMerger` became a thing, and it
simply provided a convenient entrypoint without requiring any
substantial changes.

With this commit, we still replace the internal list, but instead of
acting on addition, we now act only once, namely when ModLauncher tries
to turn the finished list into a classpath by calling `.stream` on the
list.

The motivation for this change is that we'll want to avoid adding our
bundled KFF jar altogether when there's no externally installed KFF
present. This is necessary for us to be compatible with third-party mods
which bundle Kotlin but not KFF.
And with the current implementation we cannot know whether that's no
external KFF because we only act on addition and there could always be a
later addition after the one we're currently evaluating.

Note that, barring third-party mods which also replace that internal
list, with this commit our code should still behave effectively exactly
identical to how it did before this commit.
The functional changes to how `KFFMerger` behaves to make us compatible
with such mods will be made in a later commit.
Prior to this commit, we were relying on either finding a JarJar-using
KFF jar installed by the user (in which case we'd add our Kotlin jars to
the classpath directly), or our bundled legacy KFF jar (into which we'd
inject our Kotlin jars).
This commit removes the requirement for us to bundle a KFF jar, by
adding our Kotlin jars to the classpath directly so longe as there isn't
a user-installed legacy KFF jar (and if there is, we continue to inject
into it).

This fixes compatibility with third-party mods which JarJar Kotlin libs
themselves.
Such mods do not include a KFF jar, and if the user hasn't installed one
manually, prior to this commit we'd have then injected our Kotlin into
our legacy bundled KFF jar. This would then result in a
java.lang.module.ResolutionException because both our KFF jar and the
Kotlin libs would contain overlapping packages.

With this commit, we now have the option of simply not shipping a
bundled KFF jar (we don't need KFF ourselves), so in the same scenario
we'd then also just add our Kotlin libs to the classpath directly, and
regular highest-version-resolution makes it all work correctly.
This has gone unnoticed because it only affects KFF versions that are
ancient enough to where we've always shipped a newer version anyway, so
it didn't actually matter.

But now that we no longer always ship KFF, we are mis-identifying such
old KFF versions as containing only Kotlin (because it looks to us like
it doesn't contain kotlinx-coroutines, and we don't check
kotlinx-serialization), and then incorrectly treat them as a standalone
Kotlin lib.

This commit fixes that by returning a non-0 version even without the
version file so long as the `Job` class, which has existed since initial
stable release, is found.
When there's zero language providers registered, `modFiles` will be
`null`.
Prior to this commit, we'd simply return in this case, which is wrong,
but we never actually ran into this case because we always included KFF.

We'll want to no longer do that though (see previous two commits), so we
do now run into this condition, and `return`ing results in Kotlin not
loading. So this commit instead initializes `modFiles` to an empty list.
@github-actions

Copy link
Copy Markdown

Test Results

17 files  ±0  17 suites  ±0   11m 34s ⏱️ +13s
90 tests ±0  90 ✅ ±0  0 💤 ±0  0 ❌ ±0 
94 runs  ±0  94 ✅ ±0  0 💤 ±0  0 ❌ ±0 

Results for commit e234864. ± Comparison against base commit 9b23191.

@Traben-0
Traben-0 self-requested a review July 6, 2026 05:51

@Traben-0 Traben-0 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm, though as usual with the loader another pair of eyes would be nice

@Johni0702
Johni0702 merged commit cc51fbc into master Jul 27, 2026
71 of 73 checks passed
@Johni0702
Johni0702 deleted the feature/modlauncher-kotlin-merging-refactor branch July 27, 2026 12:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants