Skip to content

Commit c1bae6c

Browse files
authored
Merge pull request #2993 from BentoBoxWorld/feature/paper-26-2-support
feat: support Minecraft 26.2 and migrate the build to Java 25
2 parents d580fb2 + d060d5a commit c1bae6c

14 files changed

Lines changed: 292 additions & 41 deletions

File tree

.github/workflows/modrinth-publish.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ jobs:
9797
26.1
9898
26.1.1
9999
26.1.2
100+
26.2
100101
101102
# Path to the built JAR — the Shadow plugin produces BentoBox-<version>.jar
102103
# Note: glob patterns are not supported; use the release tag directly.

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
44

55
## Project Overview
66

7-
BentoBox is a Bukkit/Paper library plugin (Java 21) that provides the core platform for island-style Minecraft games (SkyBlock, AcidIsland, etc.) via an extensible addon system.
7+
BentoBox is a Bukkit/Paper library plugin (Java 25) that provides the core platform for island-style Minecraft games (SkyBlock, AcidIsland, etc.) via an extensible addon system.
88

99
## Build Commands
1010

build.gradle.kts

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*
44
* This build script configures the compilation, testing, packaging, and publishing
55
* of the BentoBox Minecraft plugin. It handles:
6-
* - Java 21 compilation with proper module access
6+
* - Java 25 compilation with proper module access
77
* - Multi-repository dependency resolution
88
* - JAR shading and minimization
99
* - Test execution with JUnit 5
@@ -29,7 +29,9 @@ plugins {
2929
id("com.gradleup.shadow") version "9.3.0"
3030

3131
// Paperweight UserDev - simplifies development against PaperMC with proper mappings and reobfuscation
32-
id("io.papermc.paperweight.userdev") version "2.0.0-beta.19"
32+
// 2.0.0-SNAPSHOT (from Paper's repo, via pluginManagement in settings.gradle.kts) is required:
33+
// all 26.x dev bundles are data version 8, which released paperweight (<= beta.21) cannot read.
34+
id("io.papermc.paperweight.userdev") version "2.0.0-SNAPSHOT"
3335

3436
// Sonarcube
3537
id("org.sonarqube") version "7.2.1.6560"
@@ -46,7 +48,7 @@ paperweight.reobfArtifactConfiguration = io.papermc.paperweight.userdev.ReobfArt
4648
group = "world.bentobox" // From <groupId>
4749

4850
// Base properties from <properties>
49-
val buildVersion = "3.17.1"
51+
val buildVersion = "3.18.0"
5052
val buildNumberDefault = "-LOCAL" // Local build identifier
5153
val snapshotSuffix = "-SNAPSHOT" // Indicates development/snapshot version
5254

@@ -77,23 +79,25 @@ version = finalRevision
7779
// DEPENDENCY VERSIONS
7880
// ============================================================================
7981
// Centralized version management for all external dependencies
80-
val javaVersion = "21"
82+
// Minecraft 26.1+ dev bundles and the 26.2 paper-api are Java 25 artifacts, so BentoBox now
83+
// compiles to Java 25 bytecode. Addons that compile against BentoBox must also move to Java 25.
84+
val javaVersion = "25"
8185
val junitVersion = "5.10.2"
8286
val mockitoVersion = "5.11.0"
83-
// Pin to a concrete JitPack tag rather than the v1.21-SNAPSHOT pointer.
84-
// SNAPSHOT resolution on JitPack is flaky — it sometimes advertises a
85-
// timestamped version in maven-metadata.xml whose corresponding .pom 404s,
86-
// breaking dependency resolution mid-build. Newer tags (>= v4.111) also
87-
// currently fail to build on JitPack because the project requires a Java 25
88-
// toolchain that JitPack's default image doesn't have. v4.110.0 is the most
89-
// recent tag with both POM and JAR available on JitPack.
90-
val mockBukkitVersion = "v4.110.0"
87+
// MockBukkit's modern, per-Minecraft-version artifacts are published to Paper's Maven repo
88+
// under org.mockbukkit.mockbukkit (see the testImplementation coordinate below). The closest
89+
// available to Paper 26.2 is the 26.1.2 line (no 26.2 build exists yet); 4.113.2 is the latest.
90+
val mockBukkitVersion = "4.113.2"
9191
val mongodbVersion = "3.12.12"
9292
val mariadbVersion = "3.0.5"
9393
val mysqlVersion = "8.0.27"
9494
val postgresqlVersion = "42.2.18"
9595
val hikaricpVersion = "5.0.1"
96-
val paperVersion = "1.21.11-R0.1-SNAPSHOT"
96+
// Compile against the latest stable 26.1.2 dev bundle. This is the newest Paper API that has a
97+
// matching MockBukkit release (mockbukkit-v26.1.2); MockBukkit does not yet support 26.2's new
98+
// registries. Minecraft 26.2 is still fully supported at runtime (see ServerCompatibility and
99+
// the Modrinth game-versions list); 26.2-only blocks/entities are accessed via Enums.getIfPresent.
100+
val paperVersion = "26.1.2.build.72-stable"
97101
val bstatsVersion = "3.0.0"
98102
val vaultVersion = "1.7.1"
99103
val levelVersion = "2.21.3"
@@ -145,7 +149,7 @@ extra["revision"] = finalRevision
145149
// ============================================================================
146150
// Configures Java compiler and toolchain settings
147151
java {
148-
// Use Java 21 toolchain for compilation (enforced regardless of JVM running Gradle)
152+
// Use Java 25 toolchain for compilation (enforced regardless of JVM running Gradle)
149153
toolchain {
150154
languageVersion = JavaLanguageVersion.of(javaVersion)
151155
}
@@ -157,7 +161,7 @@ tasks.withType<JavaCompile> {
157161
// Suppress all deprecation and removal warnings during compilation
158162
options.compilerArgs.addAll(listOf("-Xlint:-deprecation", "-Xlint:-removal"))
159163
// Set explicit Java release version for Eclipse compatibility
160-
options.release.set(21)
164+
options.release.set(25)
161165
}
162166

163167
tasks.compileTestJava {
@@ -166,7 +170,7 @@ tasks.compileTestJava {
166170
// Suppress all deprecation and removal warnings during compilation
167171
options.compilerArgs.addAll(listOf("-Xlint:-deprecation", "-Xlint:-removal"))
168172
// Set explicit Java release version for Eclipse compatibility
169-
options.release.set(21)
173+
options.release.set(25)
170174
}
171175

172176

@@ -218,7 +222,7 @@ dependencies {
218222
testRuntimeOnly("org.junit.platform:junit-platform-launcher:$junitVersion")
219223
testImplementation("org.mockito:mockito-junit-jupiter:$mockitoVersion")
220224
testImplementation("org.mockito:mockito-core:$mockitoVersion")
221-
testImplementation("com.github.MockBukkit:MockBukkit:$mockBukkitVersion")
225+
testImplementation("org.mockbukkit.mockbukkit:mockbukkit-v26.1.2:$mockBukkitVersion")
222226
testImplementation("org.awaitility:awaitility:$awaitilityVersion")
223227
testImplementation("io.papermc.paper:paper-api:$paperVersion")
224228
testImplementation("com.github.MilkBowl:VaultAPI:$vaultVersion")
@@ -287,7 +291,7 @@ dependencies {
287291
paperweight {
288292
addServerDependencyTo = configurations.named(JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME).map { setOf(it) }
289293
javaLauncher = javaToolchains.launcherFor {
290-
// Use the project's configured Java version for paperweight tools (needs Java 21+)
294+
// Minecraft 26.1+ dev bundles require Java 25 to run the paperclip patch step.
291295
languageVersion = JavaLanguageVersion.of(javaVersion)
292296
}
293297
}
@@ -391,7 +395,7 @@ tasks.test {
391395
// Use JUnit Platform (required for JUnit 5)
392396
useJUnitPlatform()
393397

394-
// Enable Java 21 preview features and dynamic agent loading
398+
// Enable Java preview features and dynamic agent loading
395399
jvmArgs("--enable-preview", "-XX:+EnableDynamicAgentLoading")
396400

397401
// Add --add-opens: Required for Java 21+ to allow reflection access to restricted modules

settings.gradle.kts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,13 @@
22
* This file was generated by the Gradle 'init' task.
33
*/
44

5+
pluginManagement {
6+
repositories {
7+
gradlePluginPortal()
8+
// Paper's repo hosts the paperweight 2.0.0-SNAPSHOT, required to read the data-version-8
9+
// dev bundles used by all Minecraft 26.x releases (no released paperweight supports them).
10+
maven("https://repo.papermc.io/repository/maven-public/") { name = "PaperMC" }
11+
}
12+
}
13+
514
rootProject.name = "bentobox"

src/main/java/world/bentobox/bentobox/listeners/flags/protection/BucketListener.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import org.bukkit.Material;
55
import org.bukkit.block.Block;
66
import org.bukkit.entity.Axolotl;
7+
import org.bukkit.entity.EntityType;
78
import org.bukkit.entity.Fish;
89
import org.bukkit.entity.MushroomCow;
910
import org.bukkit.entity.Player;
@@ -13,6 +14,8 @@
1314
import org.bukkit.event.player.PlayerBucketFillEvent;
1415
import org.bukkit.event.player.PlayerInteractEntityEvent;
1516

17+
import com.google.common.base.Enums;
18+
1619
import world.bentobox.bentobox.api.flags.FlagListener;
1720
import world.bentobox.bentobox.lists.Flags;
1821

@@ -24,6 +27,15 @@
2427
*/
2528
public class BucketListener extends FlagListener {
2629

30+
/**
31+
* The Sulfur Cube entity type (Minecraft 26.2), resolved at runtime so the code still
32+
* compiles against earlier API versions. {@code null} when absent. Non-final so tests can
33+
* inject a stand-in type (the JVM constant-folds {@code static final} fields).
34+
*/
35+
@SuppressWarnings("java:S3008") // non-final by design; see Javadoc (test injection)
36+
private static EntityType SULFUR_CUBE = Enums.getIfPresent(EntityType.class, "SULFUR_CUBE")
37+
.orNull();
38+
2739
/**
2840
* Prevents emptying of buckets
2941
* @param e - event
@@ -73,6 +85,12 @@ else if (e.getRightClicked() instanceof Axolotl &&
7385
{
7486
this.checkIsland(e, e.getPlayer(), e.getRightClicked().getLocation(), Flags.AXOLOTL_SCOOPING);
7587
}
88+
else if (SULFUR_CUBE != null && e.getRightClicked().getType() == SULFUR_CUBE &&
89+
e.getPlayer().getInventory().getItemInMainHand().getType().equals(Material.BUCKET))
90+
{
91+
// Sulfur Cube (26.2) is picked up with an empty bucket
92+
this.checkIsland(e, e.getPlayer(), e.getRightClicked().getLocation(), Flags.BUCKET);
93+
}
7694
}
7795

7896

src/main/java/world/bentobox/bentobox/listeners/flags/protection/EntityInteractListener.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import org.bukkit.entity.ArmorStand;
88
import org.bukkit.entity.Boat;
99
import org.bukkit.entity.ChestBoat;
10+
import org.bukkit.entity.EntityType;
1011
import org.bukkit.entity.Player;
1112
import org.bukkit.entity.Vehicle;
1213
import org.bukkit.entity.Villager;
@@ -20,6 +21,8 @@
2021
import org.bukkit.event.player.PlayerInteractAtEntityEvent;
2122
import org.bukkit.event.player.PlayerInteractEntityEvent;
2223

24+
import com.google.common.base.Enums;
25+
2326
import world.bentobox.bentobox.api.flags.FlagListener;
2427
import world.bentobox.bentobox.lists.Flags;
2528

@@ -32,6 +35,15 @@
3235
*/
3336
public class EntityInteractListener extends FlagListener {
3437

38+
/**
39+
* The Sulfur Cube entity type (Minecraft 26.2), resolved at runtime so the code still
40+
* compiles against earlier API versions. {@code null} when absent. Non-final so tests can
41+
* inject a stand-in type (the JVM constant-folds {@code static final} fields).
42+
*/
43+
@SuppressWarnings("java:S3008") // non-final by design; see Javadoc (test injection)
44+
private static EntityType SULFUR_CUBE = Enums.getIfPresent(EntityType.class, "SULFUR_CUBE")
45+
.orNull();
46+
3547
@EventHandler(priority = EventPriority.LOW, ignoreCancelled=true)
3648
public void onPlayerInteractAtEntity(final PlayerInteractAtEntityEvent e) {
3749
if (e.getRightClicked() instanceof ArmorStand) {
@@ -54,6 +66,12 @@ public void onPlayerInteractEntity(PlayerInteractEntityEvent e)
5466
} else if (e.getRightClicked() instanceof Allay
5567
|| e.getRightClicked().getType().name().equals("COPPER_GOLEM")) {
5668
this.checkIsland(e, p, l, Flags.ALLAY);
69+
} else if (SULFUR_CUBE != null && e.getRightClicked().getType() == SULFUR_CUBE) {
70+
// Sulfur Cube (26.2) absorbs the block the player is holding - treat as placing a block
71+
Material hand = p.getInventory().getItemInMainHand().getType();
72+
if (hand.isBlock() && !hand.isAir()) {
73+
this.checkIsland(e, p, l, Flags.PLACE_BLOCKS);
74+
}
5775
}
5876
checkNameTag(e, p, l);
5977
}

src/main/java/world/bentobox/bentobox/util/Util.java

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020

2121
import javax.annotation.Nonnull;
2222

23+
import com.google.common.base.Enums;
24+
2325
import org.bukkit.Bukkit;
2426
import org.bukkit.Chunk;
2527
import org.bukkit.Location;
@@ -33,6 +35,7 @@
3335
import org.bukkit.entity.Bat;
3436
import org.bukkit.entity.EnderDragon;
3537
import org.bukkit.entity.Entity;
38+
import org.bukkit.entity.EntityType;
3639
import org.bukkit.entity.Flying;
3740
import org.bukkit.entity.IronGolem;
3841
import org.bukkit.entity.Monster;
@@ -83,6 +86,18 @@ public class Util {
8386
*/
8487
private static final String COLOR_CHAR = "\u00A7";
8588

89+
/**
90+
* The Sulfur Cube entity type (Minecraft 26.2), resolved at runtime so the code still
91+
* compiles against earlier API versions where the constant does not exist. {@code null}
92+
* when absent, in which case the {@code ==} comparisons against it are simply false.
93+
* Intentionally non-final: assigned once at class load, but left non-final so tests can
94+
* inject a stand-in type (the JVM constant-folds {@code static final} fields, defeating
95+
* reflective injection).
96+
*/
97+
@SuppressWarnings("java:S3008") // non-final by design; see Javadoc (test injection)
98+
private static EntityType SULFUR_CUBE = Enums.getIfPresent(EntityType.class, "SULFUR_CUBE")
99+
.orNull();
100+
86101
/**
87102
* Use standard color code definition: {@code &<hex>}.
88103
*/
@@ -426,8 +441,11 @@ public static boolean isHostileEntity(Entity entity) {
426441
// Most of hostile mobs extends Monster.
427442
// PufferFish is a unique fix.
428443

429-
return entity instanceof Monster || entity instanceof Flying || entity instanceof Slime ||
430-
entity instanceof Shulker || entity instanceof EnderDragon || entity instanceof PufferFish;
444+
// Sulfur Cube (26.2) is slime-like but passive, so it must not be treated as hostile
445+
// even if it implements Slime at runtime.
446+
return (entity instanceof Monster || entity instanceof Flying || entity instanceof Slime ||
447+
entity instanceof Shulker || entity instanceof EnderDragon || entity instanceof PufferFish)
448+
&& (SULFUR_CUBE == null || entity.getType() != SULFUR_CUBE);
431449
}
432450

433451

@@ -456,8 +474,10 @@ public static boolean isPassiveEntity(Entity entity) {
456474
boolean isCopperGolem = entity.getType().name().equals("COPPER_GOLEM");
457475
// And the sniffer
458476
boolean isSniffer = entity.getType().name().equals("SNIFFER");
477+
// And the Sulfur Cube (26.2): slime-like but passive
478+
boolean isSulfurCube = SULFUR_CUBE != null && entity.getType() == SULFUR_CUBE;
459479

460-
return isPassiveByClass || isPassiveWaterMob || isCopperGolem || isSniffer;
480+
return isPassiveByClass || isPassiveWaterMob || isCopperGolem || isSniffer || isSulfurCube;
461481
}
462482

463483
public static boolean isTamableEntity(Entity entity) {

src/main/java/world/bentobox/bentobox/versions/ServerCompatibility.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,11 @@ public enum ServerVersion {
177177
/**
178178
* @since 3.14.2
179179
*/
180-
V26_1_2(Compatibility.COMPATIBLE),;
180+
V26_1_2(Compatibility.COMPATIBLE),
181+
/**
182+
* @since 3.18.0
183+
*/
184+
V26_2(Compatibility.COMPATIBLE),;
181185

182186
private final Compatibility compatibility;
183187

0 commit comments

Comments
 (0)