This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
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 Level2.6.3and Vault1.7 - Version is set via
<build.version>inpom.xml(uses${revision}/ CI-bNNNsuffix)
# 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=-bLOCALBuilt jar lands in target/ and must be dropped into BentoBox/addons/ on a running BentoBox server to test manually.
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 ofChallenge,ChallengeLevel, and per-playerChallengesPlayerData, plus the BentoBoxDatabasehandles 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) — seegetUniqueID/addChallengeToLevelfor the prefixing contract.managers/ChallengesImportManager— imports/exports challenges fromdefault.json, template YAML, and library downloads viaweb/WebManager(the "Web Library" in the GUI).database/object/— Gson-serialized data objects (Challenge,ChallengeLevel,ChallengesPlayerData).requirements/holds the polymorphicRequirementhierarchy (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 onbentobox-panelutils.CommonPanel/CommonPagedPanelare the base classes;admin/contains the admin editor GUIs,user/the player-facing GUIs. Conversations (text input prompts) go throughConversationUtils.commands/— player commands (ChallengesPlayerCommand,ChallengesGlobalPlayerCommand,CompleteChallengeCommand) andcommands/admin/admin commands. Each is registered per gamemode inChallengesAddon.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/—ResetListenerwipes player data on island reset;SaveListenerflushes caches on save/quit.tasks/—TryToCompleteis 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 byTryToComplete/managers. Extend the existing event hierarchy rather than adding ad-hoc callbacks.web/WebManager— downloads the public challenges library via BentoBox'sWebManager/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).
- Target branch for PRs is
master; active development happens ondevelop. - When adding a new
Requirementsubclass, also register its Gson adapter indatabase/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
ChallengesManagerhelpers rather than raw string compares. - User-visible strings belong in
locales/en-US.ymlunder the appropriate namespace — never hardcode them in Java.
When you need to inspect source code for a dependency (e.g., BentoBox, addons):
- Check local Maven repo first:
~/.m2/repository/— sources jars are named*-sources.jar - Check the workspace: Look for sibling directories or Git submodules that may contain the dependency as a local project (e.g.,
../bentoBox,../addon-*) - Check Maven local cache for already-extracted sources before downloading anything
- 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.
Related projects are checked out as siblings under ~/git/:
Core:
bentobox/— core BentoBox framework
Game modes:
addon-acidisland/— AcidIsland game modeaddon-bskyblock/— BSkyBlock game modeBoxed/— Boxed game mode (expandable box area)CaveBlock/— CaveBlock game modeOneBlock/— AOneBlock game modeSkyGrid/— SkyGrid game modeRaftMode/— Raft survival game modeStrangerRealms/— StrangerRealms game modeBrix/— plot game modeparkour/— Parkour game modeposeidon/— Poseidon game modegg/— gg game mode
Addons:
addon-level/— island level calculationaddon-challenges/— challenges systemaddon-welcomewarpsigns/— warp signsaddon-limits/— block/entity limitsaddon-invSwitcher//invSwitcher/— inventory switcheraddon-biomes//Biomes/— biomes managementBank/— island bankBorder/— world border for islandsChat/— island chatCheckMeOut/— island submission/votingControlPanel/— game mode control panelConverter/— ASkyBlock to BSkyBlock converterDimensionalTrees/— dimension-specific treesdiscordwebhook/— Discord integrationDownloads/— BentoBox downloads siteDragonFights/— per-island ender dragon fightsExtraMobs/— additional mob spawning rulesFarmersDance/— twerking crop growthGravityFlux/— gravity addonGreenhouses-addon/— greenhouse biomesIslandFly/— island flight permissionIslandRankup/— island rankup systemLikes/— island likes/dislikesLimits/— block/entity limitslost-sheep/— lost sheep adventureMagicCobblestoneGenerator/— custom cobblestone generatorPortalStart/— portal-based island startpp/— pp addonRegionerator/— region managementResidence/— residence addonTopBlock/— top ten for OneBlockTwerkingForTrees/— twerking tree growthUpgrades/— island upgrades (Vault)Visit/— island visitingweblink/— web link addonCrowdBound/— CrowdBound addon
Data packs:
BoxedDataPack/— advancement datapack for Boxed
Documentation & tools:
docs/— main documentation sitedocs-chinese/— Chinese documentationdocs-french/— French documentationBentoBoxWorld.github.io/— GitHub Pages sitewebsite/— websitetranslation-tool/— translation tool
Check these for source before any network fetch.
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-enginewhich runs JUnit 4 tests on the JUnit 5 platform.
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. SeePanelTestHelper.setServer()andsetBentoBoxInstance()for examples. Always save/restore the previous value in@BeforeEach/@AfterEach. - Final methods (
JavaPlugin.getServer()is final): Set theserverfield on the plugin object via reflection walking the class hierarchy. SeePanelTestHelper.setPluginServer(). - Java records (
ItemTemplateRecord,TemplatedPanel.ItemSlot): Cannot be mocked. Create real instances using their public constructors. ForTemplatedPanel(which has no public constructor), usesun.misc.Unsafe.allocateInstance()then set fields via reflection. SeePanelTestHelper.createItemSlot(). - Varargs methods (
User.getTranslation(String, String...)): UseMockito.doAnswer().when()withMockito.<String>any()instead ofwhen().thenAnswer(). ThedoAnswerpattern handles varargs correctly. SeePanelTestHelper.setupUserTranslations().
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
ItemTemplateRecordfactory methods (createTemplate,createSimpleTemplate,createEmptyTemplate)createItemSlot()forTemplatedPanel.ItemSlotrecordscreateBasicChallenge()for fully-mockedChallengeobjectssetupUserTranslations()for varargs-safe translation mocking
world.bentobox:bentobox→~/git/bentobox/src/