Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
b99ab41
Do not put texture-less profiles on player heads
tastybento Aug 10, 2026
0f7002b
Update build version from 3.22.2 to 3.22.3
tastybento Aug 10, 2026
7278da1
Update Javadoc version in HeadCache.java
tastybento Aug 10, 2026
8b3f0fe
Merge pull request #3063 from BentoBoxWorld/fix/head-texture-mojang-429
tastybento Aug 10, 2026
a7c57a7
Fix literal color names showing in panels from NamedTextColor concate…
tastybento Aug 14, 2026
1b839b6
Remove YAML handler changes committed by mistake
tastybento Aug 14, 2026
09cdcaf
Add general.metrics config toggle to opt out of bStats
tastybento Aug 14, 2026
68de08e
Merge pull request #3065 from BentoBoxWorld/feature/metrics-opt-out
tastybento Aug 14, 2026
9a484c9
Reference the new org policies from the README
tastybento Aug 14, 2026
b5214f7
feat: lay dialog buttons out in a grid with columns and button width
tastybento Aug 15, 2026
f89e04f
Merge pull request #3066 from BentoBoxWorld/feature/dialog-grid-layout
tastybento Aug 15, 2026
34999f6
build: compile against Paper 26.2 now MockBukkit supports it
tastybento Aug 15, 2026
4dbecfe
Merge pull request #3067 from BentoBoxWorld/chore/paper-26.2
tastybento Aug 15, 2026
db17240
fix: widen YAML-loaded numbers to the declared numeric field type
tastybento Aug 15, 2026
5f1e6c6
refactor: drop the workarounds for 26.2 symbols missing at compile time
tastybento Aug 15, 2026
9bfe507
Merge pull request #2994 from BentoBoxWorld/feature/paper-26-2-mockbu…
tastybento Aug 15, 2026
8e0e736
Merge pull request #3068 from BentoBoxWorld/fix/yaml-numeric-widening
tastybento Aug 15, 2026
cb36be7
docs: publish JSON Schemas for the blueprint file formats
tastybento Aug 15, 2026
92fefe2
docs: bump the CLAUDE.md version note to 3.22.3
tastybento Aug 16, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .claude/rules/build-toolchain.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ Supporting Minecraft 26.x forced a chain of build changes — keep these in mind

- **Java 25.** The 26.x `paper-api` is Java 25 bytecode and its Gradle metadata requires consumers to target Java 25, so BentoBox now compiles to Java 25 (`javaVersion = "25"`, `options.release = 25`). **Addons that compile against BentoBox must also move to Java 25.**
- **paperweight `2.0.0-SNAPSHOT`.** All 26.x dev bundles are dev-bundle *data version 8*, which no released paperweight (`<= 2.0.0-beta.21`) can read. The snapshot is resolved via a `pluginManagement` block in `settings.gradle.kts` pointing at Paper's repo. The paperweight tool launcher is pinned to Java 25 (the 26.1+ paperclip patch step requires it). Revisit once a stable paperweight reads data-version-8 bundles.
- **Compile target vs. runtime support.** `paperVersion` is the latest **stable 26.1.2** dev bundle, not 26.2 — because MockBukkit has no 26.2 build and its registry mock throws on 26.2's new `minecraft:sulfur_cube_archetype` registry. Minecraft **26.2 is supported at runtime** (see `ServerCompatibility` and the Modrinth `game-versions` list); 26.2-only blocks/entities are referenced via `Enums.getIfPresent(...)` by name, never a compile-time symbol. The forward "compile against literal 26.2" work is parked in a draft PR until MockBukkit ships a 26.2 build.
- **MockBukkit coordinate.** Tests use `org.mockbukkit.mockbukkit:mockbukkit-v26.1.2:<ver>` (from Paper's repo), which **must match `paperVersion`'s MC line** — a mismatched MockBukkit fails every test at init with `InternalDataLoadException` (it validates the live API's registries against its bundled per-version data). When bumping the MC version, bump both together.
- **Compile target.** `paperVersion` is the latest **stable 26.2** dev bundle, so 26.2 symbols (`EntityType.SULFUR_CUBE`, new materials) are available at compile time. This became possible when MockBukkit 4.116.1 shipped its `mockbukkit-v26.2` artifact (earlier MockBukkit threw on 26.2's new `minecraft:sulfur_cube_archetype` registry).
- **MockBukkit coordinate.** Tests use `org.mockbukkit.mockbukkit:mockbukkit-v26.2:<ver>` (from Paper's repo), which **must match `paperVersion`'s MC line** — a mismatched MockBukkit fails every test at init with `InternalDataLoadException` (it validates the live API's registries against its bundled per-version data). When bumping the MC version, bump both together.
8 changes: 4 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,11 @@ A template like `<green>[description]</green>` looks harmless but is a trap. Tra
- `plugin.yml` and `config.yml` are filtered for the `${version}` placeholder at build time; locale files are copied without filtering.
- Locale translations are produced with Claude, not GitLocalize. When a key is added to `en-US.yml`, translate it into every other `src/main/resources/locales/*.yml` file in the same PR, preserving each file's existing style (e.g. the MiniMessage-tagged names in `zh-CN.yml` / `zh-HK.yml`).
- Java preview features are enabled for both compilation and test execution.
- The authoritative version is `buildVersion` in `build.gradle.kts` (current: `3.22.2`). Two related but different strings come out of it:
- **Gradle artifact version** (`project.version`, and so the jar name): `{buildVersion}-SNAPSHOT-LOCAL` locally, `{buildVersion}-SNAPSHOT` on CI (when `BUILD_NUMBER` is set), and the bare `{buildVersion}` when `GIT_BRANCH=origin/master`. So a local build yields `build/libs/BentoBox-3.22.2-SNAPSHOT-LOCAL.jar`.
- **`plugin.yml` version**, the one `/bentobox version` reports: the template is `${project.version}${build.number}`, so CI appends the build number — `3.22.2-SNAPSHOT-b1234`. Locally it matches the artifact version, `3.22.2-SNAPSHOT-LOCAL`.
- The authoritative version is `buildVersion` in `build.gradle.kts` (current: `3.22.3`). Two related but different strings come out of it:
- **Gradle artifact version** (`project.version`, and so the jar name): `{buildVersion}-SNAPSHOT-LOCAL` locally, `{buildVersion}-SNAPSHOT` on CI (when `BUILD_NUMBER` is set), and the bare `{buildVersion}` when `GIT_BRANCH=origin/master`. So a local build yields `build/libs/BentoBox-3.22.3-SNAPSHOT-LOCAL.jar`.
- **`plugin.yml` version**, the one `/bentobox version` reports: the template is `${project.version}${build.number}`, so CI appends the build number — `3.22.3-SNAPSHOT-b1234`. Locally it matches the artifact version, `3.22.3-SNAPSHOT-LOCAL`.

The invariant to preserve when editing this block: **exactly one** of `project.version` and `build.number` carries the build marker. Locally the marker is baked into the revision so the jar filename stays distinguishable from a CI snapshot, which is why `finalBuildNumber` is empty there; setting both is what once stamped `3.22.2-SNAPSHOT-LOCAL-LOCAL` into `plugin.yml`.
The invariant to preserve when editing this block: **exactly one** of `project.version` and `build.number` carries the build marker. Locally the marker is baked into the revision so the jar filename stays distinguishable from a CI snapshot, which is why `finalBuildNumber` is empty there; setting both is what once stamped `3.22.3-SNAPSHOT-LOCAL-LOCAL` into `plugin.yml`.

### Minecraft 26.x / Java 25 toolchain

Expand Down
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,21 +77,31 @@ You can contribute by:
* Donating or sponsoring the developers
* Coding new addons
* Adopting an Addon and maintaining it
* Translating text for BentoBox and Addons (We use GitLocalize to make this easier)
* Translating text for BentoBox and Addons — native speakers only, please (see the [AI policy](https://github.com/BentoBoxWorld/.github/blob/master/AI_POLICY.md#translations))
* Submitting good bug reports or helpful feature requests
* Fixing bugs and submitting Pull Requests for the fixes

If you contribute code it **must be in agreement** with:
* our [license](https://github.com/BentoBoxWorld/BentoBox/blob/develop/LICENSE)
* our [code of conduct](https://github.com/BentoBoxWorld/.github/blob/master/CODE_OF_CONDUCT.md)
* our contribution guidelines
* our [contribution guidelines](https://github.com/BentoBoxWorld/.github/blob/master/CONTRIBUTING.md)
* our [AI policy](https://github.com/BentoBoxWorld/.github/blob/master/AI_POLICY.md)

### Report bugs and suggest features
Bugs and feature requests must be filed on our [issue tracker](https://github.com/BentoBoxWorld/BentoBox/issues).

### Pull requests
We consider Pull Requests from non-collaborators that contain actual code improvements or bug fixes.
Do not submit PRs that only address code formatting because they will not be accepted.
AI-assisted PRs are welcome — there is no restriction on using AI tools, but you must be able to
stand behind your code, at least until it is accepted. See the [AI policy](https://github.com/BentoBoxWorld/.github/blob/master/AI_POLICY.md).

## Policies

* [Privacy Policy](https://github.com/BentoBoxWorld/.github/blob/master/PRIVACY.md) — what anonymous usage data BentoBox submits to bStats, what is never collected, and how to opt out
* [AI Policy](https://github.com/BentoBoxWorld/.github/blob/master/AI_POLICY.md) — how the project uses AI and the rules for AI-assisted contributions
* [Contributing Guide](https://github.com/BentoBoxWorld/.github/blob/master/CONTRIBUTING.md)
* [Code of Conduct](https://github.com/BentoBoxWorld/.github/blob/master/CODE_OF_CONDUCT.md)

## API

Expand Down
19 changes: 9 additions & 10 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ paperweight.reobfArtifactConfiguration = io.papermc.paperweight.userdev.ReobfArt
group = "world.bentobox" // From <groupId>

// Base properties from <properties>
val buildVersion = "3.22.2"
val buildVersion = "3.22.3"
val buildNumberDefault = "-LOCAL" // Local build identifier
val snapshotSuffix = "-SNAPSHOT" // Indicates development/snapshot version

Expand Down Expand Up @@ -89,19 +89,18 @@ val javaVersion = "25"
val junitVersion = "5.10.2"
val mockitoVersion = "5.11.0"
// MockBukkit's modern, per-Minecraft-version artifacts are published to Paper's Maven repo
// under org.mockbukkit.mockbukkit (see the testImplementation coordinate below). The closest
// available to Paper 26.2 is the 26.1.2 line (no 26.2 build exists yet); 4.113.2 is the latest.
val mockBukkitVersion = "4.113.2"
// under org.mockbukkit.mockbukkit (see the testImplementation coordinate below). 4.116.1 is the
// first release with a 26.2 artifact, built against 26.2.build.111-stable.
val mockBukkitVersion = "4.116.1"
val mongodbVersion = "3.12.12"
val mariadbVersion = "3.0.5"
val mysqlVersion = "8.0.27"
val postgresqlVersion = "42.2.18"
val hikaricpVersion = "5.0.1"
// Compile against the latest stable 26.1.2 dev bundle. This is the newest Paper API that has a
// matching MockBukkit release (mockbukkit-v26.1.2); MockBukkit does not yet support 26.2's new
// registries. Minecraft 26.2 is still fully supported at runtime (see ServerCompatibility and
// the Modrinth game-versions list); 26.2-only blocks/entities are accessed via Enums.getIfPresent.
val paperVersion = "26.1.2.build.72-stable"
// Compile against the 26.2 dev bundle MockBukkit 4.116.1 was built against, so the API under
// test and the API compiled against are the same. Note 26.2 brings Adventure 5, which makes
// ClickEvent generic (payload() rather than value()) and seals Component so it cannot be mocked.
val paperVersion = "26.2.build.111-stable"
val bstatsVersion = "3.0.0"
val vaultVersion = "1.7.1"
val levelVersion = "2.21.3"
Expand Down Expand Up @@ -257,7 +256,7 @@ dependencies {
testRuntimeOnly("org.junit.platform:junit-platform-launcher:$junitVersion")
testImplementation("org.mockito:mockito-junit-jupiter:$mockitoVersion")
testImplementation("org.mockito:mockito-core:$mockitoVersion")
testImplementation("org.mockbukkit.mockbukkit:mockbukkit-v26.1.2:$mockBukkitVersion")
testImplementation("org.mockbukkit.mockbukkit:mockbukkit-v26.2:$mockBukkitVersion")
testImplementation("org.awaitility:awaitility:$awaitilityVersion")
testImplementation("io.papermc.paper:paper-api:$paperVersion")
testImplementation("com.github.MilkBowl:VaultAPI:$vaultVersion")
Expand Down
25 changes: 25 additions & 0 deletions schemas/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Blueprint JSON Schemas

Machine-readable [JSON Schema](https://json-schema.org/) (draft 2020-12) definitions of BentoBox's on-disk blueprint formats:

- **`blueprint.schema.json`** — validates a `.blueprint` file (a single Blueprint object) or a bundle file. Also contains the shared `$defs` for blocks, spawners, entities, and display entities.
- **`blueprint-bundle.schema.json`** — validates a blueprint bundle file (`<uniqueId>.json` in a game mode's `blueprints/` folder) on its own.

The human-readable specification lives in the docs: [Blueprint File Format](https://docs.bentobox.world/en/latest/BentoBox/Blueprint-Format/).

## Validating a file

With [ajv](https://ajv.js.org/):

```bash
ajv validate --spec=draft2020 -s schemas/blueprint.schema.json -d island.blueprint
ajv validate --spec=draft2020 -s schemas/blueprint-bundle.schema.json -d default.json
```

Editors that support JSON Schema (VS Code, IntelliJ) can associate `*.blueprint` and bundle files with these schemas for inline validation and completion.

## Caveats

- ItemStacks are stored as YAML documents inside JSON strings (Bukkit `ConfigurationSerializable`); the schema treats them as opaque strings, so a schema-valid file can still fail to load if an embedded YAML document is malformed.
- Legacy `.blu` files are ZIP archives whose single entry is JSON that validates against `blueprint.schema.json`.
- The schemas describe what the current serializer emits. Keep them in sync with the `world.bentobox.bentobox.blueprints.dataobjects` classes when `@Expose`d fields change.
84 changes: 84 additions & 0 deletions schemas/blueprint-bundle.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://bentobox.world/schemas/blueprint-bundle.schema.json",
"title": "BentoBox Blueprint Bundle Format",
"description": "Schema describing the on-disk JSON format of a BentoBox blueprint bundle file.\n\nA bundle groups up to three `.blueprint` files — one per world environment (overworld / nether / end) — into a single option that appears in the island-creation UI. It also controls the cost, permission, GUI slot, usage cap, and post-creation commands for that option.\n\nFILE FORMAT:\n - Plain UTF-8 JSON.\n - One bundle per file. Filename MUST be `<uniqueId>.json` (the stem matches the `uniqueId` field).\n - Location: the `blueprints/` folder of a game-mode addon (e.g. `plugins/BentoBox/addons/BSkyBlock/blueprints/default.json`). The referenced blueprints live alongside as `<name>.blueprint` files and are validated by the sibling `blueprint.schema.json`.\n\nSERIALIZATION NOTES (Gson-based):\n - Only fields explicitly tagged `@Expose` in the Java source are emitted; producers must not emit other keys.\n - The `blueprints` map is keyed by the Java `World.Environment` enum and is written as a normal JSON object with the enum NAME (e.g. `NORMAL`) as the key.\n - Pretty-printing is enabled by the writer; consumers must not rely on whitespace.",
"type": "object",
"properties": {
"uniqueId": {
"description": "Unique identifier for this bundle. Must equal the filename stem (e.g. `default` for `default.json`). Used as the permission suffix when `requirePermission` is true: `<gamemode>.island.create.<uniqueId>`.",
"type": "string",
"minLength": 1
},
"displayName": {
"description": "Human-readable name shown in the selection GUI. May contain legacy `§` colour codes or MiniMessage tags per locale conventions.",
"type": "string"
},
"icon": {
"description": "Icon material. One of: a plain Bukkit Material enum name (e.g. `DIAMOND`), a vanilla namespaced key (e.g. `minecraft:diamond`), or a resource-pack custom model key (e.g. `myserver:island_tropical`). Default: `PAPER`.",
"type": "string",
"minLength": 1
},
"description": {
"description": "Lore lines shown under the icon. One string per line.",
"type": "array",
"items": { "type": "string" }
},
"blueprints": {
"description": "Map from world environment to the `name` of a Blueprint in the same folder. Environments with no entry are not generated for this bundle; keys correspond to Bukkit `World.Environment` enum names.",
"type": "object",
"propertyNames": {
"enum": ["NORMAL", "NETHER", "THE_END", "CUSTOM"]
},
"additionalProperties": {
"description": "The `name` field of a sibling Blueprint (i.e. the file's stem without the `.blueprint` extension).",
"type": "string",
"minLength": 1
}
},
"requirePermission": {
"description": "If true, a player must hold `<gamemode>.island.create.<uniqueId>` to use this bundle.",
"type": "boolean"
},
"slot": {
"description": "Preferred slot (0-based) in the selection GUI. Runtime clamps to the visible inventory size.",
"type": "integer",
"minimum": 0
},
"times": {
"description": "Maximum number of islands a single player may create with this bundle. `0` means unlimited.",
"type": "integer",
"minimum": 0
},
"cost": {
"description": "Vault-economy cost to use this bundle. `0` means free. Requires a Vault-compatible economy plugin at runtime.",
"type": "number",
"minimum": 0
},
"commands": {
"description": "Commands executed when an island is created with this bundle. Placeholders `[player]` and `[owner]` are substituted. Entries prefixed with `[SUDO]` run as the creating player; others run as console. (Added in BentoBox 2.6.0.)",
"type": "array",
"items": { "type": "string" }
}
},
"required": ["uniqueId"],
"additionalProperties": false,
"examples": [
{
"uniqueId": "default",
"displayName": "Default Island",
"icon": "GRASS_BLOCK",
"description": ["A standard island", "with grass and dirt"],
"blueprints": {
"NORMAL": "island",
"NETHER": "nether",
"THE_END": "end"
},
"requirePermission": false,
"slot": 0,
"times": 0,
"cost": 0.0,
"commands": ["[SUDO] me has arrived!"]
}
]
}
Loading
Loading