From 248b9aaae546dca2d280619c31bdb3dce9cade65 Mon Sep 17 00:00:00 2001
From: ShaneBeee
Date: Tue, 5 May 2026 16:11:53 -0700
Subject: [PATCH 01/26] update javadocs
---
build.gradle.kts | 10 +++++++---
src/main/javadoc/overview.html | 10 ++++++++++
2 files changed, 17 insertions(+), 3 deletions(-)
create mode 100644 src/main/javadoc/overview.html
diff --git a/build.gradle.kts b/build.gradle.kts
index 426de471..0f9494bb 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -98,12 +98,16 @@ tasks {
options.compilerArgs.add("-Xlint:deprecation")
}
javadoc {
+ val options = options as StandardJavadocDocletOptions
+ options.docTitle = "HungerGames API - $projectVersion"
+ options.overview = "src/main/javadoc/overview.html"
options.encoding = Charsets.UTF_8.name()
+
exclude("com/shanebeestudios/hg/plugin/commands")
exclude("com/shanebeestudios/hg/plugin/listeners")
- (options as StandardJavadocDocletOptions).links(
- "https://jd.papermc.io/paper/1.21.5/",
- "https://jd.advntr.dev/api/4.17.0/",
+ options.links(
+ "https://jd.papermc.io/paper/26.1.2/",
+ "https://jd.advntr.dev/api/4.25.0/",
"https://tr7zw.github.io/Item-NBT-API/v2-api/"
)
diff --git a/src/main/javadoc/overview.html b/src/main/javadoc/overview.html
new file mode 100644
index 00000000..600ce23b
--- /dev/null
+++ b/src/main/javadoc/overview.html
@@ -0,0 +1,10 @@
+
+
+ HungerGames is a simple yet fun, and very lightweight PvP arena system.
+
It provides advanced kits, spawns, and a very simple arena setup.
+
If you've never heard of the books/movies HungerGames, it's basically a free for all.
+
You must hunt for food and items, and attempt to kill others before they kill you. The last man standing wins!
+
Unlike some HungerGames plugins, this one does not require you to setup a whole server for the game.
+
You can just easily setup multiple arenas anywhere in the world.
+
+
From c015cdb4533797da7e7faefe194db0d987942f2a Mon Sep 17 00:00:00 2001
From: Shane Bee
Date: Tue, 5 May 2026 17:00:58 -0700
Subject: [PATCH 02/26] Update to use WeightedLists (#108)
* WeightedList - introducing a weighted list
* MobData - update to use weighted list
* ItemData - switch to weighted list
---
.../shanebeestudios/hg/api/data/ItemData.java | 58 ++++++++-------
.../shanebeestudios/hg/api/data/MobData.java | 40 +++++-----
.../hg/api/util/WeightedList.java | 73 +++++++++++++++++++
.../hg/plugin/managers/GameManager.java | 8 +-
.../hg/plugin/managers/ItemManager.java | 13 +---
.../hg/plugin/managers/MobManager.java | 17 ++---
6 files changed, 134 insertions(+), 75 deletions(-)
create mode 100644 src/main/java/com/shanebeestudios/hg/api/util/WeightedList.java
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/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/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();
+ }
+
+}
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;
}
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 3e10f158aa702b627a9bd8f0acaf59d434971572 Mon Sep 17 00:00:00 2001
From: ShaneBeee
Date: Tue, 5 May 2026 18:35:56 -0700
Subject: [PATCH 03/26] DeleteArenaCommand - add confirmation
---
.../plugin/commands/DeleteArenaCommand.java | 75 +++++++++++--------
.../hg/plugin/configs/Language.java | 4 +
src/main/resources/language.yml | 3 +-
3 files changed, 51 insertions(+), 31 deletions(-)
diff --git a/src/main/java/com/shanebeestudios/hg/plugin/commands/DeleteArenaCommand.java b/src/main/java/com/shanebeestudios/hg/plugin/commands/DeleteArenaCommand.java
index 0d40760d..c6239920 100644
--- a/src/main/java/com/shanebeestudios/hg/plugin/commands/DeleteArenaCommand.java
+++ b/src/main/java/com/shanebeestudios/hg/plugin/commands/DeleteArenaCommand.java
@@ -23,44 +23,59 @@ protected Argument> register() {
return LiteralArgument.literal("delete-arena")
.withPermission(Permissions.COMMAND_DELETE.permission())
.then(CustomArg.GAME.get("game")
- .executes(info -> {
- CommandSender sender = info.sender();
- Game game = info.args().getByClass("game", Game.class);
- assert game != null;
- GamePlayerData gamePlayerData = game.getGamePlayerData();
- GameArenaData gameArenaData = game.getGameArenaData();
- String name = gameArenaData.getName();
+ .then(LiteralArgument.literal("confirm")
+ .executes(info -> {
+ CommandSender sender = info.sender();
+ Game game = info.args().getByClass("game", Game.class);
+ assert game != null;
+ GamePlayerData gamePlayerData = game.getGamePlayerData();
+ GameArenaData gameArenaData = game.getGameArenaData();
+ String name = gameArenaData.getName();
- try {
- Util.sendPrefixedMessage(sender, this.lang.command_delete_attempt.replace("", name));
+ try {
+ Util.sendPrefixedMessage(sender, this.lang.command_delete_attempt.replace("", name));
- switch (gameArenaData.getStatus()) {
- case WAITING, COUNTDOWN, FREE_ROAM, RUNNING -> {
- Util.sendMessage(sender, this.lang.command_delete_stopping);
- game.getGameBlockData().forceRollback();
- game.stop(false);
- }
- case ROLLBACK -> {
- Util.sendMessage(sender, this.lang.command_delete_rollback);
- return;
+ switch (gameArenaData.getStatus()) {
+ case WAITING, COUNTDOWN, FREE_ROAM, RUNNING -> {
+ Util.sendMessage(sender, this.lang.command_delete_stopping);
+ game.getGameBlockData().forceRollback();
+ game.stop(false);
+ }
+ case ROLLBACK -> {
+ Util.sendMessage(sender, this.lang.command_delete_rollback);
+ return;
+ }
}
- }
- // This shouldn't happen, why is it here?
- if (!gamePlayerData.getPlayers().isEmpty()) {
- Util.sendMessage(sender, this.lang.command_delete_kicking);
- for (Player player : gamePlayerData.getPlayers()) {
- gamePlayerData.leaveGame(player, false);
+ // This shouldn't happen, why is it here?
+ if (!gamePlayerData.getPlayers().isEmpty()) {
+ Util.sendMessage(sender, this.lang.command_delete_kicking);
+ for (Player player : gamePlayerData.getPlayers()) {
+ gamePlayerData.leaveGame(player, false);
+ }
}
+
+ this.gameManager.deleteGame(game);
+ Util.sendMessage(sender, this.lang.command_delete_deleted.replace("", name));
+ } catch (Exception e) {
+ Util.sendMessage(sender, this.lang.command_delete_failed);
+ Util.sendMessage(sender, "Error Message: " + e.getMessage());
}
+ }))
+ .executes(info -> {
+ CommandSender sender = info.sender();
+ Game game = info.args().getByClass("game", Game.class);
+ assert game != null;
- this.gameManager.deleteGame(game);
- Util.sendMessage(sender, this.lang.command_delete_deleted.replace("", name));
- } catch (Exception e) {
- Util.sendMessage(sender, this.lang.command_delete_failed);
- Util.sendMessage(sender, "Error Message: " + e.getMessage());
+ String name = game.getGameArenaData().getName();
+ if (sender instanceof Player player) {
+ Util.sendMessage(sender, this.lang.command_delete_confirm.replace("", name),
+ name);
+ } else {
+ Util.sendMessage(sender, this.lang.command_delete_confirm_console.replace("", name));
}
- }));
+ })
+ );
}
}
diff --git a/src/main/java/com/shanebeestudios/hg/plugin/configs/Language.java b/src/main/java/com/shanebeestudios/hg/plugin/configs/Language.java
index 11fd3030..1be05907 100644
--- a/src/main/java/com/shanebeestudios/hg/plugin/configs/Language.java
+++ b/src/main/java/com/shanebeestudios/hg/plugin/configs/Language.java
@@ -65,6 +65,8 @@ public class Language {
public String command_create_session_sign_invalid;
// - Delete
public String command_delete_attempt;
+ public String command_delete_confirm;
+ public String command_delete_confirm_console;
public String command_delete_kicking;
public String command_delete_stopping;
public String command_delete_deleted;
@@ -345,6 +347,8 @@ private void loadLang() {
this.command_create_session_done = this.lang.getString("command.create.session-done");
// - Delete
this.command_delete_attempt = this.lang.getString("command.delete.attempt");
+ this.command_delete_confirm = this.lang.getString("command.delete.confirm");
+ this.command_delete_confirm_console = this.lang.getString("command.delete.confirm-console");
this.command_delete_kicking = this.lang.getString("command.delete.kicking");
this.command_delete_stopping = this.lang.getString("command.delete.stopping");
this.command_delete_deleted = this.lang.getString("command.delete.deleted");
diff --git a/src/main/resources/language.yml b/src/main/resources/language.yml
index 2bee8c00..7bd59a6b 100644
--- a/src/main/resources/language.yml
+++ b/src/main/resources/language.yml
@@ -58,6 +58,8 @@ command:
session-done: "You're all done, your arena is ready to go!"
# Delete
delete:
+ confirm-console: "Please use '/hg delete confirm'"
+ confirm: 'Are you sure you want to delete the arena ? confirm>Click to confirm'
attempt: 'Attempting to delete !'
stopping: '- Game running! Stopping..'
kicking: '- Players detected! Kicking..'
@@ -65,7 +67,6 @@ command:
failed: 'Failed to delete arena!'
rollback: 'The game is currently rolling back and cannot be deleted right now!'
no-exist: "The arena '' does not exist!"
-
# Edit
edit:
# ChestRefill
From 48e8917aec26fb36977881a8acc80f17b83f2f8d Mon Sep 17 00:00:00 2001
From: ShaneBeee
Date: Tue, 5 May 2026 21:38:43 -0700
Subject: [PATCH 04/26] GameBorderData - add multiple border centers - Ref #105
---
.../com/shanebeestudios/hg/api/game/Game.java | 2 +-
.../hg/api/game/GameBorderData.java | 60 ++++++++++++++-----
.../hg/plugin/commands/EditCommand.java | 15 +++--
.../hg/plugin/configs/ArenaConfig.java | 29 +++++++--
4 files changed, 79 insertions(+), 27 deletions(-)
diff --git a/src/main/java/com/shanebeestudios/hg/api/game/Game.java b/src/main/java/com/shanebeestudios/hg/api/game/Game.java
index 55e531c5..7d67ded4 100755
--- a/src/main/java/com/shanebeestudios/hg/api/game/Game.java
+++ b/src/main/java/com/shanebeestudios/hg/api/game/Game.java
@@ -407,7 +407,7 @@ public void stop() {
*/
public void stop(boolean death) {
if (Config.WORLD_BORDER_ENABLED) {
- this.gameBorderData.resetBorder();
+ this.gameBorderData.resetBorder(false);
}
this.gameEntityData.removeEntities();
this.gameScoreboard.resetSidebars();
diff --git a/src/main/java/com/shanebeestudios/hg/api/game/GameBorderData.java b/src/main/java/com/shanebeestudios/hg/api/game/GameBorderData.java
index 428d1875..e7f9f3b8 100644
--- a/src/main/java/com/shanebeestudios/hg/api/game/GameBorderData.java
+++ b/src/main/java/com/shanebeestudios/hg/api/game/GameBorderData.java
@@ -1,13 +1,15 @@
package com.shanebeestudios.hg.api.game;
+import com.shanebeestudios.hg.api.util.Util;
import com.shanebeestudios.hg.plugin.configs.Config;
import com.shanebeestudios.hg.plugin.tasks.WorldBorderTask;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.WorldBorder;
import org.bukkit.util.BoundingBox;
-import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.NotNull;
+import java.util.ArrayList;
import java.util.List;
import java.util.Random;
@@ -18,7 +20,7 @@ public class GameBorderData extends Data {
private final Random random = new Random();
private boolean isDefault;
- private Location centerLocation;
+ private final List centerLocations = new ArrayList<>();
private int finalBorderSize;
private int borderCountdownStart;
private int borderCountdownEnd;
@@ -32,11 +34,11 @@ public class GameBorderData extends Data {
this.isDefault = true;
}
- GameBorderData(Game game, Location centerLocation, int finalSize, int start, int end) {
+ GameBorderData(Game game, Location centerLocations, int finalSize, int start, int end) {
super(game);
this.gamePlayerData = game.getGamePlayerData();
this.worldBorder = Bukkit.createWorldBorder();
- this.centerLocation = centerLocation;
+ this.centerLocations.add(centerLocations);
this.finalBorderSize = finalSize;
this.borderCountdownStart = start;
this.borderCountdownEnd = end;
@@ -56,26 +58,34 @@ public WorldBorder getWorldBorder() {
* Initialize the {@link WorldBorder} of this game
*/
public void initialize() {
- resetBorder();
+ resetBorder(true);
this.gamePlayerData.getPlayers().forEach(player -> player.setWorldBorder(this.worldBorder));
this.worldBorderTask = new WorldBorderTask(this.game);
}
/**
* Reset the {@link WorldBorder} of this game
+ *
+ * @param start Whether this is a game start or end reset
*/
- public void resetBorder() {
+ public void resetBorder(boolean start) {
Location center;
GameArenaData gameArenaData = this.game.getGameArenaData();
List spawns = gameArenaData.getSpawns();
- if (this.centerLocation != null) {
- center = this.centerLocation;
- } else {
+ if (this.centerLocations.isEmpty()) {
switch (Config.WORLD_BORDER_CENTER) { // 'first-spawn', 'random-spawn' and 'arena-center'
case "first-spawn" -> center = spawns.getFirst();
case "random-spawn" -> center = spawns.get(this.random.nextInt(spawns.size()));
default -> center = gameArenaData.getGameRegion().getCenter();
}
+ } else {
+ if (start) {
+ // If starting a new game, pick a random center location
+ center = this.centerLocations.get(this.random.nextInt(this.centerLocations.size()));
+ } else {
+ // If game is ending, reset to arena center
+ center = gameArenaData.getGameRegion().getCenter();
+ }
}
this.worldBorder.setCenter(center);
@@ -103,22 +113,40 @@ public void startShrinking(int closingIn) {
}
/**
- * Set the center of the border of this game
+ * Add a center location to the border of this game
*
* @param centerLocation Location of the center
*/
- public void setCenterLocation(Location centerLocation) {
- this.centerLocation = centerLocation;
+ public void addCenterLocation(Location centerLocation) {
+ this.centerLocations.add(centerLocation);
+ }
+
+ /**
+ * Clear all center locations from the border of this game
+ */
+ public void clearCenterLocations() {
+ this.centerLocations.clear();
+ }
+
+ /**
+ * Set the center of the border of this game
+ *
+ * @param centerLocations Location of the center
+ */
+ public void setCenterLocations(List centerLocations) {
+ Util.log("Setting center locations for game border");
+ this.centerLocations.clear();
+ this.centerLocations.addAll(centerLocations);
this.isDefault = false;
}
/**
- * Get the center location of the border
+ * Get a list of center locations of the border
*
- * @return Center location
+ * @return Center locations
*/
- public @Nullable Location getCenterLocation() {
- return this.centerLocation;
+ public @NotNull List getCenterLocations() {
+ return this.centerLocations;
}
/**
diff --git a/src/main/java/com/shanebeestudios/hg/plugin/commands/EditCommand.java b/src/main/java/com/shanebeestudios/hg/plugin/commands/EditCommand.java
index f3de01f2..6f703d1b 100644
--- a/src/main/java/com/shanebeestudios/hg/plugin/commands/EditCommand.java
+++ b/src/main/java/com/shanebeestudios/hg/plugin/commands/EditCommand.java
@@ -73,16 +73,23 @@ private Argument> border() {
saveGame(game);
})))
- .then(LiteralArgument.literal("center_location")
+ .then(LiteralArgument.literal("add_center_location")
.then(new Location2DArgument("center_location", LocationType.BLOCK_POSITION)
.executes(info -> {
Game game = info.args().getByClass("game", Game.class);
Location2D centerLocation = info.args().getByClass("center_location", Location2D.class);
GameBorderData gameBorderData = game.getGameBorderData();
- gameBorderData.setCenterLocation(convert(centerLocation));
- Util.sendPrefixedMessage(info.sender(), "Border center location set to %s", centerLocation);
+ gameBorderData.addCenterLocation(convert(centerLocation));
+ Util.sendPrefixedMessage(info.sender(), "Border center location added: %s", centerLocation);
saveGame(game);
- })));
+ })))
+ .then(LiteralArgument.literal("clear_center_locations")
+ .executes(info -> {
+ Game game = info.args().getByClass("game", Game.class);
+ game.getGameBorderData().clearCenterLocations();
+ Util.sendPrefixedMessage(info.sender(), "Border center locations cleared");
+ saveGame(game);
+ }));
}
@SuppressWarnings("DataFlowIssue")
diff --git a/src/main/java/com/shanebeestudios/hg/plugin/configs/ArenaConfig.java b/src/main/java/com/shanebeestudios/hg/plugin/configs/ArenaConfig.java
index 609652d0..0db37f11 100755
--- a/src/main/java/com/shanebeestudios/hg/plugin/configs/ArenaConfig.java
+++ b/src/main/java/com/shanebeestudios/hg/plugin/configs/ArenaConfig.java
@@ -115,6 +115,7 @@ public void loadAllArenas() {
@SuppressWarnings("DataFlowIssue")
public boolean loadArena(FileConfiguration arenaConfig, String arenaName) {
boolean isReady = true;
+ boolean isDirty = false;
List spawns = new ArrayList<>();
Location lobbysign = null;
int timer = 0;
@@ -192,9 +193,20 @@ public boolean loadArena(FileConfiguration arenaConfig, String arenaName) {
if (arenaConfig.isSet("game_border")) {
ConfigurationSection borderSection = arenaConfig.getConfigurationSection("game_border");
GameBorderData gameBorderData = game.getGameBorderData();
- if (borderSection.isSet("center_location")) {
- Location borderCenter = LocationParser.getBlockLocFromString(borderSection.getString("center_location"));
- gameBorderData.setCenterLocation(borderCenter);
+
+ if (borderSection.isSet("center_locations")) {
+ List centerLocations = borderSection.getStringList("center_locations");
+ List centerLocationList = new ArrayList<>();
+ for (String locString : centerLocations) {
+ Location centerLocation = LocationParser.getBlockLocFromString(locString);
+ centerLocationList.add(centerLocation);
+ }
+ gameBorderData.setCenterLocations(centerLocationList);
+ } else if (borderSection.isSet("center_location")) { // Deprecated (May 5/2026)
+ String centerLocString = borderSection.getString("center_location");
+ Location borderCenter = LocationParser.getBlockLocFromString(centerLocString);
+ gameBorderData.setCenterLocations(List.of(borderCenter));
+ isDirty = true;
}
if (borderSection.isSet("final_size")) {
int borderSize = borderSection.getInt("final_size");
@@ -246,6 +258,9 @@ public boolean loadArena(FileConfiguration arenaConfig, String arenaName) {
} else {
Util.log("- Loaded arena '%s'", arenaName);
}
+ if (isDirty) {
+ saveGameToConfig(game);
+ }
return isReady;
}
@@ -300,11 +315,13 @@ public void saveGameToConfig(Game game) {
GameBorderData borderData = game.getGameBorderData();
if (!borderData.isDefault()) {
ConfigurationSection borderSection = gameSection.createSection("game_border");
- Location centerLocation = borderData.getCenterLocation();
- if (centerLocation != null) {
+ List centerLocations = new ArrayList<>();
+ for (Location centerLocation : borderData.getCenterLocations()) {
String locString = LocationParser.blockLocToString(centerLocation);
- borderSection.set("center_location", locString);
+ centerLocations.add(locString);
+
}
+ borderSection.set("center_locations", centerLocations);
borderSection.set("final_size", borderData.getFinalBorderSize());
borderSection.set("countdown_start", borderData.getBorderCountdownStart());
borderSection.set("countdown_end", borderData.getBorderCountdownEnd());
From a8896720e9c99eb5f42ebf798414b45a31e15ac0 Mon Sep 17 00:00:00 2001
From: ShaneBeee
Date: Tue, 5 May 2026 21:44:05 -0700
Subject: [PATCH 05/26] Registries - cleanup
---
.../hg/api/registry/Registries.java | 19 ++++++++++---------
1 file changed, 10 insertions(+), 9 deletions(-)
diff --git a/src/main/java/com/shanebeestudios/hg/api/registry/Registries.java b/src/main/java/com/shanebeestudios/hg/api/registry/Registries.java
index 384cee4f..3d6180ff 100644
--- a/src/main/java/com/shanebeestudios/hg/api/registry/Registries.java
+++ b/src/main/java/com/shanebeestudios/hg/api/registry/Registries.java
@@ -19,14 +19,15 @@
@SuppressWarnings({"NullableProblems", "UnstableApiUsage"})
public class Registries {
- public static final Registry ATTRIBUTE_REGISTRY = RegistryAccess.registryAccess().getRegistry(RegistryKey.ATTRIBUTE);
- public static final Registry BLOCK_TYPE_REGISTRY = RegistryAccess.registryAccess().getRegistry(RegistryKey.BLOCK);
- public static final Registry DAMAGE_TYPE_REGISTRY = RegistryAccess.registryAccess().getRegistry(RegistryKey.DAMAGE_TYPE);
- public static final Registry DATA_COMPONENT_TYPE_REGISTRY = RegistryAccess.registryAccess().getRegistry(RegistryKey.DATA_COMPONENT_TYPE);
- public static final Registry ENCHANTMENT_REGISTRY = RegistryAccess.registryAccess().getRegistry(RegistryKey.ENCHANTMENT);
- public static final Registry ENTITY_TYPE_REGISTRY = RegistryAccess.registryAccess().getRegistry(RegistryKey.ENTITY_TYPE);
- public static final Registry ITEM_TYPE_REGISTRY = RegistryAccess.registryAccess().getRegistry(RegistryKey.ITEM);
- public static final Registry POTION_EFFECT_TYPE_REGISTRY = RegistryAccess.registryAccess().getRegistry(RegistryKey.MOB_EFFECT);
- public static final Registry POTION_TYPE_REGISTRY = RegistryAccess.registryAccess().getRegistry(RegistryKey.POTION);
+ private static final RegistryAccess ACCESS = RegistryAccess.registryAccess();
+ public static final Registry ATTRIBUTE_REGISTRY = ACCESS.getRegistry(RegistryKey.ATTRIBUTE);
+ public static final Registry BLOCK_TYPE_REGISTRY = ACCESS.getRegistry(RegistryKey.BLOCK);
+ public static final Registry DAMAGE_TYPE_REGISTRY = ACCESS.getRegistry(RegistryKey.DAMAGE_TYPE);
+ public static final Registry DATA_COMPONENT_TYPE_REGISTRY = ACCESS.getRegistry(RegistryKey.DATA_COMPONENT_TYPE);
+ public static final Registry ENCHANTMENT_REGISTRY = ACCESS.getRegistry(RegistryKey.ENCHANTMENT);
+ public static final Registry ENTITY_TYPE_REGISTRY = ACCESS.getRegistry(RegistryKey.ENTITY_TYPE);
+ public static final Registry ITEM_TYPE_REGISTRY = ACCESS.getRegistry(RegistryKey.ITEM);
+ public static final Registry POTION_EFFECT_TYPE_REGISTRY = ACCESS.getRegistry(RegistryKey.MOB_EFFECT);
+ public static final Registry POTION_TYPE_REGISTRY = ACCESS.getRegistry(RegistryKey.POTION);
}
From 684e7ea9762f19192c0d773757d9a03d3782a8a8 Mon Sep 17 00:00:00 2001
From: ShaneBeee
Date: Tue, 5 May 2026 21:59:22 -0700
Subject: [PATCH 06/26] Util - remove legacy check
---
.../shanebeestudios/hg/api/gui/KitGUI.java | 19 +++++--------------
.../hg/api/parsers/ItemParser.java | 6 +-----
.../com/shanebeestudios/hg/api/util/Util.java | 2 --
3 files changed, 6 insertions(+), 21 deletions(-)
diff --git a/src/main/java/com/shanebeestudios/hg/api/gui/KitGUI.java b/src/main/java/com/shanebeestudios/hg/api/gui/KitGUI.java
index 36a9d19e..89f9a6a6 100644
--- a/src/main/java/com/shanebeestudios/hg/api/gui/KitGUI.java
+++ b/src/main/java/com/shanebeestudios/hg/api/gui/KitGUI.java
@@ -12,7 +12,6 @@
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
-import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.ItemType;
import org.bukkit.potion.PotionEffect;
@@ -41,12 +40,9 @@ public KitGUI(KitsGUI kitsGUI, Player player, KitEntry kitEntry) {
// SETUP INVENTORY
// divider
ItemStack divider = ItemType.BLACK_STAINED_GLASS_PANE.createItemStack();
- if (Util.RUNNING_1_21_5) {
- divider.setData(DataComponentTypes.TOOLTIP_DISPLAY, TooltipDisplay.tooltipDisplay()
- .hideTooltip(true));
- } else {
- divider.setData(DataComponentTypes.CUSTOM_NAME, Component.text(" "));
- }
+ divider.setData(DataComponentTypes.TOOLTIP_DISPLAY, TooltipDisplay.tooltipDisplay()
+ .hideTooltip(true));
+
for (int i = 9; i < 18; i++) {
this.inventory.setItem(i, divider);
}
@@ -96,13 +92,8 @@ public KitGUI(KitsGUI kitsGUI, Player player, KitEntry kitEntry) {
// Potions
ItemStack potion = ItemType.POTION.createItemStack();
potion.setData(DataComponentTypes.CUSTOM_NAME, Util.getMini(this.lang.kits_kit_gui_potion_effects));
- if (Util.RUNNING_1_21_5) {
- potion.setData(DataComponentTypes.TOOLTIP_DISPLAY, TooltipDisplay.tooltipDisplay()
- .hideTooltip(true));
- } else {
- //noinspection deprecation
- potion.addItemFlags(ItemFlag.HIDE_ADDITIONAL_TOOLTIP);
- }
+ potion.setData(DataComponentTypes.TOOLTIP_DISPLAY, TooltipDisplay.tooltipDisplay()
+ .hideTooltip(true));
List potionEffects = kitEntry.getPotionEffects();
List lore = new ArrayList<>();
diff --git a/src/main/java/com/shanebeestudios/hg/api/parsers/ItemParser.java b/src/main/java/com/shanebeestudios/hg/api/parsers/ItemParser.java
index de088cb7..867f5fdc 100644
--- a/src/main/java/com/shanebeestudios/hg/api/parsers/ItemParser.java
+++ b/src/main/java/com/shanebeestudios/hg/api/parsers/ItemParser.java
@@ -14,7 +14,6 @@
import org.bukkit.NamespacedKey;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.enchantments.Enchantment;
-import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.ItemType;
import org.bukkit.potion.PotionEffect;
@@ -156,13 +155,10 @@ public class ItemParser {
if (config.contains("dyed_color")) {
int color = config.getInt("dyed_color");
itemStack.setData(DataComponentTypes.DYED_COLOR, DyedItemColor.dyedItemColor().color(Color.fromRGB(color)));
- if (!Util.RUNNING_1_21_5) {
- itemStack.addItemFlags(ItemFlag.HIDE_ADDITIONAL_TOOLTIP);
- }
}
// HIDDEN COMPONENTS
- if (Util.RUNNING_1_21_5 && config.isList("hidden_components")) {
+ if (config.isList("hidden_components")) {
TooltipDisplay.Builder builder = TooltipDisplay.tooltipDisplay();
for (String compKey : config.getStringList("hidden_components")) {
NamespacedKey namespacedKey = NamespacedKey.fromString(compKey);
diff --git a/src/main/java/com/shanebeestudios/hg/api/util/Util.java b/src/main/java/com/shanebeestudios/hg/api/util/Util.java
index a80f27bf..840b4eae 100755
--- a/src/main/java/com/shanebeestudios/hg/api/util/Util.java
+++ b/src/main/java/com/shanebeestudios/hg/api/util/Util.java
@@ -26,10 +26,8 @@ public class Util {
// PUBLIC
// Quick link to help for removing legacy stuff later
- public static final boolean RUNNING_1_21_5 = isRunningMinecraft(1, 21, 5);
public static final boolean IS_RUNNING_FOLIA = classExists("io.papermc.paper.threadedregions.FoliaWatchdogThread");
-
// PRIVATE
private static final Pattern HEX_PATTERN = Pattern.compile("<#([A-Fa-f0-9]){6}>");
private static final CommandSender CONSOLE = Bukkit.getConsoleSender();
From 3799c073bfbb67d6dcbe4b7872139a49821b5cab Mon Sep 17 00:00:00 2001
From: ShaneBeee
Date: Thu, 14 May 2026 19:39:29 -0700
Subject: [PATCH 07/26] GameSidebar - update javadocs
---
.../java/com/shanebeestudios/hg/api/game/GameSidebar.java | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/main/java/com/shanebeestudios/hg/api/game/GameSidebar.java b/src/main/java/com/shanebeestudios/hg/api/game/GameSidebar.java
index 96ad8981..10574bb9 100644
--- a/src/main/java/com/shanebeestudios/hg/api/game/GameSidebar.java
+++ b/src/main/java/com/shanebeestudios/hg/api/game/GameSidebar.java
@@ -30,7 +30,7 @@ public GameSidebar(Game game) {
}
/**
- * Add a player to this scoreboard
+ * Add a player to this sidebar
*
* @param player Player to add
*/
@@ -49,7 +49,7 @@ public void removePlayer(Player player) {
}
/**
- * Set the title of this scoreboard
+ * Set the title of this sidebar
*
* @param title Title to set
*/
@@ -58,7 +58,7 @@ public void setTitle(String title) {
}
/**
- * Update this scoreboard
+ * Update this sidebar
*/
public void updateBoard() {
String alive = " " + this.lang.scoreboard_sidebar_players_alive_num.replace("", String.valueOf(this.game.getGamePlayerData().getPlayers().size()));
From b46d070eb7463e9977a1fd46705419af9b3619ae Mon Sep 17 00:00:00 2001
From: ShaneBeee
Date: Thu, 14 May 2026 19:42:16 -0700
Subject: [PATCH 08/26] build.gradle.kts - updates
---
build.gradle.kts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/build.gradle.kts b/build.gradle.kts
index 0f9494bb..40c23ca0 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -56,7 +56,7 @@ dependencies {
implementation("org.bstats:bstats-bukkit:3.2.0")
// MythicMobs
- compileOnly("io.lumine:Mythic-Dist:5.6.1")
+ compileOnly("io.lumine:Mythic-Dist:5.12.0")
// Papi
compileOnly("me.clip:placeholderapi:2.12.2")
@@ -80,7 +80,7 @@ tasks {
dependsOn("shadowJar")
from("build/libs") {
include("HungerGames-*.jar")
- destinationDir = file("/Users/ShaneBee/Desktop/Server/Minecraft/${serverLocation}/plugins/")
+ destinationDir = file("/Users/ShaneBee/Desktop/Server/Minecraft/Skript/${serverLocation}/plugins/")
}
}
From c5997e9437eb2b95810ecdf181ea2f4f6b259b44 Mon Sep 17 00:00:00 2001
From: ShaneBeee
Date: Thu, 14 May 2026 19:42:31 -0700
Subject: [PATCH 09/26] other javadoc updates
---
.../com/shanebeestudios/hg/api/parsers/ItemParser.java | 3 +++
.../com/shanebeestudios/hg/api/status/PlayerStatus.java | 3 +++
src/main/java/com/shanebeestudios/hg/api/util/Pair.java | 8 ++++++++
.../com/shanebeestudios/hg/plugin/configs/Config.java | 3 +--
4 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/src/main/java/com/shanebeestudios/hg/api/parsers/ItemParser.java b/src/main/java/com/shanebeestudios/hg/api/parsers/ItemParser.java
index 867f5fdc..1efaf5ee 100644
--- a/src/main/java/com/shanebeestudios/hg/api/parsers/ItemParser.java
+++ b/src/main/java/com/shanebeestudios/hg/api/parsers/ItemParser.java
@@ -25,6 +25,9 @@
import java.util.List;
import java.util.Locale;
+/**
+ * Parser for items from configs
+ */
public class ItemParser {
@SuppressWarnings({"UnstableApiUsage"})
diff --git a/src/main/java/com/shanebeestudios/hg/api/status/PlayerStatus.java b/src/main/java/com/shanebeestudios/hg/api/status/PlayerStatus.java
index b7835e7c..354c48f9 100644
--- a/src/main/java/com/shanebeestudios/hg/api/status/PlayerStatus.java
+++ b/src/main/java/com/shanebeestudios/hg/api/status/PlayerStatus.java
@@ -5,6 +5,9 @@
import com.shanebeestudios.hg.plugin.configs.Language;
import net.kyori.adventure.text.Component;
+/**
+ * Status of a player in and out of games
+ */
public enum PlayerStatus {
IN_GAME,
diff --git a/src/main/java/com/shanebeestudios/hg/api/util/Pair.java b/src/main/java/com/shanebeestudios/hg/api/util/Pair.java
index 9234a7fc..ccfec3eb 100644
--- a/src/main/java/com/shanebeestudios/hg/api/util/Pair.java
+++ b/src/main/java/com/shanebeestudios/hg/api/util/Pair.java
@@ -1,5 +1,13 @@
package com.shanebeestudios.hg.api.util;
+/**
+ * A pair of 2 objects.
+ *
+ * @param first First object
+ * @param second Second object
+ * @param Class type of the first object
+ * @param Class type of the second object
+ */
public record Pair(F first, S second) {
public static Pair of(F first, S second) {
diff --git a/src/main/java/com/shanebeestudios/hg/plugin/configs/Config.java b/src/main/java/com/shanebeestudios/hg/plugin/configs/Config.java
index b5dcc9cd..4e7149ba 100755
--- a/src/main/java/com/shanebeestudios/hg/plugin/configs/Config.java
+++ b/src/main/java/com/shanebeestudios/hg/plugin/configs/Config.java
@@ -209,8 +209,7 @@ private void loadConfig() {
COMMANDS_ALLOWED_IN_GAME = config.getStringList("commands.allowed-in-game");
try {
- Vault.setupEconomy();
- if (Vault.ECONOMY == null) {
+ if (!Vault.setupEconomy()) {
Util.log("Unable to setup vault!");
Util.log(" - Economy provider is missing.");
Util.log(" - Cash rewards will not be given out..");
From 1188ef5308b872458a79a4873ade140d384ff05d Mon Sep 17 00:00:00 2001
From: ShaneBeee
Date: Thu, 14 May 2026 20:33:24 -0700
Subject: [PATCH 10/26] build.gradle.kts - exclude sources for server task
---
build.gradle.kts | 1 +
1 file changed, 1 insertion(+)
diff --git a/build.gradle.kts b/build.gradle.kts
index 40c23ca0..b902b165 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -80,6 +80,7 @@ tasks {
dependsOn("shadowJar")
from("build/libs") {
include("HungerGames-*.jar")
+ exclude("*-sources.jar")
destinationDir = file("/Users/ShaneBee/Desktop/Server/Minecraft/Skript/${serverLocation}/plugins/")
}
From b532758c05268373dc1006543b1c6bc8f62ce8e0 Mon Sep 17 00:00:00 2001
From: ShaneBeee
Date: Thu, 14 May 2026 20:39:26 -0700
Subject: [PATCH 11/26] Data - add more data
---
.../com/shanebeestudios/hg/api/game/Data.java | 29 ++++++++++++++++---
1 file changed, 25 insertions(+), 4 deletions(-)
diff --git a/src/main/java/com/shanebeestudios/hg/api/game/Data.java b/src/main/java/com/shanebeestudios/hg/api/game/Data.java
index 787c3b33..d406f202 100644
--- a/src/main/java/com/shanebeestudios/hg/api/game/Data.java
+++ b/src/main/java/com/shanebeestudios/hg/api/game/Data.java
@@ -2,6 +2,7 @@
import com.shanebeestudios.hg.plugin.HungerGames;
import com.shanebeestudios.hg.plugin.configs.Language;
+import com.shanebeestudios.hg.plugin.managers.GameManager;
/**
* General class for storing different aspects of data for {@link Game Games}
@@ -10,30 +11,50 @@ public abstract class Data {
final Game game;
final HungerGames plugin;
+ final GameManager gameManager;
final Language lang;
Data(Game game) {
this.game = game;
this.plugin = game.plugin;
+ this.gameManager = this.plugin.getGameManager();
this.lang = game.lang;
}
/**
- * Get the {@link Game} this data belongs to
+ * Get the {@link Game} this data belongs to.
*
* @return Game this data belongs to
*/
public Game getGame() {
- return game;
+ return this.game;
}
/**
- * Quick method to access the main plugin
+ * Quick method to access the main plugin.
*
* @return Instance of {@link HungerGames plugin}
*/
public HungerGames getPlugin() {
- return plugin;
+ return this.plugin;
+ }
+
+ /**
+ * Get the {@link GameManager} for the plugin.
+ *
+ * @return Instance of {@link GameManager}
+ */
+ public GameManager getGameManager() {
+ return this.gameManager;
+ }
+
+ /**
+ * Get the {@link Language} for the plugin.
+ *
+ * @return Instance of {@link Language}
+ */
+ public Language getLang() {
+ return this.lang;
}
}
From f45699fbb60b16e34861dc2ead1eeb3606ae9c81 Mon Sep 17 00:00:00 2001
From: ShaneBeee
Date: Thu, 14 May 2026 20:40:24 -0700
Subject: [PATCH 12/26] Game - fix exit location of arena not working
---
.../hg/api/game/GameArenaData.java | 23 +++++++++++++++++--
.../hg/api/game/GamePlayerData.java | 5 +---
2 files changed, 22 insertions(+), 6 deletions(-)
diff --git a/src/main/java/com/shanebeestudios/hg/api/game/GameArenaData.java b/src/main/java/com/shanebeestudios/hg/api/game/GameArenaData.java
index c8c00cd5..cbde3839 100644
--- a/src/main/java/com/shanebeestudios/hg/api/game/GameArenaData.java
+++ b/src/main/java/com/shanebeestudios/hg/api/game/GameArenaData.java
@@ -5,6 +5,7 @@
import com.shanebeestudios.hg.plugin.HungerGames;
import net.kyori.adventure.text.Component;
import org.bukkit.Location;
+import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
@@ -80,7 +81,8 @@ public Game checkOverlap() {
for (Game toCheck : HungerGames.getPlugin().getGameManager().getGames()) {
if (this.game.equals(toCheck)) continue;
- if (toCheck.getGameArenaData().getGameRegion().getBoundingBox().overlaps(this.gameRegion.getBoundingBox())) return toCheck;
+ if (toCheck.getGameArenaData().getGameRegion().getBoundingBox().overlaps(this.gameRegion.getBoundingBox()))
+ return toCheck;
}
return null;
}
@@ -231,7 +233,24 @@ public Location getExitLocation() {
}
/**
- * Set exit location for this game
+ * Get the exit location for a player.
+ *
+ * If the game has a specific exit location, it will be returned.
+ * Otherwise, the global exit location for the player will be returned.
+ *
+ *
+ * @param player Player to get exit location for
+ * @return Exit location for the player
+ */
+ public Location getExitForPlayer(Player player) {
+ if (this.exit != null) {
+ return this.exit;
+ }
+ return this.gameManager.getGlobalExitLocation(player);
+ }
+
+ /**
+ * Set the exit location for this game.
*
* @param location Location where players will exit
*/
diff --git a/src/main/java/com/shanebeestudios/hg/api/game/GamePlayerData.java b/src/main/java/com/shanebeestudios/hg/api/game/GamePlayerData.java
index defe6b1f..f9594128 100644
--- a/src/main/java/com/shanebeestudios/hg/api/game/GamePlayerData.java
+++ b/src/main/java/com/shanebeestudios/hg/api/game/GamePlayerData.java
@@ -6,7 +6,6 @@
import com.shanebeestudios.hg.api.status.Status;
import com.shanebeestudios.hg.api.util.Util;
import com.shanebeestudios.hg.plugin.configs.Config;
-import com.shanebeestudios.hg.plugin.managers.GameManager;
import com.shanebeestudios.hg.plugin.managers.PlayerManager;
import com.shanebeestudios.hg.plugin.permission.Permissions;
import net.kyori.adventure.title.Title;
@@ -43,7 +42,6 @@ public class GamePlayerData extends Data {
private static final @NotNull NamespacedKey MOVE_KEY = NamespacedKey.fromString("hg:freeze_move");
private final PlayerManager playerManager;
- private final GameManager gameManager;
// Player Lists
private final Map players = new HashMap<>();
@@ -59,7 +57,6 @@ public class GamePlayerData extends Data {
protected GamePlayerData(Game game) {
super(game);
this.playerManager = this.plugin.getPlayerManager();
- this.gameManager = this.plugin.getGameManager();
}
/**
@@ -308,7 +305,7 @@ void exit(Player player, @Nullable Location exitLocation) {
player.setInvulnerable(false);
if (gameArenaData.getStatus() == Status.RUNNING)
this.game.getGameBarData().removePlayer(player);
- Location location = exitLocation != null ? exitLocation : this.gameManager.getGlobalExitLocation(player);
+ Location location = exitLocation != null ? exitLocation : this.game.getGameArenaData().getExitForPlayer(player);
PlayerData playerData = this.playerManager.getData(player);
if (playerData == null || playerData.isOnline()) {
player.teleportAsync(location);
From 0315726c429b17c2a4b78c8c8b00c0c9381206b4 Mon Sep 17 00:00:00 2001
From: ShaneBeee
Date: Fri, 15 May 2026 06:50:51 -0700
Subject: [PATCH 13/26] Util - add debug with format
---
.../com/shanebeestudios/hg/api/util/Util.java | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/src/main/java/com/shanebeestudios/hg/api/util/Util.java b/src/main/java/com/shanebeestudios/hg/api/util/Util.java
index 840b4eae..2a65ec8c 100755
--- a/src/main/java/com/shanebeestudios/hg/api/util/Util.java
+++ b/src/main/java/com/shanebeestudios/hg/api/util/Util.java
@@ -99,7 +99,7 @@ public static void warning(String format, Object... objects) {
}
/**
- * Send a debug message to console
+ * Send a debug message to console.
* This will only send if 'debug' is enabled in config.yml
*
* @param debug Debug message to log
@@ -110,6 +110,19 @@ public static void debug(String debug) {
}
}
+ /**
+ * Send a formatted debug message to the console.
+ * This will only send if 'debug' is enabled in config.yml
+ *
+ * @param format Message format
+ * @param args Arguments for message format
+ */
+ public static void debug(String format, Object... args) {
+ if (Config.SETTINGS_DEBUG) {
+ log(format, args);
+ }
+ }
+
/**
* Send a debug exception to console
* This will only send if 'debug' is enabled in config.yml
From 8b4fedf777a3261e47d71bc5c0d6072f7febc46d Mon Sep 17 00:00:00 2001
From: ShaneBeee
Date: Fri, 15 May 2026 08:10:27 -0700
Subject: [PATCH 14/26] Add player tracking config options
---
.../hg/api/data/PlayerData.java | 9 +++
.../hg/api/game/GameEntityData.java | 8 +++
.../hg/api/game/GamePlayerData.java | 13 +++-
.../hg/api/util/ItemUtils.java | 4 +-
.../hg/plugin/configs/Config.java | 61 +++++++++++++++++--
.../hg/plugin/managers/KillManager.java | 2 +-
6 files changed, 87 insertions(+), 10 deletions(-)
diff --git a/src/main/java/com/shanebeestudios/hg/api/data/PlayerData.java b/src/main/java/com/shanebeestudios/hg/api/data/PlayerData.java
index fe7ac45d..a816f207 100755
--- a/src/main/java/com/shanebeestudios/hg/api/data/PlayerData.java
+++ b/src/main/java/com/shanebeestudios/hg/api/data/PlayerData.java
@@ -5,6 +5,7 @@
import com.shanebeestudios.hg.api.util.Util;
import com.shanebeestudios.hg.plugin.HungerGames;
import org.bukkit.Bukkit;
+import org.bukkit.Color;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.attribute.Attribute;
@@ -41,6 +42,8 @@ public class PlayerData implements Cloneable {
private Location previousLocation = null;
private boolean online;
private boolean hasGameStarted;
+ private double waypointReceiveRange;
+ private Color trackingColor;
//InGame data
private GameTeam gameTeam;
@@ -59,6 +62,7 @@ public PlayerData(Player player, Game game) {
this.online = true;
}
+ @SuppressWarnings("DataFlowIssue")
public void backup() {
this.hasGameStarted = true;
this.inv = this.player.getInventory().getStorageContents();
@@ -73,6 +77,8 @@ public void backup() {
this.player.setLevel(0);
this.player.setExp(0);
this.scoreboard = this.player.getScoreboard();
+ this.waypointReceiveRange = this.player.getAttribute(Attribute.WAYPOINT_RECEIVE_RANGE).getValue();
+ this.trackingColor = this.player.getWaypointColor();
}
/**
@@ -80,6 +86,7 @@ public void backup() {
*
* @param player Player to restore data to
*/
+ @SuppressWarnings("DataFlowIssue")
public void restore(Player player) {
if (player == null || !this.hasGameStarted) return;
Util.clearInv(player);
@@ -97,6 +104,8 @@ public void restore(Player player) {
// Force back their original scoreboard
player.setScoreboard(DUMMY);
player.setScoreboard(this.scoreboard);
+ player.getAttribute(Attribute.WAYPOINT_RECEIVE_RANGE).setBaseValue(Math.max(0, this.waypointReceiveRange));
+ player.setWaypointColor(this.trackingColor);
}
// Restores later if player has an item in their inventory which changes their max health value
diff --git a/src/main/java/com/shanebeestudios/hg/api/game/GameEntityData.java b/src/main/java/com/shanebeestudios/hg/api/game/GameEntityData.java
index 5a5a7052..f087a7b1 100644
--- a/src/main/java/com/shanebeestudios/hg/api/game/GameEntityData.java
+++ b/src/main/java/com/shanebeestudios/hg/api/game/GameEntityData.java
@@ -3,7 +3,10 @@
import com.shanebeestudios.hg.api.data.MobData;
import com.shanebeestudios.hg.api.data.MobEntry;
import com.shanebeestudios.hg.plugin.HungerGames;
+import com.shanebeestudios.hg.plugin.configs.Config;
import org.bukkit.Location;
+import org.bukkit.attribute.Attribute;
+import org.bukkit.entity.Enemy;
import org.bukkit.entity.Entity;
import org.bukkit.metadata.FixedMetadataValue;
import org.jetbrains.annotations.NotNull;
@@ -52,6 +55,7 @@ public void setMobData(MobData mobData) {
* @param dayTime Whether it's daytime/nighttime
* @return Whether an entity spawned
*/
+ @SuppressWarnings("DataFlowIssue")
public boolean spawnMob(Location location, boolean dayTime) {
MobEntry mobEntry;
if (dayTime) {
@@ -63,6 +67,10 @@ public boolean spawnMob(Location location, boolean dayTime) {
Entity spawn = mobEntry.spawn(location);
if (spawn != null) {
logEntity(spawn);
+ if (spawn instanceof Enemy enemy && Config.PLAYER_TRACKING_DISTANCE > 0 && Config.PLAYER_TRACKING_ENEMY_ENTITY_COLOR != null) {
+ enemy.getAttribute(Attribute.WAYPOINT_TRANSMIT_RANGE).setBaseValue(Math.max(0, Config.PLAYER_TRACKING_DISTANCE));
+ enemy.setWaypointColor(Config.PLAYER_TRACKING_ENEMY_ENTITY_COLOR);
+ }
return true;
}
}
diff --git a/src/main/java/com/shanebeestudios/hg/api/game/GamePlayerData.java b/src/main/java/com/shanebeestudios/hg/api/game/GamePlayerData.java
index f9594128..fa2e80b0 100644
--- a/src/main/java/com/shanebeestudios/hg/api/game/GamePlayerData.java
+++ b/src/main/java/com/shanebeestudios/hg/api/game/GamePlayerData.java
@@ -217,6 +217,7 @@ public void putAllPlayersIntoArena() {
this.players.keySet().forEach(this::putPlayerIntoArena);
}
+ @SuppressWarnings("DataFlowIssue")
void putPlayerIntoArena(Player player) {
boolean savePreviousLocation = this.players.get(player);
Location loc = pickRandomSpawn();
@@ -234,13 +235,23 @@ void putPlayerIntoArena(Player player) {
// Teleport async into the arena so it loads a little more smoothly
player.teleportAsync(loc).thenAccept(a -> {
+ Util.debug("Putting player %s into arena", player.getName());
PlayerData playerData = this.playerManager.getPlayerData(player);
- assert playerData != null;
+ assert playerData != null : "This should never happen, player data should always exist";
playerData.backup();
if (savePreviousLocation && Config.SETTINGS_SAVE_PREVIOUS_LOCATION) {
playerData.setPreviousLocation(previousLocation);
}
this.game.getGameScoreboard().setupBoard(player);
+ Util.debug("Player %s has been teleported into arena", player.getName());
+
+ // Setup Tracking
+ if (Config.PLAYER_TRACKING_DISTANCE >= 0) {
+ player.getAttribute(Attribute.WAYPOINT_RECEIVE_RANGE).setBaseValue(Math.max(0, Config.PLAYER_TRACKING_DISTANCE));
+ }
+ if (Config.PLAYER_TRACKING_ENEMY_PLAYER_COLOR != null) {
+ player.setWaypointColor(Config.PLAYER_TRACKING_ENEMY_PLAYER_COLOR);
+ }
heal(player);
freeze(player);
diff --git a/src/main/java/com/shanebeestudios/hg/api/util/ItemUtils.java b/src/main/java/com/shanebeestudios/hg/api/util/ItemUtils.java
index a8c0817d..37b2194c 100644
--- a/src/main/java/com/shanebeestudios/hg/api/util/ItemUtils.java
+++ b/src/main/java/com/shanebeestudios/hg/api/util/ItemUtils.java
@@ -10,8 +10,6 @@
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.ItemType;
-import org.bukkit.inventory.meta.ItemMeta;
-import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.Nullable;
@@ -38,7 +36,7 @@ public static ItemStack getTrackingStick() {
itemStack.setData(DataComponentTypes.ITEM_NAME, Util.getMini(LANG.item_tracking_stick_name));
itemStack.setData(DataComponentTypes.MAX_STACK_SIZE, 1);
- itemStack.setData(DataComponentTypes.MAX_DAMAGE, Config.SETTINGS_TRACKING_STICK_USES);
+ itemStack.setData(DataComponentTypes.MAX_DAMAGE, Config.PLAYER_TRACKING_TRACKING_STICK_USES);
itemStack.setData(DataComponentTypes.DAMAGE, 0);
List lore = new ArrayList<>();
LANG.item_tracking_stick_lore.forEach(line -> lore.add(Util.getMini(line)));
diff --git a/src/main/java/com/shanebeestudios/hg/plugin/configs/Config.java b/src/main/java/com/shanebeestudios/hg/plugin/configs/Config.java
index 4e7149ba..5afa818a 100755
--- a/src/main/java/com/shanebeestudios/hg/plugin/configs/Config.java
+++ b/src/main/java/com/shanebeestudios/hg/plugin/configs/Config.java
@@ -4,10 +4,13 @@
import com.shanebeestudios.hg.api.util.Util;
import com.shanebeestudios.hg.api.util.Vault;
import com.shanebeestudios.hg.plugin.HungerGames;
+import org.bukkit.Color;
import org.bukkit.Location;
import org.bukkit.configuration.Configuration;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
@@ -22,18 +25,23 @@ public class Config {
public static boolean SETTINGS_DEBUG;
- //Basic settings
+ // Basic settings
public static boolean SETTINGS_BROADCAST_JOIN_MESSAGES;
public static boolean SETTINGS_BROADCAST_WIN_MESSAGES;
public static boolean HAS_ECONOMY = true;
public static boolean SETTINGS_BOSSBAR_COUNTDOWN;
- public static int SETTINGS_TRACKING_STICK_USES;
- public static int SETTINGS_PLAYERS_FOR_TRACKING_STICK;
public static int SETTINGS_TELEPORT_AT_END_TIME;
public static boolean SETTINGS_SAVE_PREVIOUS_LOCATION;
public static int SETTINGS_FREE_ROAM_TIME;
public static Location SETTINGS_GLOBAL_EXIT_LOCATION;
+ // Player Tracking
+ public static int PLAYER_TRACKING_DISTANCE;
+ public static Color PLAYER_TRACKING_ENEMY_PLAYER_COLOR;
+ public static Color PLAYER_TRACKING_ENEMY_ENTITY_COLOR;
+ public static int PLAYER_TRACKING_TRACKING_STICK_USES;
+ public static int PLAYER_TRACKING_PLAYERS_FOR_TRACKING_STICK;
+
// Scoreboard
public static boolean SCOREBOARD_HIDE_NAMETAGS;
public static boolean SCOREBOARD_SHOW_HEALTH_ENABLED;
@@ -136,8 +144,6 @@ private void loadConfig() {
SETTINGS_BROADCAST_JOIN_MESSAGES = config.getBoolean("settings.broadcast-join-messages");
SETTINGS_BROADCAST_WIN_MESSAGES = config.getBoolean("settings.broadcast-win-messages");
SETTINGS_BOSSBAR_COUNTDOWN = config.getBoolean("settings.bossbar-countdown");
- SETTINGS_TRACKING_STICK_USES = config.getInt("settings.tracking-stick-uses");
- SETTINGS_PLAYERS_FOR_TRACKING_STICK = config.getInt("settings.players-for-tracking-stick");
SETTINGS_SAVE_PREVIOUS_LOCATION = config.getBoolean("settings.save-previous-location");
SETTINGS_TELEPORT_AT_END_TIME = config.getInt("settings.teleport-at-end-time");
SETTINGS_FREE_ROAM_TIME = config.getInt("settings.free-room-time");
@@ -146,6 +152,13 @@ private void loadConfig() {
SETTINGS_GLOBAL_EXIT_LOCATION = LocationParser.getLocFromString(locString);
}
+ // Player Tracking
+ PLAYER_TRACKING_DISTANCE = this.config.getInt("player-tracking.distance");
+ PLAYER_TRACKING_ENEMY_PLAYER_COLOR = getColor("player-tracking.enemy-player-color");
+ PLAYER_TRACKING_ENEMY_ENTITY_COLOR = getColor("player-tracking.enemy-entity-color");
+ PLAYER_TRACKING_TRACKING_STICK_USES = config.getInt("player-tracking.tracking-stick-uses");
+ PLAYER_TRACKING_PLAYERS_FOR_TRACKING_STICK = config.getInt("player-tracking.players-for-tracking-stick");
+
// Scoreboard
SCOREBOARD_HIDE_NAMETAGS = config.getBoolean("scoreboard.hide-nametags");
SCOREBOARD_SHOW_HEALTH_ENABLED = config.getBoolean("scoreboard.show-health.enabled");
@@ -276,4 +289,42 @@ public void setGlobalExitLocation(Location location) {
save();
}
+ private @Nullable Color getColor(@NotNull String setting) {
+ String string = this.config.getString(setting);
+ if (string == null) {
+ Util.log("Invalid color for setting '%s'", setting);
+ return null;
+ }
+ if (string.equalsIgnoreCase("disable") || string.equalsIgnoreCase("disabled")) {
+ return null;
+ }
+ if (string.startsWith("#")) {
+ // parse from hex
+ int i = Integer.parseInt(string.substring(1), 16);
+ return Color.fromRGB(i);
+ } else if (string.contains(":")) {
+ String[] split = string.split(":");
+ if (split.length == 3) {
+ try {
+ int r = Integer.parseInt(split[0]);
+ int g = Integer.parseInt(split[1]);
+ int b = Integer.parseInt(split[2]);
+ return Color.fromRGB(r,g,b);
+ } catch (NumberFormatException ignore) {
+ Util.log("Invalid color '%s' for setting '%s'", string, setting);
+ return null;
+ }
+ }
+ } else {
+ try {
+ return Color.fromRGB(Integer.parseInt(string));
+ } catch (NumberFormatException ignore) {
+ Util.log("Invalid color '%s' for setting '%s'", string, setting);
+ return null;
+ }
+ }
+ Util.log("Invalid color '%s' for setting '%s'", string, setting);
+ return null;
+ }
+
}
diff --git a/src/main/java/com/shanebeestudios/hg/plugin/managers/KillManager.java b/src/main/java/com/shanebeestudios/hg/plugin/managers/KillManager.java
index 6f29a5d2..c20dedce 100755
--- a/src/main/java/com/shanebeestudios/hg/plugin/managers/KillManager.java
+++ b/src/main/java/com/shanebeestudios/hg/plugin/managers/KillManager.java
@@ -271,7 +271,7 @@ private List dropInventoryOfPlayer(Player player) {
}
private void checkStick(Game game) {
- if (Config.SETTINGS_PLAYERS_FOR_TRACKING_STICK == game.getGamePlayerData().getPlayers().size()) {
+ if (Config.PLAYER_TRACKING_PLAYERS_FOR_TRACKING_STICK == game.getGamePlayerData().getPlayers().size()) {
for (Player player : game.getGamePlayerData().getPlayers()) {
Util.sendMessage(player, this.lang.item_tracking_stick_bar);
Util.sendMessage(player, this.lang.item_tracking_stick_new1);
From 6a144a17d04d84af853dc7e275ee2c6318ee29af Mon Sep 17 00:00:00 2001
From: ShaneBeee
Date: Fri, 15 May 2026 08:11:00 -0700
Subject: [PATCH 15/26] Add player tracking config options - part 2 - forgot to
add config
---
src/main/resources/config.yml | 26 ++++++++++++++++++++++----
1 file changed, 22 insertions(+), 4 deletions(-)
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
index 8386bb17..c46e5b27 100755
--- a/src/main/resources/config.yml
+++ b/src/main/resources/config.yml
@@ -26,10 +26,6 @@ settings:
free-roam-time: 25
# Whether or not to show a countdown bossbar in game
bossbar-countdown: true
- # The amount of uses a tracking stick will have
- tracking-stick-uses: 8
- # Minimum amount of players required in a game for a tracking stick to work
- players-for-tracking-stick: 5
# Maximum/Minimum amount of items that will spawn in chests
max-chestcontent: 5
min-chestcontent: 1
@@ -49,6 +45,28 @@ settings:
# If 'none', will use the player's respawn location or main world's spawn location
global-exit-location: 'none'
+## PLAYER TRACKING
+player-tracking:
+ # How far in blocks players can see each other on the tracking bar
+ # Set to -1 to keep default value (whatever the player's current tracking range is)
+ # Set to 0 to disable
+ distance: 100
+
+ # Represents the color of enemy players
+ # Set to 'disable' to keep default color the player is assigned
+ # Accepts int color, `#rrggbb` or `r:g:b` (0-255 for r:g:b) formats
+ enemy-player-color: '#EB2D07'
+
+ # Represents the color of enemy entities
+ # Set to 'disable' to disable
+ enemy-entity-color: '#EBD807'
+
+ # The number of uses a tracking stick will have
+ tracking-stick-uses: 8
+
+ # Minimum number of players required in a game for a tracking stick to work
+ players-for-tracking-stick: 5
+
# Settings for different scoreboard options
# This doesn't necessarily mean the sidebar
scoreboard:
From f4e530ba9258cf7914f88a18cbea76154bc0141e Mon Sep 17 00:00:00 2001
From: ShaneBeee
Date: Fri, 15 May 2026 12:20:43 -0700
Subject: [PATCH 16/26] PlayerManager - fix spectators data not backing up
---
.../java/com/shanebeestudios/hg/api/game/GamePlayerData.java | 2 +-
.../com/shanebeestudios/hg/plugin/managers/PlayerManager.java | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/main/java/com/shanebeestudios/hg/api/game/GamePlayerData.java b/src/main/java/com/shanebeestudios/hg/api/game/GamePlayerData.java
index fa2e80b0..c3e51e72 100644
--- a/src/main/java/com/shanebeestudios/hg/api/game/GamePlayerData.java
+++ b/src/main/java/com/shanebeestudios/hg/api/game/GamePlayerData.java
@@ -337,7 +337,7 @@ public void spectate(Player spectator) {
} else {
this.playerManager.createSpectatorData(spectator, this.game);
}
- this.spectators.put(spectator, true); // TODO should we handle location saving?
+ this.spectators.put(spectator, true);
spectator.setGameMode(GameMode.SURVIVAL);
spectator.setCollidable(false);
if (Config.SPECTATE_FLY)
diff --git a/src/main/java/com/shanebeestudios/hg/plugin/managers/PlayerManager.java b/src/main/java/com/shanebeestudios/hg/plugin/managers/PlayerManager.java
index c2ba4bef..dc00ec3f 100644
--- a/src/main/java/com/shanebeestudios/hg/plugin/managers/PlayerManager.java
+++ b/src/main/java/com/shanebeestudios/hg/plugin/managers/PlayerManager.java
@@ -112,6 +112,7 @@ public void createPlayerData(Player player, Game game) {
*/
public void createSpectatorData(Player spectator, Game game) {
PlayerData playerData = new PlayerData(spectator, game);
+ playerData.backup();
this.spectatorMap.put(spectator.getUniqueId(), playerData);
}
From 3c8bc94860594b8f6ab92bf09a8b74556ca1fc4c Mon Sep 17 00:00:00 2001
From: ShaneBeee
Date: Tue, 19 May 2026 05:44:48 -0700
Subject: [PATCH 17/26] Game - dont try to stop it if its already stopped
---
.../java/com/shanebeestudios/hg/api/game/Game.java | 4 ++++
.../com/shanebeestudios/hg/api/status/Status.java | 12 ++++++++++++
2 files changed, 16 insertions(+)
diff --git a/src/main/java/com/shanebeestudios/hg/api/game/Game.java b/src/main/java/com/shanebeestudios/hg/api/game/Game.java
index 7d67ded4..af0aa717 100755
--- a/src/main/java/com/shanebeestudios/hg/api/game/Game.java
+++ b/src/main/java/com/shanebeestudios/hg/api/game/Game.java
@@ -406,6 +406,10 @@ public void stop() {
* @param death Whether the game stopped after the result of a death (false = no winnings paid out)
*/
public void stop(boolean death) {
+ if (!this.gameArenaData.getStatus().isRunning()) {
+ // Don't attempt to stop if the game isn't running
+ return;
+ }
if (Config.WORLD_BORDER_ENABLED) {
this.gameBorderData.resetBorder(false);
}
diff --git a/src/main/java/com/shanebeestudios/hg/api/status/Status.java b/src/main/java/com/shanebeestudios/hg/api/status/Status.java
index dde0b858..912c8655 100755
--- a/src/main/java/com/shanebeestudios/hg/api/status/Status.java
+++ b/src/main/java/com/shanebeestudios/hg/api/status/Status.java
@@ -56,6 +56,18 @@ public boolean isActive() {
};
}
+ /**
+ * Whether the game is currently running.
+ *
+ * @return Is currently running
+ */
+ public boolean isRunning() {
+ return switch (this) {
+ case WAITING, COUNTDOWN, FREE_ROAM, RUNNING -> true;
+ default -> false;
+ };
+ }
+
public Component getName() {
return switch (this) {
case READY -> Util.getMini(this.lang.game_status_ready);
From c4691dab7bd6e36aa4b1fa9388d2a7a9eea64e56 Mon Sep 17 00:00:00 2001
From: ShaneBeee
Date: Tue, 19 May 2026 08:01:35 -0700
Subject: [PATCH 18/26] update Rollback system
---
.../com/shanebeestudios/hg/api/game/Game.java | 33 ++++---
.../hg/api/game/GameBlockData.java | 39 +++------
.../hg/api/game/GameRegion.java | 5 +-
.../shanebeestudios/hg/api/status/Status.java | 7 +-
.../hg/plugin/configs/Config.java | 2 +
.../hg/plugin/configs/Language.java | 2 +
.../hg/plugin/tasks/PrepareArenaTask.java | 86 +++++++++++++++++++
.../hg/plugin/tasks/RollbackTask.java | 2 +-
src/main/resources/config.yml | 4 +-
src/main/resources/language.yml | 1 +
10 files changed, 141 insertions(+), 40 deletions(-)
create mode 100644 src/main/java/com/shanebeestudios/hg/plugin/tasks/PrepareArenaTask.java
diff --git a/src/main/java/com/shanebeestudios/hg/api/game/Game.java b/src/main/java/com/shanebeestudios/hg/api/game/Game.java
index af0aa717..b30993da 100755
--- a/src/main/java/com/shanebeestudios/hg/api/game/Game.java
+++ b/src/main/java/com/shanebeestudios/hg/api/game/Game.java
@@ -20,6 +20,7 @@
import com.shanebeestudios.hg.plugin.tasks.GameTimerTask;
import com.shanebeestudios.hg.plugin.tasks.MobSpawnerTask;
import com.shanebeestudios.hg.plugin.tasks.NearestPlayerCompassTask;
+import com.shanebeestudios.hg.plugin.tasks.PrepareArenaTask;
import com.shanebeestudios.hg.plugin.tasks.RollbackTask;
import com.shanebeestudios.hg.plugin.tasks.StartingTask;
import org.bukkit.Bukkit;
@@ -45,6 +46,7 @@ public class Game {
// Tasks here!
private MobSpawnerTask mobSpawnerTask;
+ private PrepareArenaTask prepareArenaTask;
private FreeRoamTask freeRoamTask;
private StartingTask startingTask;
private GameTimerTask gameTimerTask;
@@ -222,17 +224,21 @@ public int getRemainingTime() {
}
/**
- * Initialize the waiting period of the game
- * This will be called when a player first joins
+ * Prepare the arena for the game.
+ */
+ public void prepareArena() {
+ this.gameArenaData.setStatus(Status.PREPARING);
+ this.prepareArenaTask = new PrepareArenaTask(this);
+ }
+
+ /**
+ * Initialize the waiting period of the game.
+ * This will be called when a player first joins.
*/
public void startWaitingPeriod() {
this.gameArenaData.setStatus(Status.WAITING);
- long start = System.currentTimeMillis();
- int count = this.gameBlockData.logBlocksForRollback();
- this.gameBlockData.setupRandomizedBonusChests();
- long fin = System.currentTimeMillis() - start;
- if (Config.SETTINGS_DEBUG) {
- Util.log("Logged %,d blocks in %sms for arena %s", count, fin, getGameArenaData().getName());
+ if (this.gamePlayerData.getPlayers().size() >= this.gameArenaData.getMinPlayers()) {
+ startPreGameCountdown();
}
}
@@ -286,6 +292,7 @@ public void startRunningGame() {
* Cancel all active tasks
*/
public void cancelTasks() {
+ if (this.prepareArenaTask != null) this.prepareArenaTask.stop();
if (this.startingTask != null) this.startingTask.stop();
if (this.freeRoamTask != null) this.freeRoamTask.stop();
if (this.gameTimerTask != null) this.gameTimerTask.stop();
@@ -369,7 +376,13 @@ public boolean joinGame(Player player, boolean savePreviousLocation) {
case READY -> {
if (!canJoin(player)) return false;
this.gamePlayerData.addPlayerData(player, savePreviousLocation);
- startWaitingPeriod();
+ prepareArena();
+ broadcastJoin(player);
+ Util.sendPrefixedMessage(player, this.lang.game_joined_waiting_to_teleport.replace("", arenaName));
+ }
+ case PREPARING -> {
+ if (!canJoin(player)) return false;
+ this.gamePlayerData.addPlayerData(player, savePreviousLocation);
broadcastJoin(player);
Util.sendPrefixedMessage(player, this.lang.game_joined_waiting_to_teleport.replace("", arenaName));
}
@@ -543,7 +556,7 @@ void updateAfterDeath(Player player, boolean death) {
.replace("", this.gameArenaData.getName())
.replace("", player.getName()) +
(this.gameArenaData.getMinPlayers() - this.gamePlayerData.getPlayers().size() <= 0 ? "!" : ": " + this.lang.game_waiting_players_to_start
- .replace("", String.valueOf((this.gameArenaData.getMinPlayers() - this.gamePlayerData.getPlayers().size())))));
+ .replace("", String.valueOf((this.gameArenaData.getMinPlayers() - this.gamePlayerData.getPlayers().size())))));
}
this.gameBlockData.updateLobbyBlock();
this.gameScoreboard.updateBoards();
diff --git a/src/main/java/com/shanebeestudios/hg/api/game/GameBlockData.java b/src/main/java/com/shanebeestudios/hg/api/game/GameBlockData.java
index 2c1de92b..1d8ef077 100644
--- a/src/main/java/com/shanebeestudios/hg/api/game/GameBlockData.java
+++ b/src/main/java/com/shanebeestudios/hg/api/game/GameBlockData.java
@@ -5,7 +5,6 @@
import com.shanebeestudios.hg.api.util.BlockUtils;
import com.shanebeestudios.hg.plugin.configs.Config;
import org.bukkit.Location;
-import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.block.BlockType;
@@ -32,7 +31,7 @@ public class GameBlockData extends Data {
private final Random random = new Random();
private final Map> chests = new HashMap<>();
- private final List blocks = new ArrayList<>();
+ private final List blocksToRollback = new ArrayList<>();
private final List randomBonusChests = new ArrayList<>();
private final Map itemFrameData = new HashMap<>();
private final GameLobbyWall gameLobbyWall;
@@ -164,24 +163,14 @@ public boolean canBeFilled(Location location) {
return true;
}
- /**
- * Log all blocks in an arena for rollback
- */
- public int logBlocksForRollback() {
- int count = 0;
- for (Location location : this.getGame().getGameArenaData().getGameRegion().getBlocks(null)) {
- Block block = location.getBlock();
- this.blocks.add(block.getState());
- count++;
- if (Config.CHESTS_BONUS_RANDOMIZE_ENABLED && BlockUtils.isBonusBlockReplacement(block)) {
- this.randomBonusChests.add(block);
- block.setType(Material.AIR);
- }
- }
- return count;
+ public void logBlockForRollback(Block block) {
+ this.blocksToRollback.add(block.getState());
+ }
+
+ public void logRandomBonusChest(Block block) {
+ this.randomBonusChests.add(block);
}
- @SuppressWarnings("UnstableApiUsage")
public void setupRandomizedBonusChests() {
if (!Config.CHESTS_BONUS_RANDOMIZE_ENABLED) return;
@@ -207,14 +196,14 @@ public void setupRandomizedBonusChests() {
* rollback at once, which can cause heavy amounts of lag.
*/
public void forceRollback() {
- Collections.reverse(blocks);
- for (BlockState state : blocks) {
+ Collections.reverse(this.blocksToRollback);
+ for (BlockState state : this.blocksToRollback) {
state.update(true);
}
}
boolean requiresRollback() {
- return !this.blocks.isEmpty() || !this.itemFrameData.isEmpty();
+ return !this.blocksToRollback.isEmpty() || !this.itemFrameData.isEmpty();
}
/**
@@ -241,16 +230,16 @@ public Collection getItemFrameData() {
*
* @return List of all recorded blocks
*/
- public List getBlocks() {
- Collections.reverse(this.blocks);
- return this.blocks;
+ public List getBlocksToRollback() {
+ Collections.reverse(this.blocksToRollback);
+ return this.blocksToRollback;
}
/**
* Clear the current block list
*/
public void resetBlocks() {
- this.blocks.clear();
+ this.blocksToRollback.clear();
this.randomBonusChests.clear();
}
diff --git a/src/main/java/com/shanebeestudios/hg/api/game/GameRegion.java b/src/main/java/com/shanebeestudios/hg/api/game/GameRegion.java
index 56f6728b..12cd349d 100755
--- a/src/main/java/com/shanebeestudios/hg/api/game/GameRegion.java
+++ b/src/main/java/com/shanebeestudios/hg/api/game/GameRegion.java
@@ -13,6 +13,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
+import java.util.function.Predicate;
/**
* Data holder for a {@link Game Game's} bounding box
@@ -76,7 +77,7 @@ public boolean isInRegion(Location loc) {
* @param type Material type to check
* @return ArrayList of locations of all blocks of this type in this bound
*/
- public List getBlocks(@Nullable Material type) {
+ public List getBlocks(@Nullable Predicate predicate) {
World world = Bukkit.getWorld(this.world);
assert world != null;
List blockList = new ArrayList<>();
@@ -86,7 +87,7 @@ public List getBlocks(@Nullable Material type) {
for (int z = (int) this.boundingBox.getMinZ(); z < this.boundingBox.getMaxZ(); z++) {
Block block = world.getBlockAt(x, y, z);
- if (type == null || block.getType() == type) {
+ if (predicate == null || predicate.test(block)) {
blockList.add(block.getLocation());
}
}
diff --git a/src/main/java/com/shanebeestudios/hg/api/status/Status.java b/src/main/java/com/shanebeestudios/hg/api/status/Status.java
index 912c8655..28527155 100755
--- a/src/main/java/com/shanebeestudios/hg/api/status/Status.java
+++ b/src/main/java/com/shanebeestudios/hg/api/status/Status.java
@@ -34,6 +34,10 @@ public enum Status {
* Game has stopped
*/
STOPPED,
+ /**
+ * Game is preparing to start
+ */
+ PREPARING,
/**
* Game is currently rolling back blocks
*/
@@ -63,7 +67,7 @@ public boolean isActive() {
*/
public boolean isRunning() {
return switch (this) {
- case WAITING, COUNTDOWN, FREE_ROAM, RUNNING -> true;
+ case PREPARING, WAITING, COUNTDOWN, FREE_ROAM, RUNNING -> true;
default -> false;
};
}
@@ -76,6 +80,7 @@ public Component getName() {
case FREE_ROAM -> Util.getMini(this.lang.game_status_free_roam);
case RUNNING -> Util.getMini(this.lang.game_status_running);
case STOPPED -> Util.getMini(this.lang.game_status_stopped);
+ case PREPARING -> Util.getMini(this.lang.game_status_preparing);
case ROLLBACK -> Util.getMini(this.lang.game_status_rollback);
case BROKEN -> Util.getMini(this.lang.game_status_broken);
case NOT_READY -> Util.getMini(this.lang.game_status_not_ready);
diff --git a/src/main/java/com/shanebeestudios/hg/plugin/configs/Config.java b/src/main/java/com/shanebeestudios/hg/plugin/configs/Config.java
index 5afa818a..20b83d36 100755
--- a/src/main/java/com/shanebeestudios/hg/plugin/configs/Config.java
+++ b/src/main/java/com/shanebeestudios/hg/plugin/configs/Config.java
@@ -64,6 +64,7 @@ public class Config {
public static List REWARD_MESSAGES;
//Rollback
+ public static boolean ROLLBACK_ENABLED;
public static boolean ROLLBACK_ALLOW_BREAK_BLOCKS;
public static int ROLLBACK_BLOCKS_PER_SECOND;
public static boolean ROLLBACK_PROTECT_DURING_FREE_ROAM;
@@ -180,6 +181,7 @@ private void loadConfig() {
REWARD_MESSAGES = config.getStringList("reward.messages");
// Rollback
+ ROLLBACK_ENABLED = config.getBoolean("rollback.enabled");
ROLLBACK_ALLOW_BREAK_BLOCKS = config.getBoolean("rollback.allow-block-break");
ROLLBACK_BLOCKS_PER_SECOND = config.getInt("rollback.blocks-per-second");
ROLLBACK_PROTECT_DURING_FREE_ROAM = config.getBoolean("rollback.protect-during-free-roam");
diff --git a/src/main/java/com/shanebeestudios/hg/plugin/configs/Language.java b/src/main/java/com/shanebeestudios/hg/plugin/configs/Language.java
index 1be05907..d18219ad 100644
--- a/src/main/java/com/shanebeestudios/hg/plugin/configs/Language.java
+++ b/src/main/java/com/shanebeestudios/hg/plugin/configs/Language.java
@@ -237,6 +237,7 @@ public class Language {
public String game_status_not_ready;
public String game_status_free_roam;
public String game_status_countdown;
+ public String game_status_preparing;
public String player_status_in_game;
public String player_status_spectator;
public String player_status_not_in_game;
@@ -529,6 +530,7 @@ private void loadLang() {
this.game_status_not_ready = this.lang.getString("status.game-status.not-ready");
this.game_status_free_roam = this.lang.getString("status.game-status.free-roam");
this.game_status_countdown = this.lang.getString("status.game-status.countdown");
+ this.game_status_preparing = this.lang.getString("status.game-status.preparing");
this.player_status_in_game = this.lang.getString("status.player-status.in-game");
this.player_status_spectator = this.lang.getString("status.player-status.spectator");
this.player_status_not_in_game = this.lang.getString("status.player-status.not-in-game");
diff --git a/src/main/java/com/shanebeestudios/hg/plugin/tasks/PrepareArenaTask.java b/src/main/java/com/shanebeestudios/hg/plugin/tasks/PrepareArenaTask.java
new file mode 100644
index 00000000..c67b68fa
--- /dev/null
+++ b/src/main/java/com/shanebeestudios/hg/plugin/tasks/PrepareArenaTask.java
@@ -0,0 +1,86 @@
+package com.shanebeestudios.hg.plugin.tasks;
+
+import com.shanebeestudios.hg.api.game.Game;
+import com.shanebeestudios.hg.api.util.BlockUtils;
+import com.shanebeestudios.hg.api.util.Util;
+import com.shanebeestudios.hg.plugin.HungerGames;
+import com.shanebeestudios.hg.plugin.configs.Config;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.block.Block;
+
+import java.util.Iterator;
+import java.util.List;
+
+public class PrepareArenaTask implements Runnable {
+
+ private final Game game;
+ private final int blocksPerTick;
+ private Iterator blocksToLog;
+ private final int countToLog;
+ private int totalLogged = 0;
+ private int taskId;
+
+ public PrepareArenaTask(Game game) {
+ this.game = game;
+ this.blocksPerTick = Config.ROLLBACK_BLOCKS_PER_SECOND / 20;
+
+ Util.debug("Starting prepare task, grabbing blocks to log...");
+ List blocks;
+ if (Config.ROLLBACK_ENABLED) {
+ blocks = this.game.getGameArenaData().getGameRegion().getBlocks(null);
+ } else if (Config.CHESTS_BONUS_RANDOMIZE_ENABLED) {
+ blocks = this.game.getGameArenaData().getGameRegion().getBlocks(BlockUtils::isBonusBlockReplacement);
+ } else {
+ blocks = List.of();
+ }
+ this.countToLog = blocks.size();
+ this.blocksToLog = blocks.iterator();
+ Util.debug("Found %s blocks to log", this.countToLog);
+ this.taskId = schedule();
+ }
+
+ @Override
+ public void run() {
+ int logged = 0;
+
+ while (logged < this.blocksPerTick && this.blocksToLog.hasNext()) {
+ if (this.taskId == -1) {
+ // Task has been stopped
+ return;
+ }
+ Block block = this.blocksToLog.next().getBlock();
+ this.game.getGameBlockData().logBlockForRollback(block);
+ logged++;
+ this.totalLogged++;
+
+ if (Config.CHESTS_BONUS_RANDOMIZE_ENABLED && BlockUtils.isBonusBlockReplacement(block)) {
+ this.game.getGameBlockData().logRandomBonusChest(block);
+ block.setType(Material.AIR);
+ }
+ }
+ if (this.blocksToLog.hasNext()) {
+ Util.debug("Logging blocks... %,d/%,d", this.totalLogged, this.countToLog);
+ this.taskId = this.schedule();
+ return;
+ }
+
+ Util.debug("Finished logging blocks and setting up bonus chests");
+ this.blocksToLog = null;
+ this.game.getGameBlockData().setupRandomizedBonusChests();
+ Bukkit.getScheduler().cancelTask(this.taskId);
+ this.game.startWaitingPeriod();
+ }
+
+ public void stop() {
+ Util.debug("Cancelling prepare task");
+ Bukkit.getScheduler().cancelTask(this.taskId);
+ this.taskId = -1;
+ }
+
+ private int schedule() {
+ return Bukkit.getScheduler().scheduleSyncDelayedTask(HungerGames.getPlugin(), this, 1);
+ }
+
+}
diff --git a/src/main/java/com/shanebeestudios/hg/plugin/tasks/RollbackTask.java b/src/main/java/com/shanebeestudios/hg/plugin/tasks/RollbackTask.java
index a01f22ad..f12c8f2f 100755
--- a/src/main/java/com/shanebeestudios/hg/plugin/tasks/RollbackTask.java
+++ b/src/main/java/com/shanebeestudios/hg/plugin/tasks/RollbackTask.java
@@ -28,7 +28,7 @@ public RollbackTask(Game game) {
this.gameBlockData = game.getGameBlockData();
this.blocks_per_tick = Config.ROLLBACK_BLOCKS_PER_SECOND / 20;
game.getGameArenaData().setStatus(Status.ROLLBACK);
- this.blockRollbackSession = this.gameBlockData.getBlocks().iterator();
+ this.blockRollbackSession = this.gameBlockData.getBlocksToRollback().iterator();
this.itemFrameDataIterator = this.gameBlockData.getItemFrameData().iterator();
this.taskId = Bukkit.getScheduler().scheduleSyncDelayedTask(HungerGames.getPlugin(), this, 20);
}
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
index c46e5b27..c30df3e8 100755
--- a/src/main/resources/config.yml
+++ b/src/main/resources/config.yml
@@ -157,12 +157,14 @@ chests:
min-content: 1
max-content: 5
rollback:
+ # Whether to enable the rollback system
+ enabled: true
# Let players break blocks in the arena, and restore when the game is done
allow-block-break: false
# Allow players to take items out of item frames (will be rolled back)
allow-itemframe-take: false
# The amount of blocks that will be restored during rollback per second (this number is actually divided by 20 and restored in 1 tick intervals)
- blocks-per-second: 500
+ blocks-per-second: 500000
# When enabled, will not allow players to break blocks during the free-roam time
protect-during-free-roam: true
# Prevent players from trampling crops in your arenas
diff --git a/src/main/resources/language.yml b/src/main/resources/language.yml
index 7bd59a6b..03fe8de2 100644
--- a/src/main/resources/language.yml
+++ b/src/main/resources/language.yml
@@ -341,6 +341,7 @@ status:
running: 'Running'
stopped: 'Stopped'
rollback: 'Restoring...'
+ preparing: 'Preparing...'
broken: 'BROKEN'
not-ready: 'NotReady'
player-status:
From f021f3ed47392f44f9ee337dec41b892b4e6eee8 Mon Sep 17 00:00:00 2001
From: ShaneBeee
Date: Tue, 19 May 2026 08:03:51 -0700
Subject: [PATCH 19/26] config - update some links
---
src/main/resources/config.yml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
index c30df3e8..c8a788d8 100755
--- a/src/main/resources/config.yml
+++ b/src/main/resources/config.yml
@@ -2,13 +2,13 @@
# Revamped by: ShaneBee
# Issues: Please report issues on the GitHub issue tracker @ https://github.com/ShaneBeeStudios/HungerGames/issues
-# Updates: Can be found on SpigotMC @ --- https://www.spigotmc.org/resources/hungergames.65942/
+# Updates: Can be found on Modrinth @ --- https://modrinth.com/plugin/hungergames-sb
# More Info: See the wiki for more info on this config @ https://github.com/ShaneBeeStudios/HungerGames/wiki/Config.yml
# Helpful Links:
-# Bukkit Material Enums: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html
-# Bukkit Tags: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Tag.html
+# Bukkit Material Enums: https://jd.papermc.io/paper/26.1.2/org/bukkit/Material.html
+# Bukkit Tags: https://jd.papermc.io/paper/26.1.2/org/bukkit/Tag.html
settings:
# When enabled, more informative debug messages will be sent when errors occur
From abfa0c9a79c342c67e1a333b4e80fb1cd49ff544 Mon Sep 17 00:00:00 2001
From: ShaneBeee
Date: Tue, 19 May 2026 09:21:06 -0700
Subject: [PATCH 20/26] ItemParser - clamp stack size
---
.../java/com/shanebeestudios/hg/api/parsers/ItemParser.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/java/com/shanebeestudios/hg/api/parsers/ItemParser.java b/src/main/java/com/shanebeestudios/hg/api/parsers/ItemParser.java
index 1efaf5ee..ceb8ec6c 100644
--- a/src/main/java/com/shanebeestudios/hg/api/parsers/ItemParser.java
+++ b/src/main/java/com/shanebeestudios/hg/api/parsers/ItemParser.java
@@ -47,7 +47,7 @@ public class ItemParser {
// COUNT
int count = 1;
if (config.contains("count")) {
- count = config.getInt("count");
+ count = Math.clamp(config.getInt("count"), 1, 99);
}
ItemStack itemStack = itemType.createItemStack(count);
From 15fbe45121bbcbc63c311d86624cc57e40ee5c93 Mon Sep 17 00:00:00 2001
From: ShaneBeee
Date: Tue, 19 May 2026 10:25:28 -0700
Subject: [PATCH 21/26] Location handling - now properly using locations and
world keys
---
.../hg/api/game/GameRegion.java | 54 ++++++++++----
.../hg/api/parsers/LocationParser.java | 26 +------
.../hg/plugin/configs/ArenaConfig.java | 74 +++++++++++++------
.../hg/plugin/configs/Config.java | 14 ++--
4 files changed, 102 insertions(+), 66 deletions(-)
diff --git a/src/main/java/com/shanebeestudios/hg/api/game/GameRegion.java b/src/main/java/com/shanebeestudios/hg/api/game/GameRegion.java
index 12cd349d..a7208040 100755
--- a/src/main/java/com/shanebeestudios/hg/api/game/GameRegion.java
+++ b/src/main/java/com/shanebeestudios/hg/api/game/GameRegion.java
@@ -3,7 +3,7 @@
import org.bukkit.Bukkit;
import org.bukkit.HeightMap;
import org.bukkit.Location;
-import org.bukkit.Material;
+import org.bukkit.NamespacedKey;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.util.BoundingBox;
@@ -22,25 +22,43 @@
public class GameRegion {
private final BoundingBox boundingBox;
- private final String world;
+ private NamespacedKey key;
+ private String name;
public static GameRegion createNew(@NotNull Block corner1, @NotNull Block corner2) {
BoundingBox boundingBox = BoundingBox.of(corner1, corner2);
- return new GameRegion(corner1.getWorld().getName(), boundingBox);
+ return new GameRegion(corner1.getWorld().getKey(), boundingBox);
}
- public static GameRegion loadFromConfig(String world, BoundingBox boundingBox) {
- return new GameRegion(world, boundingBox);
+ public static GameRegion loadFromConfig(NamespacedKey key, BoundingBox boundingBox) {
+ return new GameRegion(key, boundingBox);
+ }
+
+
+ @Deprecated(forRemoval = true, since = "INSERT VERSION")
+ public static GameRegion loadFromConfig(String name, BoundingBox boundingBox) {
+ World world = Bukkit.getWorld(name);
+ if (world == null) {
+ return new GameRegion(name, boundingBox);
+ }
+ return new GameRegion(world.getKey(), boundingBox);
}
/**
* Create a new bounding box between 2 sets of coordinates
*
- * @param world World this bound is in
+ * @param key World this bound is in
* @param boundingBox BoundingBox for this bound
*/
- private GameRegion(String world, BoundingBox boundingBox) {
- this.world = world;
+ private GameRegion(NamespacedKey key, BoundingBox boundingBox) {
+ this.key = key;
+ this.boundingBox = boundingBox;
+ }
+
+ @Deprecated(forRemoval = true, since = "INSERT VERSION")
+ private GameRegion(String name, BoundingBox boundingBox) {
+ this.key = null;
+ this.name = name;
this.boundingBox = boundingBox;
}
@@ -74,11 +92,11 @@ public boolean isInRegion(Location loc) {
/**
* Get location of all blocks of a type within a bound
*
- * @param type Material type to check
+ * @param predicate Material predicate to check
* @return ArrayList of locations of all blocks of this type in this bound
*/
public List getBlocks(@Nullable Predicate predicate) {
- World world = Bukkit.getWorld(this.world);
+ World world = getWorld();
assert world != null;
List blockList = new ArrayList<>();
@@ -101,8 +119,17 @@ public List getBlocks(@Nullable Predicate predicate) {
*
* @return World of this bound
*/
- public World getWorld() {
- return Bukkit.getWorld(this.world);
+ public @Nullable World getWorld() {
+ if (this.key != null) {
+ return Bukkit.getWorld(this.key);
+ } else if (this.name != null) {
+ World world = Bukkit.getWorld(this.name);
+ if (world != null) {
+ this.key = world.getKey();
+ return world;
+ }
+ }
+ return null;
}
public BoundingBox getBoundingBox() {
@@ -138,9 +165,10 @@ public Location getCenter() {
@Override
public String toString() {
+ String name = this.key != null ? this.key.toString() : this.name != null ? this.name : "unavailable";
return "Bound{" +
"boundingBox=" + boundingBox +
- ", world='" + world + '\'' +
+ ", world='" + name + '\'' +
'}';
}
diff --git a/src/main/java/com/shanebeestudios/hg/api/parsers/LocationParser.java b/src/main/java/com/shanebeestudios/hg/api/parsers/LocationParser.java
index df8bff70..a19f4d41 100644
--- a/src/main/java/com/shanebeestudios/hg/api/parsers/LocationParser.java
+++ b/src/main/java/com/shanebeestudios/hg/api/parsers/LocationParser.java
@@ -8,30 +8,9 @@
/**
* Parser for {@link Location Locations}
*/
+@Deprecated(forRemoval = true, since = "INSERT VERSION")
public class LocationParser {
- /**
- * Serialize a block location to a string
- *
- * @param location Block location to serialize
- * @return Serialized block location
- */
- public static String blockLocToString(Location location) {
- return location.getWorld().getName() + ":" + location.getBlockX() + ":" + location.getBlockY() + ":" + location.getBlockZ();
- }
-
- /**
- * Serialize a location to a string
- *
- * @param location Location to serialize
- * @return Serialized location
- */
- public static String locToString(Location location) {
- float yaw = (float) Math.floor(location.getYaw());
- float pitch = (float) Math.floor(location.getPitch());
- return location.getWorld().getName() + ":" + location.getX() + ":" + location.getY() + ":" + location.getZ() + ":" + yaw + ":" + pitch;
- }
-
/**
* Deserialize a block location from a string
*
@@ -49,7 +28,8 @@ public static Location getBlockLocFromString(String stringLocation) {
* @param stringLocation String to deserialize
* @return Location from string
*/
- public static @Nullable Location getLocFromString(String stringLocation) {
+ public static @Nullable Location getLocFromString(@Nullable String stringLocation) {
+ if (stringLocation == null) return null;
String[] split = stringLocation.split(":");
if (split.length < 4) return null;
diff --git a/src/main/java/com/shanebeestudios/hg/plugin/configs/ArenaConfig.java b/src/main/java/com/shanebeestudios/hg/plugin/configs/ArenaConfig.java
index 0db37f11..2598301e 100755
--- a/src/main/java/com/shanebeestudios/hg/plugin/configs/ArenaConfig.java
+++ b/src/main/java/com/shanebeestudios/hg/plugin/configs/ArenaConfig.java
@@ -12,6 +12,7 @@
import com.shanebeestudios.hg.plugin.managers.GameManager;
import org.bukkit.Difficulty;
import org.bukkit.Location;
+import org.bukkit.NamespacedKey;
import org.bukkit.World;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
@@ -145,7 +146,12 @@ public boolean loadArena(FileConfiguration arenaConfig, String arenaName) {
// LOCATIONS
ConfigurationSection locationsSection = arenaConfig.getConfigurationSection("locations");
try {
- lobbysign = LocationParser.getBlockLocFromString(locationsSection.getString("lobby_sign"));
+ if (locationsSection.isString("lobby_sign")) {
+ lobbysign = LocationParser.getBlockLocFromString(locationsSection.getString("lobby_sign"));
+ isDirty = true;
+ } else {
+ lobbysign = locationsSection.getLocation("lobby_sign");
+ }
} catch (Exception e) {
Util.warning("Unable to load lobby sign for arena '" + arenaName + "'!");
Util.debug(e);
@@ -153,8 +159,16 @@ public boolean loadArena(FileConfiguration arenaConfig, String arenaName) {
}
try {
- for (String location : locationsSection.getStringList("spawns")) {
- spawns.add(LocationParser.getLocFromString(location));
+ for (Object object : locationsSection.getList("spawns")) {
+ if (object instanceof String s) {
+ Location locFromString = LocationParser.getLocFromString(s);
+ if (locFromString != null) {
+ spawns.add(locFromString);
+ isDirty = true;
+ }
+ } else if (object instanceof Location location) {
+ spawns.add(location);
+ }
}
} catch (Exception e) {
Util.warning("Unable to load random spawns for arena '" + arenaName + "'!");
@@ -164,9 +178,16 @@ public boolean loadArena(FileConfiguration arenaConfig, String arenaName) {
// REGION
try {
ConfigurationSection regionSection = arenaConfig.getConfigurationSection("region");
- String world = regionSection.getString("world");
BoundingBox boundingBox = regionSection.getObject("bounding_box", BoundingBox.class);
- gameRegion = GameRegion.loadFromConfig(world, boundingBox);
+
+ if (regionSection.isSet("world")) {
+ String world = regionSection.getString("world");
+ gameRegion = GameRegion.loadFromConfig(world, boundingBox);
+ isDirty = true;
+ } else if (regionSection.isSet("world_key")) {
+ String worldKey = regionSection.getString("world_key");
+ gameRegion = GameRegion.loadFromConfig(NamespacedKey.fromString(worldKey), boundingBox);
+ }
} catch (Exception e) {
Util.warning("Unable to load region bounds for arena " + arenaName + "!");
isReady = false;
@@ -195,11 +216,16 @@ public boolean loadArena(FileConfiguration arenaConfig, String arenaName) {
GameBorderData gameBorderData = game.getGameBorderData();
if (borderSection.isSet("center_locations")) {
- List centerLocations = borderSection.getStringList("center_locations");
+ List> centerLocations = borderSection.getList("center_locations");
List centerLocationList = new ArrayList<>();
- for (String locString : centerLocations) {
- Location centerLocation = LocationParser.getBlockLocFromString(locString);
- centerLocationList.add(centerLocation);
+ for (Object object : centerLocations) {
+ if (object instanceof String locString) {
+ Location centerLocation = LocationParser.getBlockLocFromString(locString);
+ centerLocationList.add(centerLocation);
+ isDirty = true;
+ } else if (object instanceof Location location) {
+ centerLocationList.add(location);
+ }
}
gameBorderData.setCenterLocations(centerLocationList);
} else if (borderSection.isSet("center_location")) { // Deprecated (May 5/2026)
@@ -242,8 +268,16 @@ public boolean loadArena(FileConfiguration arenaConfig, String arenaName) {
}
try {
if (locationsSection.isSet("exit")) {
- Location exitLocation = LocationParser.getLocFromString(locationsSection.getString("exit"));
- gameArenaData.setExitLocation(exitLocation);
+ if (locationsSection.isString("exit")) {
+ Location exitLocation = LocationParser.getLocFromString(locationsSection.getString("exit"));
+ if (exitLocation != null) {
+ gameArenaData.setExitLocation(exitLocation);
+ isDirty = true;
+ }
+ } else if (locationsSection.isLocation("exit")) {
+ Location exitLocation = locationsSection.getLocation("exit");
+ gameArenaData.setExitLocation(exitLocation);
+ }
}
} catch (Exception exception) {
@@ -293,7 +327,7 @@ public void saveGameToConfig(Game game) {
// REGION
ConfigurationSection regionSection = gameSection.createSection("region");
- regionSection.set("world", gameArenaData.getGameRegion().getWorld().getName());
+ regionSection.set("world_key", gameArenaData.getGameRegion().getWorld().getKey().toString());
regionSection.set("bounding_box", gameArenaData.getGameRegion().getBoundingBox());
// COMMANDS
@@ -301,27 +335,19 @@ public void saveGameToConfig(Game game) {
// LOCATIONS
ConfigurationSection locationsSection = gameSection.createSection("locations");
- List spawns = new ArrayList<>();
- gameArenaData.getSpawns().forEach(spawn -> spawns.add(LocationParser.locToString(spawn)));
- locationsSection.set("spawns", spawns);
+ locationsSection.set("spawns", gameArenaData.getSpawns());
Location exit = gameArenaData.getExitLocation();
if (exit != null) {
- locationsSection.set("exit", LocationParser.blockLocToString(exit));
+ locationsSection.set("exit", exit);
}
- locationsSection.set("lobby_sign", LocationParser.blockLocToString(game.getLobbyLocation()));
+ locationsSection.set("lobby_sign", game.getLobbyLocation());
// BORDER
GameBorderData borderData = game.getGameBorderData();
if (!borderData.isDefault()) {
ConfigurationSection borderSection = gameSection.createSection("game_border");
- List centerLocations = new ArrayList<>();
- for (Location centerLocation : borderData.getCenterLocations()) {
- String locString = LocationParser.blockLocToString(centerLocation);
- centerLocations.add(locString);
-
- }
- borderSection.set("center_locations", centerLocations);
+ borderSection.set("center_locations", borderData.getCenterLocations());
borderSection.set("final_size", borderData.getFinalBorderSize());
borderSection.set("countdown_start", borderData.getBorderCountdownStart());
borderSection.set("countdown_end", borderData.getBorderCountdownEnd());
diff --git a/src/main/java/com/shanebeestudios/hg/plugin/configs/Config.java b/src/main/java/com/shanebeestudios/hg/plugin/configs/Config.java
index 20b83d36..abd239a0 100755
--- a/src/main/java/com/shanebeestudios/hg/plugin/configs/Config.java
+++ b/src/main/java/com/shanebeestudios/hg/plugin/configs/Config.java
@@ -148,9 +148,12 @@ private void loadConfig() {
SETTINGS_SAVE_PREVIOUS_LOCATION = config.getBoolean("settings.save-previous-location");
SETTINGS_TELEPORT_AT_END_TIME = config.getInt("settings.teleport-at-end-time");
SETTINGS_FREE_ROAM_TIME = config.getInt("settings.free-room-time");
- String locString = config.getString("settings.global-exit-location");
- if (locString != null && locString.contains(":")) {
- SETTINGS_GLOBAL_EXIT_LOCATION = LocationParser.getLocFromString(locString);
+
+ if (this.config.isString("settings.global-exit-location")) {
+ SETTINGS_GLOBAL_EXIT_LOCATION = LocationParser.getLocFromString(this.config.getString("settings.global-exit-location"));
+ setGlobalExitLocation(SETTINGS_GLOBAL_EXIT_LOCATION);
+ } else if (this.config.isLocation("settings.global-exit-location")) {
+ SETTINGS_GLOBAL_EXIT_LOCATION = this.config.getLocation("settings.global-exit-location");
}
// Player Tracking
@@ -286,8 +289,7 @@ public void save() {
* @param location Global exit location
*/
public void setGlobalExitLocation(Location location) {
- String locString = LocationParser.locToString(location);
- this.config.set("settings.global-exit-location", locString);
+ this.config.set("settings.global-exit-location", location);
save();
}
@@ -311,7 +313,7 @@ public void setGlobalExitLocation(Location location) {
int r = Integer.parseInt(split[0]);
int g = Integer.parseInt(split[1]);
int b = Integer.parseInt(split[2]);
- return Color.fromRGB(r,g,b);
+ return Color.fromRGB(r, g, b);
} catch (NumberFormatException ignore) {
Util.log("Invalid color '%s' for setting '%s'", string, setting);
return null;
From 9da353f177e22a22a941d342a5185db8d987b083 Mon Sep 17 00:00:00 2001
From: ShaneBeee
Date: Wed, 20 May 2026 14:44:44 -0700
Subject: [PATCH 22/26] Util - remove old deprecated method
---
.../com/shanebeestudios/hg/api/util/Util.java | 26 -------------------
1 file changed, 26 deletions(-)
diff --git a/src/main/java/com/shanebeestudios/hg/api/util/Util.java b/src/main/java/com/shanebeestudios/hg/api/util/Util.java
index 2a65ec8c..dbdea06f 100755
--- a/src/main/java/com/shanebeestudios/hg/api/util/Util.java
+++ b/src/main/java/com/shanebeestudios/hg/api/util/Util.java
@@ -6,7 +6,6 @@
import dev.jorel.commandapi.arguments.CustomArgument.CustomArgumentException;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
-import net.md_5.bungee.api.ChatColor;
import org.bukkit.Bukkit;
import org.bukkit.NamespacedKey;
import org.bukkit.block.BlockFace;
@@ -15,9 +14,6 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
/**
* Generalized utility class for shortcut methods
*/
@@ -29,7 +25,6 @@ public class Util {
public static final boolean IS_RUNNING_FOLIA = classExists("io.papermc.paper.threadedregions.FoliaWatchdogThread");
// PRIVATE
- private static final Pattern HEX_PATTERN = Pattern.compile("<#([A-Fa-f0-9]){6}>");
private static final CommandSender CONSOLE = Bukkit.getConsoleSender();
private static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage();
private static String PREFIX;
@@ -150,27 +145,6 @@ public static void broadcast(String s) {
}
}
- /**
- * Shortcut for adding color to a string
- *
- * @param string String including color codes
- * @return Formatted string
- */
- @Deprecated(forRemoval = true)
- public static String getColString(String string) {
- if (isRunningMinecraft(1, 16)) {
- Matcher matcher = HEX_PATTERN.matcher(string);
- while (matcher.find()) {
- final ChatColor hexColor = ChatColor.of(matcher.group().substring(1, matcher.group().length() - 1));
- final String before = string.substring(0, matcher.start());
- final String after = string.substring(matcher.end());
- string = before + hexColor + after;
- matcher = HEX_PATTERN.matcher(string);
- }
- }
- return ChatColor.translateAlternateColorCodes('&', string);
- }
-
/**
* Convert text to MiniMessage
*
From d1495de29b4d46449a38c3c35a31235ed61c1897 Mon Sep 17 00:00:00 2001
From: ShaneBeee
Date: Wed, 20 May 2026 15:24:47 -0700
Subject: [PATCH 23/26] Util - version check updates
---
.../com/shanebeestudios/hg/api/util/Util.java | 32 +++++++++++--------
1 file changed, 19 insertions(+), 13 deletions(-)
diff --git a/src/main/java/com/shanebeestudios/hg/api/util/Util.java b/src/main/java/com/shanebeestudios/hg/api/util/Util.java
index dbdea06f..788907bf 100755
--- a/src/main/java/com/shanebeestudios/hg/api/util/Util.java
+++ b/src/main/java/com/shanebeestudios/hg/api/util/Util.java
@@ -215,26 +215,26 @@ public static void clearInv(Player player) {
}
/**
- * Check if server is running a minimum Minecraft version
+ * Check if the server is running a minimum Minecraft version.
*
- * @param major Major version to check (Most likely just going to be 1)
- * @param minor Minor version to check
- * @return True if running this version or higher
+ * @param year Year version to check
+ * @param drop Drop version to check
+ * @return True if running this version or higher, otherwise false
*/
- public static boolean isRunningMinecraft(int major, int minor) {
- return isRunningMinecraft(major, minor, 0);
+ public static boolean isRunningMinecraft(int year, int drop) {
+ return isRunningMinecraft(year, drop, 0);
}
/**
- * Check if server is running a minimum Minecraft version
+ * Check if the server is running a minimum Minecraft version.
*
- * @param major Major version to check (Most likely just going to be 1)
- * @param minor Minor version to check
+ * @param year Year version to check
+ * @param drop Drop version to check
* @param revision Revision to check
- * @return True if running this version or higher
+ * @return True if running this version or higher, otherwise false
*/
- public static boolean isRunningMinecraft(int major, int minor, int revision) {
- String[] version = Bukkit.getServer().getBukkitVersion().split("-")[0].split("\\.");
+ public static boolean isRunningMinecraft(int year, int drop, int revision) {
+ String[] version = Bukkit.getServer().getMinecraftVersion().split("\\.");
int maj = Integer.parseInt(version[0]);
int min = Integer.parseInt(version[1]);
int rev;
@@ -243,7 +243,13 @@ public static boolean isRunningMinecraft(int major, int minor, int revision) {
} catch (Exception ignore) {
rev = 0;
}
- return maj > major || min > minor || (min == minor && rev >= revision);
+ if (maj > year) {
+ return true;
+ } else if (maj == year && min > drop) {
+ return true;
+ } else {
+ return maj == year && min == drop && rev >= revision;
+ }
}
/**
From 20f336e84440004a4994042925af2d071b16d27d Mon Sep 17 00:00:00 2001
From: ShaneBeee
Date: Wed, 20 May 2026 15:25:05 -0700
Subject: [PATCH 24/26] Javadoc updates
---
build.gradle.kts | 1 +
.../hg/api/game/GameRegion.java | 10 +++-
.../hg/api/parsers/package-info.java | 4 ++
.../hg/api/region/package-info.java | 4 ++
.../hg/api/region/scheduler/package-info.java | 2 +-
.../region/scheduler/task/package-info.java | 4 ++
.../hg/plugin/HungerGames.java | 52 +++++++++++++------
.../hg/plugin/configs/package-info.java | 4 ++
.../hg/plugin/permission/Permissions.java | 3 ++
.../hg/plugin/permission/package-info.java | 4 ++
.../plugin/tasks/ChestRefillRepeatTask.java | 3 ++
.../hg/plugin/tasks/FreeRoamTask.java | 3 ++
.../hg/plugin/tasks/GameTimerTask.java | 3 ++
.../hg/plugin/tasks/MobSpawnerTask.java | 3 ++
.../tasks/NearestPlayerCompassTask.java | 3 ++
.../hg/plugin/tasks/PrepareArenaTask.java | 3 ++
.../hg/plugin/tasks/StartingTask.java | 3 ++
.../hg/plugin/tasks/WorldBorderTask.java | 4 +-
.../hg/plugin/tasks/package-info.java | 4 ++
19 files changed, 97 insertions(+), 20 deletions(-)
create mode 100644 src/main/java/com/shanebeestudios/hg/api/parsers/package-info.java
create mode 100644 src/main/java/com/shanebeestudios/hg/api/region/package-info.java
create mode 100644 src/main/java/com/shanebeestudios/hg/api/region/scheduler/task/package-info.java
create mode 100644 src/main/java/com/shanebeestudios/hg/plugin/configs/package-info.java
create mode 100644 src/main/java/com/shanebeestudios/hg/plugin/permission/package-info.java
create mode 100644 src/main/java/com/shanebeestudios/hg/plugin/tasks/package-info.java
diff --git a/build.gradle.kts b/build.gradle.kts
index b902b165..ba9eecb6 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -107,6 +107,7 @@ tasks {
exclude("com/shanebeestudios/hg/plugin/commands")
exclude("com/shanebeestudios/hg/plugin/listeners")
options.links(
+ "https://javadoc.io/doc/org.jetbrains/annotations/latest/",
"https://jd.papermc.io/paper/26.1.2/",
"https://jd.advntr.dev/api/4.25.0/",
"https://tr7zw.github.io/Item-NBT-API/v2-api/"
diff --git a/src/main/java/com/shanebeestudios/hg/api/game/GameRegion.java b/src/main/java/com/shanebeestudios/hg/api/game/GameRegion.java
index a7208040..2994235b 100755
--- a/src/main/java/com/shanebeestudios/hg/api/game/GameRegion.java
+++ b/src/main/java/com/shanebeestudios/hg/api/game/GameRegion.java
@@ -7,6 +7,7 @@
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.util.BoundingBox;
+import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -30,11 +31,18 @@ public static GameRegion createNew(@NotNull Block corner1, @NotNull Block corner
return new GameRegion(corner1.getWorld().getKey(), boundingBox);
}
+ /**
+ * @hidden
+ */
+ @ApiStatus.Internal
public static GameRegion loadFromConfig(NamespacedKey key, BoundingBox boundingBox) {
return new GameRegion(key, boundingBox);
}
-
+ /**
+ * @hidden
+ */
+ @ApiStatus.Internal
@Deprecated(forRemoval = true, since = "INSERT VERSION")
public static GameRegion loadFromConfig(String name, BoundingBox boundingBox) {
World world = Bukkit.getWorld(name);
diff --git a/src/main/java/com/shanebeestudios/hg/api/parsers/package-info.java b/src/main/java/com/shanebeestudios/hg/api/parsers/package-info.java
new file mode 100644
index 00000000..f44303f4
--- /dev/null
+++ b/src/main/java/com/shanebeestudios/hg/api/parsers/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Utility classes for parsing data
+ */
+package com.shanebeestudios.hg.api.parsers;
diff --git a/src/main/java/com/shanebeestudios/hg/api/region/package-info.java b/src/main/java/com/shanebeestudios/hg/api/region/package-info.java
new file mode 100644
index 00000000..38470ce4
--- /dev/null
+++ b/src/main/java/com/shanebeestudios/hg/api/region/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Classes related to region based scheduling
+ */
+package com.shanebeestudios.hg.api.region;
diff --git a/src/main/java/com/shanebeestudios/hg/api/region/scheduler/package-info.java b/src/main/java/com/shanebeestudios/hg/api/region/scheduler/package-info.java
index 233997b9..5c3fb248 100644
--- a/src/main/java/com/shanebeestudios/hg/api/region/scheduler/package-info.java
+++ b/src/main/java/com/shanebeestudios/hg/api/region/scheduler/package-info.java
@@ -1,5 +1,5 @@
/**
- * Utility classes for Schedulers
+ * Schedulers for scheduling tasks
* Things will differ based on Spigot/Paper vs. Folia servers
*/
package com.shanebeestudios.hg.api.region.scheduler;
diff --git a/src/main/java/com/shanebeestudios/hg/api/region/scheduler/task/package-info.java b/src/main/java/com/shanebeestudios/hg/api/region/scheduler/task/package-info.java
new file mode 100644
index 00000000..fc64f1e8
--- /dev/null
+++ b/src/main/java/com/shanebeestudios/hg/api/region/scheduler/task/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Tasks based on server software
+ */
+package com.shanebeestudios.hg.api.region.scheduler.task;
diff --git a/src/main/java/com/shanebeestudios/hg/plugin/HungerGames.java b/src/main/java/com/shanebeestudios/hg/plugin/HungerGames.java
index 90854089..f205b940 100755
--- a/src/main/java/com/shanebeestudios/hg/plugin/HungerGames.java
+++ b/src/main/java/com/shanebeestudios/hg/plugin/HungerGames.java
@@ -38,12 +38,13 @@
import org.bukkit.event.HandlerList;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
+import org.jetbrains.annotations.ApiStatus;
import java.util.HashMap;
import java.util.Map;
/**
- * Main class for HungerGames
+ * Main class for HungerGames
*/
public class HungerGames extends JavaPlugin {
@@ -83,6 +84,9 @@ public void onLoad() {
}
}
+ /**
+ * @hidden
+ */
@Override
public void onEnable() {
if (!Util.isRunningMinecraft(1, 21, 10)) {
@@ -96,6 +100,9 @@ public void onEnable() {
loadPlugin(true);
}
+ /**
+ * @hidden
+ */
@SuppressWarnings("deprecation")
public void loadPlugin(boolean load) {
long start = System.currentTimeMillis();
@@ -142,6 +149,13 @@ public void loadPlugin(boolean load) {
Util.log("HungerGames has been enabled in %.2f seconds!", (float) (System.currentTimeMillis() - start) / 1000);
}
+ /**
+ * Reload the plugin, unloading and reloading all components.
+ *
+ * This should not be used externally.
+ *
+ */
+ @ApiStatus.Internal
public void reloadPlugin() {
unloadPlugin(true);
}
@@ -168,6 +182,9 @@ private void unloadPlugin(boolean reload) {
}
}
+ /**
+ * @hidden
+ */
@Override
public void onDisable() {
// I know this seems odd, but this method just
@@ -216,7 +233,7 @@ private void loadCommands() {
}
/**
- * Get the instance of this plugin
+ * Get the instance of this plugin.
*
* @return This plugin
*/
@@ -225,7 +242,7 @@ public static HungerGames getPlugin() {
}
/**
- * Get an instance of the KillManager
+ * Get an instance of the KillManager.
*
* @return KillManager
*/
@@ -234,7 +251,7 @@ public KillManager getKillManager() {
}
/**
- * Get an instance of the plugins main item manager
+ * Get an instance of the ItemManager.
*
* @return The item manager
*/
@@ -243,7 +260,7 @@ public ItemManager getItemManager() {
}
/**
- * Get an instance of the plugins main kit manager
+ * Get an instance of the KitManager.
*
* @return The kit manager
*/
@@ -252,7 +269,7 @@ public KitManager getKitManager() {
}
/**
- * Get the instance of the game manager
+ * Get the instance of the game manager.
*
* @return The game manager
*/
@@ -261,7 +278,7 @@ public GameManager getGameManager() {
}
/**
- * Get an instance of the PlayerManager
+ * Get an instance of the PlayerManager.
*
* @return PlayerManager
*/
@@ -270,7 +287,7 @@ public PlayerManager getPlayerManager() {
}
/**
- * Get an instance of the ArenaConfig
+ * Get an instance of the ArenaConfig.
*
* @return ArenaConfig
*/
@@ -279,7 +296,7 @@ public ArenaConfig getArenaConfig() {
}
/**
- * Get an instance of HG's leaderboards
+ * Get an instance of HG's leaderboards.
*
* @return Leaderboard
*/
@@ -288,7 +305,7 @@ public Leaderboard getLeaderboard() {
}
/**
- * Get an instance of the language file
+ * Get an instance of the language file.
*
* @return Language file
*/
@@ -297,7 +314,7 @@ public Language getLang() {
}
/**
- * Get an instance of {@link Config}
+ * Get an instance of {@link Config}.
*
* @return Config file
*/
@@ -306,7 +323,7 @@ public Config getHGConfig() {
}
/**
- * Get an instance of the MobManager
+ * Get an instance of the MobManager.
*
* @return MobManager
*/
@@ -314,12 +331,8 @@ public MobManager getMobManager() {
return this.mobManager;
}
- public Metrics getMetrics() {
- return this.metrics;
- }
-
/**
- * Get an instance of the MythicMobs MobManager
+ * Get an instance of the MythicMobs MobManager.
*
* @return MythicMobs MobManager
*/
@@ -327,6 +340,11 @@ public io.lumine.mythic.api.mobs.MobManager getMythicMobManager() {
return this.mythicMobManager;
}
+ /**
+ * Get an instance of the SessionManager.
+ *
+ * @return Instance of SessionManager
+ */
// Managers
public SessionManager getSessionManager() {
return this.sessionManager;
diff --git a/src/main/java/com/shanebeestudios/hg/plugin/configs/package-info.java b/src/main/java/com/shanebeestudios/hg/plugin/configs/package-info.java
new file mode 100644
index 00000000..b794bc82
--- /dev/null
+++ b/src/main/java/com/shanebeestudios/hg/plugin/configs/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Configs related to the plugin
+ */
+package com.shanebeestudios.hg.plugin.configs;
diff --git a/src/main/java/com/shanebeestudios/hg/plugin/permission/Permissions.java b/src/main/java/com/shanebeestudios/hg/plugin/permission/Permissions.java
index f8c0bba9..d6a5f47c 100644
--- a/src/main/java/com/shanebeestudios/hg/plugin/permission/Permissions.java
+++ b/src/main/java/com/shanebeestudios/hg/plugin/permission/Permissions.java
@@ -9,6 +9,9 @@
import java.util.LinkedHashMap;
import java.util.Map;
+/**
+ * Permissions for players
+ */
public class Permissions {
public record Permission(String permission, org.bukkit.permissions.Permission bukkitPermission) {
diff --git a/src/main/java/com/shanebeestudios/hg/plugin/permission/package-info.java b/src/main/java/com/shanebeestudios/hg/plugin/permission/package-info.java
new file mode 100644
index 00000000..d0236cb7
--- /dev/null
+++ b/src/main/java/com/shanebeestudios/hg/plugin/permission/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Permission related classes for the plugin
+ */
+package com.shanebeestudios.hg.plugin.permission;
diff --git a/src/main/java/com/shanebeestudios/hg/plugin/tasks/ChestRefillRepeatTask.java b/src/main/java/com/shanebeestudios/hg/plugin/tasks/ChestRefillRepeatTask.java
index 3da31896..cec4a840 100644
--- a/src/main/java/com/shanebeestudios/hg/plugin/tasks/ChestRefillRepeatTask.java
+++ b/src/main/java/com/shanebeestudios/hg/plugin/tasks/ChestRefillRepeatTask.java
@@ -8,6 +8,9 @@
import com.shanebeestudios.hg.plugin.configs.Language;
import org.bukkit.Bukkit;
+/**
+ * Task for handling refills of chests in a game
+ */
public class ChestRefillRepeatTask implements Runnable {
private final Language lang;
diff --git a/src/main/java/com/shanebeestudios/hg/plugin/tasks/FreeRoamTask.java b/src/main/java/com/shanebeestudios/hg/plugin/tasks/FreeRoamTask.java
index 63ccf862..c14c15a4 100755
--- a/src/main/java/com/shanebeestudios/hg/plugin/tasks/FreeRoamTask.java
+++ b/src/main/java/com/shanebeestudios/hg/plugin/tasks/FreeRoamTask.java
@@ -8,6 +8,9 @@
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
+/**
+ * Task for allowing game players to free roam
+ */
public class FreeRoamTask implements Runnable {
private final Game game;
diff --git a/src/main/java/com/shanebeestudios/hg/plugin/tasks/GameTimerTask.java b/src/main/java/com/shanebeestudios/hg/plugin/tasks/GameTimerTask.java
index efe7cf72..122c3449 100755
--- a/src/main/java/com/shanebeestudios/hg/plugin/tasks/GameTimerTask.java
+++ b/src/main/java/com/shanebeestudios/hg/plugin/tasks/GameTimerTask.java
@@ -10,6 +10,9 @@
import com.shanebeestudios.hg.plugin.configs.Language;
import org.bukkit.Bukkit;
+/**
+ * Timer task for games
+ */
public class GameTimerTask implements Runnable {
private int remainingTime;
diff --git a/src/main/java/com/shanebeestudios/hg/plugin/tasks/MobSpawnerTask.java b/src/main/java/com/shanebeestudios/hg/plugin/tasks/MobSpawnerTask.java
index acada2aa..6c4ffc14 100755
--- a/src/main/java/com/shanebeestudios/hg/plugin/tasks/MobSpawnerTask.java
+++ b/src/main/java/com/shanebeestudios/hg/plugin/tasks/MobSpawnerTask.java
@@ -14,6 +14,9 @@
import java.util.Random;
+/**
+ * Task for spawning mobs in the game arena
+ */
public class MobSpawnerTask implements Runnable {
private final GamePlayerData gamePlayerData;
diff --git a/src/main/java/com/shanebeestudios/hg/plugin/tasks/NearestPlayerCompassTask.java b/src/main/java/com/shanebeestudios/hg/plugin/tasks/NearestPlayerCompassTask.java
index e14766d4..2b89a11f 100755
--- a/src/main/java/com/shanebeestudios/hg/plugin/tasks/NearestPlayerCompassTask.java
+++ b/src/main/java/com/shanebeestudios/hg/plugin/tasks/NearestPlayerCompassTask.java
@@ -11,6 +11,9 @@
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
+/**
+ * Task for handling nearest player compass functionality
+ */
public class NearestPlayerCompassTask implements Runnable {
private final Game game;
diff --git a/src/main/java/com/shanebeestudios/hg/plugin/tasks/PrepareArenaTask.java b/src/main/java/com/shanebeestudios/hg/plugin/tasks/PrepareArenaTask.java
index c67b68fa..877e6f02 100644
--- a/src/main/java/com/shanebeestudios/hg/plugin/tasks/PrepareArenaTask.java
+++ b/src/main/java/com/shanebeestudios/hg/plugin/tasks/PrepareArenaTask.java
@@ -13,6 +13,9 @@
import java.util.Iterator;
import java.util.List;
+/**
+ * Task for preparing an arena by logging blocks for rollback
+ */
public class PrepareArenaTask implements Runnable {
private final Game game;
diff --git a/src/main/java/com/shanebeestudios/hg/plugin/tasks/StartingTask.java b/src/main/java/com/shanebeestudios/hg/plugin/tasks/StartingTask.java
index 1b412e99..5e95932c 100755
--- a/src/main/java/com/shanebeestudios/hg/plugin/tasks/StartingTask.java
+++ b/src/main/java/com/shanebeestudios/hg/plugin/tasks/StartingTask.java
@@ -6,6 +6,9 @@
import com.shanebeestudios.hg.plugin.configs.Language;
import org.bukkit.Bukkit;
+/**
+ * Task for counting down before game starts
+ */
public class StartingTask implements Runnable {
private int timer;
diff --git a/src/main/java/com/shanebeestudios/hg/plugin/tasks/WorldBorderTask.java b/src/main/java/com/shanebeestudios/hg/plugin/tasks/WorldBorderTask.java
index 507bb10c..cc3fe0e1 100644
--- a/src/main/java/com/shanebeestudios/hg/plugin/tasks/WorldBorderTask.java
+++ b/src/main/java/com/shanebeestudios/hg/plugin/tasks/WorldBorderTask.java
@@ -11,7 +11,9 @@
import org.bukkit.damage.DamageSource;
import org.bukkit.damage.DamageType;
-@SuppressWarnings("UnstableApiUsage")
+/**
+ * Task for handing a world border in a game
+ */
public class WorldBorderTask implements Runnable {
private final Game game;
diff --git a/src/main/java/com/shanebeestudios/hg/plugin/tasks/package-info.java b/src/main/java/com/shanebeestudios/hg/plugin/tasks/package-info.java
new file mode 100644
index 00000000..b3ad354b
--- /dev/null
+++ b/src/main/java/com/shanebeestudios/hg/plugin/tasks/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Task management for games
+ */
+package com.shanebeestudios.hg.plugin.tasks;
From d5b796878ccaed3e9659289c997ef04d00a3e68f Mon Sep 17 00:00:00 2001
From: ShaneBeee
Date: Thu, 21 May 2026 06:34:27 -0700
Subject: [PATCH 25/26] Add update checker
---
.../hg/plugin/HungerGames.java | 2 +
.../hg/plugin/configs/Config.java | 4 +
.../hg/plugin/permission/Permissions.java | 1 +
.../hg/plugin/update/ModrinthVersion.java | 38 +++++
.../hg/plugin/update/UpdateChecker.java | 133 ++++++++++++++++++
src/main/resources/config.yml | 13 +-
6 files changed, 187 insertions(+), 4 deletions(-)
create mode 100644 src/main/java/com/shanebeestudios/hg/plugin/update/ModrinthVersion.java
create mode 100644 src/main/java/com/shanebeestudios/hg/plugin/update/UpdateChecker.java
diff --git a/src/main/java/com/shanebeestudios/hg/plugin/HungerGames.java b/src/main/java/com/shanebeestudios/hg/plugin/HungerGames.java
index f205b940..6bc2bf90 100755
--- a/src/main/java/com/shanebeestudios/hg/plugin/HungerGames.java
+++ b/src/main/java/com/shanebeestudios/hg/plugin/HungerGames.java
@@ -27,6 +27,7 @@
import com.shanebeestudios.hg.plugin.managers.Placeholders;
import com.shanebeestudios.hg.plugin.managers.PlayerManager;
import com.shanebeestudios.hg.plugin.managers.SessionManager;
+import com.shanebeestudios.hg.plugin.update.UpdateChecker;
import dev.jorel.commandapi.CommandAPI;
import dev.jorel.commandapi.CommandAPIPaperConfig;
import dev.jorel.commandapi.exceptions.UnsupportedVersionException;
@@ -146,6 +147,7 @@ public void loadPlugin(boolean load) {
setupMetrics();
+ new UpdateChecker(this);
Util.log("HungerGames has been enabled in %.2f seconds!", (float) (System.currentTimeMillis() - start) / 1000);
}
diff --git a/src/main/java/com/shanebeestudios/hg/plugin/configs/Config.java b/src/main/java/com/shanebeestudios/hg/plugin/configs/Config.java
index abd239a0..d889fe75 100755
--- a/src/main/java/com/shanebeestudios/hg/plugin/configs/Config.java
+++ b/src/main/java/com/shanebeestudios/hg/plugin/configs/Config.java
@@ -26,6 +26,8 @@ public class Config {
public static boolean SETTINGS_DEBUG;
// Basic settings
+ public static boolean SETTINGS_UPDATE_CHECKER_ENABLED;
+ public static boolean SETTINGS_UPDATE_CHECKER_ASYNC;
public static boolean SETTINGS_BROADCAST_JOIN_MESSAGES;
public static boolean SETTINGS_BROADCAST_WIN_MESSAGES;
public static boolean HAS_ECONOMY = true;
@@ -142,6 +144,8 @@ private void loadConfigFile() {
private void loadConfig() {
// Settings
SETTINGS_DEBUG = config.getBoolean("settings.debug");
+ SETTINGS_UPDATE_CHECKER_ENABLED = config.getBoolean("settings.update-checker.enabled");
+ SETTINGS_UPDATE_CHECKER_ASYNC = config.getBoolean("settings.update-checker.async");
SETTINGS_BROADCAST_JOIN_MESSAGES = config.getBoolean("settings.broadcast-join-messages");
SETTINGS_BROADCAST_WIN_MESSAGES = config.getBoolean("settings.broadcast-win-messages");
SETTINGS_BOSSBAR_COUNTDOWN = config.getBoolean("settings.bossbar-countdown");
diff --git a/src/main/java/com/shanebeestudios/hg/plugin/permission/Permissions.java b/src/main/java/com/shanebeestudios/hg/plugin/permission/Permissions.java
index d6a5f47c..cbc2d5f8 100644
--- a/src/main/java/com/shanebeestudios/hg/plugin/permission/Permissions.java
+++ b/src/main/java/com/shanebeestudios/hg/plugin/permission/Permissions.java
@@ -51,6 +51,7 @@ public boolean has(CommandSender sender) {
// Other Permissions
public static final Permission BYPASS_COMMAND_RESTRICTION = getBase("bypass.command.restriction", "Bypass command restriction while in games", PermissionDefault.OP);
+ public static final Permission UPDATE_CHECKER = getBase("update_checker", "Receive update messages on join", PermissionDefault.OP);
private static Permission getCommand(String perm, String description, PermissionDefault defaultPermission) {
return getBase("command." + perm, description, defaultPermission);
diff --git a/src/main/java/com/shanebeestudios/hg/plugin/update/ModrinthVersion.java b/src/main/java/com/shanebeestudios/hg/plugin/update/ModrinthVersion.java
new file mode 100644
index 00000000..485ee763
--- /dev/null
+++ b/src/main/java/com/shanebeestudios/hg/plugin/update/ModrinthVersion.java
@@ -0,0 +1,38 @@
+package com.shanebeestudios.hg.plugin.update;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ModrinthVersion {
+
+ private final String updateVersion;
+ private final List supportedVersions = new ArrayList<>();
+
+ public ModrinthVersion(JsonElement jsonElement) {
+ JsonObject json = jsonElement.getAsJsonObject();
+ this.updateVersion = json.get("version_number").getAsString();
+ JsonArray gameVersions = json.getAsJsonArray("game_versions");
+ gameVersions.forEach(version -> this.supportedVersions.add(version.getAsString()));
+ }
+
+ public String getUpdateVersion() {
+ return this.updateVersion;
+ }
+
+ public String getUpdateLink() {
+ return "https://modrinth.com/plugin/hungergames-sb/version/" + this.updateVersion;
+ }
+
+ public List getSupportedVersions() {
+ return this.supportedVersions;
+ }
+
+ public boolean isServerSupported(String serverVersion) {
+ return this.supportedVersions.contains(serverVersion);
+ }
+
+}
diff --git a/src/main/java/com/shanebeestudios/hg/plugin/update/UpdateChecker.java b/src/main/java/com/shanebeestudios/hg/plugin/update/UpdateChecker.java
new file mode 100644
index 00000000..fa2e7c2e
--- /dev/null
+++ b/src/main/java/com/shanebeestudios/hg/plugin/update/UpdateChecker.java
@@ -0,0 +1,133 @@
+package com.shanebeestudios.hg.plugin.update;
+
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.shanebeestudios.hg.api.region.TaskUtils;
+import com.shanebeestudios.hg.api.util.Util;
+import com.shanebeestudios.hg.plugin.HungerGames;
+import com.shanebeestudios.hg.plugin.configs.Config;
+import com.shanebeestudios.hg.plugin.permission.Permissions;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerJoinEvent;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * Utility class to check for plugin updates
+ */
+@SuppressWarnings("deprecation")
+public class UpdateChecker implements Listener {
+
+ private final HungerGames plugin;
+ private final String pluginVersion;
+ private final String serverVersion = Bukkit.getMinecraftVersion();
+ private ModrinthVersion currentUpdateVersion;
+
+ public UpdateChecker(HungerGames plugin) {
+ this.plugin = plugin;
+ this.pluginVersion = plugin.getPluginMeta().getVersion();
+
+ if (Config.SETTINGS_UPDATE_CHECKER_ENABLED) {
+ setupJoinListener();
+ checkUpdate(Config.SETTINGS_UPDATE_CHECKER_ASYNC);
+ } else {
+ Util.log("Update checker disabled!");
+ }
+ }
+
+ private void setupJoinListener() {
+ Bukkit.getPluginManager().registerEvents(new Listener() {
+ @EventHandler
+ private void onJoin(PlayerJoinEvent event) {
+ Player player = event.getPlayer();
+ if (!Permissions.UPDATE_CHECKER.has(player)) return;
+
+ TaskUtils.getEntityScheduler(player).runTaskLater(() -> getUpdateVersion(true).thenApply(version -> {
+ Util.sendPrefixedMessage(player, "Update available: " + version.getUpdateVersion());
+ Util.sendMessage(player, "Download at: " + version.getUpdateLink());
+ return true;
+ }), 30);
+ }
+ }, this.plugin);
+ }
+
+ private void checkUpdate(boolean async) {
+ Util.log("Checking for update...");
+ getUpdateVersion(async).thenApply(modrinthVersion -> {
+ Util.log("Plugin is not up to date!");
+ Util.log(" - Current version: v%s", this.pluginVersion);
+ Util.log(" - Available update: v%s", modrinthVersion.getUpdateVersion());
+ if (modrinthVersion.isServerSupported(this.serverVersion)) {
+ Util.log(" - Download at: " + modrinthVersion.getUpdateLink());
+ } else {
+ Util.log(" - Your server version (%s) does not support this update.", this.serverVersion);
+ Util.log(" - Supported Versions:");
+ for (String supportedVersion : modrinthVersion.getSupportedVersions()) {
+ Util.log(" - %s", supportedVersion);
+ }
+ }
+ return true;
+ }).exceptionally(throwable -> {
+ Util.log("Plugin is up to date!");
+ return true;
+ });
+ }
+
+ private CompletableFuture getUpdateVersion(boolean async) {
+ CompletableFuture updateVersionFuture = new CompletableFuture<>();
+ if (this.currentUpdateVersion != null) {
+ updateVersionFuture.complete(this.currentUpdateVersion);
+ } else {
+ CompletableFuture latestReleaseFuture = new CompletableFuture<>();
+ if (async) {
+ TaskUtils.getGlobalScheduler().runTaskAsync(() -> {
+ ModrinthVersion lastest = getLatestVersionFromModrinth();
+ if (lastest == null) latestReleaseFuture.cancel(true);
+ latestReleaseFuture.complete(lastest);
+ });
+ } else {
+ ModrinthVersion latest = getLatestVersionFromModrinth();
+ if (latest == null) latestReleaseFuture.cancel(true);
+ latestReleaseFuture.complete(latest);
+ }
+ latestReleaseFuture.thenApply(version -> {
+ if (version.getUpdateVersion().compareTo(this.pluginVersion) <= 0) {
+ updateVersionFuture.cancel(true);
+ } else {
+ this.currentUpdateVersion = version;
+ updateVersionFuture.complete(this.currentUpdateVersion);
+ }
+ return true;
+ });
+ }
+ return updateVersionFuture;
+ }
+
+ @SuppressWarnings("CallToPrintStackTrace")
+ private ModrinthVersion getLatestVersionFromModrinth() {
+ try {
+ URL url = new URL("https://api.modrinth.com/v2/project/UPP8KYnj/version");
+ BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
+ JsonArray elements = new Gson().fromJson(reader, JsonArray.class);
+ JsonElement latestVersion = elements.get(0);
+ return new ModrinthVersion(latestVersion);
+ } catch (IOException e) {
+ if (Config.SETTINGS_DEBUG) {
+ e.printStackTrace();
+ } else {
+ Util.log("Checking for update failed!");
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
index c8a788d8..21338d4e 100755
--- a/src/main/resources/config.yml
+++ b/src/main/resources/config.yml
@@ -6,13 +6,18 @@
# More Info: See the wiki for more info on this config @ https://github.com/ShaneBeeStudios/HungerGames/wiki/Config.yml
-# Helpful Links:
-# Bukkit Material Enums: https://jd.papermc.io/paper/26.1.2/org/bukkit/Material.html
-# Bukkit Tags: https://jd.papermc.io/paper/26.1.2/org/bukkit/Tag.html
-
settings:
# When enabled, more informative debug messages will be sent when errors occur
debug: false
+
+ # Enable this to automatically check for HungerGames updates
+ # Disable this if you do not want to check for updates, or it takes too long/has issues checking
+ update-checker:
+ # Whether to check for updates
+ enabled: true
+ # Whether the update check should be done on another thread
+ async: false
+
# When enabled, if a player joins an arena, a message will broadcast to all players on the server that a game is available to
# join and how many players til it can start.
# A message will broadcast that a game has started, and that you can still join.
From 14fead9e08f481e6745576321845e7d68994cefa Mon Sep 17 00:00:00 2001
From: ShaneBeee
Date: Thu, 21 May 2026 06:44:24 -0700
Subject: [PATCH 26/26] Update issue templates
---
.github/ISSUE_TEMPLATE/config.yml | 6 +++---
.github/ISSUE_TEMPLATE/report-a-bug.yml | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 75d6011e..b427c22d 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -3,6 +3,6 @@ contact_links:
- name: 💻 ShaneBee's Support Discord
url: https://discord.gg/UzBCFSQJyM
about: For general help with HungerGames ask in the HungerGames channel
- - name: 💿 Modrinth Resource
- url: todo
- about: Read about HungerGames on Modrinth!
+ - name: 🛠️Modrinth Resource
+ url: https://modrinth.com/plugin/hungergames-sb
+ about: Read about and download HungerGames on Modrinth!
diff --git a/.github/ISSUE_TEMPLATE/report-a-bug.yml b/.github/ISSUE_TEMPLATE/report-a-bug.yml
index 5299dc88..89af0efb 100644
--- a/.github/ISSUE_TEMPLATE/report-a-bug.yml
+++ b/.github/ISSUE_TEMPLATE/report-a-bug.yml
@@ -47,7 +47,7 @@ body:
label: Server Version
description: Which version of the server and MC are you using?
placeholder: |
- Ex: "Paper 1.21.4"
+ Ex: "Paper 26.1.2"
validations:
required: true