From c0f691da3a0bf9c47b59ef916a260dcdbc48815b Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Tue, 5 May 2026 16:26:16 -0700 Subject: [PATCH 1/3] WeightedList - introducing a weighted list --- .../hg/api/util/WeightedList.java | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 src/main/java/com/shanebeestudios/hg/api/util/WeightedList.java diff --git a/src/main/java/com/shanebeestudios/hg/api/util/WeightedList.java b/src/main/java/com/shanebeestudios/hg/api/util/WeightedList.java new file mode 100644 index 00000000..0dbed94f --- /dev/null +++ b/src/main/java/com/shanebeestudios/hg/api/util/WeightedList.java @@ -0,0 +1,73 @@ +package com.shanebeestudios.hg.api.util; + +import java.util.List; +import java.util.Random; +import java.util.TreeMap; + +/** + * A weighted list that allows for random selection based on entry weights. + * + * @param The type of elements in the list. + */ +public class WeightedList { + + private final TreeMap weightMap = new TreeMap<>(); + private final Random random = new Random(); + private double total = 0; + + /** + * Add an entry to the weighted list with a specified weight. + * + * @param entry The entry to add. + * @param weight The weight of the entry. + * @throws IllegalArgumentException if weight is not positive. + */ + public void add(T entry, int weight) { + if (weight <= 0) { + throw new IllegalArgumentException("Weight must be positive"); + } + this.total += weight; + this.weightMap.put(this.total, entry); + } + + /** + * Get the next entry from the weighted list based on weights. + * + * @return The next entry or null if the list is empty. + */ + public T nextEntry() { + if (this.total == 0) { + return null; + } + double randomValue = this.random.nextDouble() * this.total; + return this.weightMap.higherEntry(randomValue).getValue(); + } + + /** + * Get all entries in the weighted list. + * + * @return An unmodifiable list of entries. + */ + public List getEntries() { + return List.copyOf(this.weightMap.values()); + } + + /** + * Check if the weighted list is empty. + * + * @return True if the list is empty, false otherwise. + */ + public boolean isEmpty() { + return this.total == 0; + } + + /** + * Get the number of entries in the weighted list. + * + * @return The number of entries. + */ + public int size() { + return this.weightMap.size(); + } + +} From 901fc88ca878efcde0f56103b12e1e519555a0bf Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Tue, 5 May 2026 16:26:54 -0700 Subject: [PATCH 2/3] MobData - update to use weighted list --- .../shanebeestudios/hg/api/data/MobData.java | 40 ++++++++----------- .../hg/plugin/managers/MobManager.java | 17 ++++---- 2 files changed, 25 insertions(+), 32 deletions(-) diff --git a/src/main/java/com/shanebeestudios/hg/api/data/MobData.java b/src/main/java/com/shanebeestudios/hg/api/data/MobData.java index e4eaa1c2..0dde449a 100644 --- a/src/main/java/com/shanebeestudios/hg/api/data/MobData.java +++ b/src/main/java/com/shanebeestudios/hg/api/data/MobData.java @@ -1,7 +1,7 @@ package com.shanebeestudios.hg.api.data; import com.google.common.collect.ImmutableList; -import org.jetbrains.annotations.ApiStatus; +import com.shanebeestudios.hg.api.util.WeightedList; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; @@ -12,8 +12,8 @@ public class MobData { private final Random random = new Random(); - private final List dayMobs = new ArrayList<>(); - private final List nightMobs = new ArrayList<>(); + private final WeightedList dayMobs = new WeightedList<>(); + private final WeightedList nightMobs = new WeightedList<>(); private int mobCount; /** @@ -22,7 +22,7 @@ public class MobData { * @return List of MobEntries */ public List getDayMobs() { - return ImmutableList.copyOf(this.dayMobs); + return ImmutableList.copyOf(this.dayMobs.getEntries()); } /** @@ -32,16 +32,17 @@ public List getDayMobs() { */ public @Nullable MobEntry getRandomDayMob() { if (this.dayMobs.isEmpty()) return null; - return this.dayMobs.get(this.random.nextInt(this.dayMobs.size())); + return this.dayMobs.nextEntry(); } /** * Add a new mob entry to the day mobs * * @param mobEntry Mob entry to add + * @param weight Weight of mob entry */ - public void addDayMob(MobEntry mobEntry) { - this.dayMobs.add(mobEntry); + public void addDayMob(MobEntry mobEntry, int weight) { + this.dayMobs.add(mobEntry, weight); } /** @@ -50,7 +51,7 @@ public void addDayMob(MobEntry mobEntry) { * @return List of MobEntries */ public List getNightMobs() { - return ImmutableList.copyOf(this.nightMobs); + return ImmutableList.copyOf(this.nightMobs.getEntries()); } /** @@ -60,24 +61,17 @@ public List getNightMobs() { */ public @Nullable MobEntry getRandomNightMob() { if (this.nightMobs.isEmpty()) return null; - return this.nightMobs.get(this.random.nextInt(this.nightMobs.size())); + return this.nightMobs.nextEntry(); } /** * Add a new mob entry to the night mobs * * @param mobEntry Mob entry to add + * @param weight Weight of mob entry */ - public void addNightMob(MobEntry mobEntry) { - this.nightMobs.add(mobEntry); - } - - /** - * @hidden - */ - @ApiStatus.Internal - public void setMobCount(int mobCount) { - this.mobCount = mobCount; + public void addNightMob(MobEntry mobEntry, int weight) { + this.nightMobs.add(mobEntry, weight); } /** @@ -86,18 +80,18 @@ public void setMobCount(int mobCount) { * @return Count of all mobs */ public int getMobCount() { - return this.mobCount; + return this.dayMobs.size() + this.nightMobs.size(); } /** - * Get list of all MobEntries + * Get a list of all MobEntries * * @return List of MobEntries */ public List getAllMobs() { List mobs = new ArrayList<>(); - mobs.addAll(this.dayMobs); - mobs.addAll(this.nightMobs); + mobs.addAll(this.dayMobs.getEntries()); + mobs.addAll(this.nightMobs.getEntries()); return mobs; } diff --git a/src/main/java/com/shanebeestudios/hg/plugin/managers/MobManager.java b/src/main/java/com/shanebeestudios/hg/plugin/managers/MobManager.java index 48871c8b..b66dfbff 100644 --- a/src/main/java/com/shanebeestudios/hg/plugin/managers/MobManager.java +++ b/src/main/java/com/shanebeestudios/hg/plugin/managers/MobManager.java @@ -88,7 +88,6 @@ public void loadGameMobs(Game game, ConfigurationSection arenaConfig) { private MobData createMobData(ConfigurationSection mobsSection, @Nullable Game game) { MobData mobData = new MobData(); - int count = 0; String gameName = game != null ? game.getGameArenaData().getName() + ":" : ""; for (String time : Arrays.asList("day", "night")) { if (!mobsSection.contains(time)) continue; @@ -212,20 +211,20 @@ private MobData createMobData(ConfigurationSection mobsSection, @Nullable Game g mobEntry.setDeathMessage(deathMessage); } int weight = mobSection.getInt("weight", 1); - count++; - for (int i = 1; i <= weight; i++) { - if (time.equalsIgnoreCase("day")) { - mobData.addDayMob(mobEntry); - } else { - mobData.addNightMob(mobEntry); - } + if (weight <= 0) { + Util.warning("Invalid weight '%d' for mob entry '%s:%s'", weight, time, sectionKey); + continue; + } + if (time.equalsIgnoreCase("day")) { + mobData.addDayMob(mobEntry, weight); + } else { + mobData.addNightMob(mobEntry, weight); } if (Config.SETTINGS_DEBUG) { Util.log("- Loaded mob entry '%s'", mobEntryKey); } } } - mobData.setMobCount(count); return mobData; } From 815cc8c1ee2661ace8871262389ba2f2c7185f9a Mon Sep 17 00:00:00 2001 From: ShaneBeee Date: Tue, 5 May 2026 16:40:22 -0700 Subject: [PATCH 3/3] ItemData - switch to weighted list --- .../shanebeestudios/hg/api/data/ItemData.java | 58 ++++++++++--------- .../hg/plugin/managers/GameManager.java | 8 +-- .../hg/plugin/managers/ItemManager.java | 13 ++--- 3 files changed, 36 insertions(+), 43 deletions(-) diff --git a/src/main/java/com/shanebeestudios/hg/api/data/ItemData.java b/src/main/java/com/shanebeestudios/hg/api/data/ItemData.java index 3adaa363..d2bd8e17 100644 --- a/src/main/java/com/shanebeestudios/hg/api/data/ItemData.java +++ b/src/main/java/com/shanebeestudios/hg/api/data/ItemData.java @@ -1,66 +1,69 @@ package com.shanebeestudios.hg.api.data; +import com.shanebeestudios.hg.api.game.Game; +import com.shanebeestudios.hg.api.util.WeightedList; import org.bukkit.inventory.ItemStack; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; +/** + * Holder of {@link ItemStack Items} for a {@link Game} + */ public class ItemData { - private final Map> items = new HashMap<>(); - private final Map count = new HashMap<>(); + private final Map> weightedItems = new HashMap<>(); public ItemData() { for (ChestType chestType : ChestType.values()) { - this.items.put(chestType, new ArrayList<>()); + this.weightedItems.put(chestType, new WeightedList<>()); } } - public void setItems(ChestType type, List items) { - this.items.put(type, items); - } - - public List getItems(ChestType type) { - return this.items.get(type); - } - /** - * Set item count + * Add a weighted item to the item data. * - * @param chestType ChestType to count - * @param itemCount Amount of items + * @param type Chest type + * @param item Item to add + * @param weight Weight of item */ - public void setItemCount(ChestType chestType, int itemCount) { - this.count.put(chestType, itemCount); + public void addEntry(ChestType type, ItemStack item, int weight) { + this.weightedItems.get(type).add(item, weight); } /** - * Get item count by ChestType + * Get a random item. * - * @param chestType ChestType to get count from - * @return AMount of items by ChestType + * @param type Type of chest + * @return Random item */ - public int getItemCount(ChestType chestType) { - return this.count.get(chestType); + public ItemStack getRandomItem(ChestType type) { + return this.weightedItems.get(type).nextEntry(); + } + + public void setWeightedItems(ChestType type, WeightedList weightedItems) { + this.weightedItems.put(type, weightedItems); + } + + public WeightedList getWeightedItems(ChestType type) { + return this.weightedItems.get(type); } /** - * Get total item count for all chest types + * Get the total item count for all chest types. * * @return Total item count */ public int getTotalItemCount() { int count = 0; - for (int value : this.count.values()) { - count += value; + for (WeightedList value : this.weightedItems.values()) { + count += value.size(); } return count; } /** - * Represents the type of chests in game + * Represents the type of chests in a game. *

Used for logging and refilling

*/ public enum ChestType { @@ -97,4 +100,5 @@ public String getName() { return this.name; } } + } diff --git a/src/main/java/com/shanebeestudios/hg/plugin/managers/GameManager.java b/src/main/java/com/shanebeestudios/hg/plugin/managers/GameManager.java index 17f673cb..5b34327f 100755 --- a/src/main/java/com/shanebeestudios/hg/plugin/managers/GameManager.java +++ b/src/main/java/com/shanebeestudios/hg/plugin/managers/GameManager.java @@ -13,7 +13,6 @@ import com.shanebeestudios.hg.plugin.configs.Language; import org.bukkit.Bukkit; import org.bukkit.Location; -import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -234,12 +233,7 @@ public void fillChests(Game game, Block block, ChestType chestType) { * @return Random ItemStack */ public ItemStack randomItem(Game game, ChestType chestType) { - List items = game.getGameItemData().getItemData().getItems(chestType); - int r = items.size(); - if (r == 0) return new ItemStack(Material.AIR); - int i = this.random.nextInt(r); - return items.get(i); - + return game.getGameItemData().getItemData().getRandomItem(chestType); } /** diff --git a/src/main/java/com/shanebeestudios/hg/plugin/managers/ItemManager.java b/src/main/java/com/shanebeestudios/hg/plugin/managers/ItemManager.java index bb2efe3b..1b0fc1c5 100644 --- a/src/main/java/com/shanebeestudios/hg/plugin/managers/ItemManager.java +++ b/src/main/java/com/shanebeestudios/hg/plugin/managers/ItemManager.java @@ -66,30 +66,25 @@ private ItemData createItemData(ConfigurationSection itemsSection, @Nullable Gam ItemData itemData = new ItemData(); for (ChestType chestType : ChestType.values()) { - int count = 0; ConfigurationSection chestTypeSection = itemsSection.getConfigurationSection(chestType.getName()); if (chestTypeSection == null) { // If the section does not exist in a game, use defaults if (game != null && this.defaultItemData != null) { - itemData.setItems(chestType, this.defaultItemData.getItems(chestType)); - count += this.defaultItemData.getItemCount(chestType); + itemData.setWeightedItems(chestType, this.defaultItemData.getWeightedItems(chestType)); } } else { - List items = new ArrayList<>(); for (String key : chestTypeSection.getKeys(false)) { ConfigurationSection itemSection = chestTypeSection.getConfigurationSection(key); if (itemSection == null) continue; ItemStack itemStack = ItemParser.parseItem(itemSection); int weight = itemSection.getInt("weight", 1); - for (int i = 0; i < weight; i++) { - items.add(itemStack); + if (weight <= 0) { + continue; } - count++; + itemData.addEntry(chestType, itemStack, weight); } - itemData.setItems(chestType, items); } - itemData.setItemCount(chestType, count); } return itemData; }