Skip to content

Commit a972a2f

Browse files
committed
Added support for Oraxen blocks and items in chests #385
1 parent db892a2 commit a972a2f

File tree

3 files changed

+117
-96
lines changed

3 files changed

+117
-96
lines changed

src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java

Lines changed: 107 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import org.bukkit.ChunkSnapshot;
2323
import org.bukkit.Location;
2424
import org.bukkit.Material;
25-
import org.bukkit.Tag;
2625
import org.bukkit.World;
2726
import org.bukkit.World.Environment;
2827
import org.bukkit.block.Block;
@@ -277,7 +276,7 @@ private List<String> getReport() {
277276
Integer limit = addon.getBlockConfig().getLimit(type.getElement());
278277
String explain = ")";
279278
reportLines.add(Util.prettifyText(type.toString()) + ": " + String.format("%,d", type.getCount())
280-
+ " blocks (max " + limit + explain);
279+
+ " blocks (max " + limit + explain);
281280
}
282281
reportLines.add(LINE_BREAK);
283282
return reportLines;
@@ -369,14 +368,12 @@ private int limitCountAndValue(Object obj) {
369368
if (!(obj instanceof Material) && !(obj instanceof EntityType) && !(obj instanceof String)) {
370369
return 0;
371370
}
372-
371+
// Get the limit of any particular material or entity type
373372
Integer limit = addon.getBlockConfig().getLimit(obj);
374373
if (limit == null) {
375374
return getValue(obj);
376375
}
377-
378376
int count = limitCount.getOrDefault(obj, 0);
379-
380377
if (count > limit) {
381378
// Add block to ofCount
382379
this.results.ofCount.add(obj);
@@ -413,6 +410,19 @@ private void scanChests(Chunk chunk) {
413410
}
414411

415412
private void countItemStack(ItemStack i) {
413+
// Check Oraxen
414+
if (BentoBox.getInstance().getHooks().getHook("Oraxen").isPresent() && OraxenHook.exists(i)) {
415+
String id = OraxenHook.getIdByItem(i);
416+
if (id == null) {
417+
return;
418+
}
419+
id = "oraxen:" + id;
420+
for (int c = 0; c < i.getAmount(); c++) {
421+
checkBlock(id, false);
422+
}
423+
return;
424+
}
425+
416426
if (i == null || !i.getType().isBlock())
417427
return;
418428

@@ -463,18 +473,21 @@ record ChunkPair(World world, Chunk chunk, ChunkSnapshot chunkSnapshot) {
463473
}
464474

465475
private void scanAsync(ChunkPair cp) {
466-
int chunkX = cp.chunkSnapshot.getX() * 16;
467-
int chunkZ = cp.chunkSnapshot.getZ() * 16;
476+
// Get the chunk coordinates and island boundaries once per chunk scan
477+
int chunkX = cp.chunk.getX() << 4;
478+
int chunkZ = cp.chunk.getZ() << 4;
468479
int minX = island.getMinProtectedX();
469-
int maxX = minX + island.getProtectionRange() * 2;
480+
int maxX = island.getMaxProtectedX();
470481
int minZ = island.getMinProtectedZ();
471-
int maxZ = minZ + island.getProtectionRange() * 2;
482+
int maxZ = island.getMaxProtectedZ();
472483

473484
for (int x = 0; x < 16; x++) {
474485
int globalX = chunkX + x;
486+
// Check if the block is within the island's X-boundary
475487
if (globalX >= minX && globalX < maxX) {
476488
for (int z = 0; z < 16; z++) {
477489
int globalZ = chunkZ + z;
490+
// Check if the block is within the island's Z-boundary
478491
if (globalZ >= minZ && globalZ < maxZ) {
479492
for (int y = cp.world.getMinHeight(); y < cp.world.getMaxHeight(); y++) {
480493
processBlock(cp, x, y, z, globalX, globalZ);
@@ -485,105 +498,104 @@ private void scanAsync(ChunkPair cp) {
485498
}
486499
}
487500

501+
/**
502+
* Processes a single block from a chunk snapshot to calculate its contribution to the island's level.
503+
* This method is designed to be efficient by minimizing object creation and using direct checks.
504+
*
505+
* @param cp The ChunkPair containing the world, chunk, and snapshot.
506+
* @param x The block's X coordinate within the chunk (0-15).
507+
* @param y The block's Y coordinate.
508+
* @param z The block's Z coordinate within the chunk (0-15).
509+
* @param globalX The block's global X coordinate in the world.
510+
* @param globalZ The block's global Z coordinate in the world.
511+
*/
488512
private void processBlock(ChunkPair cp, int x, int y, int z, int globalX, int globalZ) {
489513
BlockData blockData = cp.chunkSnapshot.getBlockData(x, y, z);
490514
Material m = blockData.getMaterial();
491515

516+
// Determine if the block is below sea level for potential score multipliers.
492517
boolean belowSeaLevel = seaHeight > 0 && y <= seaHeight;
493-
Location loc = new Location(cp.world, globalX, y, globalZ);
494-
495-
String customRegionId = addon.isItemsAdder() ? ItemsAdderHook.getInCustomRegion(loc) : null;
496-
// Try Oraxen
497-
if (customRegionId == null && BentoBox.getInstance().getHooks().getHook("ItemsAdder").isPresent()) {
498-
customRegionId = OraxenHook.getOraxenBlockID(loc);
499-
if (customRegionId != null) {
500-
BentoBox.getInstance().logDebug(customRegionId);
518+
// Create a Location object only when needed for more complex checks.
519+
Location loc = null;
520+
521+
// === Custom Block Hooks (ItemsAdder, Oraxen) ===
522+
// These hooks can define custom blocks that override vanilla behavior.
523+
// They must be checked first.
524+
if (addon.isItemsAdder() || BentoBox.getInstance().getHooks().getHook("Oraxen").isPresent()) {
525+
loc = new Location(cp.world, globalX, y, globalZ);
526+
String customBlockId = null;
527+
if (addon.isItemsAdder()) {
528+
customBlockId = ItemsAdderHook.getInCustomRegion(loc);
501529
}
502-
}
503-
if (customRegionId != null) {
504-
checkBlock(customRegionId, belowSeaLevel);
505-
return;
506-
}
507-
508-
processSlabs(blockData, m, belowSeaLevel);
509-
processStackers(loc, m);
510-
processUltimateStacker(m, loc, belowSeaLevel);
511-
processChests(cp, cp.chunkSnapshot.getBlockType(x, y, z));
512-
processSpawnerOrBlock(m, loc, belowSeaLevel);
513-
}
514-
515-
private void processSlabs(BlockData blockData, Material m, boolean belowSeaLevel) {
516-
if (Tag.SLABS.isTagged(m)) {
517-
Slab slab = (Slab) blockData;
518-
if (slab.getType().equals(Slab.Type.DOUBLE)) {
519-
checkBlock(m, belowSeaLevel);
530+
if (customBlockId == null && BentoBox.getInstance().getHooks().getHook("Oraxen").isPresent()) {
531+
String oraxenId = OraxenHook.getOraxenBlockID(loc);
532+
if (oraxenId != null) {
533+
customBlockId = "oraxen:" + oraxenId; // Make a namespaced ID
534+
}
520535
}
521-
}
522-
}
523-
524-
private void processStackers(Location loc, Material m) {
525-
if (addon.isStackersEnabled() && (m.equals(Material.CAULDRON) || m.equals(Material.SPAWNER))) {
526-
stackedBlocks.add(loc);
527-
}
528-
}
529536

530-
private void processUltimateStacker(Material m, Location loc, boolean belowSeaLevel) {
531-
if (addon.isUltimateStackerEnabled() && !m.isAir()) {
532-
UltimateStackerCalc.addStackers(m, loc, results, belowSeaLevel, limitCountAndValue(m));
537+
if (customBlockId != null) {
538+
// If a custom block is found, count it and stop further processing for this block.
539+
checkBlock(customBlockId, belowSeaLevel);
540+
return;
541+
}
533542
}
534-
}
535543

536-
private void processChests(ChunkPair cp, Material material) {
537-
if (addon.getSettings().isIncludeChests()) {
538-
switch (material) {
539-
case CHEST:
540-
case TRAPPED_CHEST:
541-
case BARREL:
542-
case HOPPER:
543-
case DISPENSER:
544-
case DROPPER:
545-
case SHULKER_BOX:
546-
case WHITE_SHULKER_BOX:
547-
case ORANGE_SHULKER_BOX:
548-
case MAGENTA_SHULKER_BOX:
549-
case LIGHT_BLUE_SHULKER_BOX:
550-
case YELLOW_SHULKER_BOX:
551-
case LIME_SHULKER_BOX:
552-
case PINK_SHULKER_BOX:
553-
case GRAY_SHULKER_BOX:
554-
case LIGHT_GRAY_SHULKER_BOX:
555-
case CYAN_SHULKER_BOX:
556-
case PURPLE_SHULKER_BOX:
557-
case BLUE_SHULKER_BOX:
558-
case BROWN_SHULKER_BOX:
559-
case GREEN_SHULKER_BOX:
560-
case RED_SHULKER_BOX:
561-
case BLACK_SHULKER_BOX:
562-
case BREWING_STAND:
563-
case FURNACE:
564-
case BLAST_FURNACE:
565-
case SMOKER:
566-
case BEACON: // has an inventory slot
567-
case ENCHANTING_TABLE: // technically has an item slot
568-
case LECTERN: // stores a book
569-
case JUKEBOX: // stores a record
570-
// ✅ It's a container
544+
// === Spawner Handling ===
545+
// Spawners are a special case. Their type needs to be read from the block state,
546+
// which requires main thread access. We defer this by adding them to a map to process later.
547+
if (m == Material.SPAWNER) {
548+
if (loc == null) loc = new Location(cp.world, globalX, y, globalZ);
549+
spawners.put(loc, belowSeaLevel);
550+
// Spawners are also counted as regular blocks, so we continue processing.
551+
}
552+
553+
// === Container Block Identification ===
554+
// If chest counting is enabled, we identify blocks that can hold items.
555+
// We add the chunk to a set for later scanning, avoiding duplicate chunk processing.
556+
if (addon.getSettings().isIncludeChests() && m.isInteractable()) {
557+
switch (m) {
558+
case CHEST, TRAPPED_CHEST, BARREL, HOPPER, DISPENSER, DROPPER,
559+
SHULKER_BOX, WHITE_SHULKER_BOX, ORANGE_SHULKER_BOX, MAGENTA_SHULKER_BOX,
560+
LIGHT_BLUE_SHULKER_BOX, YELLOW_SHULKER_BOX, LIME_SHULKER_BOX, PINK_SHULKER_BOX,
561+
GRAY_SHULKER_BOX, LIGHT_GRAY_SHULKER_BOX, CYAN_SHULKER_BOX, PURPLE_SHULKER_BOX,
562+
BLUE_SHULKER_BOX, BROWN_SHULKER_BOX, GREEN_SHULKER_BOX, RED_SHULKER_BOX,
563+
BLACK_SHULKER_BOX,
564+
BREWING_STAND, FURNACE, BLAST_FURNACE, SMOKER,
565+
BEACON, ENCHANTING_TABLE, LECTERN, JUKEBOX:
571566
chestBlocks.add(cp.chunk);
572-
break;
567+
break;
573568
default:
574-
// Not a container
569+
// Not a container of interest.
575570
break;
576571
}
572+
}
577573

574+
// === Stacked Block Hooks (WildStacker, RoseStacker, etc.) ===
575+
// These plugins stack blocks like spawners or cauldrons. We identify potential
576+
// stacked blocks and add their locations for later, more detailed checks.
577+
if (addon.isStackersEnabled() && (m == Material.CAULDRON || m == Material.SPAWNER)) {
578+
if (loc == null) loc = new Location(cp.world, globalX, y, globalZ);
579+
stackedBlocks.add(loc);
580+
}
581+
if (addon.isUltimateStackerEnabled()) {
582+
if (loc == null) loc = new Location(cp.world, globalX, y, globalZ);
583+
UltimateStackerCalc.addStackers(m, loc, results, belowSeaLevel, limitCountAndValue(m));
578584
}
579-
}
580585

581-
private void processSpawnerOrBlock(Material m, Location loc, boolean belowSeaLevel) {
582-
if (m == Material.SPAWNER) {
583-
spawners.put(loc, belowSeaLevel);
584-
} else {
586+
// === Slab Handling ===
587+
// Double slabs are counted as a single, full block. Single slabs are counted
588+
// as regular blocks. This logic prevents double-counting.
589+
if (blockData instanceof Slab slab && slab.getType() == Slab.Type.DOUBLE) {
590+
// This is a full block, so count it and we are done with it.
585591
checkBlock(m, belowSeaLevel);
592+
return;
586593
}
594+
595+
// === Default Block Value Calculation ===
596+
// If the block is not a special case handled above (like a double slab),
597+
// count it as a regular block. This includes single slabs.
598+
checkBlock(m, belowSeaLevel);
587599
}
588600

589601
/**
@@ -636,13 +648,13 @@ private Collection<String> sortedReport(int total, Multiset<Object> uwCount) {
636648
name = Util.prettifyText(et.name() + BlockConfig.SPAWNER);
637649
value = Objects.requireNonNullElse(addon.getBlockConfig().getValue(island.getWorld(), et), 0);
638650
} else if (en.getElement() instanceof String str) {
639-
name = str;
651+
name = Util.prettifyText(str);
640652
value = Objects.requireNonNullElse(addon.getBlockConfig().getValue(island.getWorld(), str), 0);
641653
}
642-
654+
int limit = addon.getBlockConfig().getLimit(en.getElement()) == null ? en.getCount() : Math.min(en.getCount(), addon.getBlockConfig().getLimit(en.getElement()));
643655
result.add(name + ": " + String.format("%,d", en.getCount()) + " blocks x " + value + " = "
644-
+ (value * en.getCount()));
645-
total += (value * en.getCount());
656+
+ (value * limit));
657+
total += (value * limit);
646658

647659
}
648660
result.add("Subtotal = " + total);
@@ -656,7 +668,7 @@ private Collection<String> sortedReport(int total, Multiset<Object> uwCount) {
656668
public void tidyUp() {
657669
// Finalize calculations
658670
results.rawBlockCount
659-
.addAndGet((long) (results.underWaterBlockCount.get() * addon.getSettings().getUnderWaterMultiplier()));
671+
.addAndGet((long) (results.underWaterBlockCount.get() * addon.getSettings().getUnderWaterMultiplier()));
660672

661673
// Set the death penalty
662674
if (this.addon.getSettings().isSumTeamDeaths()) {
@@ -729,7 +741,7 @@ public void scanIsland(Pipeliner pipeliner) {
729741
// Chunk finished
730742
// This was the last chunk. Handle stacked blocks, spawners, chests and exit
731743
handleStackedBlocks().thenCompose(v -> handleSpawners()).thenCompose(v -> handleChests())
732-
.thenRun(() -> {
744+
.thenRun(() -> {
733745
this.tidyUp();
734746
this.getR().complete(getResults());
735747
});

src/main/java/world/bentobox/level/config/BlockConfig.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919
import org.bukkit.configuration.file.YamlConfiguration;
2020
import org.bukkit.entity.EntityType;
2121

22+
import world.bentobox.bentobox.BentoBox;
2223
import world.bentobox.bentobox.hooks.ItemsAdderHook;
24+
import world.bentobox.bentobox.hooks.OraxenHook;
2325
import world.bentobox.level.Level;
2426

2527
/**
@@ -100,6 +102,10 @@ private void addMissing(ConfigurationSection blocks) {
100102

101103
private boolean isOther(String key) {
102104
// Maybe a custom name space
105+
if (key.startsWith("oraxen:") && BentoBox.getInstance().getHooks().getHook("Oraxen").isPresent()) {
106+
return OraxenHook.exists(key.substring(7));
107+
}
108+
// Check ItemsAdder
103109
return addon.isItemsAdder() && ItemsAdderHook.isInRegistry(key);
104110
}
105111

@@ -234,7 +240,7 @@ public Integer getValue(World world, Object obj) {
234240

235241
/**
236242
* Return true if the block should be hidden
237-
* @param m block material or entity type of spanwer
243+
* @param m block material or entity type of spawner
238244
* @return true if hidden
239245
*/
240246
public boolean isHiddenBlock(Object obj) {

src/main/resources/blockconfig.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
# iasurvival:modern_stone: 100
88
# The key for a block can be found by looking at it in-game and using /iablock
99
#
10+
# For Oraxen, prefix the block name with oraxen:, for example oraxen:amethyst_ore
11+
#
1012
# The limits section lists the maximum number for any particular block. Blocks over this amount
1113
# are not counted.
1214
limits:
@@ -18,6 +20,7 @@ hidden-blocks:
1820
- air
1921
blocks:
2022
#iasurvival:modern_stone: 1
23+
#oraxen:amethyst_ore: 3
2124
acacia_button: 1
2225
acacia_door: 2
2326
acacia_fence: 2

0 commit comments

Comments
 (0)