diff --git a/pom.xml b/pom.xml
index 73b620d..0c0aefa 100644
--- a/pom.xml
+++ b/pom.xml
@@ -55,7 +55,7 @@
2.0.9
1.21.3-R0.1-SNAPSHOT
- 3.2.4-SNAPSHOT
+ 3.2.5-SNAPSHOT
1.12.0
@@ -67,7 +67,7 @@
-LOCAL
- 2.18.0
+ 2.19.0
BentoBoxWorld_Level
bentobox-world
https://sonarcloud.io
@@ -158,6 +158,11 @@
songoda-plugins
https://repo.songoda.com/repository/minecraft-plugins/
+
+
+ matteodev
+ https://maven.devs.beer/
+
@@ -245,6 +250,13 @@
1.0.0-20240329.173606-35
provided
+
+
+ dev.lone
+ api-itemsadder
+ 4.0.2-beta-release-11
+ provided
+
diff --git a/src/main/java/world/bentobox/level/Level.java b/src/main/java/world/bentobox/level/Level.java
index 5e66c27..d1b1d36 100644
--- a/src/main/java/world/bentobox/level/Level.java
+++ b/src/main/java/world/bentobox/level/Level.java
@@ -98,79 +98,98 @@ private boolean loadSettings() {
@Override
public void onEnable() {
- loadBlockSettings();
- // Start pipeline
- pipeliner = new Pipeliner(this);
- // Start Manager
- manager = new LevelsManager(this);
- // Register listeners
- this.registerListener(new IslandActivitiesListeners(this));
- this.registerListener(new JoinLeaveListener(this));
- this.registerListener(new MigrationListener(this));
-
- // Register commands for GameModes
- registeredGameModes.clear();
- getPlugin().getAddonsManager().getGameModeAddons().stream()
- .filter(gm -> !settings.getGameModes().contains(gm.getDescription().getName())).forEach(gm -> {
- log("Level hooking into " + gm.getDescription().getName());
- registerCommands(gm);
- new PlaceholderManager(this).registerPlaceholders(gm);
- registeredGameModes.add(gm);
- });
- // Register request handlers
- registerRequestHandler(new LevelRequestHandler(this));
- registerRequestHandler(new TopTenRequestHandler(this));
-
- // Check if WildStackers is enabled on the server
- // I only added support for counting blocks into the island level
- // Someone else can PR if they want spawners added to the Leveling system :)
- if (!settings.getDisabledPluginHooks().contains("WildStacker")) {
- stackersEnabled = Bukkit.getPluginManager().isPluginEnabled("WildStacker");
- if (stackersEnabled) {
- log("Hooked into WildStackers.");
- }
- }
-
- // Check if AdvancedChests is enabled on the server
- if (!settings.getDisabledPluginHooks().contains("AdvancedChests")) {
- Plugin advChest = Bukkit.getPluginManager().getPlugin("AdvancedChests");
- advChestEnabled = advChest != null;
- if (advChestEnabled) {
- // Check version
- if (compareVersions(advChest.getDescription().getVersion(), "23.0") > 0) {
- log("Hooked into AdvancedChests.");
- } else {
- logError("Could not hook into AdvancedChests " + advChest.getDescription().getVersion()
- + " - requires version 23.0 or later");
- advChestEnabled = false;
- }
- }
- }
-
- // Check if RoseStackers is enabled
- if (!settings.getDisabledPluginHooks().contains("RoseStacker")) {
- roseStackersEnabled = Bukkit.getPluginManager().isPluginEnabled("RoseStacker");
- if (roseStackersEnabled) {
- log("Hooked into RoseStackers.");
- }
- }
-
- // Check if UltimateStacker is enabled
- if (!settings.getDisabledPluginHooks().contains("UltimateStacker")) {
- ultimateStackerEnabled = Bukkit.getPluginManager().isPluginEnabled("UltimateStacker");
- if (ultimateStackerEnabled) {
- log("Hooked into UltimateStacker.");
- }
- }
- }
-
- @Override
- public void allLoaded() {
- super.allLoaded();
-
- if (this.isEnabled()) {
- this.hookExtensions();
- }
+ // Everything waits until allLoaded
+ }
+
+ @Override
+ public void allLoaded() {
+ super.allLoaded();
+ loadBlockSettings();
+ initializePipelineAndManager();
+ registerAllListeners();
+ registerGameModeCommands();
+ registerRequestHandlers();
+ hookPlugin("WildStacker", this::hookWildStackers);
+ hookAdvancedChests();
+ hookPlugin("RoseStacker", this::hookRoseStackers);
+ hookPlugin("UltimateStacker", this::hookUltimateStacker);
+
+ if (this.isEnabled()) {
+ hookExtensions();
+ }
+ }
+
+ private void initializePipelineAndManager() {
+ pipeliner = new Pipeliner(this);
+ manager = new LevelsManager(this);
+ }
+
+ private void registerAllListeners() {
+ registerListener(new IslandActivitiesListeners(this));
+ registerListener(new JoinLeaveListener(this));
+ registerListener(new MigrationListener(this));
+ }
+
+ private void registerGameModeCommands() {
+ registeredGameModes.clear();
+ getPlugin().getAddonsManager().getGameModeAddons().stream()
+ .filter(gm -> !settings.getGameModes().contains(gm.getDescription().getName())).forEach(gm -> {
+ log("Level hooking into " + gm.getDescription().getName());
+ registerCommands(gm);
+ new PlaceholderManager(this).registerPlaceholders(gm);
+ registeredGameModes.add(gm);
+ });
+ }
+
+ private void registerRequestHandlers() {
+ registerRequestHandler(new LevelRequestHandler(this));
+ registerRequestHandler(new TopTenRequestHandler(this));
+ }
+
+ /**
+ * A helper that only executes the provided hookAction if the plugin is not disabled.
+ */
+ private void hookPlugin(String pluginName, Runnable hookAction) {
+ if (!settings.getDisabledPluginHooks().contains(pluginName)) {
+ hookAction.run();
+ }
+ }
+
+ private void hookWildStackers() {
+ stackersEnabled = Bukkit.getPluginManager().isPluginEnabled("WildStacker");
+ if (stackersEnabled) {
+ log("Hooked into WildStackers.");
+ }
+ }
+
+ private void hookAdvancedChests() {
+ if (!settings.getDisabledPluginHooks().contains("AdvancedChests")) {
+ Plugin advChest = Bukkit.getPluginManager().getPlugin("AdvancedChests");
+ advChestEnabled = advChest != null;
+ if (advChestEnabled) {
+ if (compareVersions(advChest.getDescription().getVersion(), "23.0") > 0) {
+ log("Hooked into AdvancedChests.");
+ } else {
+ logError("Could not hook into AdvancedChests " + advChest.getDescription().getVersion()
+ + " - requires version 23.0 or later");
+ advChestEnabled = false;
+ }
+ }
+ }
+ }
+
+ private void hookRoseStackers() {
+ roseStackersEnabled = Bukkit.getPluginManager().isPluginEnabled("RoseStacker");
+ if (roseStackersEnabled) {
+ log("Hooked into RoseStackers.");
+ }
+ }
+
+ private void hookUltimateStacker() {
+ ultimateStackerEnabled = Bukkit.getPluginManager().isPluginEnabled("UltimateStacker");
+ if (ultimateStackerEnabled) {
+ log("Hooked into UltimateStacker.");
+ }
}
/**
@@ -448,4 +467,8 @@ public Warp getWarpHook() {
return this.warpHook;
}
+ public boolean isItemsAdder() {
+ return getPlugin().getHooks().getHook("ItemsAdder").isPresent();
+ }
+
}
diff --git a/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java b/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java
index bcaf620..9188594 100644
--- a/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java
+++ b/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java
@@ -48,10 +48,12 @@
import us.lynuxcraft.deadsilenceiv.advancedchests.chest.gui.page.ChestPage;
import world.bentobox.bentobox.BentoBox;
import world.bentobox.bentobox.database.objects.Island;
+import world.bentobox.bentobox.hooks.ItemsAdderHook;
import world.bentobox.bentobox.util.Pair;
import world.bentobox.bentobox.util.Util;
import world.bentobox.level.Level;
import world.bentobox.level.calculators.Results.Result;
+import world.bentobox.level.config.BlockConfig;
public class IslandLevelCalculator {
private static final String LINE_BREAK = "==================================";
@@ -133,6 +135,24 @@ private long calculateLevel(long blockAndDeathPoints) {
}
}
+ /**
+ * Adds value to the results based on the namespacedId and whether the block is
+ * below sea level or not
+ *
+ * @param namespacedId - namespacedId of the block
+ * @param belowSeaLevel - true if below sea level
+ */
+ private void checkBlock(String namespacedId, boolean belowSeaLevel) {
+ int count = limitCountAndValue(namespacedId);
+ if (belowSeaLevel) {
+ results.underWaterBlockCount.addAndGet(count);
+ results.uwCount.add(namespacedId);
+ } else {
+ results.rawBlockCount.addAndGet(count);
+ results.mdCount.add(namespacedId);
+ }
+ }
+
/**
* Adds value to the results based on the material and whether the block is
* below sea level or not
@@ -141,7 +161,7 @@ private long calculateLevel(long blockAndDeathPoints) {
* @param belowSeaLevel - true if below sea level
*/
private void checkBlock(Material mat, boolean belowSeaLevel) {
- int count = limitCount(mat);
+ int count = limitCountAndValue(mat);
if (belowSeaLevel) {
results.underWaterBlockCount.addAndGet(count);
results.uwCount.add(mat);
@@ -159,7 +179,7 @@ private void checkBlock(Material mat, boolean belowSeaLevel) {
* @param belowSeaLevel - true if below sea level
*/
private void checkSpawner(EntityType et, boolean belowSeaLevel) {
- Integer count = limitCount(et);
+ Integer count = limitCountAndValue(et);
if (count != null) {
if (belowSeaLevel) {
results.underWaterBlockCount.addAndGet(count);
@@ -263,14 +283,14 @@ public Results getResults() {
/**
* Get value of a material World blocks trump regular block values
*
- * @param md - Material or EntityType to check
+ * @param obj - Material, EntityType, or NamespacedId to check
* @return value
*/
- private int getValue(Object md) {
- Integer value = addon.getBlockConfig().getValue(island.getWorld(), md);
+ private int getValue(Object obj) {
+ Integer value = addon.getBlockConfig().getValue(island.getWorld(), obj);
if (value == null) {
// Not in config
- results.ncCount.add(md);
+ results.ncCount.add(obj);
return 0;
}
return value;
@@ -334,19 +354,21 @@ private void roseStackerCheck(Chunk chunk) {
* @param obj A Material or EntityType
* @return The object's value if within limit, otherwise 0.
*/
- private int limitCount(Object obj) {
+ private int limitCountAndValue(Object obj) {
// Only process if obj is a Material or EntityType
- if (!(obj instanceof Material) && !(obj instanceof EntityType))
+ if (!(obj instanceof Material) && !(obj instanceof EntityType) && !(obj instanceof String)) {
return 0;
+ }
Integer limit = addon.getBlockConfig().getLimit(obj);
- if (limit == null)
+ if (limit == null) {
return getValue(obj);
+ }
int count = limitCount.getOrDefault(obj, 0);
- if (count > limit)
+ if (count > limit) {
return 0;
-
+ }
limitCount.put(obj, count + 1);
return getValue(obj);
}
@@ -427,68 +449,87 @@ private CompletableFuture scanChunk(List chunks) {
record ChunkPair(World world, Chunk chunk, ChunkSnapshot chunkSnapshot) {
}
- /**
- * Count the blocks on the island
- *
- * @param cp chunk to scan
- */
private void scanAsync(ChunkPair cp) {
+ int chunkX = cp.chunkSnapshot.getX() * 16;
+ int chunkZ = cp.chunkSnapshot.getZ() * 16;
+ int minX = island.getMinProtectedX();
+ int maxX = minX + island.getProtectionRange() * 2;
+ int minZ = island.getMinProtectedZ();
+ int maxZ = minZ + island.getProtectionRange() * 2;
+
for (int x = 0; x < 16; x++) {
- // Check if the block coordinate is inside the protection zone and if not, don't
- // count it
- if (cp.chunkSnapshot.getX() * 16 + x < island.getMinProtectedX() || cp.chunkSnapshot.getX() * 16
- + x >= island.getMinProtectedX() + island.getProtectionRange() * 2) {
- continue;
- }
- for (int z = 0; z < 16; z++) {
- // Check if the block coordinate is inside the protection zone and if not, don't
- // count it
- if (cp.chunkSnapshot.getZ() * 16 + z < island.getMinProtectedZ() || cp.chunkSnapshot.getZ() * 16
- + z >= island.getMinProtectedZ() + island.getProtectionRange() * 2) {
- continue;
- }
- // Only count to the highest block in the world for some optimization
- for (int y = cp.world.getMinHeight(); y < cp.world.getMaxHeight(); y++) {
- BlockData blockData = cp.chunkSnapshot.getBlockData(x, y, z);
- Material m = blockData.getMaterial();
- boolean belowSeaLevel = seaHeight > 0 && y <= seaHeight;
- Location loc = new Location(cp.world, (double) x + cp.chunkSnapshot.getX() * 16, y,
- (double) z + cp.chunkSnapshot.getZ() * 16);
- // Slabs can be doubled, so check them twice
- if (Tag.SLABS.isTagged(m)) {
- Slab slab = (Slab) blockData;
- if (slab.getType().equals(Slab.Type.DOUBLE)) {
- checkBlock(m, belowSeaLevel);
+ int globalX = chunkX + x;
+ if (globalX >= minX && globalX < maxX) {
+ for (int z = 0; z < 16; z++) {
+ int globalZ = chunkZ + z;
+ if (globalZ >= minZ && globalZ < maxZ) {
+ for (int y = cp.world.getMinHeight(); y < cp.world.getMaxHeight(); y++) {
+ processBlock(cp, x, y, z, globalX, globalZ);
}
}
- // Hook for Wild Stackers (Blocks and Spawners Only) - this has to use the real
- // chunk
- if (addon.isStackersEnabled() && (m.equals(Material.CAULDRON) || m.equals(Material.SPAWNER))) {
- stackedBlocks.add(loc);
- }
+ }
+ }
+ }
+ }
- if (addon.isUltimateStackerEnabled() && !m.isAir()) {
- UltimateStackerCalc.addStackers(m, loc, results, belowSeaLevel, limitCount(m));
- }
+ private void processBlock(ChunkPair cp, int x, int y, int z, int globalX, int globalZ) {
+ BlockData blockData = cp.chunkSnapshot.getBlockData(x, y, z);
+ Material m = blockData.getMaterial();
+ if (m.isAir()) {
+ return;
+ }
- // Scan chests
- if (addon.getSettings().isIncludeChests() && blockData instanceof Container) {
- chestBlocks.add(cp.chunk);
- }
+ boolean belowSeaLevel = seaHeight > 0 && y <= seaHeight;
+ Location loc = new Location(cp.world, globalX, y, globalZ);
- // Spawners
- if (m == Material.SPAWNER) {
- // Stash the spawner because the type cannot be obtained from the chunk snapshot
- this.spawners.put(loc, belowSeaLevel);
- } else {
- // Add the value of the block's material
- checkBlock(m, belowSeaLevel);
- }
- }
+ String customRegionId = addon.isItemsAdder() ? ItemsAdderHook.getInCustomRegion(loc) : null;
+ if (customRegionId != null) {
+ checkBlock(customRegionId, belowSeaLevel);
+ return;
+ }
+
+ processSlabs(blockData, m, belowSeaLevel);
+ processStackers(loc, m);
+ processUltimateStacker(m, loc, belowSeaLevel);
+ processChests(cp, blockData);
+ processSpawnerOrBlock(m, loc, belowSeaLevel);
+ }
+
+ private void processSlabs(BlockData blockData, Material m, boolean belowSeaLevel) {
+ if (Tag.SLABS.isTagged(m)) {
+ Slab slab = (Slab) blockData;
+ if (slab.getType().equals(Slab.Type.DOUBLE)) {
+ checkBlock(m, belowSeaLevel);
}
}
}
+ private void processStackers(Location loc, Material m) {
+ if (addon.isStackersEnabled() && (m.equals(Material.CAULDRON) || m.equals(Material.SPAWNER))) {
+ stackedBlocks.add(loc);
+ }
+ }
+
+ private void processUltimateStacker(Material m, Location loc, boolean belowSeaLevel) {
+ if (addon.isUltimateStackerEnabled() && !m.isAir()) {
+ UltimateStackerCalc.addStackers(m, loc, results, belowSeaLevel, limitCountAndValue(m));
+ }
+ }
+
+ private void processChests(ChunkPair cp, BlockData blockData) {
+ if (addon.getSettings().isIncludeChests() && blockData instanceof Container) {
+ chestBlocks.add(cp.chunk);
+ }
+ }
+
+ private void processSpawnerOrBlock(Material m, Location loc, boolean belowSeaLevel) {
+ if (m == Material.SPAWNER) {
+ spawners.put(loc, belowSeaLevel);
+ } else {
+ checkBlock(m, belowSeaLevel);
+ }
+ }
+
/**
* Scan the next chunk on the island
*
@@ -536,11 +577,14 @@ private Collection sortedReport(int total, Multiset