|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project |
| 6 | + |
| 7 | +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. |
| 8 | + |
| 9 | +- Java 21, Maven, Spigot `1.21.3-R0.1-SNAPSHOT` |
| 10 | +- Depends on BentoBox `3.4.0`, optionally Level `2.6.3` and Vault `1.7` |
| 11 | +- Version is set via `<build.version>` in `pom.xml` (uses `${revision}` / CI `-bNNN` suffix) |
| 12 | + |
| 13 | +## Commands |
| 14 | + |
| 15 | +```bash |
| 16 | +# Full build + tests + shaded jar (output in target/) |
| 17 | +mvn clean verify |
| 18 | + |
| 19 | +# Compile only |
| 20 | +mvn compile |
| 21 | + |
| 22 | +# Run all tests |
| 23 | +mvn test |
| 24 | + |
| 25 | +# Run a single test class |
| 26 | +mvn test -Dtest=ChallengesManagerTest |
| 27 | + |
| 28 | +# Run a single test method |
| 29 | +mvn test -Dtest=ChallengesManagerTest#methodName |
| 30 | + |
| 31 | +# Release build (strips -SNAPSHOT) — set via profile, normally CI only |
| 32 | +mvn clean verify -Dbuild.number=-bLOCAL |
| 33 | +``` |
| 34 | + |
| 35 | +Built jar lands in `target/` and must be dropped into `BentoBox/addons/` on a running BentoBox server to test manually. |
| 36 | + |
| 37 | +## Architecture |
| 38 | + |
| 39 | +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. |
| 40 | + |
| 41 | +Key subsystems: |
| 42 | + |
| 43 | +- **`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. |
| 44 | +- **`managers/ChallengesImportManager`** — imports/exports challenges from `default.json`, template YAML, and library downloads via `web/WebManager` (the "Web Library" in the GUI). |
| 45 | +- **`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. |
| 46 | +- **`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`. |
| 47 | +- **`commands/`** — player commands (`ChallengesPlayerCommand`, `ChallengesGlobalPlayerCommand`, `CompleteChallengeCommand`) and `commands/admin/` admin commands. Each is registered per gamemode in `ChallengesAddon.onEnable`. |
| 48 | +- **`handlers/`** — BentoBox API request handlers (`ChallengeDataRequestHandler`, `LevelListRequestHandler`, etc.) exposed over the BentoBox inter-addon request API so other addons can query challenge data. |
| 49 | +- **`listeners/`** — `ResetListener` wipes player data on island reset; `SaveListener` flushes caches on save/quit. |
| 50 | +- **`tasks/`** — `TryToComplete` is the core completion pipeline: validates requirements, consumes costs, applies rewards, fires events. New reward/requirement logic typically plugs in here. |
| 51 | +- **`events/`** — custom Bukkit events (`ChallengeCompletedEvent`, `LevelCompletedEvent`, etc.) fired by `TryToComplete`/managers. Extend the existing event hierarchy rather than adding ad-hoc callbacks. |
| 52 | +- **`web/WebManager`** — downloads the public challenges library via BentoBox's `WebManager`/GitHub content. |
| 53 | + |
| 54 | +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`). |
| 55 | + |
| 56 | +## Conventions |
| 57 | + |
| 58 | +- Target branch for PRs is `master`; active development happens on `develop`. |
| 59 | +- When adding a new `Requirement` subclass, also register its Gson adapter in `database/object/adapters/` or loading will fail silently with missing fields. |
| 60 | +- Challenge/level IDs are always gamemode-prefixed on disk; when comparing or looking up, use `ChallengesManager` helpers rather than raw string compares. |
| 61 | +- User-visible strings belong in `locales/en-US.yml` under the appropriate namespace — never hardcode them in Java. |
| 62 | + |
| 63 | +## Dependency Source Lookup |
| 64 | + |
| 65 | +When you need to inspect source code for a dependency (e.g., BentoBox, addons): |
| 66 | + |
| 67 | +1. **Check local Maven repo first**: `~/.m2/repository/` — sources jars are named `*-sources.jar` |
| 68 | +2. **Check the workspace**: Look for sibling directories or Git submodules that may contain the dependency as a local project (e.g., `../bentoBox`, `../addon-*`) |
| 69 | +3. **Check Maven local cache for already-extracted sources** before downloading anything |
| 70 | +4. Only download a jar or fetch from the internet if the above steps yield nothing useful |
| 71 | + |
| 72 | +Prefer reading `.java` source files directly from a local Git clone over decompiling or extracting a jar. |
| 73 | + |
| 74 | +In general, the latest version of BentoBox should be targeted. |
| 75 | + |
| 76 | +## Project Layout |
| 77 | + |
| 78 | +Related projects are checked out as siblings under `~/git/`: |
| 79 | + |
| 80 | +**Core:** |
| 81 | +- `bentobox/` — core BentoBox framework |
| 82 | + |
| 83 | +**Game modes:** |
| 84 | +- `addon-acidisland/` — AcidIsland game mode |
| 85 | +- `addon-bskyblock/` — BSkyBlock game mode |
| 86 | +- `Boxed/` — Boxed game mode (expandable box area) |
| 87 | +- `CaveBlock/` — CaveBlock game mode |
| 88 | +- `OneBlock/` — AOneBlock game mode |
| 89 | +- `SkyGrid/` — SkyGrid game mode |
| 90 | +- `RaftMode/` — Raft survival game mode |
| 91 | +- `StrangerRealms/` — StrangerRealms game mode |
| 92 | +- `Brix/` — plot game mode |
| 93 | +- `parkour/` — Parkour game mode |
| 94 | +- `poseidon/` — Poseidon game mode |
| 95 | +- `gg/` — gg game mode |
| 96 | + |
| 97 | +**Addons:** |
| 98 | +- `addon-level/` — island level calculation |
| 99 | +- `addon-challenges/` — challenges system |
| 100 | +- `addon-welcomewarpsigns/` — warp signs |
| 101 | +- `addon-limits/` — block/entity limits |
| 102 | +- `addon-invSwitcher/` / `invSwitcher/` — inventory switcher |
| 103 | +- `addon-biomes/` / `Biomes/` — biomes management |
| 104 | +- `Bank/` — island bank |
| 105 | +- `Border/` — world border for islands |
| 106 | +- `Chat/` — island chat |
| 107 | +- `CheckMeOut/` — island submission/voting |
| 108 | +- `ControlPanel/` — game mode control panel |
| 109 | +- `Converter/` — ASkyBlock to BSkyBlock converter |
| 110 | +- `DimensionalTrees/` — dimension-specific trees |
| 111 | +- `discordwebhook/` — Discord integration |
| 112 | +- `Downloads/` — BentoBox downloads site |
| 113 | +- `DragonFights/` — per-island ender dragon fights |
| 114 | +- `ExtraMobs/` — additional mob spawning rules |
| 115 | +- `FarmersDance/` — twerking crop growth |
| 116 | +- `GravityFlux/` — gravity addon |
| 117 | +- `Greenhouses-addon/` — greenhouse biomes |
| 118 | +- `IslandFly/` — island flight permission |
| 119 | +- `IslandRankup/` — island rankup system |
| 120 | +- `Likes/` — island likes/dislikes |
| 121 | +- `Limits/` — block/entity limits |
| 122 | +- `lost-sheep/` — lost sheep adventure |
| 123 | +- `MagicCobblestoneGenerator/` — custom cobblestone generator |
| 124 | +- `PortalStart/` — portal-based island start |
| 125 | +- `pp/` — pp addon |
| 126 | +- `Regionerator/` — region management |
| 127 | +- `Residence/` — residence addon |
| 128 | +- `TopBlock/` — top ten for OneBlock |
| 129 | +- `TwerkingForTrees/` — twerking tree growth |
| 130 | +- `Upgrades/` — island upgrades (Vault) |
| 131 | +- `Visit/` — island visiting |
| 132 | +- `weblink/` — web link addon |
| 133 | +- `CrowdBound/` — CrowdBound addon |
| 134 | + |
| 135 | +**Data packs:** |
| 136 | +- `BoxedDataPack/` — advancement datapack for Boxed |
| 137 | + |
| 138 | +**Documentation & tools:** |
| 139 | +- `docs/` — main documentation site |
| 140 | +- `docs-chinese/` — Chinese documentation |
| 141 | +- `docs-french/` — French documentation |
| 142 | +- `BentoBoxWorld.github.io/` — GitHub Pages site |
| 143 | +- `website/` — website |
| 144 | +- `translation-tool/` — translation tool |
| 145 | + |
| 146 | +Check these for source before any network fetch. |
| 147 | + |
| 148 | +## Testing |
| 149 | + |
| 150 | +The project uses a mixed test stack: |
| 151 | + |
| 152 | +- **JUnit 4 + PowerMock** — existing tests in `commands/`, `tasks/`, `utils/`, `database/`, and top-level addon/manager tests. These use `@RunWith(PowerMockRunner.class)` and `@PrepareForTest`. |
| 153 | +- **JUnit 5 + Mockito** — newer tests in `panel/` package. These use `@ExtendWith(MockitoExtension.class)` with `@MockitoSettings(strictness = Strictness.LENIENT)`. |
| 154 | +- Both frameworks coexist via `junit-vintage-engine` which runs JUnit 4 tests on the JUnit 5 platform. |
| 155 | + |
| 156 | +### Writing new JUnit 5 tests alongside PowerMock |
| 157 | + |
| 158 | +PowerMock's MockMaker conflicts with mockito-inline, so **`mockStatic()` and `mockConstruction()` are not available** in JUnit 5 tests. Use these workarounds: |
| 159 | + |
| 160 | +- **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`. |
| 161 | +- **Final methods** (`JavaPlugin.getServer()` is final): Set the `server` field on the plugin object via reflection walking the class hierarchy. See `PanelTestHelper.setPluginServer()`. |
| 162 | +- **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()`. |
| 163 | +- **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()`. |
| 164 | + |
| 165 | +### PanelTestHelper |
| 166 | + |
| 167 | +`src/test/java/world/bentobox/challenges/panel/PanelTestHelper.java` is a shared utility for all panel tests. It provides: |
| 168 | +- Reflection helpers for Bukkit/BentoBox static fields |
| 169 | +- `ItemTemplateRecord` factory methods (`createTemplate`, `createSimpleTemplate`, `createEmptyTemplate`) |
| 170 | +- `createItemSlot()` for `TemplatedPanel.ItemSlot` records |
| 171 | +- `createBasicChallenge()` for fully-mocked `Challenge` objects |
| 172 | +- `setupUserTranslations()` for varargs-safe translation mocking |
| 173 | + |
| 174 | +## Key Dependencies (source locations) |
| 175 | + |
| 176 | +- `world.bentobox:bentobox` → `~/git/bentobox/src/` |
0 commit comments