From 0089cbfc8681e4d64600ffb363c2ea00e958c4de Mon Sep 17 00:00:00 2001 From: tastybento Date: Mon, 17 Feb 2025 11:52:25 +0900 Subject: [PATCH 1/7] Version 2.18.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 95c2734..7b3f395 100644 --- a/pom.xml +++ b/pom.xml @@ -67,7 +67,7 @@ -LOCAL - 2.17.1 + 2.18.0 BentoBoxWorld_Level bentobox-world https://sonarcloud.io From 399bec95f665dafb95ba413ee56826f400c05392 Mon Sep 17 00:00:00 2001 From: tastybento Date: Fri, 21 Feb 2025 20:26:19 -0800 Subject: [PATCH 2/7] Implement support for different spawner types --- pom.xml | 4 +- .../world/bentobox/level/LevelsManager.java | 33 ---- .../calculators/IslandLevelCalculator.java | 130 ++++++++++------ .../bentobox/level/calculators/Results.java | 12 +- .../bentobox/level/config/BlockConfig.java | 113 +++++++++++--- .../level/listeners/MigrationListener.java | 2 - .../bentobox/level/objects/IslandLevels.java | 24 ++- .../bentobox/level/panels/DetailsPanel.java | 142 +++++++++--------- .../java/world/bentobox/level/util/Utils.java | 125 +++++++-------- 9 files changed, 314 insertions(+), 271 deletions(-) diff --git a/pom.xml b/pom.xml index 7b3f395..73b620d 100644 --- a/pom.xml +++ b/pom.xml @@ -50,12 +50,12 @@ UTF-8 UTF-8 - 17 + 21 2.0.9 1.21.3-R0.1-SNAPSHOT - 2.7.1-SNAPSHOT + 3.2.4-SNAPSHOT 1.12.0 diff --git a/src/main/java/world/bentobox/level/LevelsManager.java b/src/main/java/world/bentobox/level/LevelsManager.java index 448757a..3f3da02 100644 --- a/src/main/java/world/bentobox/level/LevelsManager.java +++ b/src/main/java/world/bentobox/level/LevelsManager.java @@ -30,7 +30,6 @@ import world.bentobox.level.calculators.Results; import world.bentobox.level.events.IslandLevelCalculatedEvent; import world.bentobox.level.events.IslandPreLevelEvent; import world.bentobox.level.objects.IslandLevels; -import world.bentobox.level.objects.LevelsData; import world.bentobox.level.objects.TopTenData; import world.bentobox.level.util.CachedData; @@ -67,38 +66,6 @@ public class LevelsManager { } - public void migrate() { - Database oldDb = new Database<>(addon, LevelsData.class); - oldDb.loadObjects().forEach(ld -> { - try { - UUID owner = UUID.fromString(ld.getUniqueId()); - // Step through each world - ld.getLevels().keySet().stream() - // World - .map(Bukkit::getWorld).filter(Objects::nonNull) - // Island - .map(w -> addon.getIslands().getIsland(w, owner)).filter(Objects::nonNull).forEach(i -> { - // Make new database entry - World w = i.getWorld(); - IslandLevels il = new IslandLevels(i.getUniqueId()); - il.setInitialLevel(ld.getInitialLevel(w)); - il.setLevel(ld.getLevel(w)); - il.setMdCount(ld.getMdCount(w)); - il.setPointsToNextLevel(ld.getPointsToNextLevel(w)); - il.setUwCount(ld.getUwCount(w)); - // Save it - handler.saveObjectAsync(il); - }); - // Now delete the old database entry - oldDb.deleteID(ld.getUniqueId()); - } catch (Exception e) { - addon.logError("Could not migrate level data database! " + e.getMessage()); - e.printStackTrace(); - return; - } - }); - } - /** * Add an island to a top ten * diff --git a/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java b/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java index 98d9e72..5a9a457 100644 --- a/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java +++ b/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java @@ -2,14 +2,15 @@ package world.bentobox.level.calculators; import java.text.ParseException; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.EnumMap; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Queue; import java.util.Set; import java.util.UUID; @@ -31,9 +32,9 @@ import org.bukkit.block.CreatureSpawner; import org.bukkit.block.ShulkerBox; import org.bukkit.block.data.BlockData; import org.bukkit.block.data.type.Slab; +import org.bukkit.entity.EntityType; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.BlockStateMeta; -import org.bukkit.scheduler.BukkitTask; import com.bgsoftware.wildstacker.api.WildStackerAPI; import com.bgsoftware.wildstacker.api.objects.StackedBarrel; @@ -55,19 +56,6 @@ import world.bentobox.level.calculators.Results.Result; public class IslandLevelCalculator { private static final String LINE_BREAK = "=================================="; public static final long MAX_AMOUNT = 10000000; - private static final List CHESTS = Arrays.asList(Material.CHEST, Material.CHEST_MINECART, - Material.TRAPPED_CHEST, Material.SHULKER_BOX, Material.BLACK_SHULKER_BOX, Material.BLUE_SHULKER_BOX, - Material.BROWN_SHULKER_BOX, Material.CYAN_SHULKER_BOX, Material.GRAY_SHULKER_BOX, - Material.GREEN_SHULKER_BOX, Material.LIGHT_BLUE_SHULKER_BOX, Material.LIGHT_GRAY_SHULKER_BOX, - Material.LIME_SHULKER_BOX, Material.MAGENTA_SHULKER_BOX, Material.ORANGE_SHULKER_BOX, - Material.PINK_SHULKER_BOX, Material.PURPLE_SHULKER_BOX, Material.RED_SHULKER_BOX, Material.RED_SHULKER_BOX, - Material.WHITE_SHULKER_BOX, Material.YELLOW_SHULKER_BOX, Material.COMPOSTER, Material.BARREL, - Material.DISPENSER, Material.DROPPER, Material.SMOKER, Material.BLAST_FURNACE, Material.BUNDLE, - Material.RED_BUNDLE, Material.BLACK_BUNDLE, Material.BLUE_BUNDLE, Material.BROWN_BUNDLE, - Material.CYAN_BUNDLE, Material.GRAY_BUNDLE, Material.GREEN_BUNDLE, Material.LIGHT_BLUE_BUNDLE, - Material.LIGHT_GRAY_BUNDLE, Material.LIME_BUNDLE, Material.MAGENTA_BUNDLE, Material.ORANGE_BUNDLE, - Material.PINK_BUNDLE, Material.PURPLE_BUNDLE, Material.RED_BUNDLE, Material.WHITE_BUNDLE, - Material.YELLOW_BUNDLE); private static final int CHUNKS_TO_SCAN = 100; private final Level addon; private final Queue> chunksToCheck; @@ -82,7 +70,7 @@ public class IslandLevelCalculator { private final int seaHeight; private final List stackedBlocks = new ArrayList<>(); private final Set chestBlocks = new HashSet<>(); - private BukkitTask finishTask; + private final Map spawners = new HashMap<>(); /** * Constructor to get the level for an island @@ -163,6 +151,29 @@ public class IslandLevelCalculator { } } + /** + * Adds value to the results based on the material and whether the block is + * below sea level or not + * + * @param mat - material of the block + * @param belowSeaLevel - true if below sea level + */ + private void checkSpawner(EntityType et, boolean belowSeaLevel) { + if (limitCount(Material.SPAWNER) == 0) { + return; + } + Integer count = addon.getBlockConfig().getValue(island.getWorld(), et); + if (count != null) { + if (belowSeaLevel) { + results.underWaterBlockCount.addAndGet(count); + results.uwCount.add(et); + } else { + results.rawBlockCount.addAndGet(count); + results.mdCount.add(et); + } + } + } + /** * Get a set of all the chunks in island * @@ -231,31 +242,22 @@ public class IslandLevelCalculator { reportLines.add( "Blocks not counted because they exceeded limits: " + String.format("%,d", results.ofCount.size())); - Iterable> entriesSortedByCount = results.ofCount.entrySet(); - Iterator> it = entriesSortedByCount.iterator(); + Iterable> entriesSortedByCount = results.ofCount.entrySet(); + Iterator> it = entriesSortedByCount.iterator(); while (it.hasNext()) { - Entry type = it.next(); - Integer limit = addon.getBlockConfig().getBlockLimits().get(type.getElement()); + + Entry type = it.next(); + Material m = type.getElement() instanceof Material mat ? mat : Material.SPAWNER; + Integer limit = addon.getBlockConfig().getBlockLimits().get(m); String explain = ")"; if (limit == null) { - Material generic = type.getElement(); - limit = addon.getBlockConfig().getBlockLimits().get(generic); + limit = addon.getBlockConfig().getBlockLimits().get(m); explain = " - All types)"; } - reportLines.add(type.getElement().toString() + ": " + String.format("%,d", type.getCount()) + reportLines.add(Util.prettifyText(m.name()) + ": " + String.format("%,d", type.getCount()) + " blocks (max " + limit + explain); } reportLines.add(LINE_BREAK); - reportLines.add("Blocks on island that are not in config.yml"); - reportLines.add("Total number = " + String.format("%,d", results.ncCount.size())); - entriesSortedByCount = results.ncCount.entrySet(); - it = entriesSortedByCount.iterator(); - while (it.hasNext()) { - Entry type = it.next(); - reportLines.add(type.getElement().toString() + ": " + String.format("%,d", type.getCount()) + " blocks"); - } - reportLines.add(LINE_BREAK); - return reportLines; } @@ -454,6 +456,8 @@ public class IslandLevelCalculator { 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; @@ -464,22 +468,26 @@ public class IslandLevelCalculator { // 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(new Location(cp.world, (double) x + cp.chunkSnapshot.getX() * 16, y, - (double) z + cp.chunkSnapshot.getZ() * 16)); + stackedBlocks.add(loc); } if (addon.isUltimateStackerEnabled() && !m.isAir()) { - Location l = new Location(cp.chunk.getWorld(), x, y, z); - UltimateStackerCalc.addStackers(m, l, results, belowSeaLevel, limitCount(m)); + UltimateStackerCalc.addStackers(m, loc, results, belowSeaLevel, limitCount(m)); } // Scan chests - if (addon.getSettings().isIncludeChests() && CHESTS.contains(m)) { - + if (addon.getSettings().isIncludeChests() && blockData instanceof Container) { chestBlocks.add(cp.chunk); } - // Add the value of the block's material - checkBlock(m, belowSeaLevel); + + // 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); + } } } } @@ -520,16 +528,23 @@ public class IslandLevelCalculator { return result; } - private Collection sortedReport(int total, Multiset materialCount) { + private Collection sortedReport(int total, Multiset uwCount) { Collection result = new ArrayList<>(); - Iterable> entriesSortedByCount = Multisets.copyHighestCountFirst(materialCount) + Iterable> entriesSortedByCount = Multisets.copyHighestCountFirst(uwCount) .entrySet(); - for (Entry en : entriesSortedByCount) { - Material type = en.getElement(); + for (Entry en : entriesSortedByCount) { - int value = getValue(type); + int value = 0; + String name = ""; + if (en.getElement() instanceof Material md) { + value = Objects.requireNonNullElse(addon.getBlockConfig().getValue(island.getWorld(), md), 0); + name = Util.prettifyText(md.name()); + } else if (en.getElement() instanceof EntityType et) { + name = Util.prettifyText(et.name()); + value = Objects.requireNonNullElse(addon.getBlockConfig().getValue(island.getWorld(), et), 0); + } - result.add(type.toString() + ":" + String.format("%,d", en.getCount()) + " blocks x " + value + " = " + result.add(name + " :" + String.format("%,d", en.getCount()) + " blocks x " + value + " = " + (value * en.getCount())); total += (value * en.getCount()); @@ -616,8 +631,9 @@ public class IslandLevelCalculator { pipeliner.getInProcessQueue().remove(this); BentoBox.getInstance().log("Completed Level scan."); // Chunk finished - // This was the last chunk. Handle stacked blocks, then chests and exit - handleStackedBlocks().thenCompose(v -> handleChests()).thenRun(() -> { + // This was the last chunk. Handle stacked blocks, spawners, chests and exit + handleStackedBlocks().thenCompose(v -> handleSpawners()).thenCompose(v -> handleChests()) + .thenRun(() -> { this.tidyUp(); this.getR().complete(getResults()); }); @@ -625,6 +641,22 @@ public class IslandLevelCalculator { }); } + private CompletableFuture handleSpawners() { + List> futures = new ArrayList<>(); + for (Map.Entry en : this.spawners.entrySet()) { + CompletableFuture future = Util.getChunkAtAsync(en.getKey()).thenAccept(c -> { + if (en.getKey().getBlock().getType() == Material.SPAWNER) { + CreatureSpawner cs = (CreatureSpawner) en.getKey().getBlock().getState(); + checkSpawner(cs.getSpawnedType(), en.getValue()); + } + }); + futures.add(future); + } + // Return a CompletableFuture that completes when all futures are done + return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); + + } + private CompletableFuture handleChests() { List> futures = new ArrayList<>(); for (Chunk v : chestBlocks) { diff --git a/src/main/java/world/bentobox/level/calculators/Results.java b/src/main/java/world/bentobox/level/calculators/Results.java index c2bf739..f293516 100644 --- a/src/main/java/world/bentobox/level/calculators/Results.java +++ b/src/main/java/world/bentobox/level/calculators/Results.java @@ -25,10 +25,10 @@ public class Results { TIMEOUT } List report; - final Multiset mdCount = HashMultiset.create(); - final Multiset uwCount = HashMultiset.create(); - final Multiset ncCount = HashMultiset.create(); - final Multiset ofCount = HashMultiset.create(); + final Multiset mdCount = HashMultiset.create(); + final Multiset uwCount = HashMultiset.create(); + final Multiset ncCount = HashMultiset.create(); + final Multiset ofCount = HashMultiset.create(); // AtomicLong and AtomicInteger must be used because they are changed by multiple concurrent threads AtomicLong rawBlockCount = new AtomicLong(0); AtomicLong underWaterBlockCount = new AtomicLong(0); @@ -130,13 +130,13 @@ public class Results { /** * @return the mdCount */ - public Multiset getMdCount() { + public Multiset getMdCount() { return mdCount; } /** * @return the uwCount */ - public Multiset getUwCount() { + public Multiset getUwCount() { return uwCount; } /** diff --git a/src/main/java/world/bentobox/level/config/BlockConfig.java b/src/main/java/world/bentobox/level/config/BlockConfig.java index b999ac3..f92f5ed 100644 --- a/src/main/java/world/bentobox/level/config/BlockConfig.java +++ b/src/main/java/world/bentobox/level/config/BlockConfig.java @@ -2,8 +2,6 @@ package world.bentobox.level.config; import java.io.File; import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; import java.util.EnumMap; import java.util.HashMap; import java.util.List; @@ -17,6 +15,7 @@ import org.bukkit.Registry; import org.bukkit.World; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.EntityType; import world.bentobox.level.Level; @@ -27,10 +26,13 @@ import world.bentobox.level.Level; */ public class BlockConfig { + private static final String SPAWNER = "_SPAWNER"; private Map blockLimits = new EnumMap<>(Material.class); private Map blockValues = new EnumMap<>(Material.class); private final Map> worldBlockValues = new HashMap<>(); + private final Map> worldSpawnerValues = new HashMap<>(); private final List hiddenBlocks; + private Map spawnerValues = new EnumMap<>(EntityType.class); private Level addon; /** @@ -47,6 +49,7 @@ public class BlockConfig { } if (blockValues.isConfigurationSection("blocks")) { setBlockValues(loadBlockValues(blockValues)); + setSpawnerValues(loadSpawnerValues(blockValues)); } else { addon.logWarning("No block values in blockconfig.yml! All island levels will be zero!"); } @@ -58,12 +61,12 @@ public class BlockConfig { hiddenBlocks = blockValues.getStringList("hidden-blocks").stream().map(name -> { try { return Material.valueOf(name.toUpperCase(Locale.ENGLISH)); - + } catch (Exception e) { return null; } }).filter(Objects::nonNull).toList(); - + // All done blockValues.save(file); } @@ -110,6 +113,37 @@ public class BlockConfig { return bv; } + /** + * Loads the spawner values from the blocks in the config + * Format is entityname + _SPANWER, so for example ALLAY_SPAWNER + * If they are missing, then they will score 1 + * @param blockValues config section + * @return map of entity types and their score + */ + private Map loadSpawnerValues(YamlConfiguration blockValues) { + ConfigurationSection blocks = Objects.requireNonNull(blockValues.getConfigurationSection("blocks")); + Map bv = new HashMap<>(); + + + // Update spawners + Registry.MATERIAL.stream().filter(Material::isItem) + .filter(m -> m.name().endsWith("_SPAWN_EGG")).map(m -> m.name().substring(0, m.name().length() - 10)) + .forEach(m -> { + // Populate missing spawners + if (!blocks.contains(m + SPAWNER, true)) { + blocks.set(m + SPAWNER, 1); + } + // Load value + try { + EntityType et = EntityType.valueOf(m); + bv.put(et, blocks.getInt(m + SPAWNER)); + } catch (Exception e) { + e.printStackTrace(); + } + }); + return bv; + } + private Map loadBlockLimits(YamlConfiguration blockValues2) { Map bl = new EnumMap<>(Material.class); ConfigurationSection limits = Objects.requireNonNull(blockValues2.getConfigurationSection("limits")); @@ -157,39 +191,78 @@ public class BlockConfig { } /** - * Get the value of material in world + * @return the worldSpawnerValues + */ + public Map> getWorldSpawnerValues() { + return worldSpawnerValues; + } + + /** + * Get the value of of a spawner in world * @param world - world - * @param md - material + * @param obj - entity type that will spawn from this spawner or material * @return value or null if not configured with a value */ - public Integer getValue(World world, Material md) { - // Check world settings - if (getWorldBlockValues().containsKey(world) && getWorldBlockValues().get(world).containsKey(md)) { - return getWorldBlockValues().get(world).get(md); - } - // Check baseline - if (getBlockValues().containsKey(md)) { - return getBlockValues().get(md); + public Integer getValue(World world, Object obj) { + if (obj instanceof EntityType et) { + // Check world settings + if (getWorldSpawnerValues().containsKey(world) && getWorldSpawnerValues().get(world).containsKey(et)) { + return getWorldSpawnerValues().get(world).get(et); + } + // Check baseline + if (getSpawnerValues().containsKey(et)) { + return getSpawnerValues().get(et); + } + } else if (obj instanceof Material md) { + // Check world settings + if (getWorldBlockValues().containsKey(world) && getWorldBlockValues().get(world).containsKey(md)) { + return getWorldBlockValues().get(world).get(md); + } + // Check baseline + if (getBlockValues().containsKey(md)) { + return getBlockValues().get(md); + } } return null; } /** * Return true if the block should be hidden - * @param m block material + * @param m block material or entity type of spanwer * @return true if hidden */ - public boolean isHiddenBlock(Material m) { - return hiddenBlocks.contains(m); + public boolean isHiddenBlock(Object obj) { + if (obj instanceof Material m) { + return hiddenBlocks.contains(m); + } + return hiddenBlocks.contains(Material.SPAWNER); } - + /** * Return true if the block should not be hidden * @param m block material * @return false if hidden */ - public boolean isNotHiddenBlock(Material m) { - return !hiddenBlocks.contains(m); + public boolean isNotHiddenBlock(Object obj) { + if (obj instanceof Material m) { + return !hiddenBlocks.contains(m); + } else { + return !hiddenBlocks.contains(Material.SPAWNER); + } + } + + /** + * @return the spawnerValues + */ + public Map getSpawnerValues() { + return spawnerValues; + } + + /** + * @param spawnerValues the spawnerValues to set + */ + public void setSpawnerValues(Map spawnerValues) { + this.spawnerValues = spawnerValues; } } diff --git a/src/main/java/world/bentobox/level/listeners/MigrationListener.java b/src/main/java/world/bentobox/level/listeners/MigrationListener.java index 640a26c..c8dc01a 100644 --- a/src/main/java/world/bentobox/level/listeners/MigrationListener.java +++ b/src/main/java/world/bentobox/level/listeners/MigrationListener.java @@ -26,8 +26,6 @@ public class MigrationListener implements Listener @EventHandler public void onBentoBoxReady(BentoBoxReadyEvent e) { - // Perform upgrade check - this.addon.getManager().migrate(); // Load TopTens this.addon.getManager().loadTopTens(); /* diff --git a/src/main/java/world/bentobox/level/objects/IslandLevels.java b/src/main/java/world/bentobox/level/objects/IslandLevels.java index 15e2a1e..f1d41eb 100644 --- a/src/main/java/world/bentobox/level/objects/IslandLevels.java +++ b/src/main/java/world/bentobox/level/objects/IslandLevels.java @@ -1,10 +1,8 @@ package world.bentobox.level.objects; -import java.util.EnumMap; +import java.util.HashMap; import java.util.Map; -import org.bukkit.Material; - import com.google.gson.annotations.Expose; import world.bentobox.bentobox.database.objects.DataObject; @@ -59,13 +57,13 @@ public class IslandLevels implements DataObject { * Underwater count */ @Expose - private Map uwCount; + private Map uwCount; /** * MaterialData count - count of all blocks excluding under water */ @Expose - private Map mdCount; + private Map mdCount; /** * Constructor for new island @@ -73,8 +71,8 @@ public class IslandLevels implements DataObject { */ public IslandLevels(String islandUUID) { uniqueId = islandUUID; - uwCount = new EnumMap<>(Material.class); - mdCount = new EnumMap<>(Material.class); + uwCount = new HashMap<>(); + mdCount = new HashMap<>(); } /** @@ -165,23 +163,23 @@ public class IslandLevels implements DataObject { * The count of underwater blocks * @return the uwCount */ - public Map getUwCount() { + public Map getUwCount() { return uwCount; } /** * Underwater blocks - * @param uwCount the uwCount to set + * @param map the uwCount to set */ - public void setUwCount(Map uwCount) { - this.uwCount = uwCount; + public void setUwCount(Map map) { + this.uwCount = map; } /** * All blocks count except for underwater blocks * @return the mdCount */ - public Map getMdCount() { + public Map getMdCount() { return mdCount; } @@ -189,7 +187,7 @@ public class IslandLevels implements DataObject { * All blocks except for underwater blocks * @param mdCount the mdCount to set */ - public void setMdCount(Map mdCount) { + public void setMdCount(Map mdCount) { this.mdCount = mdCount; } diff --git a/src/main/java/world/bentobox/level/panels/DetailsPanel.java b/src/main/java/world/bentobox/level/panels/DetailsPanel.java index 90fbf45..b8565b0 100644 --- a/src/main/java/world/bentobox/level/panels/DetailsPanel.java +++ b/src/main/java/world/bentobox/level/panels/DetailsPanel.java @@ -4,12 +4,15 @@ import java.io.File; import java.util.ArrayList; import java.util.Comparator; import java.util.EnumMap; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; import org.bukkit.Material; import org.bukkit.World; +import org.bukkit.entity.EntityType; import org.bukkit.event.inventory.ClickType; import org.bukkit.inventory.ItemStack; @@ -110,69 +113,50 @@ public class DetailsPanel { private void updateFilters() { this.materialCountList.clear(); - switch (this.activeTab) { - case VALUE_BLOCKS -> { - Map materialCountMap = new EnumMap<>(Material.class); - - materialCountMap.putAll(this.levelsData.getMdCount()); - - // Add underwater blocks. - this.levelsData.getUwCount().forEach((material, count) -> materialCountMap.put(material, - materialCountMap.computeIfAbsent(material, key -> 0) + count)); - - // Remove zero value blocks - materialCountMap.entrySet().removeIf(en -> { - Integer value = this.addon.getBlockConfig().getValue(world, en.getKey()); - return value == null || value == 0; - }); - - // Remove any hidden blocks - materialCountMap.keySet().removeIf(this.addon.getBlockConfig()::isHiddenBlock); - - materialCountMap.entrySet().stream().sorted((Map.Entry.comparingByKey())).forEachOrdered(entry -> { - if (entry.getValue() > 0) { - this.materialCountList.add(new Pair<>(entry.getKey(), entry.getValue())); - } - }); - - } - case ALL_BLOCKS -> { - Map materialCountMap = new EnumMap<>(Material.class); - - materialCountMap.putAll(this.levelsData.getMdCount()); - - // Add underwater blocks. - this.levelsData.getUwCount().forEach((material, count) -> materialCountMap.put(material, - materialCountMap.computeIfAbsent(material, key -> 0) + count)); - - // Remove any hidden blocks - materialCountMap.keySet().removeIf(this.addon.getBlockConfig()::isHiddenBlock); - - materialCountMap.entrySet().stream().sorted((Map.Entry.comparingByKey())) - .forEachOrdered(entry -> this.materialCountList.add(new Pair<>(entry.getKey(), entry.getValue()))); - } - case ABOVE_SEA_LEVEL -> this.levelsData.getMdCount().entrySet().stream() - .filter(en -> this.addon.getBlockConfig().isNotHiddenBlock(en.getKey())) - .sorted((Map.Entry.comparingByKey())) - .forEachOrdered(entry -> this.materialCountList.add(new Pair<>(entry.getKey(), entry.getValue()))); - - case UNDERWATER -> this.levelsData.getUwCount().entrySet().stream() - .filter(en -> this.addon.getBlockConfig().isNotHiddenBlock(en.getKey())) - .sorted((Map.Entry.comparingByKey())) - .forEachOrdered(entry -> this.materialCountList.add(new Pair<>(entry.getKey(), entry.getValue()))); - - case SPAWNER -> { + if (this.activeTab == Tab.SPAWNER) { if (this.addon.getBlockConfig().isNotHiddenBlock(Material.SPAWNER)) { - int aboveWater = this.levelsData.getMdCount().getOrDefault(Material.SPAWNER, 0); - int underWater = this.levelsData.getUwCount().getOrDefault(Material.SPAWNER, 0); + Map spawnerCountMap = new EnumMap<>(EntityType.class); + spawnerCountMap = this.levelsData.getMdCount().entrySet().stream() + .filter(en -> en.getKey() instanceof EntityType) + .collect(Collectors.toMap(en -> (EntityType) en.getKey(), Map.Entry::getValue)); - // TODO: spawners need some touch... - this.materialCountList.add(new Pair<>(Material.SPAWNER, underWater + aboveWater)); + spawnerCountMap.entrySet().stream().sorted((Map.Entry.comparingByKey())).forEachOrdered(entry -> { + if (entry.getValue() > 0) { + this.materialCountList.add(new Pair<>(entry.getKey(), entry.getValue())); + } + }); } - } + } else { + Map materialCountMap = new HashMap<>(); + + if (this.activeTab != Tab.UNDERWATER) { + // All above water blocks + materialCountMap.putAll(this.levelsData.getMdCount()); + } + if (this.activeTab != Tab.ABOVE_SEA_LEVEL) { + // All underwater blocks. + materialCountMap.putAll(this.levelsData.getUwCount()); + } + // Remove any hidden blocks + materialCountMap.keySet().removeIf(this.addon.getBlockConfig()::isHiddenBlock); + + // Remove any zero amount items + materialCountMap.values().removeIf(i -> i < 1); + + if (this.activeTab == Tab.VALUE_BLOCKS) { + // Remove zero-value blocks + materialCountMap.entrySet().removeIf(en -> Optional + .ofNullable(this.addon.getBlockConfig().getValue(world, en.getKey())).orElse(0) == 0); + } + // All done filtering, add the left overs + materialCountList.addAll( + materialCountMap.entrySet().stream().map(entry -> new Pair<>(entry.getKey(), entry.getValue())) + //.sorted(Comparator.comparing(pair -> ((Enum) pair.getKey()).name())) + .collect(Collectors.toList())); + } - Comparator> sorter; + Comparator> sorter; switch (this.activeFilter) { case COUNT -> { @@ -556,37 +540,47 @@ public class DetailsPanel { * @param materialCount materialCount which button must be created. * @return PanelItem for generator tier. */ - private PanelItem createMaterialButton(ItemTemplateRecord template, Pair materialCount) { + private PanelItem createMaterialButton(ItemTemplateRecord template, Pair materialCount) { PanelItemBuilder builder = new PanelItemBuilder(); if (template.icon() != null) { builder.icon(template.icon().clone()); - } else { - builder.icon(PanelUtils.getMaterialItem(materialCount.getKey())); } - + // Show amount, if < 64 if (materialCount.getValue() < 64) { builder.amount(materialCount.getValue()); } - if (template.title() != null) { - builder.name(this.user.getTranslation(this.world, template.title(), TextVariables.NUMBER, - String.valueOf(materialCount.getValue()), "[material]", - Utils.prettifyObject(materialCount.getKey(), this.user))); + final String reference = "level.gui.buttons.material."; + String description = ""; + String blockId = ""; + Object key = materialCount.getKey(); // Can be a Material or EntityType + if (key instanceof Material m) { + builder.icon(PanelUtils.getMaterialItem(m)); + if (template.title() != null) { + builder.name(this.user.getTranslation(this.world, template.title(), TextVariables.NUMBER, + String.valueOf(materialCount.getValue()), "[material]", Utils.prettifyObject(m, this.user))); + } + description = Utils.prettifyDescription(m, this.user); + blockId = this.user.getTranslationOrNothing(reference + "id", "[id]", m.name()); + + } else if (key instanceof EntityType e) { + builder.icon(PanelUtils.getEntityEgg(e)); + if (template.title() != null) { + builder.name(this.user.getTranslation(this.world, template.title(), TextVariables.NUMBER, + String.valueOf(materialCount.getValue()), "[material]", Utils.prettifyObject(e, this.user))); + } + description = Utils.prettifyDescription(e, this.user); + blockId = this.user.getTranslationOrNothing(reference + "id", "[id]", e.name()); } - String description = Utils.prettifyDescription(materialCount.getKey(), this.user); - - final String reference = "level.gui.buttons.material."; - String blockId = this.user.getTranslationOrNothing(reference + "id", "[id]", materialCount.getKey().name()); - - int blockValue = this.addon.getBlockConfig().getBlockValues().getOrDefault(materialCount.getKey(), 0); + int blockValue = this.addon.getBlockConfig().getBlockValues().getOrDefault(key, 0); String value = blockValue > 0 ? this.user.getTranslationOrNothing(reference + "value", TextVariables.NUMBER, String.valueOf(blockValue)) : ""; - int blockLimit = this.addon.getBlockConfig().getBlockLimits().getOrDefault(materialCount.getKey(), 0); + int blockLimit = this.addon.getBlockConfig().getBlockLimits().getOrDefault(key, 0); String limit = blockLimit > 0 ? this.user.getTranslationOrNothing(reference + "limit", TextVariables.NUMBER, String.valueOf(blockLimit)) @@ -706,7 +700,7 @@ public class DetailsPanel { /** * This variable stores the list of elements to display. */ - private final List> materialCountList; + private final List> materialCountList; /** * This variable holds current pageIndex for multi-page generator choosing. diff --git a/src/main/java/world/bentobox/level/util/Utils.java b/src/main/java/world/bentobox/level/util/Utils.java index 1433666..938e62d 100644 --- a/src/main/java/world/bentobox/level/util/Utils.java +++ b/src/main/java/world/bentobox/level/util/Utils.java @@ -10,7 +10,9 @@ package world.bentobox.level.util; import java.util.List; import org.bukkit.Material; +import org.bukkit.entity.EntityType; import org.bukkit.permissions.PermissionAttachmentInfo; +import org.eclipse.jdt.annotation.Nullable; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.hooks.LangUtilsHook; @@ -18,7 +20,6 @@ import world.bentobox.bentobox.hooks.LangUtilsHook; public class Utils { - private static final String LEVEL_MATERIALS = "level.materials."; private Utils() {} // Private constructor as this is a utility class only with static methods @@ -138,92 +139,72 @@ public class Utils return currentValue; } - /** - * Prettify Material object for user. - * @param object Object that must be pretty. - * @param user User who will see the object. - * @return Prettified string for Material. + * Reference string in translations. */ - public static String prettifyObject(Material object, User user) - { - // Nothing to translate - if (object == null) - { + public static final String ENTITIES = "level.entities."; + private static final String LEVEL_MATERIALS = "level.materials."; + + public static String prettifyObject(Object object, User user) { + if (object == null || !(object instanceof Enum)) { return ""; } + // All supported objects are enums so we can use name() safely. + String key = ((Enum) object).name().toLowerCase(); + String translation = ""; - // Find addon structure with: - // [addon]: - // materials: - // [material]: - // name: [name] - String translation = user.getTranslationOrNothing(LEVEL_MATERIALS + object.name().toLowerCase() + ".name"); + if (object instanceof Material) { + // Try our translations for Material. + translation = user.getTranslationOrNothing(LEVEL_MATERIALS + key + ".name"); + if (!translation.isEmpty()) + return translation; - if (!translation.isEmpty()) - { - // We found our translation. - return translation; + translation = user.getTranslationOrNothing(LEVEL_MATERIALS + key); + if (!translation.isEmpty()) + return translation; + + translation = user.getTranslationOrNothing("materials." + key); + if (!translation.isEmpty()) + return translation; + + // Fallback to our hook for Material. + return LangUtilsHook.getMaterialName((Material) object, user); + } else if (object instanceof EntityType) { + // Try our translations for EntityType. + translation = user.getTranslationOrNothing(ENTITIES + key + ".name"); + if (!translation.isEmpty()) + return translation; + + translation = user.getTranslationOrNothing(ENTITIES + key); + if (!translation.isEmpty()) + return translation; + + translation = user.getTranslationOrNothing("entities." + key); + if (!translation.isEmpty()) + return translation; + + // Fallback to our hook for EntityType. + return LangUtilsHook.getEntityName((EntityType) object, user); } - // Find addon structure with: - // [addon]: - // materials: - // [material]: [name] - - translation = user.getTranslationOrNothing(LEVEL_MATERIALS + object.name().toLowerCase()); - - if (!translation.isEmpty()) - { - // We found our translation. - return translation; - } - - // Find general structure with: - // materials: - // [material]: [name] - - translation = user.getTranslationOrNothing("materials." + object.name().toLowerCase()); - - if (!translation.isEmpty()) - { - // We found our translation. - return translation; - } - - // Use Lang Utils Hook to translate material - return LangUtilsHook.getMaterialName(object, user); + // In case of an unexpected type, return an empty string. + return ""; } - - /** - * Prettify Material object description for user. - * @param object Object that must be pretty. - * @param user User who will see the object. - * @return Prettified description string for Material. - */ - public static String prettifyDescription(Material object, User user) - { - // Nothing to translate - if (object == null) - { + public static String prettifyDescription(Object object, User user) { + if (object == null || !(object instanceof Enum)) { return ""; } + String key = ((Enum) object).name().toLowerCase(); - // Find addon structure with: - // [addon]: - // materials: - // [material]: - // description: [text] - String translation = user.getTranslationOrNothing(LEVEL_MATERIALS + object.name().toLowerCase() + ".description"); - - if (!translation.isEmpty()) - { - // We found our translation. - return translation; + if (object instanceof Material) { + String translation = user.getTranslationOrNothing(LEVEL_MATERIALS + key + ".description"); + return translation != null ? translation : ""; + } else if (object instanceof EntityType) { + String translation = user.getTranslationOrNothing(ENTITIES + key + ".description"); + return translation != null ? translation : ""; } - // No text to return. return ""; } } From 5a88d287cc1f4ffffb013a31d8fd2d9d2eac6264 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 22 Feb 2025 10:18:56 -0800 Subject: [PATCH 3/7] Fix tests --- src/main/java/world/bentobox/level/Level.java | 2 +- src/test/java/world/bentobox/level/LevelsManagerTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/world/bentobox/level/Level.java b/src/main/java/world/bentobox/level/Level.java index e13a70d..5e66c27 100644 --- a/src/main/java/world/bentobox/level/Level.java +++ b/src/main/java/world/bentobox/level/Level.java @@ -106,7 +106,7 @@ public class Level extends Addon { // Register listeners this.registerListener(new IslandActivitiesListeners(this)); this.registerListener(new JoinLeaveListener(this)); - this.registerListener(new MigrationListener(this)); + this.registerListener(new MigrationListener(this)); // Register commands for GameModes registeredGameModes.clear(); diff --git a/src/test/java/world/bentobox/level/LevelsManagerTest.java b/src/test/java/world/bentobox/level/LevelsManagerTest.java index 2155ee2..4ea67e1 100644 --- a/src/test/java/world/bentobox/level/LevelsManagerTest.java +++ b/src/test/java/world/bentobox/level/LevelsManagerTest.java @@ -216,7 +216,7 @@ public class LevelsManagerTest { islands.add(il); } // Supply no island levels first (for migrate), then islands - when(handler.loadObjects()).thenReturn(Collections.emptyList(), islands); + when(handler.loadObjects()).thenReturn(islands); when(handler.objectExists(anyString())).thenReturn(true); when(levelsData.getLevel()).thenReturn(-5L, -4L, -3L, -2L, -1L, 0L, 1L, 2L, 3L, 4L, 5L, 45678L); when(levelsData.getUniqueId()).thenReturn(uuid.toString()); @@ -231,7 +231,7 @@ public class LevelsManagerTest { when(iwm.getPermissionPrefix(any())).thenReturn("bskyblock."); lm = new LevelsManager(addon); - lm.migrate(); + } /** From 3ca769f194da31f4a15335df6b1e919bf3b3e030 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 22 Feb 2025 13:37:02 -0800 Subject: [PATCH 4/7] Fix display of blocks on initial opening of detail display --- .../world/bentobox/level/LevelsManager.java | 1 + .../bentobox/level/calculators/Results.java | 2 - .../calculators/UltimateStackerCalc.java | 2 - .../bentobox/level/objects/IslandLevels.java | 40 ++++++++++++ .../bentobox/level/panels/DetailsPanel.java | 65 ++++++++++--------- .../java/world/bentobox/level/util/Utils.java | 1 - src/main/resources/locales/en-US.yml | 1 + .../bentobox/level/LevelsManagerTest.java | 1 - 8 files changed, 78 insertions(+), 35 deletions(-) diff --git a/src/main/java/world/bentobox/level/LevelsManager.java b/src/main/java/world/bentobox/level/LevelsManager.java index 3f3da02..61cb122 100644 --- a/src/main/java/world/bentobox/level/LevelsManager.java +++ b/src/main/java/world/bentobox/level/LevelsManager.java @@ -256,6 +256,7 @@ public class LevelsManager { if (ld != null) { levelsCache.put(id, ld); } else { + // Clean up just in case handler.deleteID(id); levelsCache.put(id, new IslandLevels(id)); } diff --git a/src/main/java/world/bentobox/level/calculators/Results.java b/src/main/java/world/bentobox/level/calculators/Results.java index f293516..b744046 100644 --- a/src/main/java/world/bentobox/level/calculators/Results.java +++ b/src/main/java/world/bentobox/level/calculators/Results.java @@ -4,8 +4,6 @@ import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; -import org.bukkit.Material; - import com.google.common.collect.HashMultiset; import com.google.common.collect.Multiset; diff --git a/src/main/java/world/bentobox/level/calculators/UltimateStackerCalc.java b/src/main/java/world/bentobox/level/calculators/UltimateStackerCalc.java index bc89f1c..e12a5e8 100644 --- a/src/main/java/world/bentobox/level/calculators/UltimateStackerCalc.java +++ b/src/main/java/world/bentobox/level/calculators/UltimateStackerCalc.java @@ -6,8 +6,6 @@ import org.bukkit.Material; import com.craftaro.ultimatestacker.api.UltimateStackerApi; import com.craftaro.ultimatestacker.api.utils.Stackable; -import world.bentobox.bentobox.BentoBox; - /** * Isolates UltimateStacker imports so that they are only loaded if the plugin exists */ diff --git a/src/main/java/world/bentobox/level/objects/IslandLevels.java b/src/main/java/world/bentobox/level/objects/IslandLevels.java index f1d41eb..2a4f709 100644 --- a/src/main/java/world/bentobox/level/objects/IslandLevels.java +++ b/src/main/java/world/bentobox/level/objects/IslandLevels.java @@ -1,8 +1,12 @@ package world.bentobox.level.objects; import java.util.HashMap; +import java.util.Locale; import java.util.Map; +import org.bukkit.Material; +import org.bukkit.entity.EntityType; + import com.google.gson.annotations.Expose; import world.bentobox.bentobox.database.objects.DataObject; @@ -172,6 +176,9 @@ public class IslandLevels implements DataObject { * @param map the uwCount to set */ public void setUwCount(Map map) { + // Loaded objects come in as strings, so need to be converted to Material Or EntityTypes + uwCount = convertMap(uwCount); + this.uwCount = map; } @@ -180,9 +187,42 @@ public class IslandLevels implements DataObject { * @return the mdCount */ public Map getMdCount() { + // Loaded objects come in as strings, so need to be converted to Material Or EntityTypes + mdCount = convertMap(mdCount); return mdCount; } + private Map convertMap(Map mdCount) { + Map convertedMap = new HashMap<>(); + + for (Map.Entry entry : mdCount.entrySet()) { + Object key = entry.getKey(); + Integer value = entry.getValue(); + + if (key instanceof String) { + String keyStr = (String) key; + // First, try converting to Material + Material material = Material.matchMaterial(keyStr); + if (material != null) { + convertedMap.put(material, value); + } else { + // Fallback to converting to EntityType (using uppercase as enum constants are uppercase) + try { + EntityType entityType = EntityType.valueOf(keyStr.toUpperCase(Locale.ENGLISH)); + convertedMap.put(entityType, value); + } catch (IllegalArgumentException ex) { + // No valid Material or EntityType found. + convertedMap.put(key, value); // Leave the key unchanged. + } + } + } else { + // If the key is not a String, add it directly. + convertedMap.put(key, value); + } + } + return convertedMap; + } + /** * All blocks except for underwater blocks * @param mdCount the mdCount to set diff --git a/src/main/java/world/bentobox/level/panels/DetailsPanel.java b/src/main/java/world/bentobox/level/panels/DetailsPanel.java index b8565b0..fe89fd0 100644 --- a/src/main/java/world/bentobox/level/panels/DetailsPanel.java +++ b/src/main/java/world/bentobox/level/panels/DetailsPanel.java @@ -61,7 +61,7 @@ public class DetailsPanel { this.levelsData = null; } - // By default no-filters are active. + // Default Filters this.activeTab = Tab.VALUE_BLOCKS; this.activeFilter = Filter.NAME; this.materialCountList = new ArrayList<>(Material.values().length); @@ -139,12 +139,12 @@ public class DetailsPanel { } // Remove any hidden blocks materialCountMap.keySet().removeIf(this.addon.getBlockConfig()::isHiddenBlock); - // Remove any zero amount items materialCountMap.values().removeIf(i -> i < 1); if (this.activeTab == Tab.VALUE_BLOCKS) { // Remove zero-value blocks + // TODO BUG IS HERE - ALL REMOVED materialCountMap.entrySet().removeIf(en -> Optional .ofNullable(this.addon.getBlockConfig().getValue(world, en.getKey())).orElse(0) == 0); } @@ -155,7 +155,7 @@ public class DetailsPanel { .collect(Collectors.toList())); } - + // Sort and filter Comparator> sorter; switch (this.activeFilter) { @@ -205,7 +205,6 @@ public class DetailsPanel { } this.materialCountList.sort(sorter); - this.pageIndex = 0; } @@ -519,7 +518,7 @@ public class DetailsPanel { */ private PanelItem createMaterialButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) { if (this.materialCountList.isEmpty()) { - // Does not contain any generators. + // Nothing to show return null; } @@ -546,45 +545,49 @@ public class DetailsPanel { if (template.icon() != null) { builder.icon(template.icon().clone()); } - // Show amount, if < 64 + // Show amount if less than 64. if (materialCount.getValue() < 64) { builder.amount(materialCount.getValue()); } final String reference = "level.gui.buttons.material."; - String description = ""; + Object key = materialCount.getKey(); + String description = Utils.prettifyDescription(key, this.user); String blockId = ""; - Object key = materialCount.getKey(); // Can be a Material or EntityType - if (key instanceof Material m) { - builder.icon(PanelUtils.getMaterialItem(m)); - if (template.title() != null) { - builder.name(this.user.getTranslation(this.world, template.title(), TextVariables.NUMBER, - String.valueOf(materialCount.getValue()), "[material]", Utils.prettifyObject(m, this.user))); - } - description = Utils.prettifyDescription(m, this.user); - blockId = this.user.getTranslationOrNothing(reference + "id", "[id]", m.name()); + int blockValue = 0; + int blockLimit = 0; + String value = ""; + String limit = ""; + String displayMaterial = Utils.prettifyObject(key, this.user); + if (key instanceof Material m) { + // Material-specific settings. + builder.icon(PanelUtils.getMaterialItem(m)); + blockId = this.user.getTranslationOrNothing(reference + "id", "[id]", m.name()); + blockValue = this.addon.getBlockConfig().getBlockValues().getOrDefault(m, 0); + blockLimit = this.addon.getBlockConfig().getBlockLimits().getOrDefault(m, 0); } else if (key instanceof EntityType e) { + // EntityType-specific settings. builder.icon(PanelUtils.getEntityEgg(e)); - if (template.title() != null) { - builder.name(this.user.getTranslation(this.world, template.title(), TextVariables.NUMBER, - String.valueOf(materialCount.getValue()), "[material]", Utils.prettifyObject(e, this.user))); - } - description = Utils.prettifyDescription(e, this.user); - blockId = this.user.getTranslationOrNothing(reference + "id", "[id]", e.name()); + description += this.user.getTranslation(this.world, "level.gui.buttons.spawner.block-name"); // Put spawner on the end + blockId = this.user.getTranslationOrNothing(reference + "id", "[id]", e.name() + "_SPAWNER"); + blockValue = this.addon.getBlockConfig().getSpawnerValues().getOrDefault(e, 0); + blockLimit = this.addon.getBlockConfig().getBlockLimits().getOrDefault(Material.SPAWNER, 0); } - int blockValue = this.addon.getBlockConfig().getBlockValues().getOrDefault(key, 0); - String value = blockValue > 0 + if (template.title() != null) { + builder.name(this.user.getTranslation(this.world, template.title(), TextVariables.NUMBER, + String.valueOf(materialCount.getValue()), "[material]", displayMaterial)); + } + + value = blockValue > 0 ? this.user.getTranslationOrNothing(reference + "value", TextVariables.NUMBER, String.valueOf(blockValue)) - : ""; - - int blockLimit = this.addon.getBlockConfig().getBlockLimits().getOrDefault(key, 0); - String limit = blockLimit > 0 + : ""; + limit = blockLimit > 0 ? this.user.getTranslationOrNothing(reference + "limit", TextVariables.NUMBER, String.valueOf(blockLimit)) - : ""; + : ""; String count = this.user.getTranslationOrNothing(reference + "count", TextVariables.NUMBER, String.valueOf(materialCount.getValue())); @@ -594,6 +597,10 @@ public class DetailsPanel { String valueText = calculatedValue > 0 ? this.user.getTranslationOrNothing(reference + "calculated", TextVariables.NUMBER, String.valueOf(calculatedValue)) : ""; + // Hide block ID unless user is an operator. + if (!user.isOp()) { + blockId = ""; + } if (template.description() != null) { builder.description(this.user .getTranslation(this.world, template.description(), "[description]", description, "[id]", blockId, diff --git a/src/main/java/world/bentobox/level/util/Utils.java b/src/main/java/world/bentobox/level/util/Utils.java index 938e62d..267d865 100644 --- a/src/main/java/world/bentobox/level/util/Utils.java +++ b/src/main/java/world/bentobox/level/util/Utils.java @@ -12,7 +12,6 @@ import java.util.List; import org.bukkit.Material; import org.bukkit.entity.EntityType; import org.bukkit.permissions.PermissionAttachmentInfo; -import org.eclipse.jdt.annotation.Nullable; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.hooks.LangUtilsHook; diff --git a/src/main/resources/locales/en-US.yml b/src/main/resources/locales/en-US.yml index 3d7dbdc..d11e474 100755 --- a/src/main/resources/locales/en-US.yml +++ b/src/main/resources/locales/en-US.yml @@ -142,6 +142,7 @@ level: name: "&f&l Spawners" description: |- &7 Display only spawners. + block-name: "&b Spawner" filters: name: name: "&f&l Sort by Name" diff --git a/src/test/java/world/bentobox/level/LevelsManagerTest.java b/src/test/java/world/bentobox/level/LevelsManagerTest.java index 4ea67e1..b443123 100644 --- a/src/test/java/world/bentobox/level/LevelsManagerTest.java +++ b/src/test/java/world/bentobox/level/LevelsManagerTest.java @@ -16,7 +16,6 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; -import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; From 745be0b5092ca712d784d23a82d98ef3c572ab2b Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 22 Feb 2025 13:44:57 -0800 Subject: [PATCH 5/7] Handle edge case of blank spawner --- .../level/calculators/IslandLevelCalculator.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java b/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java index 5a9a457..af8dc91 100644 --- a/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java +++ b/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java @@ -646,8 +646,13 @@ public class IslandLevelCalculator { for (Map.Entry en : this.spawners.entrySet()) { CompletableFuture future = Util.getChunkAtAsync(en.getKey()).thenAccept(c -> { if (en.getKey().getBlock().getType() == Material.SPAWNER) { - CreatureSpawner cs = (CreatureSpawner) en.getKey().getBlock().getState(); - checkSpawner(cs.getSpawnedType(), en.getValue()); + EntityType et = ((CreatureSpawner) en.getKey().getBlock().getState()).getSpawnedType(); + if (et != null) { + checkSpawner(et, en.getValue()); + } else { + // This spawner has no spawning capability. Just list it as a spawner block + checkBlock(Material.SPAWNER, en.getValue()); + } } }); futures.add(future); From 53beedcc1dfa6eabf44a6a83e9a28abab784f7e3 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 22 Feb 2025 15:20:12 -0800 Subject: [PATCH 6/7] Fixed up value panel --- .../calculators/IslandLevelCalculator.java | 62 ++-- .../level/commands/IslandValueCommand.java | 4 +- .../bentobox/level/config/BlockConfig.java | 42 +-- .../bentobox/level/panels/DetailsPanel.java | 284 ++++++++-------- .../bentobox/level/panels/ValuePanel.java | 317 +++++++++++------- 5 files changed, 392 insertions(+), 317 deletions(-) diff --git a/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java b/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java index af8dc91..bcaf620 100644 --- a/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java +++ b/src/main/java/world/bentobox/level/calculators/IslandLevelCalculator.java @@ -60,7 +60,7 @@ public class IslandLevelCalculator { private final Level addon; private final Queue> chunksToCheck; private final Island island; - private final Map limitCount; + private final Map limitCount; private final CompletableFuture r; private final Results results; @@ -89,7 +89,7 @@ public class IslandLevelCalculator { results = new Results(); duration = System.currentTimeMillis(); chunksToCheck = getChunksToScan(island); - this.limitCount = new EnumMap<>(addon.getBlockConfig().getBlockLimits()); + this.limitCount = new HashMap<>(); // Get the initial island level results.initialLevel.set(addon.getInitialIslandLevel(island)); // Set up the worlds @@ -159,10 +159,7 @@ public class IslandLevelCalculator { * @param belowSeaLevel - true if below sea level */ private void checkSpawner(EntityType et, boolean belowSeaLevel) { - if (limitCount(Material.SPAWNER) == 0) { - return; - } - Integer count = addon.getBlockConfig().getValue(island.getWorld(), et); + Integer count = limitCount(et); if (count != null) { if (belowSeaLevel) { results.underWaterBlockCount.addAndGet(count); @@ -247,14 +244,9 @@ public class IslandLevelCalculator { while (it.hasNext()) { Entry type = it.next(); - Material m = type.getElement() instanceof Material mat ? mat : Material.SPAWNER; - Integer limit = addon.getBlockConfig().getBlockLimits().get(m); + Integer limit = addon.getBlockConfig().getLimit(type); String explain = ")"; - if (limit == null) { - limit = addon.getBlockConfig().getBlockLimits().get(m); - explain = " - All types)"; - } - reportLines.add(Util.prettifyText(m.name()) + ": " + String.format("%,d", type.getCount()) + reportLines.add(Util.prettifyText(type.toString()) + ": " + String.format("%,d", type.getCount()) + " blocks (max " + limit + explain); } reportLines.add(LINE_BREAK); @@ -271,10 +263,10 @@ public class IslandLevelCalculator { /** * Get value of a material World blocks trump regular block values * - * @param md - Material to check - * @return value of a material + * @param md - Material or EntityType to check + * @return value */ - private int getValue(Material md) { + private int getValue(Object md) { Integer value = addon.getBlockConfig().getValue(island.getWorld(), md); if (value == null) { // Not in config @@ -335,24 +327,28 @@ public class IslandLevelCalculator { } /** - * Checks if a block has been limited or not and whether a block has any value - * or not - * - * @param md Material - * @return value of the block if can be counted + * Checks if the given object (Material or EntityType) has reached its limit. + * If it hasn't, the object's value is returned and its count is incremented. + * If the object is not a Material or EntityType, or if it has exceeded its limit, 0 is returned. + * + * @param obj A Material or EntityType + * @return The object's value if within limit, otherwise 0. */ - private int limitCount(Material md) { - if (limitCount.containsKey(md)) { - int count = limitCount.get(md); - if (count > 0) { - limitCount.put(md, --count); - return getValue(md); - } else { - results.ofCount.add(md); - return 0; - } - } - return getValue(md); + private int limitCount(Object obj) { + // Only process if obj is a Material or EntityType + if (!(obj instanceof Material) && !(obj instanceof EntityType)) + return 0; + + Integer limit = addon.getBlockConfig().getLimit(obj); + if (limit == null) + return getValue(obj); + + int count = limitCount.getOrDefault(obj, 0); + if (count > limit) + return 0; + + limitCount.put(obj, count + 1); + return getValue(obj); } /** diff --git a/src/main/java/world/bentobox/level/commands/IslandValueCommand.java b/src/main/java/world/bentobox/level/commands/IslandValueCommand.java index 82f49b4..9e3c4f5 100644 --- a/src/main/java/world/bentobox/level/commands/IslandValueCommand.java +++ b/src/main/java/world/bentobox/level/commands/IslandValueCommand.java @@ -123,8 +123,8 @@ public class IslandValueCommand extends CompositeCommand int count = lvData.getMdCount().getOrDefault(material, 0) + lvData.getUwCount().getOrDefault(material, 0); user.sendMessage("level.conversations.you-have", TextVariables.NUMBER, String.valueOf(count)); - int limit = this.addon.getBlockConfig().getBlockLimits().getOrDefault(material, -1); - if (limit > 0) { + Integer limit = this.addon.getBlockConfig().getLimit(material); + if (limit != null) { user.sendMessage("level.conversations.you-can-place", TextVariables.NUMBER, String.valueOf(limit)); } diff --git a/src/main/java/world/bentobox/level/config/BlockConfig.java b/src/main/java/world/bentobox/level/config/BlockConfig.java index f92f5ed..4ac7e76 100644 --- a/src/main/java/world/bentobox/level/config/BlockConfig.java +++ b/src/main/java/world/bentobox/level/config/BlockConfig.java @@ -27,7 +27,7 @@ import world.bentobox.level.Level; public class BlockConfig { private static final String SPAWNER = "_SPAWNER"; - private Map blockLimits = new EnumMap<>(Material.class); + private Map blockLimits = new HashMap<>(); private Map blockValues = new EnumMap<>(Material.class); private final Map> worldBlockValues = new HashMap<>(); private final Map> worldSpawnerValues = new HashMap<>(); @@ -45,7 +45,9 @@ public class BlockConfig { public BlockConfig(Level addon, YamlConfiguration blockValues, File file) throws IOException { this.addon = addon; if (blockValues.isConfigurationSection("limits")) { - setBlockLimits(loadBlockLimits(blockValues)); + for (String key : blockValues.getConfigurationSection("limits").getKeys(false)) { + blockLimits.put(key, blockValues.getConfigurationSection("limits").getInt(key)); + } } if (blockValues.isConfigurationSection("blocks")) { setBlockValues(loadBlockValues(blockValues)); @@ -144,31 +146,19 @@ public class BlockConfig { return bv; } - private Map loadBlockLimits(YamlConfiguration blockValues2) { - Map bl = new EnumMap<>(Material.class); - ConfigurationSection limits = Objects.requireNonNull(blockValues2.getConfigurationSection("limits")); - for (String material : limits.getKeys(false)) { - try { - Material mat = Material.valueOf(material); - bl.put(mat, limits.getInt(material, 0)); - } catch (Exception e) { - addon.logError("Unknown material (" + material + ") in blockconfig.yml Limits section. Skipping..."); - } + /** + * Return the limits for any particular material or entity type + * @param obj material or entity type + * @return the limit or null if there isn't one + */ + public Integer getLimit(Object obj) { + if (obj instanceof Material m) { + return blockLimits.get(m.name()); } - return bl; - } - - /** - * @return the blockLimits - */ - public final Map getBlockLimits() { - return blockLimits; - } - /** - * @param bl the blockLimits to set - */ - private void setBlockLimits(Map bl) { - this.blockLimits = bl; + if (obj instanceof EntityType et) { + return blockLimits.get(et.name().concat(SPAWNER)); + } + return null; } /** * @return the blockValues diff --git a/src/main/java/world/bentobox/level/panels/DetailsPanel.java b/src/main/java/world/bentobox/level/panels/DetailsPanel.java index fe89fd0..0349e1a 100644 --- a/src/main/java/world/bentobox/level/panels/DetailsPanel.java +++ b/src/main/java/world/bentobox/level/panels/DetailsPanel.java @@ -7,6 +7,7 @@ import java.util.EnumMap; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; @@ -27,7 +28,6 @@ import world.bentobox.bentobox.api.panels.builders.TemplatedPanelBuilder; import world.bentobox.bentobox.api.panels.reader.ItemTemplateRecord; import world.bentobox.bentobox.api.user.User; import world.bentobox.bentobox.database.objects.Island; -import world.bentobox.bentobox.util.Pair; import world.bentobox.level.Level; import world.bentobox.level.objects.IslandLevels; import world.bentobox.level.util.Utils; @@ -36,10 +36,112 @@ import world.bentobox.level.util.Utils; * This class opens GUI that shows generator view for user. */ public class DetailsPanel { + + private static final String SPAWNER = "_SPAWNER"; + // --------------------------------------------------------------------- - // Section: Internal Constructor + // Section: Enums // --------------------------------------------------------------------- + /** + * This enum holds possible tabs for current gui. + */ + private enum Tab { + /** + * All block Tab + */ + ALL_BLOCKS, + /** + * Blocks that have value + */ + VALUE_BLOCKS, + /** + * Above Sea level Tab. + */ + ABOVE_SEA_LEVEL, + /** + * Underwater Tab. + */ + UNDERWATER, + /** + * Spawner Tab. + */ + SPAWNER + } + + /** + * Sorting order of blocks. + */ + private enum Filter { + /** + * By name + */ + NAME, + /** + * By value + */ + VALUE, + /** + * By number + */ + COUNT + } + + // --------------------------------------------------------------------- + // Section: Variables + // --------------------------------------------------------------------- + + /** + * This variable holds targeted island. + */ + private final Island island; + + /** + * This variable holds targeted island level data. + */ + private final IslandLevels levelsData; + + /** + * This variable allows to access addon object. + */ + private final Level addon; + + /** + * This variable holds user who opens panel. Without it panel cannot be opened. + */ + private final User user; + + /** + * This variable holds a world to which gui referee. + */ + private final World world; + + /** + * Record that stores a Material or EntityType as a key and a value + */ + private record BlockRec(Object key, Integer value, Integer limit) { + } + + /** + * This variable stores the list of elements to display. + */ + private final List blockCountList; + + /** + * This variable holds current pageIndex for multi-page generator choosing. + */ + private int pageIndex; + + /** + * This variable stores which tab currently is active. + */ + private Tab activeTab; + + /** + * This variable stores active filter for items. + */ + private Filter activeFilter; + /** * This is internal constructor. It is used internally in current class to avoid * creating objects everywhere. @@ -64,7 +166,7 @@ public class DetailsPanel { // Default Filters this.activeTab = Tab.VALUE_BLOCKS; this.activeFilter = Filter.NAME; - this.materialCountList = new ArrayList<>(Material.values().length); + this.blockCountList = new ArrayList<>(); this.updateFilters(); } @@ -96,7 +198,7 @@ public class DetailsPanel { panelBuilder.registerTypeBuilder("NEXT", this::createNextButton); panelBuilder.registerTypeBuilder("PREVIOUS", this::createPreviousButton); - panelBuilder.registerTypeBuilder("BLOCK", this::createMaterialButton); + panelBuilder.registerTypeBuilder("BLOCK", this::createBlockButton); panelBuilder.registerTypeBuilder("FILTER", this::createFilterButton); @@ -111,7 +213,7 @@ public class DetailsPanel { * This method updates filter of elements based on tabs. */ private void updateFilters() { - this.materialCountList.clear(); + this.blockCountList.clear(); if (this.activeTab == Tab.SPAWNER) { if (this.addon.getBlockConfig().isNotHiddenBlock(Material.SPAWNER)) { @@ -122,7 +224,7 @@ public class DetailsPanel { spawnerCountMap.entrySet().stream().sorted((Map.Entry.comparingByKey())).forEachOrdered(entry -> { if (entry.getValue() > 0) { - this.materialCountList.add(new Pair<>(entry.getKey(), entry.getValue())); + this.blockCountList.add(new BlockRec(entry.getKey(), entry.getValue(), 0)); } }); } @@ -144,49 +246,48 @@ public class DetailsPanel { if (this.activeTab == Tab.VALUE_BLOCKS) { // Remove zero-value blocks - // TODO BUG IS HERE - ALL REMOVED materialCountMap.entrySet().removeIf(en -> Optional .ofNullable(this.addon.getBlockConfig().getValue(world, en.getKey())).orElse(0) == 0); } // All done filtering, add the left overs - materialCountList.addAll( - materialCountMap.entrySet().stream().map(entry -> new Pair<>(entry.getKey(), entry.getValue())) - //.sorted(Comparator.comparing(pair -> ((Enum) pair.getKey()).name())) + blockCountList.addAll( + materialCountMap.entrySet().stream() + .map(entry -> new BlockRec(entry.getKey(), entry.getValue(), 0)) .collect(Collectors.toList())); } // Sort and filter - Comparator> sorter; + Comparator sorter; switch (this.activeFilter) { case COUNT -> { sorter = (o1, o2) -> { - if (o1.getValue().equals(o2.getValue())) { - String o1Name = Utils.prettifyObject(o1.getKey(), this.user); - String o2Name = Utils.prettifyObject(o2.getKey(), this.user); + if (o1.value().equals(o2.value())) { + String o1Name = Utils.prettifyObject(o1.key(), this.user); + String o2Name = Utils.prettifyObject(o2.key(), this.user); return String.CASE_INSENSITIVE_ORDER.compare(o1Name, o2Name); } else { - return Integer.compare(o2.getValue(), o1.getValue()); + return Integer.compare(o2.value(), o1.value()); } }; } case VALUE -> { sorter = (o1, o2) -> { - int blockLimit = this.addon.getBlockConfig().getBlockLimits().getOrDefault(o1.getKey(), 0); - int o1Count = blockLimit > 0 ? Math.min(o1.getValue(), blockLimit) : o1.getValue(); + int blockLimit = Objects.requireNonNullElse(this.addon.getBlockConfig().getLimit(o1.key()), 0); + int o1Count = blockLimit > 0 ? Math.min(o1.value(), blockLimit) : o1.value(); - blockLimit = this.addon.getBlockConfig().getBlockLimits().getOrDefault(o2.getKey(), 0); - int o2Count = blockLimit > 0 ? Math.min(o2.getValue(), blockLimit) : o2.getValue(); + blockLimit = Objects.requireNonNullElse(this.addon.getBlockConfig().getLimit(o2.key()), 0); + int o2Count = blockLimit > 0 ? Math.min(o2.value(), blockLimit) : o2.value(); long o1Value = (long) o1Count - * this.addon.getBlockConfig().getBlockValues().getOrDefault(o1.getKey(), 0); + * this.addon.getBlockConfig().getBlockValues().getOrDefault(o1.key(), 0); long o2Value = (long) o2Count - * this.addon.getBlockConfig().getBlockValues().getOrDefault(o2.getKey(), 0); + * this.addon.getBlockConfig().getBlockValues().getOrDefault(o2.key(), 0); if (o1Value == o2Value) { - String o1Name = Utils.prettifyObject(o1.getKey(), this.user); - String o2Name = Utils.prettifyObject(o2.getKey(), this.user); + String o1Name = Utils.prettifyObject(o1.key(), this.user); + String o2Name = Utils.prettifyObject(o2.key(), this.user); return String.CASE_INSENSITIVE_ORDER.compare(o1Name, o2Name); } else { @@ -196,15 +297,15 @@ public class DetailsPanel { } default -> { sorter = (o1, o2) -> { - String o1Name = Utils.prettifyObject(o1.getKey(), this.user); - String o2Name = Utils.prettifyObject(o2.getKey(), this.user); + String o1Name = Utils.prettifyObject(o1.key(), this.user); + String o2Name = Utils.prettifyObject(o2.key(), this.user); return String.CASE_INSENSITIVE_ORDER.compare(o1Name, o2Name); }; } } - this.materialCountList.sort(sorter); + this.blockCountList.sort(sorter); this.pageIndex = 0; } @@ -380,7 +481,7 @@ public class DetailsPanel { * @return the panel item */ private PanelItem createNextButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) { - long size = this.materialCountList.size(); + long size = this.blockCountList.size(); if (size <= slot.amountMap().getOrDefault("BLOCK", 1) || 1.0 * size / slot.amountMap().getOrDefault("BLOCK", 1) <= this.pageIndex + 1) { @@ -516,42 +617,42 @@ public class DetailsPanel { * @param slot the slot * @return the panel item */ - private PanelItem createMaterialButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) { - if (this.materialCountList.isEmpty()) { + private PanelItem createBlockButton(ItemTemplateRecord template, TemplatedPanel.ItemSlot slot) { + if (this.blockCountList.isEmpty()) { // Nothing to show return null; } int index = this.pageIndex * slot.amountMap().getOrDefault("BLOCK", 1) + slot.slot(); - if (index >= this.materialCountList.size()) { + if (index >= this.blockCountList.size()) { // Out of index. return null; } - return this.createMaterialButton(template, this.materialCountList.get(index)); + return this.createMaterialButton(template, this.blockCountList.get(index)); } /** - * This method creates button for material. + * This method creates button for block. * * @param template the template of the button - * @param materialCount materialCount which button must be created. - * @return PanelItem for generator tier. + * @param blockCount count + * @return PanelItem button */ - private PanelItem createMaterialButton(ItemTemplateRecord template, Pair materialCount) { + private PanelItem createMaterialButton(ItemTemplateRecord template, BlockRec blockCount) { PanelItemBuilder builder = new PanelItemBuilder(); if (template.icon() != null) { builder.icon(template.icon().clone()); } // Show amount if less than 64. - if (materialCount.getValue() < 64) { - builder.amount(materialCount.getValue()); + if (blockCount.value() < 64) { + builder.amount(blockCount.value()); } final String reference = "level.gui.buttons.material."; - Object key = materialCount.getKey(); + Object key = blockCount.key(); String description = Utils.prettifyDescription(key, this.user); String blockId = ""; int blockValue = 0; @@ -565,19 +666,19 @@ public class DetailsPanel { builder.icon(PanelUtils.getMaterialItem(m)); blockId = this.user.getTranslationOrNothing(reference + "id", "[id]", m.name()); blockValue = this.addon.getBlockConfig().getBlockValues().getOrDefault(m, 0); - blockLimit = this.addon.getBlockConfig().getBlockLimits().getOrDefault(m, 0); + blockLimit = Objects.requireNonNullElse(this.addon.getBlockConfig().getLimit(m), 0); } else if (key instanceof EntityType e) { // EntityType-specific settings. builder.icon(PanelUtils.getEntityEgg(e)); description += this.user.getTranslation(this.world, "level.gui.buttons.spawner.block-name"); // Put spawner on the end - blockId = this.user.getTranslationOrNothing(reference + "id", "[id]", e.name() + "_SPAWNER"); + blockId = this.user.getTranslationOrNothing(reference + "id", "[id]", e.name().concat(SPAWNER)); blockValue = this.addon.getBlockConfig().getSpawnerValues().getOrDefault(e, 0); - blockLimit = this.addon.getBlockConfig().getBlockLimits().getOrDefault(Material.SPAWNER, 0); + blockLimit = Objects.requireNonNullElse(this.addon.getBlockConfig().getLimit(e), 0); } if (template.title() != null) { builder.name(this.user.getTranslation(this.world, template.title(), TextVariables.NUMBER, - String.valueOf(materialCount.getValue()), "[material]", displayMaterial)); + String.valueOf(blockCount.value()), "[material]", displayMaterial)); } value = blockValue > 0 @@ -590,10 +691,10 @@ public class DetailsPanel { : ""; String count = this.user.getTranslationOrNothing(reference + "count", TextVariables.NUMBER, - String.valueOf(materialCount.getValue())); + String.valueOf(blockCount.value())); long calculatedValue = (long) Math.min(blockLimit > 0 ? blockLimit : Integer.MAX_VALUE, - materialCount.getValue()) * blockValue; + blockCount.value()) * blockValue; String valueText = calculatedValue > 0 ? this.user.getTranslationOrNothing(reference + "calculated", TextVariables.NUMBER, String.valueOf(calculatedValue)) : ""; @@ -627,100 +728,5 @@ public class DetailsPanel { new DetailsPanel(addon, world, user).build(); } - // --------------------------------------------------------------------- - // Section: Enums - // --------------------------------------------------------------------- - /** - * This enum holds possible tabs for current gui. - */ - private enum Tab { - /** - * All block Tab - */ - ALL_BLOCKS, - /** - * Blocks that have value - */ - VALUE_BLOCKS, - /** - * Above Sea level Tab. - */ - ABOVE_SEA_LEVEL, - /** - * Underwater Tab. - */ - UNDERWATER, - /** - * Spawner Tab. - */ - SPAWNER - } - - /** - * Sorting order of blocks. - */ - private enum Filter { - /** - * By name - */ - NAME, - /** - * By value - */ - VALUE, - /** - * By number - */ - COUNT - } - - // --------------------------------------------------------------------- - // Section: Variables - // --------------------------------------------------------------------- - - /** - * This variable holds targeted island. - */ - private final Island island; - - /** - * This variable holds targeted island level data. - */ - private final IslandLevels levelsData; - - /** - * This variable allows to access addon object. - */ - private final Level addon; - - /** - * This variable holds user who opens panel. Without it panel cannot be opened. - */ - private final User user; - - /** - * This variable holds a world to which gui referee. - */ - private final World world; - - /** - * This variable stores the list of elements to display. - */ - private final List> materialCountList; - - /** - * This variable holds current pageIndex for multi-page generator choosing. - */ - private int pageIndex; - - /** - * This variable stores which tab currently is active. - */ - private Tab activeTab; - - /** - * This variable stores active filter for items. - */ - private Filter activeFilter; } diff --git a/src/main/java/world/bentobox/level/panels/ValuePanel.java b/src/main/java/world/bentobox/level/panels/ValuePanel.java index df59b12..421f919 100644 --- a/src/main/java/world/bentobox/level/panels/ValuePanel.java +++ b/src/main/java/world/bentobox/level/panels/ValuePanel.java @@ -6,11 +6,13 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; +import java.util.Objects; import java.util.function.Consumer; import java.util.stream.Collectors; import org.bukkit.Material; import org.bukkit.World; +import org.bukkit.entity.EntityType; import org.bukkit.event.inventory.ClickType; import org.bukkit.inventory.ItemStack; @@ -34,11 +36,86 @@ import world.bentobox.level.util.Utils; */ public class ValuePanel { - // --------------------------------------------------------------------- - // Section: Internal Constructor + // Section: Enums // --------------------------------------------------------------------- + /** + * Sorting order of blocks. + */ + private enum Filter { + /** + * By name asc + */ + NAME_ASC, + /** + * By name desc + */ + NAME_DESC, + /** + * By value asc + */ + VALUE_ASC, + /** + * By value desc + */ + VALUE_DESC, + } + + private record BlockRecord(Object keyl, Integer value, Integer limit) { + } + + // --------------------------------------------------------------------- + // Section: Constants + // --------------------------------------------------------------------- + + private static final String BLOCK = "BLOCK"; + + private static final String SPAWNER = "_SPAWNER"; + + // --------------------------------------------------------------------- + // Section: Variables + // --------------------------------------------------------------------- + + /** + * This variable allows to access addon object. + */ + private final Level addon; + + /** + * This variable holds user who opens panel. Without it panel cannot be opened. + */ + private final User user; + + /** + * This variable holds a world to which gui referee. + */ + private final World world; + + /** + * This variable stores the list of elements to display. + */ + private final List blockRecordList; + + /** + * This variable stores the list of elements to display. + */ + private List elementList; + + /** + * This variable holds current pageIndex for multi-page generator choosing. + */ + private int pageIndex; + + /** + * This variable stores which tab currently is active. + */ + private String searchText; + + /** + * This variable stores active filter for items. + */ + private Filter activeFilter; /** * This is internal constructor. It is used internally in current class to avoid creating objects everywhere. @@ -56,7 +133,8 @@ public class ValuePanel this.user = user; this.activeFilter = Filter.NAME_ASC; - this.materialRecordList = Arrays.stream(Material.values()). + + this.blockRecordList = Arrays.stream(Material.values()). filter(Material::isBlock). filter(Material::isItem). // Remove things like PITCHER_CROP filter(m -> !m.name().startsWith("LEGACY_")). @@ -64,13 +142,22 @@ public class ValuePanel map(material -> { Integer value = this.addon.getBlockConfig().getValue(this.world, material); - Integer limit = this.addon.getBlockConfig().getBlockLimits().get(material); + Integer limit = this.addon.getBlockConfig().getLimit(material); - return new MaterialRecord(material, + return new BlockRecord(material, value != null ? value : 0, limit != null ? limit : 0); }). collect(Collectors.toList()); + // Add spawn eggs + this.blockRecordList.addAll(Arrays.stream(EntityType.values()).filter(EntityType::isSpawnable) + .filter(EntityType::isAlive) + .filter(this.addon.getBlockConfig()::isNotHiddenBlock).map(et -> { + Integer value = this.addon.getBlockConfig().getValue(this.world, et); + Integer limit = this.addon.getBlockConfig().getLimit(et); + + return new BlockRecord(et, value != null ? value : 0, limit != null ? limit : 0); + }).collect(Collectors.toList())); this.elementList = new ArrayList<>(Material.values().length); this.searchText = ""; @@ -108,7 +195,7 @@ public class ValuePanel */ private void updateFilters() { - Comparator sorter; + Comparator sorter; switch (this.activeFilter) { @@ -118,8 +205,8 @@ public class ValuePanel { if (o1.value().equals(o2.value())) { - String o1Name = Utils.prettifyObject(o1.material(), this.user); - String o2Name = Utils.prettifyObject(o2.material(), this.user); + String o1Name = Utils.prettifyObject(o1.keyl(), this.user); + String o2Name = Utils.prettifyObject(o2.keyl(), this.user); return String.CASE_INSENSITIVE_ORDER.compare(o1Name, o2Name); } @@ -135,8 +222,8 @@ public class ValuePanel { if (o1.value().equals(o2.value())) { - String o1Name = Utils.prettifyObject(o1.material(), this.user); - String o2Name = Utils.prettifyObject(o2.material(), this.user); + String o1Name = Utils.prettifyObject(o1.keyl(), this.user); + String o2Name = Utils.prettifyObject(o2.keyl(), this.user); return String.CASE_INSENSITIVE_ORDER.compare(o1Name, o2Name); } @@ -150,8 +237,8 @@ public class ValuePanel sorter = (o1, o2) -> { - String o1Name = Utils.prettifyObject(o1.material(), this.user); - String o2Name = Utils.prettifyObject(o2.material(), this.user); + String o1Name = Utils.prettifyObject(o1.keyl(), this.user); + String o2Name = Utils.prettifyObject(o2.keyl(), this.user); return String.CASE_INSENSITIVE_ORDER.compare(o2Name, o1Name); }; @@ -160,25 +247,26 @@ public class ValuePanel sorter = (o1, o2) -> { - String o1Name = Utils.prettifyObject(o1.material(), this.user); - String o2Name = Utils.prettifyObject(o2.material(), this.user); + String o1Name = Utils.prettifyObject(o1.keyl(), this.user); + String o2Name = Utils.prettifyObject(o2.keyl(), this.user); return String.CASE_INSENSITIVE_ORDER.compare(o1Name, o2Name); }; } - this.materialRecordList.sort(sorter); + this.blockRecordList.sort(sorter); if (!this.searchText.isBlank()) { - this.elementList = new ArrayList<>(this.materialRecordList.size()); + this.elementList = new ArrayList<>(this.blockRecordList.size()); final String text = this.searchText.toLowerCase(); - this.materialRecordList.forEach(rec -> + this.blockRecordList.forEach(rec -> { - if (rec.material.name().toLowerCase().contains(text) || - Utils.prettifyObject(rec.material(), this.user).toLowerCase().contains(text)) + if (rec.keyl.toString().toLowerCase().contains(text) + || + Utils.prettifyObject(rec.keyl(), this.user).toLowerCase().contains(text)) { this.elementList.add(rec); } @@ -186,7 +274,7 @@ public class ValuePanel } else { - this.elementList = this.materialRecordList; + this.elementList = this.blockRecordList; } this.pageIndex = 0; @@ -585,7 +673,6 @@ public class ValuePanel return null; } - @SuppressWarnings("deprecation") int index = this.pageIndex * slot.amountMap().getOrDefault(BLOCK, 1) + slot.slot(); if (index >= this.elementList.size()) @@ -597,6 +684,84 @@ public class ValuePanel return this.createMaterialButton(template, this.elementList.get(index)); } + /** + * This method creates button for block. + * + * @param template the template of the button + * @param blockCount count + * @return PanelItem button + */ + private PanelItem createMaterialButton(ItemTemplateRecord template, BlockRecord blockCount) { + PanelItemBuilder builder = new PanelItemBuilder(); + + if (template.icon() != null) { + builder.icon(template.icon().clone()); + } + // Show amount if less than 64. + if (blockCount.value() < 64) { + builder.amount(blockCount.value()); + } + + final String reference = "level.gui.buttons.material."; + Object key = blockCount.keyl(); + String description = Utils.prettifyDescription(key, this.user); + String blockId = ""; + int blockValue = 0; + int blockLimit = 0; + String value = ""; + String limit = ""; + String displayMaterial = Utils.prettifyObject(key, this.user); + + if (key instanceof Material m) { + // Material-specific settings. + builder.icon(PanelUtils.getMaterialItem(m)); + blockId = this.user.getTranslationOrNothing(reference + "id", "[id]", m.name()); + blockValue = this.addon.getBlockConfig().getBlockValues().getOrDefault(m, 0); + blockLimit = Objects.requireNonNullElse(this.addon.getBlockConfig().getLimit(m), 0); + } else if (key instanceof EntityType e) { + // EntityType-specific settings. + builder.icon(PanelUtils.getEntityEgg(e)); + description += this.user.getTranslation(this.world, "level.gui.buttons.spawner.block-name"); // Put spawner on the end + blockId = this.user.getTranslationOrNothing(reference + "id", "[id]", e.name().concat(SPAWNER)); + blockValue = this.addon.getBlockConfig().getSpawnerValues().getOrDefault(e, 0); + blockLimit = Objects.requireNonNullElse(this.addon.getBlockConfig().getLimit(e), 0); + } + + if (template.title() != null) { + builder.name(this.user.getTranslation(this.world, template.title(), TextVariables.NUMBER, + String.valueOf(blockCount.value()), "[material]", displayMaterial)); + } + + value = blockValue > 0 + ? this.user.getTranslationOrNothing(reference + "value", TextVariables.NUMBER, + String.valueOf(blockValue)) + : ""; + limit = blockLimit > 0 + ? this.user.getTranslationOrNothing(reference + "limit", TextVariables.NUMBER, + String.valueOf(blockLimit)) + : ""; + + // Hide block ID unless user is an operator. + if (!user.isOp()) { + blockId = ""; + } + String underWater; + + if (this.addon.getSettings().getUnderWaterMultiplier() > 1.0) { + underWater = this.user.getTranslationOrNothing(reference + "underwater", TextVariables.NUMBER, + String.valueOf(blockCount.value() * this.addon.getSettings().getUnderWaterMultiplier())); + } else { + underWater = ""; + } + if (template.description() != null) { + builder.description(this.user + .getTranslation(this.world, template.description(), "[description]", description, "[id]", blockId, + "[value]", value, "[underwater]", underWater, "[limit]", limit) + .replaceAll("(?m)^[ \\t]*\\r?\\n", "").replaceAll("(? 0) { builder.amount(materialRecord.value()); } - + if (template.title() != null) { builder.name(this.user.getTranslation(this.world, template.title(), "[material]", Utils.prettifyObject(materialRecord.material(), this.user))); } - + String description = Utils.prettifyDescription(materialRecord.material(), this.user); - + final String reference = "level.gui.buttons.material."; String blockId = this.user.getTranslationOrNothing(reference + "id", "[id]", materialRecord.material().name()); - + String value = this.user.getTranslationOrNothing(reference + "value", TextVariables.NUMBER, String.valueOf(materialRecord.value())); - + String underWater; - + if (this.addon.getSettings().getUnderWaterMultiplier() > 1.0) { underWater = this.user.getTranslationOrNothing(reference + "underwater", @@ -650,10 +816,10 @@ public class ValuePanel { underWater = ""; } - + String limit = materialRecord.limit() > 0 ? this.user.getTranslationOrNothing(reference + "limit", TextVariables.NUMBER, String.valueOf(materialRecord.limit())) : ""; - + if (template.description() != null) { builder.description(this.user.getTranslation(this.world, template.description(), @@ -666,14 +832,14 @@ public class ValuePanel replaceAll("(? { addon.log("Material: " + materialRecord.material()); return true; }); - + return builder.build(); - } + }*/ // --------------------------------------------------------------------- @@ -696,87 +862,4 @@ public class ValuePanel new ValuePanel(addon, world, user).build(); } - - // --------------------------------------------------------------------- - // Section: Enums - // --------------------------------------------------------------------- - - - /** - * Sorting order of blocks. - */ - private enum Filter - { - /** - * By name asc - */ - NAME_ASC, - /** - * By name desc - */ - NAME_DESC, - /** - * By value asc - */ - VALUE_ASC, - /** - * By value desc - */ - VALUE_DESC, - } - - - private record MaterialRecord(Material material, Integer value, Integer limit) - { - } - - // --------------------------------------------------------------------- - // Section: Constants - // --------------------------------------------------------------------- - - private static final String BLOCK = "BLOCK"; - - // --------------------------------------------------------------------- - // Section: Variables - // --------------------------------------------------------------------- - - /** - * This variable allows to access addon object. - */ - private final Level addon; - - /** - * This variable holds user who opens panel. Without it panel cannot be opened. - */ - private final User user; - - /** - * This variable holds a world to which gui referee. - */ - private final World world; - - /** - * This variable stores the list of elements to display. - */ - private final List materialRecordList; - - /** - * This variable stores the list of elements to display. - */ - private List elementList; - - /** - * This variable holds current pageIndex for multi-page generator choosing. - */ - private int pageIndex; - - /** - * This variable stores which tab currently is active. - */ - private String searchText; - - /** - * This variable stores active filter for items. - */ - private Filter activeFilter; } From 5add77cb472ab02b908c5d21ea2c73c07c836775 Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 22 Feb 2025 16:52:25 -0800 Subject: [PATCH 7/7] Update GitHub build to Java 21 --- .github/workflows/build.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 825b18d..f16ddcd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,22 +11,22 @@ jobs: name: Build runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - - name: Set up JDK 17 - uses: actions/setup-java@v3 + - name: Set up JDK 21 + uses: actions/setup-java@v4 with: distribution: 'adopt' - java-version: 17 + java-version: '21' - name: Cache SonarCloud packages - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.sonar/cache key: ${{ runner.os }}-sonar restore-keys: ${{ runner.os }}-sonar - name: Cache Maven packages - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}