diff --git a/pom.xml b/pom.xml index 961b3e7..a2b10dd 100644 --- a/pom.xml +++ b/pom.xml @@ -171,6 +171,12 @@ Oraxen Repository https://repo.oraxen.com/releases + + + nexo + Nexo Repository + https://repo.nexomc.com/releases + @@ -277,6 +283,19 @@ 4.0.10 provided + + + com.nexomc + nexo + 1.19.1 + + + dev.triumphteam + triumph-gui + + + provided + io.th0rgal diff --git a/src/main/java/world/bentobox/level/Level.java b/src/main/java/world/bentobox/level/Level.java index b9fe809..ae91392 100644 --- a/src/main/java/world/bentobox/level/Level.java +++ b/src/main/java/world/bentobox/level/Level.java @@ -488,4 +488,12 @@ public boolean isItemsAdder() { return !getSettings().isDisableItemsAdder() && getPlugin().getHooks().getHook("ItemsAdder").isPresent(); } + /** + * @return true if the Nexo plugin is enabled and not disabled in config + */ + public boolean isNexo() { + return !getSettings().getDisabledPluginHooks().contains("Nexo") + && Bukkit.getPluginManager().isPluginEnabled("Nexo"); + } + } diff --git a/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java b/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java index 59fbd2a..a7425eb 100644 --- a/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java +++ b/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java @@ -43,6 +43,10 @@ import com.google.common.collect.Multiset; import com.google.common.collect.Multiset.Entry; import com.google.common.collect.Multisets; +import com.nexomc.nexo.api.NexoBlocks; +import com.nexomc.nexo.api.NexoFurniture; +import com.nexomc.nexo.api.NexoItems; +import com.nexomc.nexo.mechanics.custom_block.CustomBlockMechanic; import dev.rosewood.rosestacker.api.RoseStackerAPI; import us.lynuxcraft.deadsilenceiv.advancedchests.AdvancedChestsAPI; @@ -426,7 +430,19 @@ private void countItemStack(ItemStack i) { } return; } - + // Check Nexo + if (addon.isNexo() && NexoItems.exists(i)) { + String id = NexoItems.idFromItem(i); + if (id == null) { + return; + } + id = "nexo:" + id; + for (int c = 0; c < i.getAmount(); c++) { + checkBlock(id, false); + } + return; + } + if (i == null || !i.getType().isBlock()) return; @@ -477,8 +493,8 @@ record ChunkPair(World world, Chunk chunk, ChunkSnapshot chunkSnapshot) { } private void scanAsync(ChunkPair cp) { - // Track chunks for Oraxen furniture entity scanning (done on main thread later) - if (BentoBox.getInstance().getHooks().getHook("Oraxen").isPresent()) { +// Track chunks for furniture entity scanning (Oraxen and Nexo are entity-based) + if (BentoBox.getInstance().getHooks().getHook("Oraxen").isPresent() || addon.isNexo()) { furnitureChunks.add(cp.chunk); } // Get the chunk coordinates and island boundaries once per chunk scan @@ -526,10 +542,11 @@ private void processBlock(ChunkPair cp, int x, int y, int z, int globalX, int gl // Create a Location object only when needed for more complex checks. Location loc = null; - // === Custom Block Hooks (ItemsAdder, Oraxen) === + // === Custom Block Hooks (ItemsAdder, Oraxen, Nexo) === // These hooks can define custom blocks that override vanilla behavior. // They must be checked first. - if (addon.isItemsAdder() || BentoBox.getInstance().getHooks().getHook("Oraxen").isPresent()) { + if (addon.isItemsAdder() || BentoBox.getInstance().getHooks().getHook("Oraxen").isPresent() + || addon.isNexo()) { loc = new Location(cp.world, globalX, y, globalZ); String customBlockId = null; if (addon.isItemsAdder()) { @@ -541,6 +558,12 @@ private void processBlock(ChunkPair cp, int x, int y, int z, int globalX, int gl customBlockId = "oraxen:" + oraxenId; // Make a namespaced ID } } + if (customBlockId == null && addon.isNexo()) { + CustomBlockMechanic nexoMechanic = NexoBlocks.customBlockMechanic(loc); + if (nexoMechanic != null) { + customBlockId = "nexo:" + nexoMechanic.getItemID(); + } + } if (customBlockId != null) { // If a custom block is found, count it and stop further processing for this block. @@ -750,6 +773,7 @@ public void scanIsland(Pipeliner pipeliner) { // This was the last chunk. Handle stacked blocks, spawners, chests and exit handleStackedBlocks().thenCompose(v -> handleSpawners()).thenCompose(v -> handleChests()) .thenCompose(v -> handleOraxenFurniture()) + .thenCompose(v -> handleNexoFurniture()) .thenRun(() -> { this.tidyUp(); this.getR().complete(getResults()); @@ -822,7 +846,7 @@ private CompletableFuture handleOraxenFurniture() { || loc.getBlockZ() < minZ || loc.getBlockZ() >= maxZ) { continue; } - FurnitureMechanic mechanic = OraxenHook.getFurnitureMechanic(entity); + var mechanic = OraxenHook.getFurnitureMechanic(entity); if (mechanic == null) { continue; } @@ -835,6 +859,47 @@ private CompletableFuture handleOraxenFurniture() { return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); } + /** + * Scans entities in each island chunk for Nexo furniture and counts them toward the island level. + * Nexo furniture is entity-based (ItemDisplay entities), so it is invisible to the normal block + * scanner. Only entities for which a FurnitureMechanic can be resolved are counted, which + * naturally filters to base furniture entities. + * + * @return a CompletableFuture that completes when all chunks have been checked + */ + private CompletableFuture handleNexoFurniture() { + if (!addon.isNexo() || furnitureChunks.isEmpty()) { + return CompletableFuture.completedFuture(null); + } + int minX = island.getMinProtectedX(); + int maxX = island.getMaxProtectedX(); + int minZ = island.getMinProtectedZ(); + int maxZ = island.getMaxProtectedZ(); + List> futures = new ArrayList<>(); + for (Chunk chunk : furnitureChunks) { + CompletableFuture future = Util.getChunkAtAsync(chunk.getWorld(), chunk.getX(), chunk.getZ()) + .thenAccept(c -> { + for (Entity entity : c.getEntities()) { + Location loc = entity.getLocation(); + // Confirm entity is within the island's protected bounds + if (loc.getBlockX() < minX || loc.getBlockX() >= maxX + || loc.getBlockZ() < minZ || loc.getBlockZ() >= maxZ) { + continue; + } + // getFurnitureMechanic returns non-null only for furniture base entities + var mechanic = NexoFurniture.furnitureMechanic(entity); + if (mechanic == null) { + continue; + } + boolean belowSeaLevel = seaHeight > 0 && loc.getBlockY() <= seaHeight; + checkBlock("nexo:" + mechanic.getItemID(), belowSeaLevel); + } + }); + futures.add(future); + } + return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); + } + private CompletableFuture handleStackedBlocks() { // Deal with any stacked blocks List> futures = new ArrayList<>(); diff --git a/src/main/java/world/bentobox/level/config/BlockConfig.java b/src/main/java/world/bentobox/level/config/BlockConfig.java index b554264..2c9793a 100644 --- a/src/main/java/world/bentobox/level/config/BlockConfig.java +++ b/src/main/java/world/bentobox/level/config/BlockConfig.java @@ -19,6 +19,8 @@ import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.EntityType; +import com.nexomc.nexo.api.NexoItems; + import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.hooks.ItemsAdderHook; import world.bentobox.bentobox.hooks.OraxenHook; @@ -105,6 +107,9 @@ private boolean isOther(String key) { if (key.startsWith("oraxen:") && BentoBox.getInstance().getHooks().getHook("Oraxen").isPresent()) { return OraxenHook.exists(key.substring(7)); } + if (key.startsWith("nexo:") && addon.isNexo()) { + return NexoItems.exists(key.substring(5)); + } // Check ItemsAdder return addon.isItemsAdder() && ItemsAdderHook.isInRegistry(key); } diff --git a/src/main/java/world/bentobox/level/util/Utils.java b/src/main/java/world/bentobox/level/util/Utils.java index dff4a02..5c23c0b 100644 --- a/src/main/java/world/bentobox/level/util/Utils.java +++ b/src/main/java/world/bentobox/level/util/Utils.java @@ -161,6 +161,8 @@ public static String prettifyObject(Object object, User user) { // Remove prefix if (key.startsWith("oraxen:")) { key = key.substring(7); + } else if (key.startsWith("nexo:")) { + key = key.substring(5); } }