Skip to content

Latest commit

 

History

History
64 lines (36 loc) · 6.43 KB

File metadata and controls

64 lines (36 loc) · 6.43 KB

CLAUDE.md

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

Project

Boxed is a BentoBox GameModeAddon for Minecraft (Paper) where each player is confined to a small expandable box. Completing Minecraft advancements grows the box. Built against bentobox 3.13.0, Paper API 1.21.11, Java 21.

Build / Test

  • Build (default goal is clean package): mvn clean package
  • Run all tests: mvn test
  • Run a single test class: mvn test -Dtest=AdvancementsManagerTest
  • Run a single test method: mvn test -Dtest=AdvancementsManagerTest#testMethodName
  • Jacoco coverage report is generated as part of the build (target/site/jacoco/).

The build.version property in pom.xml is the canonical version. The ci and master Maven profiles activate from Jenkins env vars (BUILD_NUMBER, GIT_BRANCH=origin/master) to compute the final artifact name; don't hand-edit revision.

Surefire is configured with a long list of --add-opens JVM args required by Mockito's inline mock maker on Java 21+ — if you add new tests that touch JDK internals, extend that argLine in pom.xml rather than fighting module access errors locally. The maven-compiler-plugin runs with <fork>true</fork> to work around a JDK 25 in-process javac NPE (this.hashes is null).

Architecture

Boxed is a BentoBox addon, not a standalone plugin. Boxed extends GameModeAddon and is loaded by BentoBox at runtime. BoxedPladdon is the Paper plugin-loader shim so Paper recognises the jar.

The two-world "seed + game" generator model

This is the core concept and touches almost everything:

  1. Seed world (<worldname>/seed, and /seed_nether): a real vanilla-ish world generated once using BoxedSeedChunkGenerator + SeedBiomeGenerator / NetherSeedBiomeGenerator. It's where Minecraft's normal terrain + structures (villages, shipwrecks, fortresses, etc.) actually get generated by the server.
  2. Game world (<worldname>, <worldname>_nether): the world players actually play in. It uses BoxedChunkGenerator (a subclass of AbstractBoxedChunkGenerator) which does not generate terrain from scratch — instead, Boxed.copyChunks(...) pre-reads every chunk inside islandDistance from the seed world during createWorlds() and stores them in the chunk generator. When the game world asks for a chunk, the generator serves back the pre-captured copy.

This is why first boot is extremely slow and RAM-hungry (see README.md warnings): the entire seed region is force-loaded up front. Any change to world generation, structure handling, or world naming must respect both worlds and the copy step in Boxed.copyChunks() / createOverWorld() / createNether(). The generatorMaps / generatorMap fields in Boxed.java route world names → generators for getDefaultWorldGenerator (used by Multiverse and similar world-management plugins) and for the hook in allLoaded() that calls WorldManagementHook.registerWorld.

isUsesNewChunkGeneration() returns true, which tells BentoBox this addon uses the modern chunk-generation API.

Advancements drive box size

AdvancementsManager is the other key subsystem. Box growth is data-driven from advancements.yml: each advancement key maps to an integer "box growth" increment. AdvancementListener watches for player advancement events and asks the manager to update the island's protection-range. Per-island state lives in objects/IslandAdvancements.java (a BentoBox DataObject persisted via its database layer). AdvancementsManager.save() is called in onDisable() — any new cached state it holds should be flushed there too.

Because advancements are per-world in vanilla but Boxed runs multiple worlds on one server, the InvSwitcher addon is required at runtime to keep advancements separate between worlds. The code logs a warning if InvSwitcher/Border aren't installed but does not hard-fail.

Structures

NewAreaListener plus objects/IslandStructures.java, BoxedJigsawBlock, BoxedStructureBlock, and ToBePlacedStructures handle placing/tracking vanilla structures inside player boxes. AdminPlaceStructureCommand is the admin-side hook for manual placement. If you're adding structure logic, both the "captured from seed world" pathway and the "placed into player box" pathway need to stay in sync.

Flags

Boxed.MOVE_BOX (protection flag, owner-only by default) and Boxed.ALLOW_MOVE_BOX (world setting) gate the enderpearl-box-teleport feature implemented in EnderPearlListener. They are scoped to this game mode only via setGameModes(...) in onEnable(), and MOVE_BOX is conditionally registered/unregistered depending on ALLOW_MOVE_BOX — keep that conditional registration intact if you touch flag setup.

Resources packaged at build time

pom.xml filters src/main/resources but copies structures/*.nbt, locales/*.yml, and blueprints/*.blu|*.json unfiltered into the jar at specific targetPaths. New resource types need a matching <resource> block or they won't ship.

Testing notes

Tests use JUnit 5 (Jupiter) + Mockito 5 mockStatic + MockBukkit (v1.21-SNAPSHOT via jitpack.io). There is no PowerMock and no custom ServerMocks helper. All test classes extend CommonTestSetup, which:

  • Calls MockBukkit.mock() and registers Mockito.mockStatic(Bukkit.class, RETURNS_DEEP_STUBS) — use the inherited mockedBukkit field to stub Bukkit.* calls rather than creating your own.
  • Exposes ready-made @Mock fields (plugin, mockPlayer, world, location, iwm, im, island, pim, itemFactory, inv, notifier, fm, spigot, hooksManager, bm, sch, lm, phm) plus the MockBukkit server and mockedUtil statics. Don't re-declare these in subclasses.
  • Forces org.bukkit.Tag.LEAVES to initialise before the static Bukkit mock is installed — if you touch tag-related code and see stale deep-stubs across tests, broaden that list in CommonTestSetup.

A small WhiteBox helper (reflection-based private static field setter) replaces PowerMock's Whitebox.setInternalState. Any test that needs its own MockedStatic<...> (e.g. DatabaseSetup, User) must create it after super.setUp() and close it via closeOnDemand() before super.tearDown() — the parent's Mockito.framework().clearInlineMocks() in tearDown will otherwise corrupt the local static.

Jacoco excludes org/bukkit/Material* to avoid "class too large to mock" failures; keep that exclusion if you rearrange the build section.