Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
a6bf1b4
Version 1.5.3
tastybento Jan 11, 2026
2267237
Modernize build and test stack: Paper 1.21.11, BentoBox 3.14.0, JUnit 5
tastybento Apr 11, 2026
74ab114
Add language filter and word-wrap descriptions in web library panel
tastybento Apr 11, 2026
af7d120
Document dependency source lookup and sibling project layout
tastybento Apr 11, 2026
c909a19
Show pending barrier on library button while catalog downloads
tastybento Apr 11, 2026
db7dd1c
Handle malformed catalog entries gracefully in web library download
tastybento Apr 12, 2026
169b96f
Convert all locale files from legacy &-codes to MiniMessage formatting
tastybento Apr 12, 2026
651bff8
Bump addon api-version to 3.12.0
tastybento Apr 12, 2026
72d766b
Word-wrap reward text to 30 characters in challenge/level lore
tastybento Apr 12, 2026
b96381c
Update build.version to 1.6.0
tastybento Apr 12, 2026
463d3fb
Add Modrinth publish workflow for automated release uploads
tastybento Apr 12, 2026
a250175
Add AbstractChallengesTest base class and expand TryToComplete test c…
tastybento Apr 12, 2026
9c160d3
Add ChallengesImportManagerTest with 19 tests covering import/export …
tastybento Apr 12, 2026
735f535
Add tests for ChallengesManager and admin commands (79 new tests)
tastybento Apr 12, 2026
3bc5001
Replace 82 duplicated string literals with constants (SonarCloud S1192)
tastybento Apr 12, 2026
1467bfc
Fix JaCoCo coverage by including agent in Surefire argLine
tastybento Apr 12, 2026
519a427
Add JUnit 5 panel tests with 77 new tests covering all panel subsystems
tastybento Apr 12, 2026
46c8a00
Add testing guidance to CLAUDE.md for JUnit 5 alongside PowerMock
tastybento Apr 12, 2026
4782f91
Pin modrinth-publish action to full commit SHA for supply chain security
tastybento Apr 12, 2026
7a177bd
Fix 5 SonarCloud blocker issues
tastybento Apr 12, 2026
48b503a
Update all Maven plugins to latest stable versions
tastybento Apr 12, 2026
a2a1e48
Migrate test suite from PowerMock to JUnit 5 + Mockito.mockStatic + M…
tastybento Apr 12, 2026
c12c15c
Merge remote-tracking branch 'origin/develop' into worktree-junit5-pa…
tastybento Apr 12, 2026
2ef260e
Merge pull request #395 from BentoBoxWorld/worktree-junit5-panel-tests
tastybento Apr 12, 2026
6bbd941
Remove public modifier from JUnit 5 test classes and methods (java:S5…
tastybento Apr 12, 2026
35daab8
Remove unused imports from test classes and update line separator usage
tastybento Apr 12, 2026
3ea498d
Refactor comparison logic to use switch expressions for improved read…
tastybento Apr 12, 2026
6f358a8
Improve Javadoc comments for clarity and consistency across multiple …
tastybento Apr 12, 2026
da553a2
Refactor test classes to use local ServerMock variables for improved …
tastybento Apr 12, 2026
a7c4be1
Refactor condition checks to use isEmpty() for improved readability
tastybento Apr 12, 2026
5393b19
Refactor button handling logic to use if statement for improved clarity
tastybento Apr 12, 2026
c6d7f66
Refactor setUp methods in test classes to remove unnecessary exceptio…
tastybento Apr 12, 2026
f272793
Refactor command handling to use getFirst() for improved consistency …
tastybento Apr 12, 2026
4dc71d4
Refactor getElements method to use switch expression for improved rea…
tastybento Apr 12, 2026
7d68775
Refactor getElements method to use switch expression for improved rea…
tastybento Apr 12, 2026
c274e0e
Add JUnit 5 test coverage for events, player data, handlers, and list…
tastybento Apr 12, 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
88 changes: 88 additions & 0 deletions .github/workflows/modrinth-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
name: Publish to Modrinth

# Triggers when you publish a GitHub Release
on:
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: 'Release tag to publish (e.g. 1.6.0)'
required: true
type: string

jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read

steps:
# 1. Check out the repository at the release tag
- name: Checkout repository
uses: actions/checkout@v4

# 2. Set up Java 21
- name: Set up Java 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'

# 3. Cache Maven dependencies to speed up builds
- name: Cache Maven packages
uses: actions/cache@v4
with:
path: ~/.m2
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-m2

# 4. Build the JAR
# GIT_BRANCH=origin/master activates the Maven 'master' profile which
# produces a clean version number (e.g. 1.6.0) without -SNAPSHOT/-LOCAL.
- name: Build with Maven
run: mvn -B clean package -DskipTests
env:
GIT_BRANCH: origin/master

# 5. Upload the JAR to Modrinth
#
# Prerequisites — add these secrets in your GitHub repo settings
# (Settings → Secrets and variables → Actions):
#
# MODRINTH_TOKEN — personal access token from https://modrinth.com/settings/pats
# (scope: "Create versions")
# MODRINTH_PROJECT_ID — your Modrinth project ID for Challenges, visible on the
# project page under the three-dot menu ("Copy ID")
#
- name: Publish to Modrinth
uses: cloudnode-pro/modrinth-publish@0be4916ad5f081d936eb5615aa35ce3f0949979c # v2
with:
token: ${{ secrets.MODRINTH_TOKEN }}
project: ${{ secrets.MODRINTH_PROJECT_ID }}

# Use the release tag, or the manually supplied tag when triggered via workflow_dispatch
version: ${{ github.event.release.tag_name || inputs.tag }}

# Use the GitHub release body as the changelog (empty when run manually)
changelog: ${{ github.event.release.body }}

# Challenges is a BentoBox addon running on Paper-based servers
loaders: |-
paper
purpur

# Minecraft versions supported (BentoBox API 3.12.0 targets Paper 1.21.11+)
game-versions: |-
1.21.5
1.21.6
1.21.7
1.21.8
1.21.9
1.21.10
1.21.11
26.1
26.1.1

# Path to the built JAR
files: target/Challenges-${{ github.event.release.tag_name || inputs.tag }}.jar
176 changes: 176 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project

Challenges is a BentoBox addon (Bukkit/Spigot plugin module) that adds player challenges to any BentoBox GameMode addon (BSkyBlock, AcidIsland, SkyGrid, CaveBlock). It is not a standalone plugin — it is loaded by BentoBox at runtime.

- Java 21, Maven, Spigot `1.21.3-R0.1-SNAPSHOT`
- Depends on BentoBox `3.4.0`, optionally Level `2.6.3` and Vault `1.7`
- Version is set via `<build.version>` in `pom.xml` (uses `${revision}` / CI `-bNNN` suffix)

## Commands

```bash
# Full build + tests + shaded jar (output in target/)
mvn clean verify

# Compile only
mvn compile

# Run all tests
mvn test

# Run a single test class
mvn test -Dtest=ChallengesManagerTest

# Run a single test method
mvn test -Dtest=ChallengesManagerTest#methodName

# Release build (strips -SNAPSHOT) — set via profile, normally CI only
mvn clean verify -Dbuild.number=-bLOCAL
```

Built jar lands in `target/` and must be dropped into `BentoBox/addons/` on a running BentoBox server to test manually.

## Architecture

Entry point: `ChallengesAddon` (extends BentoBox `Addon`). `ChallengesPladdon` is the Paper-plugin wrapper used when BentoBox is loaded as a Paper plugin. `onLoad` reads `config.yml` into `Settings`, `onEnable` registers commands against each hooked `GameModeAddon`, instantiates managers, registers flags, and wires listeners.

Key subsystems:

- **`managers/ChallengesManager`** — central service. Owns in-memory caches of `Challenge`, `ChallengeLevel`, and per-player `ChallengesPlayerData`, plus the BentoBox `Database` handles that persist them. All mutation of challenges/levels/player progress should go through this manager so caches and DB stay consistent. Challenges and levels are keyed by a unique ID that is prefixed by the gamemode (e.g. `bskyblock_mychallenge`) — see `getUniqueID` / `addChallengeToLevel` for the prefixing contract.
- **`managers/ChallengesImportManager`** — imports/exports challenges from `default.json`, template YAML, and library downloads via `web/WebManager` (the "Web Library" in the GUI).
- **`database/object/`** — Gson-serialized data objects (`Challenge`, `ChallengeLevel`, `ChallengesPlayerData`). `requirements/` holds the polymorphic `Requirement` hierarchy (Island, Inventory, Other, Statistic) used to gate completion; `adapters/` contains Gson type adapters that make the polymorphism work — new requirement types must be registered there.
- **`panel/`** — all GUI code, built on `bentobox-panelutils`. `CommonPanel`/`CommonPagedPanel` are the base classes; `admin/` contains the admin editor GUIs, `user/` the player-facing GUIs. Conversations (text input prompts) go through `ConversationUtils`.
- **`commands/`** — player commands (`ChallengesPlayerCommand`, `ChallengesGlobalPlayerCommand`, `CompleteChallengeCommand`) and `commands/admin/` admin commands. Each is registered per gamemode in `ChallengesAddon.onEnable`.
- **`handlers/`** — BentoBox API request handlers (`ChallengeDataRequestHandler`, `LevelListRequestHandler`, etc.) exposed over the BentoBox inter-addon request API so other addons can query challenge data.
- **`listeners/`** — `ResetListener` wipes player data on island reset; `SaveListener` flushes caches on save/quit.
- **`tasks/`** — `TryToComplete` is the core completion pipeline: validates requirements, consumes costs, applies rewards, fires events. New reward/requirement logic typically plugs in here.
- **`events/`** — custom Bukkit events (`ChallengeCompletedEvent`, `LevelCompletedEvent`, etc.) fired by `TryToComplete`/managers. Extend the existing event hierarchy rather than adding ad-hoc callbacks.
- **`web/WebManager`** — downloads the public challenges library via BentoBox's `WebManager`/GitHub content.

Resources in `src/main/resources/`: `addon.yml` (addon metadata read by BentoBox), `config.yml` (user settings → `config/Settings`), `default.json` (bundled default challenges), `template.yml`, `panels/*.yml` (GUI layouts loaded by panelutils), `locales/*.yml` (translations — managed via GitLocalize, do not hand-edit beyond `en-US.yml`).

## Conventions

- Target branch for PRs is `master`; active development happens on `develop`.
- When adding a new `Requirement` subclass, also register its Gson adapter in `database/object/adapters/` or loading will fail silently with missing fields.
- Challenge/level IDs are always gamemode-prefixed on disk; when comparing or looking up, use `ChallengesManager` helpers rather than raw string compares.
- User-visible strings belong in `locales/en-US.yml` under the appropriate namespace — never hardcode them in Java.

## Dependency Source Lookup

When you need to inspect source code for a dependency (e.g., BentoBox, addons):

1. **Check local Maven repo first**: `~/.m2/repository/` — sources jars are named `*-sources.jar`
2. **Check the workspace**: Look for sibling directories or Git submodules that may contain the dependency as a local project (e.g., `../bentoBox`, `../addon-*`)
3. **Check Maven local cache for already-extracted sources** before downloading anything
4. Only download a jar or fetch from the internet if the above steps yield nothing useful

Prefer reading `.java` source files directly from a local Git clone over decompiling or extracting a jar.

In general, the latest version of BentoBox should be targeted.

## Project Layout

Related projects are checked out as siblings under `~/git/`:

**Core:**
- `bentobox/` — core BentoBox framework

**Game modes:**
- `addon-acidisland/` — AcidIsland game mode
- `addon-bskyblock/` — BSkyBlock game mode
- `Boxed/` — Boxed game mode (expandable box area)
- `CaveBlock/` — CaveBlock game mode
- `OneBlock/` — AOneBlock game mode
- `SkyGrid/` — SkyGrid game mode
- `RaftMode/` — Raft survival game mode
- `StrangerRealms/` — StrangerRealms game mode
- `Brix/` — plot game mode
- `parkour/` — Parkour game mode
- `poseidon/` — Poseidon game mode
- `gg/` — gg game mode

**Addons:**
- `addon-level/` — island level calculation
- `addon-challenges/` — challenges system
- `addon-welcomewarpsigns/` — warp signs
- `addon-limits/` — block/entity limits
- `addon-invSwitcher/` / `invSwitcher/` — inventory switcher
- `addon-biomes/` / `Biomes/` — biomes management
- `Bank/` — island bank
- `Border/` — world border for islands
- `Chat/` — island chat
- `CheckMeOut/` — island submission/voting
- `ControlPanel/` — game mode control panel
- `Converter/` — ASkyBlock to BSkyBlock converter
- `DimensionalTrees/` — dimension-specific trees
- `discordwebhook/` — Discord integration
- `Downloads/` — BentoBox downloads site
- `DragonFights/` — per-island ender dragon fights
- `ExtraMobs/` — additional mob spawning rules
- `FarmersDance/` — twerking crop growth
- `GravityFlux/` — gravity addon
- `Greenhouses-addon/` — greenhouse biomes
- `IslandFly/` — island flight permission
- `IslandRankup/` — island rankup system
- `Likes/` — island likes/dislikes
- `Limits/` — block/entity limits
- `lost-sheep/` — lost sheep adventure
- `MagicCobblestoneGenerator/` — custom cobblestone generator
- `PortalStart/` — portal-based island start
- `pp/` — pp addon
- `Regionerator/` — region management
- `Residence/` — residence addon
- `TopBlock/` — top ten for OneBlock
- `TwerkingForTrees/` — twerking tree growth
- `Upgrades/` — island upgrades (Vault)
- `Visit/` — island visiting
- `weblink/` — web link addon
- `CrowdBound/` — CrowdBound addon

**Data packs:**
- `BoxedDataPack/` — advancement datapack for Boxed

**Documentation & tools:**
- `docs/` — main documentation site
- `docs-chinese/` — Chinese documentation
- `docs-french/` — French documentation
- `BentoBoxWorld.github.io/` — GitHub Pages site
- `website/` — website
- `translation-tool/` — translation tool

Check these for source before any network fetch.

## Testing

The project uses a mixed test stack:

- **JUnit 4 + PowerMock** — existing tests in `commands/`, `tasks/`, `utils/`, `database/`, and top-level addon/manager tests. These use `@RunWith(PowerMockRunner.class)` and `@PrepareForTest`.
- **JUnit 5 + Mockito** — newer tests in `panel/` package. These use `@ExtendWith(MockitoExtension.class)` with `@MockitoSettings(strictness = Strictness.LENIENT)`.
- Both frameworks coexist via `junit-vintage-engine` which runs JUnit 4 tests on the JUnit 5 platform.

### Writing new JUnit 5 tests alongside PowerMock

PowerMock's MockMaker conflicts with mockito-inline, so **`mockStatic()` and `mockConstruction()` are not available** in JUnit 5 tests. Use these workarounds:

- **Static singletons** (`Bukkit.getServer()`, `BentoBox.getInstance()`): Set via reflection on the private static field. See `PanelTestHelper.setServer()` and `setBentoBoxInstance()` for examples. Always save/restore the previous value in `@BeforeEach`/`@AfterEach`.
- **Final methods** (`JavaPlugin.getServer()` is final): Set the `server` field on the plugin object via reflection walking the class hierarchy. See `PanelTestHelper.setPluginServer()`.
- **Java records** (`ItemTemplateRecord`, `TemplatedPanel.ItemSlot`): Cannot be mocked. Create real instances using their public constructors. For `TemplatedPanel` (which has no public constructor), use `sun.misc.Unsafe.allocateInstance()` then set fields via reflection. See `PanelTestHelper.createItemSlot()`.
- **Varargs methods** (`User.getTranslation(String, String...)`): Use `Mockito.doAnswer().when()` with `Mockito.<String>any()` instead of `when().thenAnswer()`. The `doAnswer` pattern handles varargs correctly. See `PanelTestHelper.setupUserTranslations()`.

### PanelTestHelper

`src/test/java/world/bentobox/challenges/panel/PanelTestHelper.java` is a shared utility for all panel tests. It provides:
- Reflection helpers for Bukkit/BentoBox static fields
- `ItemTemplateRecord` factory methods (`createTemplate`, `createSimpleTemplate`, `createEmptyTemplate`)
- `createItemSlot()` for `TemplatedPanel.ItemSlot` records
- `createBasicChallenge()` for fully-mocked `Challenge` objects
- `setupUserTranslations()` for varargs-safe translation mocking

## Key Dependencies (source locations)

- `world.bentobox:bentobox` → `~/git/bentobox/src/`
Loading
Loading