Skip to content

feat!: modernise BetterGoPaint for Minecraft 26 and AntiRedstoneClock quality - #34

Merged
TheMeinerLP merged 22 commits into
developfrom
ccr-afa57460-m0d844
Oct 10, 2026
Merged

TheMeinerLP merged 22 commits into
developfrom
ccr-afa57460-m0d844

Conversation

@OneLiteFeather

Copy link
Copy Markdown

Proposed changes

Brings BetterGoPaint to the newest stable Minecraft line and to the quality bar of AntiRedstoneClock-Remastered. The work is planned and recorded as OpenSpec changes. All eight are implemented and archived under openspec/changes/archive/, and their specs live in openspec/specs/.

Platform

  • Paper API 26.2, FastAsyncWorldEdit 2.16.1, Java 25, Gradle 9.8. Supported versions: 26.1.1, 26.1.2, 26.2. api-version is 26.1.
  • One version catalog. Shadow, plugin-yml, run-paper, Hangar and Spotless move to their current coordinates. The build-logic build, ServerLib, paperlib and EldoNexus publishing are removed.
  • run-<version> starts Paper with FAWE already installed.

Behaviour fixes

  • config.yml is honoured again. The settings extended FAWE's Config, which on Paper 26 could not set the plugin's fields. Every edit was ignored, with one "Failed to set config option" warning per key. Settings are now immutable records behind SettingsHolder. A small YAML loader writes every key with a comment, replaces invalid values with their default and swaps the settings atomically on /gopaint reload.
  • The update check ran on the main thread during onEnable. It now runs in the background with timeouts and can be turned off with generic.check-for-updates.
  • Brush menus are recognised by their InventoryHolder instead of their title, so other plugins' chests with the same title are no longer hijacked.
  • Exported brushes are stored as versioned item data under bettergopaint:brush. Renamed or edited items keep working, and items exported by 1.x are converted on first use without throwing.
  • Files in lang/ fall back to the bundled English messages.

Commands and texts

  • /gopaint (aliases /gp, /bgp) is now built with cloud and Brigadier. It has a generated help, range-checked and tab-completed size, and one permission per subcommand.
  • All player-facing text (commands, menu, exported-item lore) comes from bettergopaint.properties and is rendered in the player's language.

Architecture

  • Guice wires the plugin: Platform, Settings, Brush, Service, Listener and Command modules. Guice, jakarta.inject and aopalliance are shaded and relocated; Guava comes from Paper.

Tooling

  • 40 unit tests (JUnit 6, AssertJ, Mockito, JaCoCo): codec, settings, commands, listeners, update check, DI graph and translation completeness.
  • Release Please on main. pr-lint/commitlint enforce Conventional Commits. PR builds run on Linux, Windows and macOS with Java 25. A Trivy gate runs before Hangar/Modrinth publishing, plus SBOM upload, a weekly security scan and the shared Renovate presets.
  • Diátaxis docs in docs/ for GitBook (tutorial, how-tos, reference for brushes, commands, permissions, configuration and versions), plus new issue forms and CONTRIBUTING.md.

Types of changes

  • Bugfix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation Update (if none of the other choices apply)

Breaking: requires Paper 26.1.1+ and Java 25; servers on 1.21 stay on 1.1.x. /gopaint reload now needs bettergopaint.command.admin.reload, which the bettergopaint.admin bundle grants. Each subcommand has its own bettergopaint.command.* node, defaulting to true.

Checklist

  • I have read the CONTRIBUTING.md
  • I have added tests that prove my fix is effective or that my feature works
  • I have added necessary documentation (if appropriate)

Further comments

Verified: ./gradlew clean build is green, with 40 tests and the Spotless license check. openspec validate --all --strict passes. On run-26.2 and run-26.1.2 the plugin enables without errors or config warnings, /gopaint, info and reload respond, config.yml is written with comments, and a lang/ folder containing only a German file loads.

Not verified: painting with each brush in game, because the environment had no Minecraft client. Please run ./gradlew run-26.2, try every brush once and //undo.

Manual steps for maintainers, also listed in the archived OpenSpec task lists:

  1. Bring main up to date and make it the default branch; Release Please runs on main.
  2. Protect main and require Build Pull Request Branch, Conventional PR title and Conventional commits.
  3. Check the HANGAR_KEY and MODRINTH_KEY secrets, and confirm the Dependency-Track parent-uuid in release-please.yml, which was copied from AntiRedstoneClock.
  4. Connect GitBook to docs/.

Commits follow Conventional Commits, so the first release through Release Please becomes 2.0.0.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QLYZtULzHeMRpNygcFFWgp


Generated by Claude Code

claude added 10 commits October 9, 2026 15:50
Compile against Paper API 26.2 and FastAsyncWorldEdit 2.16.1 with Gradle 9.8 and Java 25.
Versions move into one catalog, shadow/plugin-yml/run-paper/hangar/spotless use their current
coordinates, and build-logic, ServerLib, paperlib and EldoNexus publishing are dropped.
Translations use MiniMessageTranslationStore because Adventure removed TranslationRegistry;
extra language files in lang/ now fall back to the bundled English bundle.
run-<version> tasks start Paper with FastAsyncWorldEdit installed.

BREAKING CHANGE: requires Paper 26.1.1 or newer and Java 25. Servers on 1.21 stay on 1.1.x.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLYZtULzHeMRpNygcFFWgp
JUnit 6, AssertJ and Mockito with JaCoCo reports. Covers brush cycling and lookup,
the Paint Brush curve and translation key completeness.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLYZtULzHeMRpNygcFFWgp
Mirror the AntiRedstoneClock-Remastered pipeline: pr-lint, multi-OS PR builds on Java 25,
Release Please on main with a Trivy gate before Hangar/Modrinth publishing and an SBOM upload,
weekly Trivy scan, shared Renovate presets and new issue forms. Snapshot publishing on every push
and release-drafter are removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLYZtULzHeMRpNygcFFWgp
Tutorial, how-to guides, reference pages for brushes, commands, permissions, configuration
and supported versions, background pages and developer guides, published through GitBook.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLYZtULzHeMRpNygcFFWgp
OpenSpec changes for the four delivered parts (platform upgrade, release pipeline, tests,
documentation) and the planned follow-ups: cloud commands, brush menu hardening, non-blocking
update check and dependency injection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLYZtULzHeMRpNygcFFWgp
The first update check ran on the main thread during onEnable. It now runs from an async
timer with connect and request timeouts, logs failures at debug level, tells the console once per
new version and players only on join. generic.check-for-updates turns it off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLYZtULzHeMRpNygcFFWgp
One command tree /gopaint (aliases /gp, /bgp) through cloud's PaperCommandManager with
per-subcommand permissions, a generated help, range-checked and tab-completed sizes and messages
from the translation bundle. The Bukkit Command subclass and the separate reload command are gone.

BREAKING CHANGE: /gopaint reload needs bettergopaint.command.admin.reload (granted by the
bettergopaint.admin bundle); every subcommand needs its own bettergopaint.command.* node, which
defaults to true.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLYZtULzHeMRpNygcFFWgp
…m data

The menu and the brush picker are recognised by their InventoryHolder instead of the title,
so foreign inventories with the same title are no longer hijacked. Menu text comes from the
translation bundle, rendered for the player's locale. Exported brushes are stored as versioned
data under bettergopaint:brush; renamed or edited items keep working and 1.x items are converted on
first use without ever throwing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLYZtULzHeMRpNygcFFWgp
Settings no longer extend FAWE's Config, which on Paper 26 could not set the plugin's fields
and ignored every value in config.yml. They are now immutable records behind SettingsHolder,
read by a small YAML loader that writes every key back with its comment, replaces invalid values
with their default and swaps the snapshot atomically on reload.

Listeners, commands and services receive narrow dependencies through Guice modules instead of the
plugin instance. Guice, jakarta.inject and aopalliance are shaded and relocated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLYZtULzHeMRpNygcFFWgp
All eight changes are implemented. Their delta specs are merged into openspec/specs/; the steps
that need a game client, GitHub repository settings or GitBook access are listed as manual
follow-ups in the archived task lists.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLYZtULzHeMRpNygcFFWgp
@github-actions

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Unit Test Results

 23 files   23 suites   12s ⏱️
118 tests 118 ✅ 0 💤 0 ❌
119 runs  119 ✅ 0 💤 0 ❌

Results for commit de922fa.

♻️ This comment has been updated with latest results.

claude added 12 commits October 9, 2026 17:40
A setup tutorial for admins, how-to guides for upgrading from 1.x, limiting builders,
applying config changes, turning off update notices and reporting bugs, reference pages for
console messages and files, and background on how settings are loaded and why 2.x needs Paper 26.
ReferenceDocsTest fails the build when config keys, defaults, permissions, commands, supported
versions or the navigation drift from the code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLYZtULzHeMRpNygcFFWgp
Two OpenSpec changes: brushes compute on WorldEdit extents instead of the Bukkit world, which
also makes strokes thread-safe, and scheduling plus shared state become Folia-ready while the Folia
flag stays off until FastAsyncWorldEdit supports it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLYZtULzHeMRpNygcFFWgp
…ushes

Add an immutable StrokeContext that is captured on the player's thread, a
factory that converts the brush settings to WorldEdit types without asking
the world for anything, a WorldEdit mask for the BetterGoPaint mask and a
runner that executes a stroke in the player's WorldEdit session.

Tests get a map-backed MemoryExtent and mock block types, because the
WorldEdit block registry cannot initialise without a server.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLYZtULzHeMRpNygcFFWgp
Brushes now compute their blocks from a StrokeContext and an Extent. The
geometry helpers (Sphere, Height, Surface, ConnectedBlocks) and the
Bezier curve use BlockVector3 and Vector3, so no Bukkit world, block or
location call remains in objects/ and utils/. InteractListener captures
the context on the player's thread and queues a WorldEdit Brush that runs
in the player's edit session with its global mask and one history entry.

Brushes read all positions before they write, which keeps the result the
same as when the Bukkit world was read.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLYZtULzHeMRpNygcFFWgp
Explain the stroke context, the player's WorldEdit session and //gmask in
the stroke explanation, document how brushes are tested without a server
and note where the implementation differs from the design decisions.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLYZtULzHeMRpNygcFFWgp
The legacy BukkitScheduler is not available on Folia. Run the update
check with Bukkit.getAsyncScheduler().runAtFixedRate and cancel the
returned ScheduledTask on shutdown.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLYZtULzHeMRpNygcFFWgp
PlayerBrushManager keeps its per-player brushes in a ConcurrentHashMap
and takes a brush factory so the concurrency behaviour can be tested
without a server.

The Paint Brush stroke points move from a static map into the brush
instance and are dropped with the player's brush state on quit.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLYZtULzHeMRpNygcFFWgp
paper-plugin.yml declares folia-supported only when -PfoliaSupported=true
is passed. Without it the flag stays false, so release builds cannot
declare Folia support by accident.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLYZtULzHeMRpNygcFFWgp
run-folia-<version> starts a local Folia test server with run-paper's
Folia downloads. It installs FastAsyncWorldEdit from -PfoliaFaweUrl.
Without that property the task fails when it runs, with a message that
names the property, so normal builds are unaffected.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLYZtULzHeMRpNygcFFWgp
Scope and non-goals now says BetterGoPaint is prepared for Folia but
does not declare support until a stable FastAsyncWorldEdit release
supports it. Supported versions links to that page, and the testing
guide describes run-folia-<version> for local testing only.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLYZtULzHeMRpNygcFFWgp
Without -PfoliaSupported=true the test server starts but Folia skips BetterGoPaint, which looks
like a broken setup. The task now fails early and names both required properties.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLYZtULzHeMRpNygcFFWgp
Both changes are implemented; their specs move to openspec/specs/. In-game checks with a client
and a Folia test with a FAWE fork remain listed as manual follow-ups.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QLYZtULzHeMRpNygcFFWgp
@TheMeinerLP
TheMeinerLP merged commit 93bc6ad into develop Oct 10, 2026
10 checks passed
@TheMeinerLP
TheMeinerLP deleted the ccr-afa57460-m0d844 branch October 10, 2026 11:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants