Skip to content

Java Versions and Toolchains

Petrus Pradella edited this page Jul 23, 2026 · 1 revision

Java Versions and Toolchains

EverNifeCore compiles modern Java source and ships Java 8 bytecode for the modules that must run on old Minecraft servers - all from a single JDK 25 toolchain, with no compiler pin anywhere. This page explains how that works and where the exceptions are.

The rules live in the root build.gradle; the version numbers live in the version catalog (gradle/libs.versions.toml). See Building from Source for the build commands.


One toolchain: JDK 25

Launch Gradle with JDK 25 and it compiles and runs everything, tests included. The root build applies a single toolchain to every subproject:

java {
    toolchain.languageVersion = JavaLanguageVersion.of(25)
}

There is no separate compiler pin anywhere in the build. Gradle, the production compile, the test compile and the test execution all run on that one Java 25 toolchain. Older setups pinned JDK 17 for the dual-target compile; that pin is gone (see "Jabel" below).

Every configuration is also tagged TargetJvmVersion = 25, which lets the Java-8-bytecode modules still resolve JVM-11+ dependencies on the compile classpath.


Dual-target: modern syntax, Java 8 bytecode

Four modules must run on a Java 8 JVM (a 1.7.10 server can be that old) yet are written in modern Java. They are the dual-target set:

:api-contracts   :common   :libby   :minecraft

For these, the root build applies:

compileJava {
    sourceCompatibility = 25   // modern *syntax* (an IDE/javac hint; Jabel lifts the source check)
    options.release = 8        // Java 8 *bytecode* and Java 8 *API* floor
}

Two things are pinned here, and they are different:

  • options.release = 8 caps both the emitted bytecode (runs on a Java 8 JVM) and the API floor (no Java 9+ library API can slip into code that has to run on an old server).
  • Jabel lifts javac's source-level check so the Java 25 compiler accepts modern syntax while still emitting Java 8 bytecode.

What Jabel does

Jabel is the FinalCraft fork br.com.finalcraft:jabel (the same one EveryDatabase and EveryConfig use), applied as an annotation processor on the dual-target modules. It lets you write var, switch expressions, and text blocks and have them downgraded to Java 8 bytecode.

The upstream bsideup Jabel could not hook a javac newer than 17 - that was the only reason the old JDK 17 pin existed. The FinalCraft fork works on JDK 25, so the pin is gone.

Not available in the dual-target modules: record and sealed. Those have no Java 8 bytecode form Jabel can produce, so they are off-limits in common, minecraft, api-contracts, and libby. (They are fine in hytale - see below.)


The load-bearing exception: version-compat modules

The four version-compatibility modules use a different rule - plain source/targetCompatibility = 1.8, no Jabel, no options.release:

:minecraft:modules:v1_7_10    :minecraft:modules:v1_12_2
:minecraft:modules:v1_16_5    :minecraft:modules:VersionedPlugins

This is deliberate and must not be "fixed" to options.release = 8. These modules reach for sun.misc.Unsafe (through UnsafeUtil, and ImpIFCFlagRegistry on top of it) to rewrite a final field in WorldGuard's flag registry. options.release deliberately hides the JDK's internal packages, so switching to it fails to compile. The old source/target pair is what keeps sun.misc reachable.

The trade-off: these four modules get no API floor. Only code review stops a Java 9+ API from reaching a 1.7.10 server. They are plain Java 8 syntax, so no Jabel and no modern-syntax features.

For what these modules actually do (WorldGuard compatibility, not NMS), see Version Compatibility.


Hytale: plain Java 25

hytale is deliberately not in the dual-target set. It is plain Java 25 - the Java 25 toolchain, no Jabel, no bytecode cap. Records, sealed types, and modern library APIs are all fine there, because Hytale targets a modern runtime. See Hytale Platform.


Summary

Module(s) Bytecode Compiler Modern syntax record/sealed API floor
common, minecraft, api-contracts, libby Java 8 JDK 25 + Jabel + release=8 yes (var, switch expr, text blocks) no Java 8
minecraft:modules:* Java 8 JDK 25, source/target=1.8 no no none (review only)
hytale Java 25 JDK 25 yes yes Java 25

Other build facts worth knowing:

  • Lombok (1.18.46) is declared once at the root for every subproject.
  • Test code is never capped. In every module the tests compile and run on the full Java 25 API, even where main is capped to Java 8 - so tests can use records, modern collections, etc.
  • FCScheduler uses virtual threads on Java 21+ (via EveryLibs' VirtualThreadedScheduledExecutor), falling back to a fixed thread pool bounded to the CPU core count on older JVMs. See Scheduler and Threading.

See also

Clone this wiki locally