Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
c7831cb
Merge pull request #362 from BentoBoxWorld/develop
tastybento Apr 2, 2025
ae7969d
Merge pull request #369 from BentoBoxWorld/develop
tastybento May 11, 2025
1b5933f
Merge pull request #375 from BentoBoxWorld/develop
tastybento Jun 4, 2025
4bf9574
Merge pull request #378 from BentoBoxWorld/develop
tastybento Jun 21, 2025
766f57f
Merge pull request #379 from BentoBoxWorld/develop
tastybento Jun 28, 2025
08cb0e7
Merge pull request #381 from BentoBoxWorld/develop
tastybento Aug 2, 2025
d4e7fce
Merge pull request #384 from BentoBoxWorld/develop
tastybento Aug 16, 2025
3faaff7
Merge pull request #388 from BentoBoxWorld/develop
tastybento Oct 5, 2025
045a634
Merge pull request #401 from BentoBoxWorld/develop
tastybento Feb 21, 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
b755519
Merge pull request #412 from BentoBoxWorld/feature/block-donation
tastybento Apr 12, 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/`
27 changes: 14 additions & 13 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 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
50 changes: 50 additions & 0 deletions src/main/java/world/bentobox/level/LevelsManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -542,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);
}
}

/**
* 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
18 changes: 18 additions & 0 deletions src/main/java/world/bentobox/level/calculators/Results.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ public enum Result {
* Total points before any death penalties
*/
AtomicLong totalPoints = new AtomicLong(0);
/**
* Points contributed via block donation (permanent)
*/
AtomicLong donatedPoints = new AtomicLong(0);
final Result state;

public Results(Result state) {
Expand Down Expand Up @@ -179,4 +183,18 @@ public void setInitialCount(Long count) {
this.initialCount.set(count);
}

/**
* @return the donated points
*/
public long getDonatedPoints() {
return donatedPoints.get();
}

/**
* @param points the donated points to set
*/
public void setDonatedPoints(long points) {
this.donatedPoints.set(points);
}

}
Loading
Loading