diff --git a/pom.xml b/pom.xml index 21cc282..ee3d307 100644 --- a/pom.xml +++ b/pom.xml @@ -55,7 +55,7 @@ 2.0.9 1.21.5-R0.1-SNAPSHOT - 3.4.0 + 3.7.4 1.12.0 @@ -67,7 +67,7 @@ -LOCAL - 2.21.5 + 2.22.0 BentoBoxWorld_Level bentobox-world https://sonarcloud.io @@ -163,6 +163,12 @@ matteodev https://maven.devs.beer/ + + + oraxen + Oraxen Repository + https://repo.oraxen.com/releases + @@ -195,8 +201,7 @@ world.bentobox bentobox - ${bentobox.version} - provided + 3.7.4-SNAPSHOT world.bentobox @@ -257,6 +262,59 @@ 4.0.10 provided + + + io.th0rgal + oraxen + 1.193.1 + + + me.gabytm.util + actions-spigot + + + org.jetbrains + annotations + + + com.ticxo + PlayerAnimator + + + com.github.stefvanschie.inventoryframework + IF + + + io.th0rgal + protectionlib + + + dev.triumphteam + triumph-gui + + + org.bstats + bstats-bukkit + + + com.jeff-media + custom-block-data + + + com.jeff-media + persistent-data-serializer + + + com.jeff_media + MorePersistentDataTypes + + + gs.mclo + java + + + provided + diff --git a/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java b/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java index a8005bd..596f9ce 100644 --- a/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java +++ b/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java @@ -22,7 +22,6 @@ import org.bukkit.ChunkSnapshot; import org.bukkit.Location; import org.bukkit.Material; -import org.bukkit.Tag; import org.bukkit.World; import org.bukkit.World.Environment; import org.bukkit.block.Block; @@ -49,6 +48,7 @@ import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.database.objects.Island; import world.bentobox.bentobox.hooks.ItemsAdderHook; +import world.bentobox.bentobox.hooks.OraxenHook; import world.bentobox.bentobox.util.Pair; import world.bentobox.bentobox.util.Util; import world.bentobox.level.Level; @@ -276,7 +276,7 @@ private List getReport() { Integer limit = addon.getBlockConfig().getLimit(type.getElement()); String explain = ")"; reportLines.add(Util.prettifyText(type.toString()) + ": " + String.format("%,d", type.getCount()) - + " blocks (max " + limit + explain); + + " blocks (max " + limit + explain); } reportLines.add(LINE_BREAK); return reportLines; @@ -368,14 +368,12 @@ private int limitCountAndValue(Object obj) { if (!(obj instanceof Material) && !(obj instanceof EntityType) && !(obj instanceof String)) { return 0; } - + // Get the limit of any particular material or entity type Integer limit = addon.getBlockConfig().getLimit(obj); if (limit == null) { return getValue(obj); } - int count = limitCount.getOrDefault(obj, 0); - if (count > limit) { // Add block to ofCount this.results.ofCount.add(obj); @@ -412,6 +410,19 @@ private void scanChests(Chunk chunk) { } private void countItemStack(ItemStack i) { + // Check Oraxen + if (BentoBox.getInstance().getHooks().getHook("Oraxen").isPresent() && OraxenHook.exists(i)) { + String id = OraxenHook.getIdByItem(i); + if (id == null) { + return; + } + id = "oraxen:" + id; + for (int c = 0; c < i.getAmount(); c++) { + checkBlock(id, false); + } + return; + } + if (i == null || !i.getType().isBlock()) return; @@ -462,18 +473,21 @@ record ChunkPair(World world, Chunk chunk, ChunkSnapshot chunkSnapshot) { } private void scanAsync(ChunkPair cp) { - int chunkX = cp.chunkSnapshot.getX() * 16; - int chunkZ = cp.chunkSnapshot.getZ() * 16; + // Get the chunk coordinates and island boundaries once per chunk scan + int chunkX = cp.chunk.getX() << 4; + int chunkZ = cp.chunk.getZ() << 4; int minX = island.getMinProtectedX(); - int maxX = minX + island.getProtectionRange() * 2; + int maxX = island.getMaxProtectedX(); int minZ = island.getMinProtectedZ(); - int maxZ = minZ + island.getProtectionRange() * 2; + int maxZ = island.getMaxProtectedZ(); for (int x = 0; x < 16; x++) { int globalX = chunkX + x; + // Check if the block is within the island's X-boundary if (globalX >= minX && globalX < maxX) { for (int z = 0; z < 16; z++) { int globalZ = chunkZ + z; + // Check if the block is within the island's Z-boundary if (globalZ >= minZ && globalZ < maxZ) { for (int y = cp.world.getMinHeight(); y < cp.world.getMaxHeight(); y++) { processBlock(cp, x, y, z, globalX, globalZ); @@ -484,98 +498,104 @@ private void scanAsync(ChunkPair cp) { } } + /** + * Processes a single block from a chunk snapshot to calculate its contribution to the island's level. + * This method is designed to be efficient by minimizing object creation and using direct checks. + * + * @param cp The ChunkPair containing the world, chunk, and snapshot. + * @param x The block's X coordinate within the chunk (0-15). + * @param y The block's Y coordinate. + * @param z The block's Z coordinate within the chunk (0-15). + * @param globalX The block's global X coordinate in the world. + * @param globalZ The block's global Z coordinate in the world. + */ 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(); + // Determine if the block is below sea level for potential score multipliers. boolean belowSeaLevel = seaHeight > 0 && y <= seaHeight; - Location loc = new Location(cp.world, globalX, y, globalZ); - - 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, cp.chunkSnapshot.getBlockType(x, y, z)); - 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); + // Create a Location object only when needed for more complex checks. + Location loc = null; + + // === Custom Block Hooks (ItemsAdder, Oraxen) === + // These hooks can define custom blocks that override vanilla behavior. + // They must be checked first. + if (addon.isItemsAdder() || BentoBox.getInstance().getHooks().getHook("Oraxen").isPresent()) { + loc = new Location(cp.world, globalX, y, globalZ); + String customBlockId = null; + if (addon.isItemsAdder()) { + customBlockId = ItemsAdderHook.getInCustomRegion(loc); + } + if (customBlockId == null && BentoBox.getInstance().getHooks().getHook("Oraxen").isPresent()) { + String oraxenId = OraxenHook.getOraxenBlockID(loc); + if (oraxenId != null) { + customBlockId = "oraxen:" + oraxenId; // Make a namespaced ID + } } - } - } - - 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)); + if (customBlockId != null) { + // If a custom block is found, count it and stop further processing for this block. + checkBlock(customBlockId, belowSeaLevel); + return; + } } - } - private void processChests(ChunkPair cp, Material material) { - if (addon.getSettings().isIncludeChests()) { - switch (material) { - case CHEST: - case TRAPPED_CHEST: - case BARREL: - case HOPPER: - case DISPENSER: - case DROPPER: - case SHULKER_BOX: - case WHITE_SHULKER_BOX: - case ORANGE_SHULKER_BOX: - case MAGENTA_SHULKER_BOX: - case LIGHT_BLUE_SHULKER_BOX: - case YELLOW_SHULKER_BOX: - case LIME_SHULKER_BOX: - case PINK_SHULKER_BOX: - case GRAY_SHULKER_BOX: - case LIGHT_GRAY_SHULKER_BOX: - case CYAN_SHULKER_BOX: - case PURPLE_SHULKER_BOX: - case BLUE_SHULKER_BOX: - case BROWN_SHULKER_BOX: - case GREEN_SHULKER_BOX: - case RED_SHULKER_BOX: - case BLACK_SHULKER_BOX: - case BREWING_STAND: - case FURNACE: - case BLAST_FURNACE: - case SMOKER: - case BEACON: // has an inventory slot - case ENCHANTING_TABLE: // technically has an item slot - case LECTERN: // stores a book - case JUKEBOX: // stores a record - // ✅ It's a container + // === Spawner Handling === + // Spawners are a special case. Their type needs to be read from the block state, + // which requires main thread access. We defer this by adding them to a map to process later. + if (m == Material.SPAWNER) { + if (loc == null) loc = new Location(cp.world, globalX, y, globalZ); + spawners.put(loc, belowSeaLevel); + // Spawners are also counted as regular blocks, so we continue processing. + } + + // === Container Block Identification === + // If chest counting is enabled, we identify blocks that can hold items. + // We add the chunk to a set for later scanning, avoiding duplicate chunk processing. + if (addon.getSettings().isIncludeChests() && m.isInteractable()) { + switch (m) { + case CHEST, TRAPPED_CHEST, BARREL, HOPPER, DISPENSER, DROPPER, + SHULKER_BOX, WHITE_SHULKER_BOX, ORANGE_SHULKER_BOX, MAGENTA_SHULKER_BOX, + LIGHT_BLUE_SHULKER_BOX, YELLOW_SHULKER_BOX, LIME_SHULKER_BOX, PINK_SHULKER_BOX, + GRAY_SHULKER_BOX, LIGHT_GRAY_SHULKER_BOX, CYAN_SHULKER_BOX, PURPLE_SHULKER_BOX, + BLUE_SHULKER_BOX, BROWN_SHULKER_BOX, GREEN_SHULKER_BOX, RED_SHULKER_BOX, + BLACK_SHULKER_BOX, + BREWING_STAND, FURNACE, BLAST_FURNACE, SMOKER, + BEACON, ENCHANTING_TABLE, LECTERN, JUKEBOX: chestBlocks.add(cp.chunk); - break; + break; default: - // ❌ Not a container + // Not a container of interest. break; } + } + // === Stacked Block Hooks (WildStacker, RoseStacker, etc.) === + // These plugins stack blocks like spawners or cauldrons. We identify potential + // stacked blocks and add their locations for later, more detailed checks. + if (addon.isStackersEnabled() && (m == Material.CAULDRON || m == Material.SPAWNER)) { + if (loc == null) loc = new Location(cp.world, globalX, y, globalZ); + stackedBlocks.add(loc); + } + if (addon.isUltimateStackerEnabled()) { + if (loc == null) loc = new Location(cp.world, globalX, y, globalZ); + UltimateStackerCalc.addStackers(m, loc, results, belowSeaLevel, limitCountAndValue(m)); } - } - private void processSpawnerOrBlock(Material m, Location loc, boolean belowSeaLevel) { - if (m == Material.SPAWNER) { - spawners.put(loc, belowSeaLevel); - } else { + // === Slab Handling === + // Double slabs are counted as a single, full block. Single slabs are counted + // as regular blocks. This logic prevents double-counting. + if (blockData instanceof Slab slab && slab.getType() == Slab.Type.DOUBLE) { + // This is a full block, so count it and we are done with it. checkBlock(m, belowSeaLevel); + return; } + + // === Default Block Value Calculation === + // If the block is not a special case handled above (like a double slab), + // count it as a regular block. This includes single slabs. + checkBlock(m, belowSeaLevel); } /** @@ -628,13 +648,13 @@ private Collection sortedReport(int total, Multiset uwCount) { name = Util.prettifyText(et.name() + BlockConfig.SPAWNER); value = Objects.requireNonNullElse(addon.getBlockConfig().getValue(island.getWorld(), et), 0); } else if (en.getElement() instanceof String str) { - name = str; + name = Util.prettifyText(str); value = Objects.requireNonNullElse(addon.getBlockConfig().getValue(island.getWorld(), str), 0); } - + int limit = addon.getBlockConfig().getLimit(en.getElement()) == null ? en.getCount() : Math.min(en.getCount(), addon.getBlockConfig().getLimit(en.getElement())); result.add(name + ": " + String.format("%,d", en.getCount()) + " blocks x " + value + " = " - + (value * en.getCount())); - total += (value * en.getCount()); + + (value * limit)); + total += (value * limit); } result.add("Subtotal = " + total); @@ -648,7 +668,7 @@ private Collection sortedReport(int total, Multiset uwCount) { public void tidyUp() { // Finalize calculations results.rawBlockCount - .addAndGet((long) (results.underWaterBlockCount.get() * addon.getSettings().getUnderWaterMultiplier())); + .addAndGet((long) (results.underWaterBlockCount.get() * addon.getSettings().getUnderWaterMultiplier())); // Set the death penalty if (this.addon.getSettings().isSumTeamDeaths()) { @@ -721,7 +741,7 @@ public void scanIsland(Pipeliner pipeliner) { // Chunk finished // This was the last chunk. Handle stacked blocks, spawners, chests and exit handleStackedBlocks().thenCompose(v -> handleSpawners()).thenCompose(v -> handleChests()) - .thenRun(() -> { + .thenRun(() -> { this.tidyUp(); this.getR().complete(getResults()); }); diff --git a/src/main/java/world/bentobox/level/commands/IslandValueCommand.java b/src/main/java/world/bentobox/level/commands/IslandValueCommand.java index 1b8e22a..c4011f7 100644 --- a/src/main/java/world/bentobox/level/commands/IslandValueCommand.java +++ b/src/main/java/world/bentobox/level/commands/IslandValueCommand.java @@ -11,10 +11,12 @@ import org.bukkit.inventory.PlayerInventory; import org.eclipse.jdt.annotation.NonNull; +import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.commands.CompositeCommand; import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.hooks.ItemsAdderHook; +import world.bentobox.bentobox.hooks.OraxenHook; import world.bentobox.bentobox.util.Util; import world.bentobox.level.Level; import world.bentobox.level.objects.IslandLevels; @@ -77,6 +79,15 @@ private void executeHandCommand(User user) { return; } + // Oraxen + if (BentoBox.getInstance().getHooks().getHook("Oraxen").isPresent()) { + String id = OraxenHook.getIdByItem(mainHandItem); + if (id != null) { + printValue(user, "oraxen:" + id); + return; + } + } + // ItemsAdder if (addon.isItemsAdder()) { Optional id = ItemsAdderHook.getNamespacedId(mainHandItem); if (id.isPresent()) { @@ -145,7 +156,7 @@ public Optional> tabComplete(User user, String alias, List } List options = new ArrayList<>( - Arrays.stream(Material.values()).filter(Material::isBlock).map(Material::name).toList()); + Arrays.stream(Material.values()).filter(Material::isBlock).map(Material::name).map(String::toLowerCase).toList()); options.add("HAND"); diff --git a/src/main/java/world/bentobox/level/config/BlockConfig.java b/src/main/java/world/bentobox/level/config/BlockConfig.java index 2c4b8a8..feb7495 100644 --- a/src/main/java/world/bentobox/level/config/BlockConfig.java +++ b/src/main/java/world/bentobox/level/config/BlockConfig.java @@ -19,7 +19,9 @@ import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.EntityType; +import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.hooks.ItemsAdderHook; +import world.bentobox.bentobox.hooks.OraxenHook; import world.bentobox.level.Level; /** @@ -100,6 +102,10 @@ private void addMissing(ConfigurationSection blocks) { private boolean isOther(String key) { // Maybe a custom name space + if (key.startsWith("oraxen:") && BentoBox.getInstance().getHooks().getHook("Oraxen").isPresent()) { + return OraxenHook.exists(key.substring(7)); + } + // Check ItemsAdder return addon.isItemsAdder() && ItemsAdderHook.isInRegistry(key); } @@ -234,7 +240,7 @@ public Integer getValue(World world, Object obj) { /** * Return true if the block should be hidden - * @param m block material or entity type of spanwer + * @param m block material or entity type of spawner * @return true if hidden */ public boolean isHiddenBlock(Object obj) { diff --git a/src/main/java/world/bentobox/level/panels/ValuePanel.java b/src/main/java/world/bentobox/level/panels/ValuePanel.java index a976def..9c635fd 100644 --- a/src/main/java/world/bentobox/level/panels/ValuePanel.java +++ b/src/main/java/world/bentobox/level/panels/ValuePanel.java @@ -13,12 +13,14 @@ import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.Registry; +import org.bukkit.Tag; import org.bukkit.World; import org.bukkit.event.inventory.ClickType; import org.bukkit.inventory.ItemStack; import com.google.common.base.Enums; +import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.localization.TextVariables; import world.bentobox.bentobox.api.panels.PanelItem; import world.bentobox.bentobox.api.panels.TemplatedPanel; @@ -134,11 +136,12 @@ private ValuePanel(Level addon, this.activeFilter = Filter.NAME_ASC; - addon.getBlockConfig().getBlockValues().entrySet().stream().filter(en -> this.getIcon(en.getKey()) != null) - .filter(en -> addon.getBlockConfig().isNotHiddenBlock(en.getKey())) - .forEach(en -> blockRecordList - .add(new BlockRecord(en.getKey(), Objects.requireNonNullElse(en.getValue(), 0), - Objects.requireNonNullElse(addon.getBlockConfig().getLimit(en.getKey()), 0)))); + addon.getBlockConfig().getBlockValues().entrySet().stream() + .filter(en -> this.getIcon(en.getKey()) != null) + .filter(en -> addon.getBlockConfig().isNotHiddenBlock(en.getKey())) + .forEach(en -> blockRecordList + .add(new BlockRecord(en.getKey(), Objects.requireNonNullElse(en.getValue(), 0), + Objects.requireNonNullElse(addon.getBlockConfig().getLimit(en.getKey()), 0)))); this.elementList = new ArrayList<>(); this.searchText = ""; @@ -649,7 +652,7 @@ private PanelItem createMaterialButton(ItemTemplateRecord template, TemplatedPan { if (this.elementList.isEmpty()) { - // Does not contain any generators. + // Does not contain any. return null; } @@ -665,16 +668,71 @@ private PanelItem createMaterialButton(ItemTemplateRecord template, TemplatedPan } private Material getIcon(String key) { + // Filter out some names + key = key.replaceAll("wall_", ""); + key = key.replaceAll("_hanging", ""); Material icon = Registry.MATERIAL.get(NamespacedKey.fromString(key)); if (icon == null && key.endsWith("_spawner")) { icon = Registry.MATERIAL.get(NamespacedKey.fromString(key.substring(0, key.length() - 2) + "_egg")); } + // ItemsAdder if (icon == null && addon.isItemsAdder() && ItemsAdderHook.isInRegistry(key)) { icon = ItemsAdderHook.getItemStack(key).map(ItemStack::getType).orElse(null); } if (icon != null && icon.isItem()) { return icon; } + // Not an item, but maybe still something + if (icon != null) { + switch (icon) { + case BUBBLE_COLUMN: return Material.WATER_BUCKET; + case NETHER_PORTAL: return Material.PURPLE_STAINED_GLASS_PANE; + case END_GATEWAY: return Material.BLACK_STAINED_GLASS_PANE; + case END_PORTAL: return Material.BLACK_STAINED_GLASS_PANE; + case SOUL_FIRE: return Material.SOUL_TORCH; + case WALL_TORCH: return Material.TORCH; + case TWISTING_VINES_PLANT: return Material.VINE; + case CAVE_VINES_PLANT: return Material.VINE; + case BAMBOO_SAPLING: return Material.BAMBOO; + case KELP_PLANT: return Material.KELP; + case SWEET_BERRY_BUSH: return Material.SWEET_BERRIES; + case LAVA, FIRE: return Material.LAVA_BUCKET; + case PISTON_HEAD: return Material.PISTON; + case REDSTONE_WIRE: return Material.REDSTONE; + case TORCHFLOWER_CROP: return Material.TORCHFLOWER_SEEDS; + case TALL_SEAGRASS: return Material.SEAGRASS; + case WATER: return Material.WATER_BUCKET; + case VOID_AIR: return Material.BARRIER; + case COCOA: return Material.COCOA_BEANS; + case TRIPWIRE: return Material.TRIPWIRE_HOOK; + case BEETROOTS: return Material.BEETROOT_SEEDS; + case POTATOES: return Material.POTATO; + case CARROTS: return Material.CARROT; + case PITCHER_CROP: return Material.PITCHER_POD; + case BIG_DRIPLEAF_STEM: return Material.BIG_DRIPLEAF; + default: {} + } + if (Tag.FLOWER_POTS.isTagged(icon)) { + return Material.FLOWER_POT; + } + if (Tag.WALL_SIGNS.isTagged(icon) || Tag.ALL_HANGING_SIGNS.isTagged(icon)) { + return Material.OAK_SIGN; + } + if (Tag.CANDLE_CAKES.isTagged(icon)) { + return Material.CAKE; + } + if (Tag.CAULDRONS.isTagged(icon)) { + return Material.CAULDRON; + } + if (Tag.ICE.isTagged(icon)) { + return Material.ICE; + } + + } + // Try Oraxen + if (key.startsWith("oraxen:") && BentoBox.getInstance().getHooks().getHook("Oraxen").isPresent()) { + return Material.PAPER; + } return null; } @@ -702,13 +760,15 @@ private PanelItem createMaterialButton(ItemTemplateRecord template, BlockRecord String.valueOf(blockValue)); String limitTranslation = blockLimit > 0 ? this.user.getTranslationOrNothing(baseKey + "limit", TextVariables.NUMBER, String.valueOf(blockLimit)) - : ""; + : ""; // Determine icon and display material text Material icon = getIcon(key); builder.icon((icon == null || icon == Material.AIR) ? Material.PAPER : icon); - - String displayMaterial = (icon == null) ? Util.prettifyText(key) : Utils.prettifyObject(icon, user); + if (key.startsWith("oraxen:")) { + key = key.substring(7); + } + String displayMaterial = (icon == null) ? Util.prettifyText(key) : Utils.prettifyObject(key, user); // Special handling for spawn eggs if (icon != null && icon.name().endsWith("_SPAWN_EGG")) { displayMaterial = Util.prettifyText(key); diff --git a/src/main/java/world/bentobox/level/util/Utils.java b/src/main/java/world/bentobox/level/util/Utils.java index 304271a..dff4a02 100644 --- a/src/main/java/world/bentobox/level/util/Utils.java +++ b/src/main/java/world/bentobox/level/util/Utils.java @@ -158,6 +158,10 @@ public static String prettifyObject(Object object, User user) { key = ((Enum) object).name().toLowerCase(); } else { key = (String) object; + // Remove prefix + if (key.startsWith("oraxen:")) { + key = key.substring(7); + } } // Try our translations for Material. @@ -177,7 +181,7 @@ public static String prettifyObject(Object object, User user) { // Fallback to our hook for Material. return LangUtilsHook.getMaterialName((Material) object, user); } else { - return key; + return world.bentobox.bentobox.util.Util.prettifyText(key); } } else if (object instanceof EntityType) { String key = ((Enum) object).name().toLowerCase(); diff --git a/src/main/resources/blockconfig.yml b/src/main/resources/blockconfig.yml index 14aa591..6eebbbc 100644 --- a/src/main/resources/blockconfig.yml +++ b/src/main/resources/blockconfig.yml @@ -7,6 +7,8 @@ # iasurvival:modern_stone: 100 # The key for a block can be found by looking at it in-game and using /iablock # +# For Oraxen, prefix the block name with oraxen:, for example oraxen:amethyst_ore +# # The limits section lists the maximum number for any particular block. Blocks over this amount # are not counted. limits: @@ -18,6 +20,7 @@ hidden-blocks: - air blocks: #iasurvival:modern_stone: 1 + #oraxen:amethyst_ore: 3 acacia_button: 1 acacia_door: 2 acacia_fence: 2