Install Oracle JDK 25 and Oracle JDK 27 locally. Gradle and all plugin/test
compilation use Oracle 25 with --release 25 (class major 69). All Gradle Test
tasks, including worker forks, and the maintained Paper integration fixture run
on Oracle 27. Test processes receive Oracle 27 as JAVA_HOME and first on PATH
so child JVMs use the same installation. Paper itself requires Java 25 or newer.
Both ./gradlew and ./scripts/rebuild.sh call scripts/java-env.sh. On every
invocation it resolves the installed paths with /usr/libexec/java_home -F -v 25
and /usr/libexec/java_home -F -v 27, validates actual Java major versions and
the Oracle Corporation vendor, and checks each javac major. It fails clearly
if an installation is absent, invalid, or from another vendor. No patch-version
folder is hardcoded; installing a newer Oracle patch requires no project edit.
For an explicit installed-home selection, set PLOTMARKERS_BUILD_JAVA_HOME and/or
PLOTMARKERS_TEST_JAVA_HOME. ONE_MB_BUILD_JAVA_HOME and ONE_MB_TEST_JAVA_HOME
are fallback aliases when the corresponding project variable is unset. Explicit
selections undergo the same validation and cannot select another major or vendor.
The existing shell JAVA_HOME and Java executable on PATH do not control the build.
The wrapper uses Gradle 9.8.2 because Java 27 toolchain support starts with Gradle 9.8. It pins the Gradle JVM to the validated Oracle 25 home and limits Gradle toolchain discovery to the two validated installations. Automatic detection and JDK downloads are disabled. Compiler and test toolchains declare both their major and Oracle vendor explicitly; settings checks reject a different Gradle JVM or effective toolchain properties.
Use the supported home variables rather than protected org.gradle.java.* or
toolchain command-line/JVM-option overrides. These are rejected. A stale
gradle/gradle-daemon-jvm.properties also stops the wrapper because daemon criteria
can override Java selection before project settings load. If settings validation
fails, inspect project and ~/.gradle/gradle.properties, IDE Gradle settings, and
Gradle init scripts for stale overrides. The normal workflow needs no changes to
global Java/Gradle settings. Preserve the project selection hook if regenerating
the wrapper scripts.
The current target is Paper 26.3 build 175 STABLE, with API
io.papermc.paper:paper-api:26.3.build.175-stable. Version 2.0.4 build 033 is
reserved for this migration; reuse 033 for verification retries. Do not change
Minecraft versions or fall back to an experimental channel when a check fails.
gradle.properties pins the release, API build, stable channel and official
server SHA-256. scripts/start-test-server.sh reads those values directly, verifies
the server checksum, and refuses server directories outside this repository's
ignored servers/ directory. Its installed test runtime selection follows the
same Oracle 27 validation; it does not change the Java 25 build target.
./scripts/rebuild.shThis runs ./gradlew --no-daemon clean build docsCheck. Checks cover release
metadata and current documentation, the launcher property references, both JAR
descriptors, the mappings namespace, and all packaged classes. Plugin classes must
target Java 25; bundled library classes may target older Java but cannot require
newer or preview bytecode. Main and test class files must use major 69 without
preview bytecode. -Xlint:deprecation -Werror remains enabled.
Four JUnit toolchain regression tests check the actual test-worker Java version,
vendor, and selected home; plugin and test bytecode; and child JVMs launched from
both JAVA_HOME and PATH. These verify build infrastructure. The separate
populated Paper fixture below provides plugin integration coverage.
To report the actual Gradle JVM, compiler executable, and test launcher paths, versions, and vendors without a rebuild, run:
./gradlew --no-daemon verifyJavaToolchainsThe full rebuild also runs this task. Inspect build/test-results/test/ and
build/reports/tests/test/ for test results; test output records actual worker and
child JVM identities. Report successful execution and any failures from the
current run rather than treating toolchain configuration alone as verification.
Install only build/libs/1MB-PlotMarkers-v2.0.4-033-j25-26.3.jar. The thin JAR
requires externally supplied BMUtils and bStats; artifact checks cover it, but
the maintained runtime smoke test uses the shaded JAR.
Use servers/Paper-26.3-stable/. Preserve servers/Paper-26.2/,
servers/Paper-26.3/, and servers/jdk-refresh-031/ as historical rollback
fixtures. Never test against production. All worlds,
databases, downloads, JARs and raw evidence remain ignored by Git.
The former shared test server at
/Users/floris/MinecraftServer/test-1mb-3.14-mc-26.3/ no longer exists
(confirmed 2026-10-25). This repository-local fixture remains the maintained
test environment. No replacement shared deployment target is designated; see
deployment guidance. Historical release
records retain their original deployment paths and receipts.
The stable fixture is a separate filesystem copy of the project's existing
populated fixture. It includes spawn/, local PlotSquared SQLite databases,
PlotMarkers config and icon, BlueMap assets and maps, and Multiverse world
configuration. It runs only BlueMap, PlotSquared, WorldEdit, Multiverse-Core, and
PlotMarkers. CMI, LuckPerms, DiscordSRV and other optional plugins are absent.
Do not copy production credentials. Disable bStats/BlueMap metrics and PlotSquared
Mojang/background UUID lookups; use the fixture's existing local records.
Bind Minecraft and BlueMap to 127.0.0.1. Ports are 28773 (TCP game), 28774
(UDP query, disabled), 28775 (TCP RCON, disabled), and 28776 (TCP BlueMap).
The smoke test checks that all four ports are free before every launch. Keep
Paper exploit protections at safe defaults, including duplication, unsafe portal,
tripwire bypass, and oversized-component sanitizer settings.
PaperScript in this disposable instance uses STABLE for default_channel and
check_latest_channel_only, with session plotmarkers-paper-26-3-033. Download
and verify only the reviewed build:
cd servers/Paper-26.3-stable
./paperscript.sh --yes --no-metadata-cache download --version 26.3 --build 175 --channel STABLE
./paperscript.sh verifyInstall the shaded JAR as the sole PlotMarkers JAR in the fixture's plugins/
folder, then from the repository root run:
python3 scripts/smoke-test.py --failure-pathsThe harness resolves Oracle 27 through the shared helper before changing fixture
data. --java-home /absolute/path/to/installed/oracle-jdk-home may select another
installed Oracle 27; other major versions and vendors are rejected. The JSON
summary records the selected runtime home and its actual version/vendor.
The Python 3 standard-library harness performs two complete startup/shutdown
cycles per invocation. It checks generated diagnostics, invalid arguments,
integration availability, the plot world generator, 361 POIs and 114 shapes over
BlueMap HTTP, custom icon bytes, two reloads per cycle, and identical marker
content after restart. Marker checks query the running HTTP server after fresh
creation logs; they do not require BlueMap's optional marker-file exports.
It requires all four dimensions to save, process exit 0,
and no linkage, task, enable, or disable errors. It also checks that PlotMarkers
configuration/icons and the logical contents of the local PlotSquared database
are unchanged. --failure-paths temporarily removes an icon and adds an unmatched
map entry, verifies warnings and continued marker generation, and restores both.
Run it only against a disposable fixture.
Evidence is written to servers/verification-033/<run-id>/: console and Paper
logs, HTTP marker snapshots, and a JSON summary. The
build 033 release record records
actual results, dependency versions, warnings and limitations. Historical
build 032 and
build 031 records remain unchanged.
For an interactive console instead, run ./scripts/start-test-server.sh after
checking the ports. Useful commands are version, plugins, plotmarkers info,
plotmarkers version, plotmarkers status, plotmarkers debug,
mv info minecraft:builders, bluemap reload, and stop.
These are observed installations on this Mac, not configured path pins:
| Role | Runtime version | Vendor |
|---|---|---|
| Gradle, plugin compiler, test compiler | 25.0.5.0.3+1-LTS-2 |
Oracle Corporation |
| Test worker, child JVMs, Paper integration fixture | 27.0.1.0.3+1-2 |
Oracle Corporation |
The build home was
/Library/Java/JavaVirtualMachines/jdk-25.0.5.0.3.jdk/Contents/Home, with compiler
bin/javac. The test home was
/Library/Java/JavaVirtualMachines/jdk-27.0.1.0.3.jdk/Contents/Home, with launcher
bin/java. Both were discovered dynamically by the commands documented above.
Gradle's metadata displays the normalized vendor Oracle; the running JVMs
report Oracle Corporation.
./scripts/rebuild.sh --offline --info --warning-mode fail passed all 11 tasks
after the initial dependency download. All four JUnit tests passed with no skips,
failures, or errors. The log confirms Oracle 25 compiled both source sets and
Oracle 27 executed the test worker and both child JVMs. All eight main/test class
files were verified as Java 25, major 69, minor 0; independent javap -verbose
checks confirmed the plugin and test class headers. Both release JAR checks and
documentation checks passed.
Starting ./gradlew --no-daemon --offline verifyJavaToolchains --warning-mode fail
with shell JAVA_HOME set to the discovered Oracle 27 home still selected Oracle
25 for Gradle and compilation and Oracle 27 for tests. No stale user Gradle
properties/init scripts, Maven toolchains, repository Gradle JVM settings, or
daemon criteria were found during inspection. No global settings were changed.
The 46 launcher guard checks passed: missing, empty, wrong-major and simulated
non-Oracle installations; protected CLI/JVM-option overrides; invalid smoke-test
homes; and an isolated stale daemon-criteria fixture. The simulated metadata
fixtures were used only to prove rejection, never for compilation or tests.
Two additional environment-property checks confirmed conflicting installation
paths and auto-download=true were superseded by the wrapper's validated paths
and false, giving 48 successful guard/precedence checks in total.
Raw local evidence is retained in ignored .gradle/toolchain-verification/:
clean-build.log, shell-java-27.log, javap.txt, guard-tests.txt, and
project-env-precedence.txt.
python3 scripts/smoke-test.py --failure-paths passed both complete startup and
shutdown cycles on Oracle 27. The tests verified 361 POIs, 114 shapes, custom icon
bytes, repeated reloads, missing-icon/unmatched-map recovery, identical markers
after restart, four saved dimensions, and unchanged plugin configuration, icons,
and logical PlotSquared database contents. Both server processes exited 0.
The installed shaded JAR matched the clean build byte for byte, SHA-256
a50d84c8a1df9e465c9918f12be8813d262a3c08b4fc8734111b4e31ce2bfc75.
The initial harness run revealed its dependency on optional marker-file exports;
that wait was replaced with checks against BlueMap's populated live HTTP response.
The initial run was interrupted with a clean server shutdown before rerunning.
Successful evidence is in
servers/verification-033/1792922345058908000/summary.json, with console logs,
Paper logs, and marker snapshots alongside it. The harness transcript is in
.gradle/toolchain-verification/paper-smoke.log.
Remaining limits: the JUnit tests cover toolchain behavior, while the populated fixture supplies automated plugin integration coverage. Interactive Java/Bedrock player scenarios below and standalone thin-JAR runtime deployment were not tested in this toolchain change. Existing third-party runtime warnings are retained in the smoke-test summary; they did not cause a test failure. All work remains local; no release or repository changes were published.
On a disposable copy, connect a Java player and, when Floodgate is available, a Bedrock player. Use known local UUID records, including a renamed account.
- As a non-op, run all PlotMarkers diagnostics and tab completion. These commands intentionally require no permission. Check PlotSquared denies unauthorized claims/deletes and that denied operations leave marker data unchanged.
- With appropriate PlotSquared permissions, claim, transfer, merge, unlink and delete disposable plots. Verify POI owners and shape boundaries after each action, including fast repeated operations and cancelled actions.
- Quit, kick, reconnect and switch worlds. Verify last-seen updates, UUID/name presentation, and absence of scheduled-task errors. Restart and confirm the resulting plots and markers persist.
- Open BlueMap in a browser and inspect icon anchors, dates, owner text, custom colors, heights, and merged outlines. Reload BlueMap during marker generation and stop the server during queued updates; check clean shutdown and restart.
PlotMarkers has no inventories, custom items/PDC, economy transactions, command
rewards, or LuckPerms integration. Those feature-specific checks do not apply.
UUID access remains based on Player#getUniqueId() and PlotSquared plot owners;
no username-to-UUID network lookup or LuckPerms command is added.