|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Build & test |
| 6 | + |
| 7 | +Maven project, Java 21. Default goal is `clean package`. |
| 8 | + |
| 9 | +- Build the addon jar: `mvn clean package` (output in `target/`, named `StrangerRealms-${build.version}-SNAPSHOT-LOCAL.jar` locally) |
| 10 | +- Run all tests: `mvn test` |
| 11 | +- Run a single test class: `mvn test -Dtest=StrangerRealmsTest` |
| 12 | +- Run a single test method: `mvn test -Dtest=StrangerRealmsTest#methodName` |
| 13 | +- Skip tests during a build: `mvn package -DskipTests` |
| 14 | + |
| 15 | +Surefire is preconfigured with the `--add-opens` flags required by MockBukkit on JDK 21 — don't run tests outside Maven without replicating them. JaCoCo runs on the `test` phase and writes an XML report to `target/site/jacoco/`. |
| 16 | + |
| 17 | +Versioning is driven by Maven properties: `build.version` (in `pom.xml`) sets the release version, and the `master` / `ci` profiles flip `-SNAPSHOT` and `-bNNN` build numbers automatically from Jenkins env vars. Bump `build.version` in `pom.xml` to cut a new release. CI runs on Jenkins at codemc.org (see `pom.xml` `<ciManagement>`) and on GitHub Actions (`.github/workflows/build.yml`). |
| 18 | + |
| 19 | +## Architecture |
| 20 | + |
| 21 | +StrangerRealms is a **BentoBox GameModeAddon** — it does not run as a standalone Bukkit/Paper plugin. The runtime entry point Paper sees is `StrangerRealmsPladdon` (a `Pladdon`), which constructs and returns the real addon, `StrangerRealms`. `plugin.yml` points Paper at the Pladdon; `addon.yml` points BentoBox at `StrangerRealms`. When changing entry points or lifecycle, both files matter. |
| 22 | + |
| 23 | +### Addon lifecycle (StrangerRealms.java) |
| 24 | + |
| 25 | +BentoBox calls these in order — keep work in the right phase: |
| 26 | + |
| 27 | +1. `onLoad()` — load `Settings` from `config.yml`, construct the `NetherChunkMaker` (it's needed during world creation), and register player/admin command trees (`ClaimCommand`, `UnclaimCommand`, `SpawnCommand`, `WorldBorderCommand`). |
| 28 | +2. `createWorlds()` — creates the overworld, nether (Upside Down), and end via `WorldCreator`. The nether uses the custom `NetherChunks` ChunkGenerator + `NetherBiomeProvider`. If `Bukkit.getWorld(worldName + "_nether")` is missing at this point the chunks database is cleared — i.e., deleting the nether world folder forces full regeneration. |
| 29 | +3. `onEnable()` — registers listeners (`PlayerListener`, `NetherChunkMaker`, `TeamListener`, `NetherRedstoneListener`), registers the Warped Compass recipe, and ensures a spawn `Island` exists. |
| 30 | +4. `allLoaded()` — persists settings. |
| 31 | + |
| 32 | +`Settings` implements BentoBox's `WorldSettings`, so it doubles as the world configuration object returned from `getWorldSettings()`. It is serialized via `@ConfigEntry` annotations and stored at `addons/StrangerRealms/config.yml`. Adding a new option means adding a field + annotations here, not editing `config.yml` directly (the file is regenerated from the class). |
| 33 | + |
| 34 | +### Domain vocabulary |
| 35 | + |
| 36 | +In user-facing strings and commands the unit of ownership is a **claim**, but in code (and in BentoBox APIs) it is an **`Island`**. They are the same thing — `ClaimCommand` reuses BentoBox's island-create flow and even pulls locale keys from the core "island" namespace. Don't try to rename `Island` to `Claim` in code; treat them as synonyms. |
| 37 | + |
| 38 | +### Upside Down generation (generator/) |
| 39 | + |
| 40 | +The nether is a darkened, distressed mirror of the Overworld, generated lazily: |
| 41 | + |
| 42 | +- `NetherChunks` (ChunkGenerator) lays down the base terrain — a sculk-laced deep-dark floor below `NETHER_FLOOR`. Above that, the chunk is mostly air. |
| 43 | +- `NetherBiomeProvider` assigns biomes; `NetherChunkMaker.BIOME_MAPPING` maps overworld biomes → crimson/warped/basalt variants. |
| 44 | +- `NetherChunkMaker` (Listener) is the interesting piece: on `ChunkLoadEvent` in the nether world, it copies the corresponding overworld chunk's blocks into the nether chunk (with attrition / corruption controlled by `Settings.attrition`). The set of chunks it has already processed is persisted via `NetherChunksMade` (a BentoBox `DataObject` table named `StrangerChunks`) so generation only happens once per chunk. |
| 45 | +- The **Warped Compass** (recipe registered in `StrangerRealms#registerWarpedCompassRecipe`) is consumed during nether portal travel to force re-generation of surrounding chunks — that is, to clear them from `NetherChunksMade` so they get rebuilt from the current overworld state. |
| 46 | +- `NetherRedstoneListener` is the "Glimmer": with probability `Settings.redstoneChance`, button/lever interactions in the Upside Down replay at the same coordinates in the overworld. |
| 47 | + |
| 48 | +### Border system (border/) |
| 49 | + |
| 50 | +StrangerRealms ships its own world-border implementation and **conflicts with BentoBox's `Border` addon** — `onEnable()` warns if `Border` is loaded. There are two strategies behind the `BorderShower` interface: |
| 51 | + |
| 52 | +- `ShowBarrier` — custom client-side barrier-block visualisation. |
| 53 | +- `ShowWorldBorder` — uses the vanilla world-border packet. |
| 54 | + |
| 55 | +`PerPlayerBorderProxy` is the entry point used by the rest of the addon; it currently fans calls out to *both* strategies (so each player sees both). Per-player border *type* selection (`BorderType` enum) is the intended extension point if you need to vary by player. |
| 56 | + |
| 57 | +The world border size is dynamic: `StrangerRealms#getBorderSize()` returns `barrierIncreaseBlocks × onlinePlayers` unless `manualBorderSize` is set. Shrinks are animated gradually via a Bukkit scheduled task at `barrierReductionSpeed` seconds per block — there is at most one such task; calling `getBorderSize()` again replaces it. |
| 58 | + |
| 59 | +### Resources packaged into the jar (pom.xml `<resources>`) |
| 60 | + |
| 61 | +- `src/main/resources/config.yml`, `addon.yml`, `plugin.yml` — filtered (Maven replaces `${...}` placeholders). |
| 62 | +- `src/main/resources/locales/*.yml` → `./locales/` — **not filtered**. ~22 translations live here. |
| 63 | +- `src/main/resources/blueprints/*.{blu,json}` → `./blueprints/` — BentoBox blueprint bundle for new claim layouts. |
| 64 | +- `src/main/resources/structures/*.nbt` → `./structures/` — directory may not exist yet; the resource entry is preconfigured. |
| 65 | + |
| 66 | +## Testing patterns |
| 67 | + |
| 68 | +Tests use JUnit 5 + Mockito + MockBukkit. Two reusable base classes live in `src/test/java/world/bentobox/stranger/`: |
| 69 | + |
| 70 | +- `CommonTestSetup` — sets up `MockBukkit.mock()`, a static `Bukkit` mock, a mocked `BentoBox` plugin (injected via `WhiteBox` reflection into `BentoBox.instance`), and a mocked `IslandsManager` / `IslandWorldManager` with one default island. Extend this for most listener/command tests. |
| 71 | +- `RanksManagerTestSetup` — adds rank-manager setup on top of `CommonTestSetup`. |
| 72 | +- `WhiteBox` — utility for setting private static fields (e.g., the BentoBox singleton). |
| 73 | +- `TestWorldSettings` — minimal `WorldSettings` returned from `iwm.getWorldSettings()`. |
| 74 | + |
| 75 | +Always call `super.setUp()` / `super.tearDown()` from subclasses — the base class is responsible for closing the static `Bukkit` mock and calling `MockBukkit.unmock()`, and forgetting either causes test interference and leaked database directories. |
| 76 | + |
| 77 | +`StrangerRealmsTest` exercises full addon loading; it builds a temporary `addon.jar` from `src/main/resources/config.yml` and feeds it through BentoBox's addon manager. Use it as the reference pattern when you need to test addon-level wiring rather than a single class. |
| 78 | + |
| 79 | +## Companion addons & integration notes |
| 80 | + |
| 81 | +- **Do not** install BentoBox's `Border` addon alongside this — they fight. `InvSwitcher` is recommended (warned about on enable if missing). Real users typically also run `Warps`. |
| 82 | +- Spigot NMS is a required repository (`<repository id="nms-repo">`) — it's used for world regeneration. Don't remove it from `pom.xml`. |
0 commit comments