This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
InvSwitcher is a BentoBox addon for Minecraft (Spigot/Bukkit) that gives players separate inventories, ender chests, health, food, experience, advancements, game modes, and statistics per game-world. Nether and End dimensions are automatically grouped with their overworld.
- Build:
mvn clean package - Run tests:
mvn test -Dmaven.compiler.forceJavacCompilerUse=true - Run a single test class:
mvn test -Dmaven.compiler.forceJavacCompilerUse=true -Dtest=StoreTest - Run a single test method:
mvn test -Dmaven.compiler.forceJavacCompilerUse=true -Dtest=StoreTest#testMethodName
Requires Java 21. The build produces a shaded JAR in target/. The -Dmaven.compiler.forceJavacCompilerUse=true flag is needed to work around a compiler hashing bug with the current JDK.
This is a BentoBox Addon (extends Addon, not a standalone Bukkit plugin). Key flow:
- InvSwitcher - Addon entry point. Loads config, resolves configured world names to
Worldobjects (including nether/end variants), creates theStore, and registersPlayerListener. - Store - Core logic. Maintains an in-memory
Map<UUID, InventoryStorage>cache backed by BentoBox'sDatabase. On world change: stores current player state to the old world's slot, clears the player, then loads the new world's slot. Handles XP math manually (Bukkit'sgetTotalExperience()is unreliable). Tracks per-playercurrentKeyto map saves/loads to the correct storage slot. - InventoryStorage -
DataObject(BentoBox DB entity) keyed by player UUID. All per-world data is stored asMap<String, ...>where the key is either the overworld name (e.g.,"oneblock_world") or an island-specific key (e.g.,"oneblock_world/islandId"). Persisted via BentoBox's database abstraction (JSON, MySQL, etc. — not YAML, which is explicitly unsupported). - PlayerListener - Listens to
PlayerChangedWorldEvent,PlayerJoinEvent,PlayerQuitEvent,IslandEnterEvent, andPlayerRespawnEventto trigger store/load operations. - Settings -
ConfigObjectloaded fromconfig.yml. Each switchable aspect (inventory, health, food, etc.) has a world-level boolean toggle and a per-island sub-toggle.
- World name normalization: nether (
_nether) and end (_the_end) suffixes are stripped to map all three dimensions to the same overworld key. - Per-island inventory switching: When
islandsActiveis enabled and a player owns multiple concurrent islands, data is keyed as"worldName/islandId"instead of just"worldName". Each data type (inventory, health, etc.) has its own per-island sub-toggle. - Storage key transitions: When a player goes from 1 island to multiple, their
currentKeymust be upgraded from a world-only key to an island-specific key. This is handled proactively inPlayerListener.onIslandEnterviaStore.upgradeWorldKeyToIsland()before any save/load occurs. - Backward compatibility migration:
Store.getInventory()migrates world-only data to island-specific keys on first load if no island-specific data exists yet. - Statistics saving runs asynchronously via
Bukkit.getScheduler()except during shutdown (where scheduling is unavailable). - Tests use JUnit 5 + MockBukkit + Mockito. The surefire plugin requires extensive
--add-opensflags (already configured in pom.xml).