Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
c9851fe
Initial plan
Copilot Mar 2, 2026
c5c6ef2
Fix top ten ordering: use ConcurrentHashMap for thread safety and inv…
Copilot Mar 2, 2026
9c535c5
Merge pull request #403 from BentoBoxWorld/copilot/fix-top-ten-order
tastybento Mar 2, 2026
2fd5615
Initial plan
Copilot Mar 14, 2026
d572a33
Fix incorrect block icons for hanging signs, vines, and cave vines
Copilot Mar 14, 2026
a4fc233
Merge pull request #404 from BentoBoxWorld/copilot/fix-bamboo-hanging…
tastybento Mar 14, 2026
0d5000d
Initial plan
Copilot Mar 15, 2026
f242433
Merge pull request #405 from BentoBoxWorld/copilot/add-placeholder-po…
tastybento Mar 15, 2026
a09b0a4
Initial plan
Copilot Apr 5, 2026
76190aa
Add Russian locale file (ru.yml) with MiniMessage formatting
Copilot Apr 5, 2026
449cb18
Merge pull request #407 from BentoBoxWorld/copilot/update-russian-lan…
tastybento Apr 5, 2026
358a0e1
Translate Czech (cs): bring up to date with en-US
tastybento Apr 7, 2026
4f3d815
Translate Spanish (es): bring up to date with en-US
tastybento Apr 7, 2026
8037192
Translate French (fr): bring up to date with en-US
tastybento Apr 7, 2026
64931a1
Translate Dutch (nl): bring up to date with en-US
tastybento Apr 7, 2026
b3ec23e
Translate Polish (pl): bring up to date with en-US
tastybento Apr 7, 2026
63a460d
Translate Indonesian (id): bring up to date with en-US
tastybento Apr 7, 2026
b9d4ca0
Translate Ukrainian (uk): bring up to date with en-US
tastybento Apr 7, 2026
995c418
Translate Chinese Simplified (zh-CN): bring up to date with en-US
tastybento Apr 7, 2026
4128b1a
Translate Portuguese (pt): bring up to date with en-US
tastybento Apr 7, 2026
d00883e
Translate Vietnamese (vi): bring up to date with en-US
tastybento Apr 7, 2026
615dc67
Translate Hungarian (hu): bring up to date with en-US
tastybento Apr 7, 2026
b5620a2
Translate German (de): bring up to date with en-US
tastybento Apr 7, 2026
6702b49
Translate Korean (ko): bring up to date with en-US
tastybento Apr 7, 2026
54b50be
Translate Latvian (lv): bring up to date with en-US
tastybento Apr 7, 2026
d68da46
Translate Turkish (tr): bring up to date with en-US
tastybento Apr 7, 2026
9c0a5e1
Version 2.24.0
tastybento Apr 7, 2026
322a605
Bump BentoBox to 3.14.0, migrate locales to MiniMessage, refresh Mave…
tastybento Apr 11, 2026
8346ae3
Add block donation feature (#220)
tastybento Apr 11, 2026
9a4ec0f
Sync locales with new donation strings and document panel upgrade caveat
tastybento Apr 11, 2026
f0fc85d
Fix donation panel lore color leak and default italic
tastybento Apr 11, 2026
4ae01bb
Confirm hand donations and localize point totals
tastybento Apr 11, 2026
f27f8f7
Fix review feedback: donation panel safety, LevelsManager data integr…
Copilot Apr 11, 2026
aa92264
Require Member rank minimum on block donation flag
tastybento Apr 11, 2026
e68f503
Address Sonar findings on DonationPanel
tastybento Apr 11, 2026
bfce0e8
Merge pull request #413 from BentoBoxWorld/feature/flag-minimum-rank
tastybento Apr 11, 2026
2c546e3
Update CLAUDE.md
tastybento Apr 11, 2026
099fc44
Address second review: recalc after donation, fix tab-complete indexi…
Copilot Apr 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,100 @@ JaCoCo coverage reports are generated during `mvn verify`.
| `locales/` | `src/main/resources/locales/` | Translation strings |
| `panels/` | `src/main/resources/panels/` | GUI layout definitions |

**Panel template upgrades:** Files under `panels/` are copied to the addon's data folder (`plugins/BentoBox/addons/Level/panels/`) on first run and are **not** overwritten on upgrade. If a release modifies a panel template (new tabs, buttons, slots, etc.), the release notes/changelog must explicitly instruct users to delete the affected on-disk panel file so it regenerates — otherwise existing servers will silently keep the old layout.

**Current upgrade-sensitive example:** If `src/main/resources/panels/detail_panel.yml` changes (for example by adding a new `DONATED` tab), existing servers must delete/regenerate `plugins/BentoBox/addons/Level/panels/detail_panel.yml` after upgrading or they will continue using the old panel definition and the new tab will not appear.
## Code Conventions

- Null safety via Eclipse JDT annotations (`@NonNull`, `@Nullable`) — honour these on public APIs
- BentoBox framework patterns: `CompositeCommand` for commands, `@ConfigEntry`/`@ConfigComment` for config, `@StoreAt` for database objects
- Pre- and post-events (`IslandPreLevelEvent`, `IslandLevelCalculatedEvent`) follow BentoBox's cancellable event pattern — fire both when adding new calculation triggers

## Dependency Source Lookup

When you need to inspect source code for a dependency (e.g., BentoBox, addons):

1. **Check local Maven repo first**: `~/.m2/repository/` — sources jars are named `*-sources.jar`
2. **Check the workspace**: Look for sibling directories or Git submodules that may contain the dependency as a local project (e.g., `../bentoBox`, `../addon-*`)
3. **Check Maven local cache for already-extracted sources** before downloading anything
4. 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.

## Project Layout

Related projects are checked out as siblings under `~/git/`:

**Core:**
- `bentobox/` — core BentoBox framework

**Game modes:**
- `addon-acidisland/` — AcidIsland game mode
- `addon-bskyblock/` — BSkyBlock game mode
- `Boxed/` — Boxed game mode (expandable box area)
- `CaveBlock/` — CaveBlock game mode
- `OneBlock/` — AOneBlock game mode
- `SkyGrid/` — SkyGrid game mode
- `RaftMode/` — Raft survival game mode
- `StrangerRealms/` — StrangerRealms game mode
- `Brix/` — plot game mode
- `parkour/` — Parkour game mode
- `poseidon/` — Poseidon game mode
- `gg/` — gg game mode

**Addons:**
- `addon-level/` — island level calculation
- `addon-challenges/` — challenges system
- `addon-welcomewarpsigns/` — warp signs
- `addon-limits/` — block/entity limits
- `addon-invSwitcher/` / `invSwitcher/` — inventory switcher
- `addon-biomes/` / `Biomes/` — biomes management
- `Bank/` — island bank
- `Border/` — world border for islands
- `Chat/` — island chat
- `CheckMeOut/` — island submission/voting
- `ControlPanel/` — game mode control panel
- `Converter/` — ASkyBlock to BSkyBlock converter
- `DimensionalTrees/` — dimension-specific trees
- `discordwebhook/` — Discord integration
- `Downloads/` — BentoBox downloads site
- `DragonFights/` — per-island ender dragon fights
- `ExtraMobs/` — additional mob spawning rules
- `FarmersDance/` — twerking crop growth
- `GravityFlux/` — gravity addon
- `Greenhouses-addon/` — greenhouse biomes
- `IslandFly/` — island flight permission
- `IslandRankup/` — island rankup system
- `Likes/` — island likes/dislikes
- `Limits/` — block/entity limits
- `lost-sheep/` — lost sheep adventure
- `MagicCobblestoneGenerator/` — custom cobblestone generator
- `PortalStart/` — portal-based island start
- `pp/` — pp addon
- `Regionerator/` — region management
- `Residence/` — residence addon
- `TopBlock/` — top ten for OneBlock
- `TwerkingForTrees/` — twerking tree growth
- `Upgrades/` — island upgrades (Vault)
- `Visit/` — island visiting
- `weblink/` — web link addon
- `CrowdBound/` — CrowdBound addon

**Data packs:**
- `BoxedDataPack/` — advancement datapack for Boxed

**Documentation & tools:**
- `docs/` — main documentation site
- `docs-chinese/` — Chinese documentation
- `docs-french/` — French documentation
- `BentoBoxWorld.github.io/` — GitHub Pages site
- `website/` — website
- `translation-tool/` — translation tool

Check these for source before any network fetch.

## Key Dependencies (source locations)

- `world.bentobox:bentobox` → `~/git/bentobox/src/`
29 changes: 15 additions & 14 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
<mock-bukkit.version>v1.21-SNAPSHOT</mock-bukkit.version>
<!-- More visible way how to change dependency versions -->
<paper.version>1.21.11-R0.1-SNAPSHOT</paper.version>
<bentobox.version>3.10.2</bentobox.version>
<bentobox.version>3.14.1-SNAPSHOT</bentobox.version>
<!-- Warps addon version -->
<warps.version>1.12.0</warps.version>
<!-- Visit addon version -->
Expand All @@ -69,7 +69,7 @@
<!-- Do not change unless you want different name for local builds. -->
<build.number>-LOCAL</build.number>
<!-- This allows to change between versions. -->
<build.version>2.23.0</build.version>
<build.version>2.24.0</build.version>
<sonar.projectKey>BentoBoxWorld_Level</sonar.projectKey>
<sonar.organization>bentobox-world</sonar.organization>
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
Expand Down Expand Up @@ -222,7 +222,7 @@
<dependency>
<groupId>world.bentobox</groupId>
<artifactId>bentobox</artifactId>
<version>3.10.0</version>
<version>${bentobox.version}</version>
</dependency>
<dependency>
<groupId>world.bentobox</groupId>
Expand Down Expand Up @@ -378,25 +378,26 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId>
<version>3.4.0</version>
<version>3.5.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.3.1</version>
<version>3.5.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.14.1</version>
<version>3.15.0</version>
<configuration>
<release>${java.version}</release>
<fork>true</fork>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.4</version>
<version>3.5.5</version>
<configuration>
<argLine>
${argLine}
Expand Down Expand Up @@ -434,12 +435,12 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.4.2</version>
<version>3.5.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.11.1</version>
<version>3.12.0</version>
<configuration>
<doclint>none</doclint> <!-- Turnoff all checks -->
<failOnError>false</failOnError>
Expand All @@ -458,7 +459,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.3.1</version>
<version>3.4.0</version>
<executions>
<execution>
<id>attach-sources</id>
Expand All @@ -471,17 +472,17 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<version>3.1.3</version>
<version>3.1.4</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>3.1.3</version>
<version>3.1.4</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.6.0</version>
<version>3.6.2</version>
<configuration>
<minimizeJar>true</minimizeJar>
<artifactSet>
Expand Down Expand Up @@ -513,7 +514,7 @@
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.13</version>
<version>0.8.14</version>
<configuration>
<append>true</append>
<excludes>
Expand Down
19 changes: 19 additions & 0 deletions src/main/java/world/bentobox/level/Level.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.util.UUID;

import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
Expand All @@ -17,8 +18,10 @@
import world.bentobox.bentobox.api.addons.Addon;
import world.bentobox.bentobox.api.addons.GameModeAddon;
import world.bentobox.bentobox.api.configuration.Config;
import world.bentobox.bentobox.api.flags.Flag;
import world.bentobox.bentobox.api.user.User;
import world.bentobox.bentobox.database.objects.Island;
import world.bentobox.bentobox.managers.RanksManager;
import world.bentobox.bentobox.util.Util;
import world.bentobox.level.calculators.Pipeliner;
import world.bentobox.level.commands.AdminLevelCommand;
Expand All @@ -27,6 +30,7 @@
import world.bentobox.level.commands.AdminStatsCommand;
import world.bentobox.level.commands.AdminTopCommand;
import world.bentobox.level.commands.IslandDetailCommand;
import world.bentobox.level.commands.IslandDonateCommand;
import world.bentobox.level.commands.IslandLevelCommand;
import world.bentobox.level.commands.IslandTopCommand;
import world.bentobox.level.commands.IslandValueCommand;
Expand All @@ -49,6 +53,17 @@ public class Level extends Addon {
// The 10 in top ten
public static final int TEN = 10;

/**
* Flag to control who can donate blocks to raise island level.
* Default: OWNER only. Can be extended down to MEMBER rank.
*/
public static final Flag BLOCK_DONATION = new Flag.Builder("ISLAND_BLOCK_DONATION", Material.HOPPER)
.type(Flag.Type.PROTECTION)
.defaultRank(RanksManager.OWNER_RANK)
.minimumRank(RanksManager.MEMBER_RANK)
.mode(Flag.Mode.BASIC)
.build();

// Settings
private ConfigSettings settings;
private Config<ConfigSettings> configObject = new Config<>(this, ConfigSettings.class);
Expand Down Expand Up @@ -114,6 +129,9 @@ public void allLoaded() {
hookPlugin("RoseStacker", this::hookRoseStackers);
hookPlugin("UltimateStacker", this::hookUltimateStacker);

// Register the block donation flag
getPlugin().getFlagsManager().registerFlag(this, BLOCK_DONATION);

if (this.isEnabled()) {
hookExtensions();
}
Expand Down Expand Up @@ -250,6 +268,7 @@ private void registerCommands(GameModeAddon gm) {
new IslandTopCommand(this, playerCmd);
new IslandValueCommand(this, playerCmd);
new IslandDetailCommand(this, playerCmd);
new IslandDonateCommand(this, playerCmd);
});
}

Expand Down
52 changes: 52 additions & 0 deletions src/main/java/world/bentobox/level/LevelsManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ private boolean addToTopTen(Island island, long lv) {
&& hasTopTenPerm(island.getWorld(), island.getOwner())) {
topTenLists.computeIfAbsent(island.getWorld(), k -> new TopTenData(island.getWorld())).getTopTen()
.put(island.getUniqueId(), lv);
// Invalidate the cache for this world because of the update
cache.remove(island.getWorld());
return true;
}
return false;
Expand Down Expand Up @@ -540,4 +542,54 @@ public void deleteIsland(String uniqueId) {
handler.deleteID(uniqueId);
}

// ---- Block Donation Methods ----

/**
* Record a block donation for an island. Items should already be removed from the player's inventory.
*
* @param island the island receiving the donation
* @param donorUUID UUID of the donating player
* @param material the material name being donated
* @param count how many blocks
* @param points the point value of this donation
*/
public void donateBlocks(@NonNull Island island, @NonNull UUID donorUUID, @NonNull String material, int count, long points) {
IslandLevels ld = getLevelsData(island);
ld.addDonation(donorUUID.toString(), material, count, points);
handler.saveObjectAsync(ld);
}

/**
* Queue a full level recalculation for the island. Call this after donations
* so that the level/top-ten update immediately.
*
* @param island the island to recalculate
*/
public void recalculateAfterDonation(@NonNull Island island) {
UUID owner = island.getOwner();
if (owner != null) {
calculateLevel(owner, island);
}
}
Comment on lines +556 to +573
Copy link

Copilot AI Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

donateBlocks(...) only updates the donation fields and saves the object, but it never triggers a level recalculation or updates the stored level/pointsToNextLevel fields. Since getIslandLevel(...) reads IslandLevels#getLevel(), donations won’t actually affect island level/top-ten output until some later /island level recalculation happens. Consider queueing a recalculation here (or in the donate command/panel after a successful donation), or explicitly updating the persisted level fields so the “donation raises level” behavior is immediate and consistent.

Copilot uses AI. Check for mistakes.

/**
* Get the total donated points for an island.
*
* @param island the island
* @return total donated points
*/
public long getDonatedPoints(@NonNull Island island) {
return getLevelsData(island).getDonatedPoints();
}

/**
* Get the donated blocks map for an island.
*
* @param island the island
* @return map of material name to count
*/
public Map<String, Integer> getDonatedBlocks(@NonNull Island island) {
return getLevelsData(island).getDonatedBlocks();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,22 @@ private List<String> getReport() {
+ " blocks (max " + limit + explain);
}
reportLines.add(LINE_BREAK);
// Donated blocks section
if (results.donatedPoints.get() > 0) {
reportLines.add("Donated blocks (permanent contributions):");
reportLines.add("Total donated points = " + String.format("%,d", results.donatedPoints.get()));
Map<String, Integer> donatedBlocks = addon.getManager().getDonatedBlocks(island);
donatedBlocks.entrySet().stream()
.sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
.forEach(entry -> {
Integer value = addon.getBlockConfig().getBlockValues().getOrDefault(entry.getKey().toLowerCase(java.util.Locale.ENGLISH), 0);
long totalValue = (long) value * entry.getValue();
reportLines.add(" " + Util.prettifyText(entry.getKey()) + " x "
+ String.format("%,d", entry.getValue())
+ " = " + String.format("%,d", totalValue) + " points");
});
reportLines.add(LINE_BREAK);
}
return reportLines;
}

Expand Down Expand Up @@ -701,6 +717,11 @@ public void tidyUp() {
results.rawBlockCount
.addAndGet((long) (results.underWaterBlockCount.get() * addon.getSettings().getUnderWaterMultiplier()));

// Add donated block points (permanent contributions that persist across recalculations)
long donatedPoints = addon.getManager().getDonatedPoints(island);
results.rawBlockCount.addAndGet(donatedPoints);
results.donatedPoints.set(donatedPoints);

// Set the death penalty
if (this.addon.getSettings().isSumTeamDeaths()) {
for (UUID uuid : this.island.getMemberSet()) {
Expand Down
Loading
Loading