From 97e9dcbaa97b553577c8d910eb2961ecca981e1a Mon Sep 17 00:00:00 2001 From: MISHA <208148594+lokspel@users.noreply.github.com> Date: Sat, 7 Mar 2026 14:52:06 +0400 Subject: [PATCH 01/31] Dev (#274) --- .../manager/gui/guis/queue/QueueSelectorGui.java | 3 +++ .../listener/FireworkRocketCooldownListener.java | 9 ++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/dev/nandi0813/practice/manager/gui/guis/queue/QueueSelectorGui.java b/core/src/main/java/dev/nandi0813/practice/manager/gui/guis/queue/QueueSelectorGui.java index 8d41b963..b28de2b3 100644 --- a/core/src/main/java/dev/nandi0813/practice/manager/gui/guis/queue/QueueSelectorGui.java +++ b/core/src/main/java/dev/nandi0813/practice/manager/gui/guis/queue/QueueSelectorGui.java @@ -289,6 +289,7 @@ private void setupCategoryPage(int pageId, String title, int configSize, ItemSta // Apply only the LB lore (which is static) but NOT %in_queue%/%in_fight% String lbFormat = GUIFile.getString(guiPath + ".LB-FORMAT"); rawTemplate.setLore(QueueGuiUtil.replaceLore(lbFormat, rawTemplate.getLore(), ladder)); + rawTemplate.replace("%weight_class%", getWeightClass().getName()); rawTemplate.setAmount(1); pageTemplates.put(slot, rawTemplate.get()); } @@ -324,6 +325,8 @@ private void updateIconWithQueueData(NormalLadder ladder, GUIItem icon, String g String lbFormat = GUIFile.getString(guiPath + ".LB-FORMAT"); icon.setLore(QueueGuiUtil.replaceLore(lbFormat, icon.getLore(), ladder)); + icon.replace("%weight_class%", getWeightClass().getName()); + if (duelMatchSize > 0 && duelMatchSize <= 64) { icon.setAmount(duelMatchSize); } else { diff --git a/spigot_modern/src/main/java/dev/nandi0813/practice_modern/listener/FireworkRocketCooldownListener.java b/spigot_modern/src/main/java/dev/nandi0813/practice_modern/listener/FireworkRocketCooldownListener.java index 7d5201bb..1c177772 100644 --- a/spigot_modern/src/main/java/dev/nandi0813/practice_modern/listener/FireworkRocketCooldownListener.java +++ b/spigot_modern/src/main/java/dev/nandi0813/practice_modern/listener/FireworkRocketCooldownListener.java @@ -12,6 +12,7 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.inventory.ItemStack; @@ -41,9 +42,11 @@ public void onFireworkRocketUse(PlayerInteractEvent e) { return; } - // Check if player is wearing elytra - ItemStack chestplate = player.getInventory().getChestplate(); - if (chestplate == null || chestplate.getType() != Material.ELYTRA) { + // Apply cooldown only for valid firework usage: + // - Player gliding with elytra (boost attempt) OR + // - Any non-air click (ground/block usage) + // Ignore empty RIGHT_CLICK_AIR without gliding + if (!player.isGliding() && e.getAction() == Action.RIGHT_CLICK_AIR) { return; } From c754efbbfe27fbd871c38a39eb2f3e653a08bd11 Mon Sep 17 00:00:00 2001 From: MISHA <208148594+lokspel@users.noreply.github.com> Date: Sat, 7 Mar 2026 15:17:44 +0400 Subject: [PATCH 02/31] fix(firework-cooldown): add right-click filter (#279) --- .../listener/FireworkRocketCooldownListener.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/spigot_modern/src/main/java/dev/nandi0813/practice_modern/listener/FireworkRocketCooldownListener.java b/spigot_modern/src/main/java/dev/nandi0813/practice_modern/listener/FireworkRocketCooldownListener.java index 1c177772..20077e32 100644 --- a/spigot_modern/src/main/java/dev/nandi0813/practice_modern/listener/FireworkRocketCooldownListener.java +++ b/spigot_modern/src/main/java/dev/nandi0813/practice_modern/listener/FireworkRocketCooldownListener.java @@ -42,12 +42,16 @@ public void onFireworkRocketUse(PlayerInteractEvent e) { return; } - // Apply cooldown only for valid firework usage: - // - Player gliding with elytra (boost attempt) OR - // - Any non-air click (ground/block usage) - // Ignore empty RIGHT_CLICK_AIR without gliding - if (!player.isGliding() && e.getAction() == Action.RIGHT_CLICK_AIR) { - return; + // Apply cooldown only for valid RIGHT-CLICK firework usage: + // - RIGHT_CLICK_AIR while gliding (elytra boost) + // - RIGHT_CLICK_BLOCK (ground/block launch) + // Ignore: LEFT clicks, PHYSICAL, empty RIGHT_CLICK_AIR without gliding + Action action = e.getAction(); + if (action != Action.RIGHT_CLICK_AIR && action != Action.RIGHT_CLICK_BLOCK) { + return; // Ignore left clicks & PHYSICAL + } + if (!player.isGliding() && action == Action.RIGHT_CLICK_AIR) { + return; // Ignore empty air clicks } // Deduplicate: PlayerInteractEvent fires once per hand (MAIN + OFF), skip the second call this tick From 7f2c8b86d72d5a185cd2de4d179ab4be67019d51 Mon Sep 17 00:00:00 2001 From: MISHA <208148594+lokspel@users.noreply.github.com> Date: Sat, 7 Mar 2026 19:06:17 +0400 Subject: [PATCH 03/31] fix(firework): prevent cooldown spam/hang on held clicks (#282) --- .../listener/FireworkRocketCooldownListener.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/spigot_modern/src/main/java/dev/nandi0813/practice_modern/listener/FireworkRocketCooldownListener.java b/spigot_modern/src/main/java/dev/nandi0813/practice_modern/listener/FireworkRocketCooldownListener.java index 20077e32..c008d91f 100644 --- a/spigot_modern/src/main/java/dev/nandi0813/practice_modern/listener/FireworkRocketCooldownListener.java +++ b/spigot_modern/src/main/java/dev/nandi0813/practice_modern/listener/FireworkRocketCooldownListener.java @@ -57,10 +57,9 @@ public void onFireworkRocketUse(PlayerInteractEvent e) { // Deduplicate: PlayerInteractEvent fires once per hand (MAIN + OFF), skip the second call this tick UUID uuid = player.getUniqueId(); if (!handledThisTick.add(uuid)) { + handledThisTick.remove(uuid); return; } - // Clean up after this tick so the next click is processed normally - Bukkit.getScheduler().runTask(ZonePractice.getInstance(), () -> handledThisTick.remove(uuid)); // Check if player is in FFA FFA ffa = FFAManager.getInstance().getFFAByPlayer(player); @@ -102,5 +101,4 @@ public void onFireworkRocketUse(PlayerInteractEvent e) { ); } } - -} +} \ No newline at end of file From 55b9c5a6bbfc2b94371e7af8e0551b364b0b1265 Mon Sep 17 00:00:00 2001 From: MISHA <208148594+lokspel@users.noreply.github.com> Date: Sun, 8 Mar 2026 13:33:44 +0400 Subject: [PATCH 04/31] Dev (#285) * fix(firework-cooldown): add right-click filter * fix(firework): prevent cooldown spam/hang on held clicks * Improve pre-login kick message with better wording --- .../java/dev/nandi0813/practice/listener/PlayerPreLogin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/dev/nandi0813/practice/listener/PlayerPreLogin.java b/core/src/main/java/dev/nandi0813/practice/listener/PlayerPreLogin.java index c26c43cc..aead95c0 100644 --- a/core/src/main/java/dev/nandi0813/practice/listener/PlayerPreLogin.java +++ b/core/src/main/java/dev/nandi0813/practice/listener/PlayerPreLogin.java @@ -22,7 +22,7 @@ public void onPreLogin(AsyncPlayerPreLoginEvent event) { if (!ZonePractice.isFullyLoaded()) { String message = LanguageManager.getString("PLUGIN-LOADING-MESSAGE"); if (message == null || message.isEmpty()) { - message = "The server is still loading, please wait..."; + message = "The server is still loading. Please try again in a moment."; } event.disallow(AsyncPlayerPreLoginEvent.Result.KICK_OTHER, Common.mmToNormal(message)); return; From 7ec83968adfee6db46b99dd735873140274f2af8 Mon Sep 17 00:00:00 2001 From: Nandor Dukat Date: Sun, 8 Mar 2026 11:02:38 +0100 Subject: [PATCH 05/31] refactored chat listener to fix party and staff chat logging --- .../practice/util/ChatFormatUtil.java | 74 +++++++++++++++ .../listener/PlayerChatListener.java | 74 ++++++--------- .../listener/PlayerChatListener.java | 91 ++++++++----------- 3 files changed, 140 insertions(+), 99 deletions(-) create mode 100644 core/src/main/java/dev/nandi0813/practice/util/ChatFormatUtil.java diff --git a/core/src/main/java/dev/nandi0813/practice/util/ChatFormatUtil.java b/core/src/main/java/dev/nandi0813/practice/util/ChatFormatUtil.java new file mode 100644 index 00000000..f92d11ff --- /dev/null +++ b/core/src/main/java/dev/nandi0813/practice/util/ChatFormatUtil.java @@ -0,0 +1,74 @@ +package dev.nandi0813.practice.util; + +import dev.nandi0813.practice.manager.backend.ConfigManager; +import dev.nandi0813.practice.manager.backend.LanguageManager; +import dev.nandi0813.practice.manager.profile.Profile; +import dev.nandi0813.practice.manager.profile.group.Group; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.List; + +public enum ChatFormatUtil { + ; + + /** + * Builds the fully-replaced party chat format string. + */ + public static String buildPartyChatMessage(Player player, String rawMessage) { + return LanguageManager.getString("GENERAL-CHAT.PARTY-CHAT") + .replace("%%player%%", player.getName()) + .replace("%%message%%", rawMessage.replaceFirst("@", "")); + } + + /** + * Builds the fully-replaced staff chat format string. + */ + public static String buildStaffChatMessage(Player player, String rawMessage) { + return LanguageManager.getString("GENERAL-CHAT.STAFF-CHAT") + .replace("%%player%%", player.getName()) + .replace("%%message%%", rawMessage); + } + + /** + * Collects all online players with the staff chat permission. + */ + public static List getStaffRecipients() { + List staff = new ArrayList<>(); + for (Player online : Bukkit.getOnlinePlayers()) { + if (online.hasPermission("zpp.staffmode.chat")) { + staff.add(online); + } + } + return staff; + } + + /** + * Resolves the server chat format string (group format or default), + * then replaces all static placeholders (division, player, message). + */ + public static String buildServerChatMessage(Profile profile, Player player, String message) { + final String format; + if (ConfigManager.getBoolean("PLAYER.GROUP-CHAT.ENABLED")) { + Group group = profile.getGroup(); + if (group != null && group.getChatFormat() != null) { + format = group.getChatFormat(); + } else { + format = LanguageManager.getString("GENERAL-CHAT.SERVER-CHAT"); + } + } else { + format = LanguageManager.getString("GENERAL-CHAT.SERVER-CHAT"); + } + + String division = profile.getStats().getDivision() != null ? profile.getStats().getDivision().getFullName() : ""; + String divisionShort = profile.getStats().getDivision() != null ? profile.getStats().getDivision().getShortName() : ""; + + return format + .replace("%%division%%", division) + .replace("%%division_short%%", divisionShort) + .replace("%%player%%", player.getName()) + .replace("%%message%%", message); + } +} + diff --git a/spigot_1_8_8/src/main/java/dev/nandi0813/practice_1_8_8/listener/PlayerChatListener.java b/spigot_1_8_8/src/main/java/dev/nandi0813/practice_1_8_8/listener/PlayerChatListener.java index 5afaf046..dee256a8 100644 --- a/spigot_1_8_8/src/main/java/dev/nandi0813/practice_1_8_8/listener/PlayerChatListener.java +++ b/spigot_1_8_8/src/main/java/dev/nandi0813/practice_1_8_8/listener/PlayerChatListener.java @@ -6,9 +6,8 @@ import dev.nandi0813.practice.manager.party.PartyManager; import dev.nandi0813.practice.manager.profile.Profile; import dev.nandi0813.practice.manager.profile.ProfileManager; -import dev.nandi0813.practice.manager.profile.group.Group; +import dev.nandi0813.practice.util.ChatFormatUtil; import dev.nandi0813.practice.util.Common; -import dev.nandi0813.practice.util.playerutil.PlayerUtil; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import org.bukkit.Bukkit; import org.bukkit.entity.Player; @@ -28,15 +27,12 @@ public void onPlayerChat(AsyncPlayerChatEvent e) { // --- Party chat --- if (ConfigManager.getBoolean("CHAT.PARTY-CHAT-ENABLED") && profile.isParty() && party != null && message.startsWith("@")) { - e.setCancelled(true); - if (party.isPartyChat() || party.getLeader() == player) { - final String partyMsg = LanguageManager.getString("GENERAL-CHAT.PARTY-CHAT") - .replace("%%player%%", player.getName()) - .replace("%%message%%", message.replaceFirst("@", "")); - Bukkit.getScheduler().runTask(dev.nandi0813.practice.ZonePractice.getInstance(), - () -> party.sendMessage(partyMsg)); + e.getRecipients().clear(); + e.getRecipients().addAll(party.getMembers()); + applyLegacyFormat(e, ChatFormatUtil.buildPartyChatMessage(player, message)); } else { + e.setCancelled(true); final String cantUse = LanguageManager.getString("PARTY.CANT-USE-PARTY-CHAT"); Bukkit.getScheduler().runTask(dev.nandi0813.practice.ZonePractice.getInstance(), () -> Common.sendMMMessage(player, cantUse)); @@ -46,55 +42,39 @@ public void onPlayerChat(AsyncPlayerChatEvent e) { // --- Staff chat (toggle) --- if (profile.isStaffChat()) { - e.setCancelled(true); - Bukkit.getScheduler().runTask(dev.nandi0813.practice.ZonePractice.getInstance(), - () -> PlayerUtil.sendStaffMessage(player, message)); + applyStaffChat(e, player, message); return; } // --- Staff chat (shortcut: #message) --- if (player.hasPermission("zpp.staff") && ConfigManager.getBoolean("CHAT.STAFF-CHAT.SHORTCUT") && message.startsWith("#")) { - e.setCancelled(true); - final String staffMsg = message.replaceFirst("#", ""); - Bukkit.getScheduler().runTask(dev.nandi0813.practice.ZonePractice.getInstance(), - () -> PlayerUtil.sendStaffMessage(player, staffMsg)); + applyStaffChat(e, player, message.replaceFirst("#", "")); return; } // --- Custom server chat --- if (ConfigManager.getBoolean("CHAT.SERVER-CHAT-ENABLED")) { - // Build the format string - final String format; - if (ConfigManager.getBoolean("PLAYER.GROUP-CHAT.ENABLED")) { - Group group = profile.getGroup(); - if (group != null && group.getChatFormat() != null) { - format = group.getChatFormat(); - } else { - format = LanguageManager.getString("GENERAL-CHAT.SERVER-CHAT"); - } - } else { - format = LanguageManager.getString("GENERAL-CHAT.SERVER-CHAT"); - } - - String division = profile.getStats().getDivision() != null ? profile.getStats().getDivision().getFullName() : ""; - String divisionShort = profile.getStats().getDivision() != null ? profile.getStats().getDivision().getShortName() : ""; - - String preFormatted = format - .replace("%%division%%", division) - .replace("%%division_short%%", divisionShort) - .replace("%%player%%", player.getName()) - .replace("%%message%%", message); + applyLegacyFormat(e, ChatFormatUtil.buildServerChatMessage(profile, player, message)); + } + } - // Serialize the MiniMessage string to a legacy §-coloured string and inject - // it as the entire chat line via setFormat(). Bukkit calls: - // String.format(format, playerName, message) - // We put the fully-built line in slot %2$s and suppress slot %1$s, - // so the event is NOT cancelled — Bukkit delivers it natively to all recipients. - String legacy = LegacyComponentSerializer.legacySection() - .serialize(Common.deserializeMiniMessage(preFormatted)); + /** + * Restricts recipients to staff and applies the staff chat format. + */ + private void applyStaffChat(AsyncPlayerChatEvent e, Player player, String rawMessage) { + e.getRecipients().clear(); + e.getRecipients().addAll(ChatFormatUtil.getStaffRecipients()); + applyLegacyFormat(e, ChatFormatUtil.buildStaffChatMessage(player, rawMessage)); + } - e.setFormat("%2$s"); - e.setMessage(legacy); - } + /** + * Serialises a MiniMessage string to a legacy §-coloured string and injects + * it into the event via setFormat / setMessage so Bukkit delivers it natively. + */ + private void applyLegacyFormat(AsyncPlayerChatEvent e, String miniMessageString) { + String legacy = LegacyComponentSerializer.legacySection() + .serialize(Common.deserializeMiniMessage(miniMessageString)); + e.setFormat("%2$s"); + e.setMessage(legacy); } } diff --git a/spigot_modern/src/main/java/dev/nandi0813/practice_modern/listener/PlayerChatListener.java b/spigot_modern/src/main/java/dev/nandi0813/practice_modern/listener/PlayerChatListener.java index 8ff801b4..1598e786 100644 --- a/spigot_modern/src/main/java/dev/nandi0813/practice_modern/listener/PlayerChatListener.java +++ b/spigot_modern/src/main/java/dev/nandi0813/practice_modern/listener/PlayerChatListener.java @@ -7,12 +7,12 @@ import dev.nandi0813.practice.manager.party.PartyManager; import dev.nandi0813.practice.manager.profile.Profile; import dev.nandi0813.practice.manager.profile.ProfileManager; -import dev.nandi0813.practice.manager.profile.group.Group; +import dev.nandi0813.practice.util.ChatFormatUtil; import dev.nandi0813.practice.util.Common; import dev.nandi0813.practice.util.SoftDependUtil; import dev.nandi0813.practice.util.PAPIUtil; -import dev.nandi0813.practice.util.playerutil.PlayerUtil; import io.papermc.paper.event.player.AsyncChatEvent; +import net.kyori.adventure.audience.Audience; import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import org.bukkit.Bukkit; import org.bukkit.entity.Player; @@ -20,6 +20,9 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; +import java.util.Collection; +import java.util.Set; + public class PlayerChatListener implements Listener { @EventHandler(priority = EventPriority.NORMAL) @@ -31,15 +34,11 @@ public void onPlayerChat(AsyncChatEvent e) { // --- Party chat --- if (ConfigManager.getBoolean("CHAT.PARTY-CHAT-ENABLED") && profile.isParty() && party != null && message.startsWith("@")) { - e.setCancelled(true); - if (party.isPartyChat() || party.getLeader() == player) { - final String partyMsg = LanguageManager.getString("GENERAL-CHAT.PARTY-CHAT") - .replace("%%player%%", player.getName()) - .replace("%%message%%", message.replaceFirst("@", "")); - Bukkit.getScheduler().runTask(ZonePractice.getInstance(), - () -> party.sendMessage(partyMsg)); + setViewers(e, party.getMembers()); + applyRenderer(e, ChatFormatUtil.buildPartyChatMessage(player, message)); } else { + e.setCancelled(true); final String cantUse = LanguageManager.getString("PARTY.CANT-USE-PARTY-CHAT"); Bukkit.getScheduler().runTask(ZonePractice.getInstance(), () -> Common.sendMMMessage(player, cantUse)); @@ -49,61 +48,49 @@ public void onPlayerChat(AsyncChatEvent e) { // --- Staff chat (toggle) --- if (profile.isStaffChat()) { - e.setCancelled(true); - Bukkit.getScheduler().runTask(ZonePractice.getInstance(), - () -> PlayerUtil.sendStaffMessage(player, message)); + applyStaffChat(e, player, message); return; } // --- Staff chat (shortcut: #message) --- if (player.hasPermission("zpp.staff") && ConfigManager.getBoolean("CHAT.STAFF-CHAT.SHORTCUT") && message.startsWith("#")) { - e.setCancelled(true); - final String staffMsg = message.replaceFirst("#", ""); - Bukkit.getScheduler().runTask(ZonePractice.getInstance(), - () -> PlayerUtil.sendStaffMessage(player, staffMsg)); + applyStaffChat(e, player, message.replaceFirst("#", "")); return; } // --- Custom server chat --- if (ConfigManager.getBoolean("CHAT.SERVER-CHAT-ENABLED")) { - // Build the format string - final String format; - if (ConfigManager.getBoolean("PLAYER.GROUP-CHAT.ENABLED")) { - Group group = profile.getGroup(); - if (group != null && group.getChatFormat() != null) { - format = group.getChatFormat(); - } else { - format = LanguageManager.getString("GENERAL-CHAT.SERVER-CHAT"); - } - } else { - format = LanguageManager.getString("GENERAL-CHAT.SERVER-CHAT"); - } - - String division = profile.getStats().getDivision() != null ? profile.getStats().getDivision().getFullName() : ""; - String divisionShort = profile.getStats().getDivision() != null ? profile.getStats().getDivision().getShortName() : ""; + applyRenderer(e, ChatFormatUtil.buildServerChatMessage(profile, player, message)); + } + } - // Replace all static placeholders now; leave PAPI to the renderer (per-viewer) - String preFormatted = format - .replace("%%division%%", division) - .replace("%%division_short%%", divisionShort) - .replace("%%player%%", player.getName()) - .replace("%%message%%", message); + /** + * Restricts viewers to staff and applies the staff chat renderer. + */ + private void applyStaffChat(AsyncChatEvent e, Player player, String rawMessage) { + setViewers(e, ChatFormatUtil.getStaffRecipients()); + applyRenderer(e, ChatFormatUtil.buildStaffChatMessage(player, rawMessage)); + } - // Use the Adventure ChatRenderer so we don't cancel — the event itself - // delivers the component to each viewer through the normal pipeline. - // NOTE: AsyncChatEvent fires on an async thread; the renderer is also invoked - // async by Paper. PlaceholderAPI is NOT thread-safe, so PAPI placeholders are - // resolved here on the async thread only when isPAPI_ENABLED is true. - // Most PAPI expansions are effectively read-only and safe in practice, but if - // stricter safety is needed, pre-resolve per-viewer on the main thread before - // the event fires (e.g. via a sync task cache). - e.renderer((source, sourceDisplayName, msg, viewer) -> { - if (SoftDependUtil.isPAPI_ENABLED && viewer instanceof Player viewerPlayer) { - return PAPIUtil.runThroughFormat(viewerPlayer, preFormatted); - } + /** + * Replaces the viewer set with the given collection of players. + */ + private void setViewers(AsyncChatEvent e, Collection targets) { + Set viewers = e.viewers(); + viewers.clear(); + viewers.addAll(targets); + } - return ZonePractice.getMiniMessage().deserialize(preFormatted); - }); - } + /** + * Sets a ChatRenderer that deserialises the given MiniMessage string, + * resolving PAPI placeholders per-viewer when available. + */ + private void applyRenderer(AsyncChatEvent e, String miniMessageString) { + e.renderer((source, sourceDisplayName, msg, viewer) -> { + if (SoftDependUtil.isPAPI_ENABLED && viewer instanceof Player viewerPlayer) { + return PAPIUtil.runThroughFormat(viewerPlayer, miniMessageString); + } + return ZonePractice.getMiniMessage().deserialize(miniMessageString); + }); } } From 505a6d1d298d64d6a5dcbf584fc8bd0c592485db Mon Sep 17 00:00:00 2001 From: Nandor Dukat Date: Sun, 8 Mar 2026 11:47:11 +0100 Subject: [PATCH 06/31] added console to audience for party chat and staff chat logging on modern version --- .../nandi0813/practice_modern/listener/PlayerChatListener.java | 1 + 1 file changed, 1 insertion(+) diff --git a/spigot_modern/src/main/java/dev/nandi0813/practice_modern/listener/PlayerChatListener.java b/spigot_modern/src/main/java/dev/nandi0813/practice_modern/listener/PlayerChatListener.java index 1598e786..7763c78e 100644 --- a/spigot_modern/src/main/java/dev/nandi0813/practice_modern/listener/PlayerChatListener.java +++ b/spigot_modern/src/main/java/dev/nandi0813/practice_modern/listener/PlayerChatListener.java @@ -78,6 +78,7 @@ private void applyStaffChat(AsyncChatEvent e, Player player, String rawMessage) private void setViewers(AsyncChatEvent e, Collection targets) { Set viewers = e.viewers(); viewers.clear(); + viewers.add(ZonePractice.getAdventure().console()); viewers.addAll(targets); } From 4503c1237e31255277fc332aba9dbfa76afa5b2b Mon Sep 17 00:00:00 2001 From: Nandor Dukat Date: Sun, 8 Mar 2026 11:47:36 +0100 Subject: [PATCH 07/31] fixed death cause compatibility issue for party ffa death and changed default color fallback to gray --- .../fight/match/type/partyffa/PartyFFA.java | 81 ++++++++++++------- .../manager/nametag/TabIntegration.java | 25 +++--- .../manager/profile/group/GroupManager.java | 2 +- core/src/main/resources/language.yml | 3 +- 4 files changed, 73 insertions(+), 38 deletions(-) diff --git a/core/src/main/java/dev/nandi0813/practice/manager/fight/match/type/partyffa/PartyFFA.java b/core/src/main/java/dev/nandi0813/practice/manager/fight/match/type/partyffa/PartyFFA.java index 56ef9c25..815deb62 100644 --- a/core/src/main/java/dev/nandi0813/practice/manager/fight/match/type/partyffa/PartyFFA.java +++ b/core/src/main/java/dev/nandi0813/practice/manager/fight/match/type/partyffa/PartyFFA.java @@ -12,6 +12,7 @@ import dev.nandi0813.practice.manager.fight.match.util.MatchPlayerUtil; import dev.nandi0813.practice.manager.fight.util.Stats.Statistic; import dev.nandi0813.practice.manager.inventory.InventoryManager; +import dev.nandi0813.practice.manager.ladder.abstraction.interfaces.DeathResult; import dev.nandi0813.practice.manager.ladder.abstraction.Ladder; import dev.nandi0813.practice.manager.party.Party; import dev.nandi0813.practice.manager.nametag.NametagManager; @@ -19,6 +20,7 @@ import dev.nandi0813.practice.manager.server.sound.SoundType; import dev.nandi0813.practice.module.util.ClassImport; import dev.nandi0813.practice.util.playerutil.PlayerUtil; +import dev.nandi0813.practice.manager.fight.match.util.TempKillPlayer; import lombok.Getter; import org.bukkit.entity.Player; @@ -88,35 +90,60 @@ public void teleportPlayer(Player player) { @Override protected void killPlayer(Player player, String deathMessage) { - // Check if this ladder supports FFA mode using the Match helper methods - // Respawnable ladders don't support standard FFA death mechanics - if (isRespawnableLadder()) { - return; // Respawnable ladders (Bridges, BedWars, etc.) don't support FFA - } + PartyFfaRound round = this.getCurrentRound(); - // Scoring ladders (like Boxing) have special win conditions - if (isScoringLadder()) { - return; + // Use the Match helper method to handle ladder-specific death behavior + DeathResult result = handleLadderDeath(player); + + switch (result) { + case TEMPORARY_DEATH: + // Ladder supports respawning - create temp kill + asRespawnableLadder().ifPresent(respawnableLadder -> { + new TempKillPlayer(round, player, respawnableLadder.getRespawnTime()); + SoundManager.getInstance().getSound(SoundType.MATCH_PLAYER_TEMP_DEATH).play(this.getPeople()); + }); + ClassImport.getClasses().getPlayerUtil().clearInventory(player); + player.setHealth(20); + return; + + case ELIMINATED: + if (isScoringLadder()) { + // Scoring ladder (like Boxing) - death doesn't end round + return; + } + + // Standard or respawnable-eliminated death behavior + this.getCurrentStat(player).end(true); + SoundManager.getInstance().getSound(SoundType.MATCH_PLAYER_DEATH).play(this.getPeople()); + + PlayerUtil.setFightPlayer(player); + + if (ladder.isDropInventoryPartyGames()) + addEntityChange(ClassImport.getClasses().getPlayerUtil().dropPlayerInventory(player)); + else + ClassImport.getClasses().getPlayerUtil().clearInventory(player); + + // Send a death notification message + String playerDieMsg = LanguageManager.getString("MATCH.PARTY-FFA.PLAYER-DIE"); + if (playerDieMsg != null) { + this.sendMessage(playerDieMsg + .replace("%player%", player.getName()) + .replace("%alivePlayers%", String.valueOf(this.getAlivePlayers().size())), true); + } + + Player winnerPlayer = this.getWinnerPlayer(); + if (winnerPlayer != null) { + round.setRoundWinner(winnerPlayer); + round.endRound(); + } else { + MatchPlayerUtil.hidePlayerPartyGames(player, this.players); + } + break; + + case NO_ACTION: + // Ladder handled everything + break; } - - // Default death behavior for standard ladders in FFA - this.getCurrentStat(player).end(true); - SoundManager.getInstance().getSound(SoundType.MATCH_PLAYER_DEATH).play(this.getPeople()); - - PlayerUtil.setFightPlayer(player); - - if (ladder.isDropInventoryPartyGames()) - addEntityChange(ClassImport.getClasses().getPlayerUtil().dropPlayerInventory(player)); - else - ClassImport.getClasses().getPlayerUtil().clearInventory(player); - - PartyFfaRound round = this.getCurrentRound(); - Player winnerPlayer = this.getWinnerPlayer(); - if (winnerPlayer != null) { - round.setRoundWinner(winnerPlayer); - round.endRound(); - } else - MatchPlayerUtil.hidePlayerPartyGames(player, this.players); } @Override diff --git a/core/src/main/java/dev/nandi0813/practice/manager/nametag/TabIntegration.java b/core/src/main/java/dev/nandi0813/practice/manager/nametag/TabIntegration.java index ea398fe2..29440df8 100644 --- a/core/src/main/java/dev/nandi0813/practice/manager/nametag/TabIntegration.java +++ b/core/src/main/java/dev/nandi0813/practice/manager/nametag/TabIntegration.java @@ -285,14 +285,21 @@ public void resetNametag(Player player) { // Get TAB's NameTagManager to reset nametag values NameTagManager nameTagManager = tabAPI.getNameTagManager(); - if (nameTagManager == null) return; - - // Reset nametag prefix and suffix to default (null values reset to TAB's config) - nameTagManager.setPrefix(tabPlayer, null); - nameTagManager.setSuffix(tabPlayer, null); + if (nameTagManager != null) { + // Reset nametag prefix and suffix to default (null values reset to TAB's config) + nameTagManager.setPrefix(tabPlayer, null); + nameTagManager.setSuffix(tabPlayer, null); + } - // NOTE: We do NOT reset tablist formatting here - // Tablist should remain as configured by TAB (group-based formatting from lobby) + // Also reset tablist formatting to TAB's defaults so stale values don't persist + if (tablistFormattingEnabled) { + TabListFormatManager tabListFormatManager = tabAPI.getTabListFormatManager(); + if (tabListFormatManager != null) { + tabListFormatManager.setPrefix(tabPlayer, null); + tabListFormatManager.setName(tabPlayer, null); + tabListFormatManager.setSuffix(tabPlayer, null); + } + } } catch (Exception e) { // Silently fail - TAB integration is best-effort @@ -315,7 +322,7 @@ private String componentToLegacy(Component component) { * @return Legacy color code string (e.g., "§a" for green) */ private String getColorCode(NamedTextColor color) { - if (color == null) return "§f"; // Default to white + if (color == null) return "§7"; // Default to gray // Map NamedTextColor to legacy color codes if (color == NamedTextColor.BLACK) return "§0"; @@ -335,7 +342,7 @@ private String getColorCode(NamedTextColor color) { if (color == NamedTextColor.YELLOW) return "§e"; if (color == NamedTextColor.WHITE) return "§f"; - return "§f"; // Default to white if unknown + return "§7"; // Default to gray if unknown } /** diff --git a/core/src/main/java/dev/nandi0813/practice/manager/profile/group/GroupManager.java b/core/src/main/java/dev/nandi0813/practice/manager/profile/group/GroupManager.java index 73fa360b..63942ad1 100644 --- a/core/src/main/java/dev/nandi0813/practice/manager/profile/group/GroupManager.java +++ b/core/src/main/java/dev/nandi0813/practice/manager/profile/group/GroupManager.java @@ -57,7 +57,7 @@ public void loadGroups() { this.getInt("GROUPS." + groupName + ".CUSTOM-KIT"), this.getInt("GROUPS." + groupName + ".MODIFIABLE-KIT-PER-LADDER"), ZonePractice.getMiniMessage().deserialize(this.getString("GROUPS." + groupName + ".LOBBY-NAMETAG.PREFIX")), - NamedTextColor.NAMES.valueOr(this.getString("GROUPS." + groupName + ".LOBBY-NAMETAG.NAME-COLOR").toLowerCase(), NamedTextColor.WHITE), + NamedTextColor.NAMES.valueOr(this.getString("GROUPS." + groupName + ".LOBBY-NAMETAG.NAME-COLOR").toLowerCase(), NamedTextColor.GRAY), ZonePractice.getMiniMessage().deserialize(this.getString("GROUPS." + groupName + ".LOBBY-NAMETAG.SUFFIX")), this.getInt("GROUPS." + groupName + ".LOBBY-NAMETAG.SORT-PRIORITY"), chatFormat, diff --git a/core/src/main/resources/language.yml b/core/src/main/resources/language.yml index 0abbcdcb..d6286e24 100644 --- a/core/src/main/resources/language.yml +++ b/core/src/main/resources/language.yml @@ -1,4 +1,4 @@ -VERSION: 17 +VERSION: 18 CONSOLE-NAME: "Console" CANT-USE-CONSOLE: "You can't use this command from the console." @@ -1415,6 +1415,7 @@ MATCH: - "%rankedExtension%" PARTY-FFA: PLAYER-LEFT: "%player% left the match." + PLAYER-DIE: "%player% has been eliminated. (%alivePlayers% players remaining)" START: MATCH-STARTING: "Match is starting in %seconds% %secondName%." MATCH-STARTED: "Match has been started." From 7b4a92ee69bcc3f823cd630ca4dfaa5b14dc788a Mon Sep 17 00:00:00 2001 From: Nandor Dukat Date: Sun, 8 Mar 2026 11:56:21 +0100 Subject: [PATCH 08/31] changed fireball and tnt kb to be more similar to minemens on modern practice --- core/src/main/resources/modern/config.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/main/resources/modern/config.yml b/core/src/main/resources/modern/config.yml index ce66858d..eeaf9797 100644 --- a/core/src/main/resources/modern/config.yml +++ b/core/src/main/resources/modern/config.yml @@ -205,15 +205,15 @@ MATCH-SETTINGS: FIREWORK-ROCKET: COOLDOWN: 1 # Cooldown in seconds for using firework rockets with elytra. Default is 1 second. FIREBALL-FIGHT: # These are multiplier values, so please test what is optimal for you and don't change them too much at once or the difference will multiply. - FIREBALL-SPEED: 1.3 - FIREBALL-YIELD: 2 + FIREBALL-SPEED: 1.2 + FIREBALL-YIELD: 1.6 EXPLOSION: TNT: - HORIZONTAL: 1.9 - VERTICAL: 2.1 + HORIZONTAL: 1.3 + VERTICAL: 1.2 FIREBALL: - HORIZONTAL: 1.8 - VERTICAL: 1.7 + HORIZONTAL: 1.4 + VERTICAL: 1.3 BOXING: CUSTOM-ATTACK-COOLDOWN: ENABLED: true From b9a9e3726d76e8d97c57dfe5f6a0722b09cc8dc2 Mon Sep 17 00:00:00 2001 From: Nandor Dukat Date: Sun, 8 Mar 2026 15:26:47 +0100 Subject: [PATCH 09/31] changed fireball knockback calculation --- .../practice_1_8_8/interfaces/PlayerUtil.java | 13 ++++++++++--- .../practice_modern/interfaces/PlayerUtil.java | 13 ++++++++++--- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/spigot_1_8_8/src/main/java/dev/nandi0813/practice_1_8_8/interfaces/PlayerUtil.java b/spigot_1_8_8/src/main/java/dev/nandi0813/practice_1_8_8/interfaces/PlayerUtil.java index fcb4742e..383fdf3f 100644 --- a/spigot_1_8_8/src/main/java/dev/nandi0813/practice_1_8_8/interfaces/PlayerUtil.java +++ b/spigot_1_8_8/src/main/java/dev/nandi0813/practice_1_8_8/interfaces/PlayerUtil.java @@ -121,14 +121,21 @@ public void applyFireballKnockback(final Player player, final Fireball fireball) final float yield = fireball.getYield() > 0 ? fireball.getYield() : 1.0f; Bukkit.getScheduler().runTaskLater(ZonePractice.getInstance(), () -> { - double distance = playerLoc.distance(fireballLoc); + double dx = playerLoc.getX() - fireballLoc.getX(); + double dz = playerLoc.getZ() - fireballLoc.getZ(); + double horizontalDistance = Math.sqrt(dx * dx + dz * dz); + double fullDistance = playerLoc.distance(fireballLoc); + + // Blend horizontal and full 3D distance so that jumping still weakens + // the knockback, but not as drastically as using full 3D distance alone. + double effectiveDistance = (horizontalDistance * 0.7) + (fullDistance * 0.3); double safeDistance = 0.6; double factor = 1.0; - if (distance > safeDistance) { + if (effectiveDistance > safeDistance) { double decayRange = yield * 2.0; - factor = 1.0 - ((distance - safeDistance) / decayRange); + factor = 1.0 - ((effectiveDistance - safeDistance) / decayRange); } if (factor < 0) factor = 0; diff --git a/spigot_modern/src/main/java/dev/nandi0813/practice_modern/interfaces/PlayerUtil.java b/spigot_modern/src/main/java/dev/nandi0813/practice_modern/interfaces/PlayerUtil.java index b0ea5cdc..b494cb08 100644 --- a/spigot_modern/src/main/java/dev/nandi0813/practice_modern/interfaces/PlayerUtil.java +++ b/spigot_modern/src/main/java/dev/nandi0813/practice_modern/interfaces/PlayerUtil.java @@ -114,18 +114,25 @@ public void applyFireballKnockback(final Player player, final Fireball fireball) final float yield = fireball.getYield() > 0 ? fireball.getYield() : 1.0f; Bukkit.getScheduler().runTaskLater(ZonePractice.getInstance(), () -> { - double distance = playerLoc.distance(fireballLoc); + double dx = playerLoc.getX() - fireballLoc.getX(); + double dz = playerLoc.getZ() - fireballLoc.getZ(); + double horizontalDistance = Math.sqrt(dx * dx + dz * dz); + double fullDistance = playerLoc.distance(fireballLoc); + + // Blend horizontal and full 3D distance so that jumping still weakens + // the knockback, but not as drastically as using full 3D distance alone. + double effectiveDistance = (horizontalDistance * 0.7) + (fullDistance * 0.3); double safeDistance = 0.6; double factor = 1.0; - if (distance > safeDistance) { + if (effectiveDistance > safeDistance) { double impactRadius = yield * 2.5; double decayRange = impactRadius - safeDistance; if (decayRange <= 0.1) decayRange = 1.0; - factor = 1.0 - ((distance - safeDistance) / decayRange); + factor = 1.0 - ((effectiveDistance - safeDistance) / decayRange); } if (factor <= 0.1) return; From 18591efc80610a6492f8bf20f8fc2a6920ac882f Mon Sep 17 00:00:00 2001 From: Nandor Dukat Date: Sun, 8 Mar 2026 15:28:25 +0100 Subject: [PATCH 10/31] changed rounds hashmap to use ConcurrentHashMap --- .../java/dev/nandi0813/practice/manager/fight/match/Match.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/dev/nandi0813/practice/manager/fight/match/Match.java b/core/src/main/java/dev/nandi0813/practice/manager/fight/match/Match.java index 9d51dc55..be697460 100644 --- a/core/src/main/java/dev/nandi0813/practice/manager/fight/match/Match.java +++ b/core/src/main/java/dev/nandi0813/practice/manager/fight/match/Match.java @@ -67,7 +67,7 @@ public abstract class Match extends BukkitRunnable implements Spectatable, dev.n // Round protected final int winsNeeded; - protected final Map rounds = new HashMap<>(); + protected final Map rounds = new java.util.concurrent.ConcurrentHashMap<>(); // Player variables protected final Map matchPlayers = new HashMap<>(); From 3dc1458f5037501ad88db51e4c5c3a05b454b0be Mon Sep 17 00:00:00 2001 From: Nandor Dukat Date: Sun, 8 Mar 2026 15:29:59 +0100 Subject: [PATCH 11/31] fixed chunk unload event handling --- .../practice_modern/listener/ArenaListener.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/spigot_modern/src/main/java/dev/nandi0813/practice_modern/listener/ArenaListener.java b/spigot_modern/src/main/java/dev/nandi0813/practice_modern/listener/ArenaListener.java index 916f0b3f..6355a45d 100644 --- a/spigot_modern/src/main/java/dev/nandi0813/practice_modern/listener/ArenaListener.java +++ b/spigot_modern/src/main/java/dev/nandi0813/practice_modern/listener/ArenaListener.java @@ -1,7 +1,10 @@ package dev.nandi0813.practice_modern.listener; +import dev.nandi0813.practice.ZonePractice; import dev.nandi0813.practice.manager.arena.util.ArenaWorldUtil; import dev.nandi0813.practice.manager.server.ServerManager; +import org.bukkit.Bukkit; +import org.bukkit.World; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.world.ChunkUnloadEvent; @@ -16,7 +19,12 @@ public class ArenaListener implements Listener { public void onChunkUnload(ChunkUnloadEvent e) { if (LOAD_CHUNKS) { if (LOADED_CHUNKS.contains(e.getChunk())) { - e.getChunk().getWorld().getChunkAtAsync(e.getChunk().getX(), e.getChunk().getZ()); + final World world = e.getChunk().getWorld(); + final int cx = e.getChunk().getX(); + final int cz = e.getChunk().getZ(); + Bukkit.getScheduler().runTask(ZonePractice.getInstance(), () -> + world.getChunkAtAsync(cx, cz) + ); } } } From 43610459ba0499b97cd1d3f89bb278733ca9141f Mon Sep 17 00:00:00 2001 From: Nandor Dukat Date: Sun, 8 Mar 2026 15:33:59 +0100 Subject: [PATCH 12/31] fixed block check in unloaded chunks --- .../practice/manager/fight/match/util/MatchUtil.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/dev/nandi0813/practice/manager/fight/match/util/MatchUtil.java b/core/src/main/java/dev/nandi0813/practice/manager/fight/match/util/MatchUtil.java index d2640da8..1364de77 100644 --- a/core/src/main/java/dev/nandi0813/practice/manager/fight/match/util/MatchUtil.java +++ b/core/src/main/java/dev/nandi0813/practice/manager/fight/match/util/MatchUtil.java @@ -88,7 +88,9 @@ public static int getRandomElo() { } public static void safePlayerTeleportBlock(Block block) { - if (block != null && block.getType().equals(Material.AIR)) + if (block == null) return; + if (!block.getWorld().isChunkLoaded(block.getX() >> 4, block.getZ() >> 4)) return; + if (block.getType().equals(Material.AIR)) block.setType(Material.BEDROCK); } From 2786bc243fd758bd591ea0ce4c7c50901522790c Mon Sep 17 00:00:00 2001 From: Nandor Dukat Date: Sun, 8 Mar 2026 15:46:34 +0100 Subject: [PATCH 13/31] fixed arena and ladder icons showing in GUIs --- .../manager/fight/ffa/game/LadderSelector.java | 13 ++++++++----- .../gui/guis/selectors/DuelRoundSelectorGui.java | 14 ++++---------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/dev/nandi0813/practice/manager/fight/ffa/game/LadderSelector.java b/core/src/main/java/dev/nandi0813/practice/manager/fight/ffa/game/LadderSelector.java index f9771742..2efc232c 100644 --- a/core/src/main/java/dev/nandi0813/practice/manager/fight/ffa/game/LadderSelector.java +++ b/core/src/main/java/dev/nandi0813/practice/manager/fight/ffa/game/LadderSelector.java @@ -47,11 +47,14 @@ public void update() { ladderSlots.clear(); for (NormalLadder ladder : ffaArena.getAssignedLadders()) { - ItemStack ladderItem = LADDER_ITEM.cloneItem() - .setMaterial(ladder.getIcon().getType()) - .setDamage(ladder.getIcon().getDurability()) - .replace("%ladder%", ladder.getDisplayName()) - .get(); + GUIItem guiItem = LADDER_ITEM.cloneItem() + .replace("%ladder%", ladder.getDisplayName()); + + if (ladder.getIcon() != null) { + guiItem.setBaseItem(ladder.getIcon()); + } + + ItemStack ladderItem = guiItem.get(); int slot = inventory.firstEmpty(); inventory.setItem(slot, ladderItem); diff --git a/core/src/main/java/dev/nandi0813/practice/manager/gui/guis/selectors/DuelRoundSelectorGui.java b/core/src/main/java/dev/nandi0813/practice/manager/gui/guis/selectors/DuelRoundSelectorGui.java index a2648ec3..27a87960 100644 --- a/core/src/main/java/dev/nandi0813/practice/manager/gui/guis/selectors/DuelRoundSelectorGui.java +++ b/core/src/main/java/dev/nandi0813/practice/manager/gui/guis/selectors/DuelRoundSelectorGui.java @@ -60,12 +60,9 @@ public void build() { inventory.setItem(0, BACK_TO_ITEM); - ItemStack ladderIcon = ladder.getIcon().clone(); GUIItem ladderIconItem = SHOW_LADDER.cloneItem(); - if (ladderIcon != null && ladderIcon.getType() != null) { - ladderIconItem - .setMaterial(ladderIcon.getType()) - .setDamage(ladderIcon.getDurability()); + if (ladder.getIcon() != null) { + ladderIconItem.setBaseItem(ladder.getIcon()); } inventory.setItem(5, ladderIconItem @@ -73,12 +70,9 @@ public void build() { .get()); if (arena != null) { - ItemStack arenaIcon = arena.getIcon().clone(); GUIItem arenaIconItem = SHOW_ARENA.cloneItem(); - if (arenaIcon != null && arenaIcon.getType() != null) { - arenaIconItem - .setMaterial(arenaIcon.getType()) - .setDamage(arenaIcon.getDurability()); + if (arena.getIcon() != null) { + arenaIconItem.setBaseItem(arena.getIcon()); } inventory.setItem(6, arenaIconItem From 48c185df273d32024025a5db8015a9ec371f4547 Mon Sep 17 00:00:00 2001 From: Nandor Dukat Date: Sun, 8 Mar 2026 15:56:55 +0100 Subject: [PATCH 14/31] changed logic in ffa main setup GUI to enable arena automatically when it can and admin wants to open it --- .../gui/setup/arena/arenasettings/ffa/ArenaMainGui.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/src/main/java/dev/nandi0813/practice/manager/gui/setup/arena/arenasettings/ffa/ArenaMainGui.java b/core/src/main/java/dev/nandi0813/practice/manager/gui/setup/arena/arenasettings/ffa/ArenaMainGui.java index f1eb66ed..ab171247 100644 --- a/core/src/main/java/dev/nandi0813/practice/manager/gui/setup/arena/arenasettings/ffa/ArenaMainGui.java +++ b/core/src/main/java/dev/nandi0813/practice/manager/gui/setup/arena/arenasettings/ffa/ArenaMainGui.java @@ -138,6 +138,10 @@ public void handleClickEvent(InventoryClickEvent e) { GUIManager.getInstance().searchGUI(GUIType.Arena_Summary).open(player); break; case 31: + if (!ffaArena.isEnabled() && ffaArena.isReadyToEnable()) { + ArenaUtil.changeStatus(player, ffaArena); + } + if (ffaArena.isEnabled()) { FFA ffa = ffaArena.getFfa(); From baab9ec680e9ed04affd8c88613287b088b3c6c2 Mon Sep 17 00:00:00 2001 From: Nandor Dukat Date: Sun, 8 Mar 2026 16:03:15 +0100 Subject: [PATCH 15/31] fixed fire spread rollback logic --- .../interfaces/AbstractBuildListener.java | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/dev/nandi0813/practice/module/interfaces/AbstractBuildListener.java b/core/src/main/java/dev/nandi0813/practice/module/interfaces/AbstractBuildListener.java index 219f18ac..0452a6d6 100644 --- a/core/src/main/java/dev/nandi0813/practice/module/interfaces/AbstractBuildListener.java +++ b/core/src/main/java/dev/nandi0813/practice/module/interfaces/AbstractBuildListener.java @@ -428,16 +428,28 @@ public void onBlockFromTo(BlockFromToEvent e) { @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onBlockSpread(BlockSpreadEvent e) { Block source = e.getSource(); - if (!source.hasMetadata(PLACED_IN_FIGHT)) return; - MetadataValue mv = source.getMetadata(PLACED_IN_FIGHT).get(0); - if (!(mv.value() instanceof Spectatable spectatable)) return; + Spectatable spectatable = null; + + if (source.hasMetadata(PLACED_IN_FIGHT)) { + MetadataValue mv = source.getMetadata(PLACED_IN_FIGHT).get(0); + if (mv.value() instanceof Spectatable s) { + spectatable = s; + } + } + + if (spectatable == null) { + spectatable = getByBlock(source); + if (spectatable == null) return; + } + if (!spectatable.isBuild()) return; + final Spectatable finalSpectatable = spectatable; final Block newBlock = e.getNewState().getBlock(); org.bukkit.Bukkit.getScheduler().runTask(ZonePractice.getInstance(), () -> { if (newBlock.hasMetadata(PLACED_IN_FIGHT)) return; - tagAndTrack(newBlock, spectatable); + tagAndTrack(newBlock, finalSpectatable); }); } From 79b595afadd133409be2a71004a3cf3fac869a8d Mon Sep 17 00:00:00 2001 From: Nandor Dukat Date: Sun, 8 Mar 2026 17:08:52 +0100 Subject: [PATCH 16/31] fixed fire conflicting arena rollback, fixed enderpearl glitch when player leaving ffa arena --- .../manager/arena/util/ArenaUtil.java | 4 + .../practice/manager/fight/ffa/game/FFA.java | 10 ++ .../interfaces/AbstractBuildListener.java | 95 ++++++++++++++++++- .../fightmapchange/FightChangeOptimized.java | 39 +++++++- 4 files changed, 146 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/dev/nandi0813/practice/manager/arena/util/ArenaUtil.java b/core/src/main/java/dev/nandi0813/practice/manager/arena/util/ArenaUtil.java index a8d621fe..267dabca 100644 --- a/core/src/main/java/dev/nandi0813/practice/manager/arena/util/ArenaUtil.java +++ b/core/src/main/java/dev/nandi0813/practice/manager/arena/util/ArenaUtil.java @@ -109,6 +109,10 @@ public static boolean changeStatus(Player player, DisplayArena arena) { Common.sendMMMessage(player, LanguageManager.getString("ARENA.STATUS-CHANGE.NO-FFA-POSITIONS")); returnVal = false; } + if (arena.getAssignedLadders().isEmpty()) { + Common.sendMMMessage(player, "Please assign at least one ladder to the arena!"); + return false; + } } } else { if (!MatchManager.getInstance().getLiveMatchesByArena(arena).isEmpty()) { diff --git a/core/src/main/java/dev/nandi0813/practice/manager/fight/ffa/game/FFA.java b/core/src/main/java/dev/nandi0813/practice/manager/fight/ffa/game/FFA.java index 23fbb0db..0bdfebcb 100644 --- a/core/src/main/java/dev/nandi0813/practice/manager/fight/ffa/game/FFA.java +++ b/core/src/main/java/dev/nandi0813/practice/manager/fight/ffa/game/FFA.java @@ -27,6 +27,8 @@ import dev.nandi0813.practice.util.playerutil.PlayerUtil; import lombok.Getter; import org.bukkit.Bukkit; +import org.bukkit.entity.EnderPearl; +import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import java.util.*; @@ -144,6 +146,14 @@ public void removePlayer(Player player) { this.sendMessage(LanguageManager.getString("FFA.GAME.PLAYER-LEAVE").replace("%player%", player.getName()), true); + // Remove in-flight ender pearls to prevent the player from being + // teleported back to the arena world after they have left. + for (Entity entity : player.getWorld().getEntities()) { + if (entity instanceof EnderPearl pearl && player.equals(pearl.getShooter())) { + pearl.remove(); + } + } + players.remove(player); fightPlayers.remove(player); statistics.remove(player); diff --git a/core/src/main/java/dev/nandi0813/practice/module/interfaces/AbstractBuildListener.java b/core/src/main/java/dev/nandi0813/practice/module/interfaces/AbstractBuildListener.java index 0452a6d6..bf308a6f 100644 --- a/core/src/main/java/dev/nandi0813/practice/module/interfaces/AbstractBuildListener.java +++ b/core/src/main/java/dev/nandi0813/practice/module/interfaces/AbstractBuildListener.java @@ -18,6 +18,7 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBurnEvent; import org.bukkit.event.block.BlockFormEvent; import org.bukkit.event.block.BlockFromToEvent; import org.bukkit.event.block.BlockPistonExtendEvent; @@ -425,7 +426,7 @@ public void onBlockFromTo(BlockFromToEvent e) { // BLOCK SPREAD (fire, mushrooms, etc.) // ========================================================================= - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onBlockSpread(BlockSpreadEvent e) { Block source = e.getSource(); @@ -445,6 +446,13 @@ public void onBlockSpread(BlockSpreadEvent e) { if (!spectatable.isBuild()) return; + // Cancel fire spread during rollback to prevent fire from re-igniting + // freshly-restored flammable blocks while the multi-tick rollback is in progress. + if (spectatable.getFightChange() != null && spectatable.getFightChange().isRollingBack()) { + e.setCancelled(true); + return; + } + final Spectatable finalSpectatable = spectatable; final Block newBlock = e.getNewState().getBlock(); org.bukkit.Bukkit.getScheduler().runTask(ZonePractice.getInstance(), () -> { @@ -453,6 +461,91 @@ public void onBlockSpread(BlockSpreadEvent e) { }); } + // ========================================================================= + // BLOCK BURN (fire destroying blocks) + // ========================================================================= + + /** + * Tracks blocks destroyed by fire so they are restored during rollback. + * Runs at LOWEST so the block still holds its original material when captured. + *

+ * Also tracks adjacent fire blocks (the igniting fire and fire above) so that + * rollback removes the fire along with restoring the burned block. + */ + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + public void onBlockBurn(BlockBurnEvent e) { + Block block = e.getBlock(); + + Spectatable spectatable = null; + + // Fast path: block already tagged + if (block.hasMetadata(PLACED_IN_FIGHT)) { + MetadataValue mv = block.getMetadata(PLACED_IN_FIGHT).get(0); + if (mv.value() instanceof Spectatable s) { + spectatable = s; + } + } + + // Slow path: natural arena block — look up by cuboid + if (spectatable == null) { + spectatable = getByBlock(block); + if (spectatable == null) return; + } + + if (!spectatable.isBuild()) return; + + // Cancel burns during rollback to prevent fire from destroying + // freshly-restored blocks while the multi-tick rollback is in progress. + if (spectatable.getFightChange() != null && spectatable.getFightChange().isRollingBack()) { + e.setCancelled(true); + return; + } + + // Track the block's original state for rollback + if (block.hasMetadata(PLACED_IN_FIGHT)) { + spectatable.addBlockChange(ClassImport.createChangeBlock(block)); + } else { + spectatable.getFightChange().addArenaBlockChange(ClassImport.createChangeBlock(block)); + } + + // Track adjacent fire blocks so rollback removes the fire that caused/surrounds the burn. + // Without this, restoring the burned block leaves fire sitting on top of it. + trackAdjacentFire(block, spectatable); + + // After the burn event, the burned block may be replaced with fire. + // Schedule a 1-tick delayed check to track it if so. + final Spectatable finalSpectatable = spectatable; + org.bukkit.Bukkit.getScheduler().runTask(ZonePractice.getInstance(), () -> { + String typeName = block.getType().name(); + if ((typeName.equals("FIRE") || typeName.equals("SOUL_FIRE")) && !block.hasMetadata(PLACED_IN_FIGHT)) { + tagAndTrack(block, finalSpectatable); + } + }); + } + + /** + * Checks all six faces around a block for fire (FIRE / SOUL_FIRE) and tracks + * any untracked fire blocks for rollback so they are removed when the arena resets. + */ + private void trackAdjacentFire(Block center, Spectatable spectatable) { + final Block[] adjacent = { + center.getRelative(0, 1, 0), // above + center.getRelative(0, -1, 0), // below + center.getRelative(1, 0, 0), // east + center.getRelative(-1, 0, 0), // west + center.getRelative(0, 0, 1), // south + center.getRelative(0, 0, -1) // north + }; + + for (Block adj : adjacent) { + String typeName = adj.getType().name(); + if (!typeName.equals("FIRE") && !typeName.equals("SOUL_FIRE")) continue; + if (adj.hasMetadata(PLACED_IN_FIGHT)) continue; + + tagAndTrack(adj, spectatable); + } + } + // ========================================================================= // FALLING BLOCKS (sand, gravel, concrete powder, anvils, etc.) // ========================================================================= diff --git a/core/src/main/java/dev/nandi0813/practice/util/fightmapchange/FightChangeOptimized.java b/core/src/main/java/dev/nandi0813/practice/util/fightmapchange/FightChangeOptimized.java index b7cf008e..d1947b26 100644 --- a/core/src/main/java/dev/nandi0813/practice/util/fightmapchange/FightChangeOptimized.java +++ b/core/src/main/java/dev/nandi0813/practice/util/fightmapchange/FightChangeOptimized.java @@ -59,6 +59,14 @@ public class FightChangeOptimized { // Reusable rollback task private RollbackTask rollbackTask; + /** + * True while a rollback is in progress. Used by block spread/burn listeners to + * cancel new fire spread during the multi-tick rollback window so fire doesn't + * re-appear on blocks that have already been restored. + */ + @Getter + private volatile boolean rollingBack = false; + /** * Constructor for all fight types (Match, Event, FFA). * @@ -235,6 +243,8 @@ public void rollback(int maxCheck, int maxChange) { * @param onComplete Called on the main thread when rollback finishes, or {@code null} */ public void rollback(int maxCheck, int maxChange, @org.jetbrains.annotations.Nullable Runnable onComplete) { + rollingBack = true; + // Remove all entities (both tracked and cuboid entities in one pass) removeAllEntities(); @@ -245,7 +255,9 @@ public void rollback(int maxCheck, int maxChange, @org.jetbrains.annotations.Nul } if (blocks.isEmpty()) { - // Nothing to restore — fire callback immediately + // Nothing to restore — still extinguish any lingering fire + extinguishFire(); + rollingBack = false; if (onComplete != null) { if (org.bukkit.Bukkit.isPrimaryThread()) { onComplete.run(); @@ -259,6 +271,8 @@ public void rollback(int maxCheck, int maxChange, @org.jetbrains.annotations.Nul // Quick rollback if server is shutting down if (!ZonePractice.getInstance().isEnabled()) { quickRollback(); + extinguishFire(); + rollingBack = false; if (onComplete != null) onComplete.run(); return; } @@ -328,6 +342,24 @@ public void quickRollback() { } } + /** + * Scans the arena cuboid and extinguishes any remaining fire (FIRE / SOUL_FIRE) blocks. + *

+ * During multi-tick rollback, fire can spread to freshly-restored flammable blocks + * before the rollback finishes. This sweep ensures no fire persists after rollback. + */ + private void extinguishFire() { + if (cuboid == null) return; + + for (Block block : cuboid) { + String typeName = block.getType().name(); + if (typeName.equals("FIRE") || typeName.equals("SOUL_FIRE")) { + block.setType(org.bukkit.Material.AIR, false); + block.removeMetadata(PLACED_IN_FIGHT, ZonePractice.getInstance()); + } + } + } + /** * Reusable rollback task that processes blocks over multiple ticks. *

@@ -412,6 +444,10 @@ public void run() { isRunning = false; blocks.clear(); // Clear the map + // Extinguish any fire that spread during the multi-tick rollback + extinguishFire(); + rollingBack = false; + if (onComplete != null) { onComplete.run(); // already on main thread (runTaskTimer) } @@ -428,6 +464,7 @@ public void run() { } catch (Exception e) { this.cancel(); isRunning = false; + rollingBack = false; Common.sendConsoleMMMessage("Rollback error at block " + processedBlocks + "/" + totalBlocks + ": " + e.getMessage()); e.printStackTrace(); } From c13fda4403903b0f977ba122c6a3a570193a523a Mon Sep 17 00:00:00 2001 From: MISHA <208148594+lokspel@users.noreply.github.com> Date: Mon, 9 Mar 2026 00:44:02 +0400 Subject: [PATCH 17/31] fix: prevent synchronous chunk loading in Cuboid#getChunks (#291) --- .../dev/nandi0813/practice/util/Cuboid.java | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/dev/nandi0813/practice/util/Cuboid.java b/core/src/main/java/dev/nandi0813/practice/util/Cuboid.java index 2e78ce22..789617a3 100644 --- a/core/src/main/java/dev/nandi0813/practice/util/Cuboid.java +++ b/core/src/main/java/dev/nandi0813/practice/util/Cuboid.java @@ -544,18 +544,24 @@ public Block getRelativeBlock(World w, int x, int y, int z) { * @return A list of Chunk objects */ public List getChunks() { - List res = new ArrayList(); + List res = new ArrayList<>(); World w = this.getWorld(); - int x1 = this.getLowerX() & ~0xf; - int x2 = this.getUpperX() & ~0xf; - int z1 = this.getLowerZ() & ~0xf; - int z2 = this.getUpperZ() & ~0xf; - for (int x = x1; x <= x2; x += 16) { - for (int z = z1; z <= z2; z += 16) { - res.add(w.getChunkAt(x >> 4, z >> 4)); + int x1 = this.getLowerX() >> 4; + int x2 = this.getUpperX() >> 4; + int z1 = this.getLowerZ() >> 4; + int z2 = this.getUpperZ() >> 4; + + for (int x = x1; x <= x2; x++) { + for (int z = z1; z <= z2; z++) { + + if (w.isChunkLoaded(x, z)) { + res.add(w.getChunkAt(x, z)); + } + } } + return res; } From e7db84e0b1d91b02fc277ab8032538caeb6a460b Mon Sep 17 00:00:00 2001 From: MISHA <208148594+lokspel@users.noreply.github.com> Date: Mon, 9 Mar 2026 10:07:46 +0400 Subject: [PATCH 18/31] fix: ensure chunks are loaded when getting arena chunks (#295) --- core/src/main/java/dev/nandi0813/practice/util/Cuboid.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/dev/nandi0813/practice/util/Cuboid.java b/core/src/main/java/dev/nandi0813/practice/util/Cuboid.java index 789617a3..28f7e9e6 100644 --- a/core/src/main/java/dev/nandi0813/practice/util/Cuboid.java +++ b/core/src/main/java/dev/nandi0813/practice/util/Cuboid.java @@ -555,10 +555,11 @@ public List getChunks() { for (int x = x1; x <= x2; x++) { for (int z = z1; z <= z2; z++) { - if (w.isChunkLoaded(x, z)) { - res.add(w.getChunkAt(x, z)); + if (!w.isChunkLoaded(x, z)) { + w.loadChunk(x, z); } + res.add(w.getChunkAt(x, z)); } } From 4afec5949df62353a173bb4497628b02ee2708db Mon Sep 17 00:00:00 2001 From: MISHA <208148594+lokspel@users.noreply.github.com> Date: Mon, 9 Mar 2026 12:43:28 +0400 Subject: [PATCH 19/31] Dev (#298) --- .../practice/manager/arena/ArenaManager.java | 7 ++ .../gui/setup/server/ServerHubGui.java | 5 +- .../sidebar/adapter/PracticeAdapter.java | 6 +- .../FireworkRocketCooldownListener.java | 64 ++++--------------- 4 files changed, 28 insertions(+), 54 deletions(-) diff --git a/core/src/main/java/dev/nandi0813/practice/manager/arena/ArenaManager.java b/core/src/main/java/dev/nandi0813/practice/manager/arena/ArenaManager.java index 762ad426..ea244761 100644 --- a/core/src/main/java/dev/nandi0813/practice/manager/arena/ArenaManager.java +++ b/core/src/main/java/dev/nandi0813/practice/manager/arena/ArenaManager.java @@ -91,6 +91,13 @@ public List getEnabledArenas() { return enabledArenas; } + public List getEnabledFFAArenas() { + List enabledArenas = new ArrayList<>(); + for (FFAArena arena : this.getFFAArenas()) + if (arena.isEnabled()) enabledArenas.add(arena); + return enabledArenas; + } + public void loadArenas(final StartUpCallback boolCallback) { Bukkit.getScheduler().runTaskAsynchronously(ZonePractice.getInstance(), () -> { diff --git a/core/src/main/java/dev/nandi0813/practice/manager/gui/setup/server/ServerHubGui.java b/core/src/main/java/dev/nandi0813/practice/manager/gui/setup/server/ServerHubGui.java index 1d2315af..0c742a2d 100644 --- a/core/src/main/java/dev/nandi0813/practice/manager/gui/setup/server/ServerHubGui.java +++ b/core/src/main/java/dev/nandi0813/practice/manager/gui/setup/server/ServerHubGui.java @@ -127,7 +127,10 @@ private ItemStack getInformationItem() { .replace("%onlineStaffs%", String.valueOf(PlayerUtil.getOnlineStaff().size())) .replace("%requiredDivision%", DivisionManager.getInstance().getMinimumForRanked() != null ? DivisionManager.getInstance().getMinimumForRanked().getFullName() : "&cN/A") .replace("%lobbyStatus%", ServerManager.getLobby() != null ? GUIFile.getString("GUIS.SETUP.SERVER.SERVER-MANAGER.ICONS.INFORMATIONS.STATUS-NAMES.SET") : GUIFile.getString("GUIS.SETUP.SERVER.SERVER-MANAGER.ICONS.INFORMATIONS.STATUS-NAMES.UNSET")) - .replace("%enabledArena%", String.valueOf(ArenaManager.getInstance().getEnabledArenas().size())) + .replace("%enabledArena%", String.valueOf( + ArenaManager.getInstance().getEnabledArenas().size() + + ArenaManager.getInstance().getEnabledFFAArenas().size() + )) .replace("%enabledLadder%", String.valueOf(LadderManager.getInstance().getEnabledLadders().size())) .replace("%enabledEvents%", String.valueOf(EventManager.getInstance().getEnabledEvents().size())) .get(); diff --git a/core/src/main/java/dev/nandi0813/practice/manager/sidebar/adapter/PracticeAdapter.java b/core/src/main/java/dev/nandi0813/practice/manager/sidebar/adapter/PracticeAdapter.java index b66a1670..6685b669 100644 --- a/core/src/main/java/dev/nandi0813/practice/manager/sidebar/adapter/PracticeAdapter.java +++ b/core/src/main/java/dev/nandi0813/practice/manager/sidebar/adapter/PracticeAdapter.java @@ -602,8 +602,10 @@ public List getLines(Player player) { line = line .replace("%tps%", String.valueOf(TPSUtil.get1MinTPSRounded())) .replace("%arenas%", String.valueOf(ArenaManager.getInstance().getArenaList().size())) - .replace("%enabledArenas%", String.valueOf(ArenaManager.getInstance().getEnabledArenas().size())); - + .replace("%enabledArenas%", String.valueOf( + ArenaManager.getInstance().getEnabledArenas().size() + + ArenaManager.getInstance().getEnabledFFAArenas().size() + )); sidebar.add(PAPIUtil.runThroughFormat(player, line)); } } diff --git a/spigot_modern/src/main/java/dev/nandi0813/practice_modern/listener/FireworkRocketCooldownListener.java b/spigot_modern/src/main/java/dev/nandi0813/practice_modern/listener/FireworkRocketCooldownListener.java index c008d91f..395faa60 100644 --- a/spigot_modern/src/main/java/dev/nandi0813/practice_modern/listener/FireworkRocketCooldownListener.java +++ b/spigot_modern/src/main/java/dev/nandi0813/practice_modern/listener/FireworkRocketCooldownListener.java @@ -1,24 +1,16 @@ package dev.nandi0813.practice_modern.listener; -import dev.nandi0813.practice.ZonePractice; import dev.nandi0813.practice.manager.fight.ffa.FFAManager; import dev.nandi0813.practice.manager.fight.ffa.game.FFA; import dev.nandi0813.practice.manager.fight.match.Match; import dev.nandi0813.practice.manager.fight.match.MatchManager; import dev.nandi0813.practice.manager.fight.match.enums.RoundStatus; import dev.nandi0813.practice.module.util.ClassImport; -import org.bukkit.Material; -import org.bukkit.Bukkit; +import org.bukkit.entity.Firework; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; -import org.bukkit.event.block.Action; -import org.bukkit.event.player.PlayerInteractEvent; -import org.bukkit.inventory.ItemStack; - -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; +import org.bukkit.event.entity.EntitySpawnEvent; /** * Handles firework rocket cooldown for elytra boost in modern Minecraft versions. @@ -28,67 +20,37 @@ */ public class FireworkRocketCooldownListener implements Listener { - // Tracks players whose interact event was already handled this tick to prevent - // duplicate processing from the MAIN_HAND + OFF_HAND double-fire. - private final Set handledThisTick = new HashSet<>(); - @EventHandler - public void onFireworkRocketUse(PlayerInteractEvent e) { - Player player = e.getPlayer(); - ItemStack item = e.getItem(); - - // Check if player is using a firework rocket - if (item == null || item.getType() != Material.FIREWORK_ROCKET) { - return; - } - - // Apply cooldown only for valid RIGHT-CLICK firework usage: - // - RIGHT_CLICK_AIR while gliding (elytra boost) - // - RIGHT_CLICK_BLOCK (ground/block launch) - // Ignore: LEFT clicks, PHYSICAL, empty RIGHT_CLICK_AIR without gliding - Action action = e.getAction(); - if (action != Action.RIGHT_CLICK_AIR && action != Action.RIGHT_CLICK_BLOCK) { - return; // Ignore left clicks & PHYSICAL - } - if (!player.isGliding() && action == Action.RIGHT_CLICK_AIR) { - return; // Ignore empty air clicks - } + public void onFireworkSpawn(EntitySpawnEvent e) { - // Deduplicate: PlayerInteractEvent fires once per hand (MAIN + OFF), skip the second call this tick - UUID uuid = player.getUniqueId(); - if (!handledThisTick.add(uuid)) { - handledThisTick.remove(uuid); - return; - } + if (!(e.getEntity() instanceof Firework firework)) return; + if (!(firework.getShooter() instanceof Player player)) return; - // Check if player is in FFA + // FFA FFA ffa = FFAManager.getInstance().getFFAByPlayer(player); if (ffa != null) { + int duration = ffa.getPlayers().get(player).getFireworkRocketCooldown(); - if (duration <= 0) { - return; - } + if (duration <= 0) return; ClassImport.getClasses().getItemCooldownHandler().handleFireworkRocketFFA( player, ffa.getFightPlayers().get(player), duration, - e, + null, "MATCH.COOLDOWN.FIREWORK-ROCKET-COOLDOWN" ); return; } - // Check if player is in a match + // Match Match match = MatchManager.getInstance().getLiveMatchByPlayer(player); if (match != null) { + int duration = match.getLadder().getFireworkRocketCooldown(); - if (duration <= 0) { - return; - } + if (duration <= 0) return; if (!match.getCurrentRound().getRoundStatus().equals(RoundStatus.LIVE)) { - e.setCancelled(true); return; } @@ -96,7 +58,7 @@ public void onFireworkRocketUse(PlayerInteractEvent e) { player, match.getMatchPlayers().get(player), duration, - e, + null, "MATCH.COOLDOWN.FIREWORK-ROCKET-COOLDOWN" ); } From debc39953c95bdfce03250a062e1d3e29da02fae Mon Sep 17 00:00:00 2001 From: Nandor Dukat Date: Mon, 9 Mar 2026 09:56:41 +0100 Subject: [PATCH 20/31] fireball fight velocity fix during jump --- .../practice_1_8_8/interfaces/PlayerUtil.java | 37 ++++++++++++----- .../interfaces/PlayerUtil.java | 40 +++++++++++++------ 2 files changed, 55 insertions(+), 22 deletions(-) diff --git a/spigot_1_8_8/src/main/java/dev/nandi0813/practice_1_8_8/interfaces/PlayerUtil.java b/spigot_1_8_8/src/main/java/dev/nandi0813/practice_1_8_8/interfaces/PlayerUtil.java index 383fdf3f..bdb337b5 100644 --- a/spigot_1_8_8/src/main/java/dev/nandi0813/practice_1_8_8/interfaces/PlayerUtil.java +++ b/spigot_1_8_8/src/main/java/dev/nandi0813/practice_1_8_8/interfaces/PlayerUtil.java @@ -124,11 +124,10 @@ public void applyFireballKnockback(final Player player, final Fireball fireball) double dx = playerLoc.getX() - fireballLoc.getX(); double dz = playerLoc.getZ() - fireballLoc.getZ(); double horizontalDistance = Math.sqrt(dx * dx + dz * dz); - double fullDistance = playerLoc.distance(fireballLoc); - // Blend horizontal and full 3D distance so that jumping still weakens - // the knockback, but not as drastically as using full 3D distance alone. - double effectiveDistance = (horizontalDistance * 0.7) + (fullDistance * 0.3); + // Use only horizontal distance for factor calculation so that jumping + // (which only increases vertical separation) does not reduce the knockback strength. + double effectiveDistance = horizontalDistance; double safeDistance = 0.6; double factor = 1.0; @@ -141,17 +140,35 @@ public void applyFireballKnockback(final Player player, final Fireball fireball) if (factor < 0) factor = 0; if (factor > 1) factor = 1; - Vector direction = playerLoc.toVector().subtract(fireballLoc.toVector()); - if (direction.lengthSquared() == 0) { - direction = new Vector(0, 0.1, 0); + // Compute horizontal direction separately so the vertical component + // of the direction vector doesn't steal from horizontal velocity. + double horizontalLen = Math.sqrt(dx * dx + dz * dz); + double horizDirX; + double horizDirZ; + + if (horizontalLen < 0.001) { + // Fireball is almost directly below – pick the player's facing direction + Vector facing = playerLoc.getDirection(); + double facingLen = Math.sqrt(facing.getX() * facing.getX() + facing.getZ() * facing.getZ()); + if (facingLen < 0.001) { + horizDirX = 0; + horizDirZ = 0; + } else { + horizDirX = facing.getX() / facingLen; + horizDirZ = facing.getZ() / facingLen; + } } else { - direction.normalize(); + horizDirX = dx / horizontalLen; + horizDirZ = dz / horizontalLen; } + // Apply a slight reduction when airborne so it's weaker than grounded, but not drastically + double airMultiplier = player.isOnGround() ? 1.0 : 0.8; + Vector velocity = new Vector( - direction.getX() * FB_VELOCITY_HORIZONTAL_MULTIPLICATIVE * factor, + horizDirX * FB_VELOCITY_HORIZONTAL_MULTIPLICATIVE * factor * airMultiplier, FB_VELOCITY_VERTICAL_MULTIPLICATIVE * factor, - direction.getZ() * FB_VELOCITY_HORIZONTAL_MULTIPLICATIVE * factor + horizDirZ * FB_VELOCITY_HORIZONTAL_MULTIPLICATIVE * factor * airMultiplier ); player.setVelocity(velocity); diff --git a/spigot_modern/src/main/java/dev/nandi0813/practice_modern/interfaces/PlayerUtil.java b/spigot_modern/src/main/java/dev/nandi0813/practice_modern/interfaces/PlayerUtil.java index b494cb08..6b28e557 100644 --- a/spigot_modern/src/main/java/dev/nandi0813/practice_modern/interfaces/PlayerUtil.java +++ b/spigot_modern/src/main/java/dev/nandi0813/practice_modern/interfaces/PlayerUtil.java @@ -117,13 +117,12 @@ public void applyFireballKnockback(final Player player, final Fireball fireball) double dx = playerLoc.getX() - fireballLoc.getX(); double dz = playerLoc.getZ() - fireballLoc.getZ(); double horizontalDistance = Math.sqrt(dx * dx + dz * dz); - double fullDistance = playerLoc.distance(fireballLoc); - // Blend horizontal and full 3D distance so that jumping still weakens - // the knockback, but not as drastically as using full 3D distance alone. - double effectiveDistance = (horizontalDistance * 0.7) + (fullDistance * 0.3); + // Use only horizontal distance for factor calculation so that jumping + // (which only increases vertical separation) does not reduce the knockback strength. + double effectiveDistance = horizontalDistance; - double safeDistance = 0.6; + double safeDistance = 0.8; double factor = 1.0; if (effectiveDistance > safeDistance) { @@ -138,18 +137,35 @@ public void applyFireballKnockback(final Player player, final Fireball fireball) if (factor <= 0.1) return; if (factor > 1) factor = 1; - Vector direction = playerLoc.toVector().subtract(fireballLoc.toVector()); - - if (direction.lengthSquared() == 0) { - direction = new Vector(0, 0.1, 0); + // Compute horizontal direction separately so the vertical component + // of the direction vector doesn't steal from horizontal velocity. + double horizontalLen = Math.sqrt(dx * dx + dz * dz); + double horizDirX; + double horizDirZ; + + if (horizontalLen < 0.001) { + // Fireball is almost directly below – pick the player's facing direction + Vector facing = playerLoc.getDirection(); + double facingLen = Math.sqrt(facing.getX() * facing.getX() + facing.getZ() * facing.getZ()); + if (facingLen < 0.001) { + horizDirX = 0; + horizDirZ = 0; + } else { + horizDirX = facing.getX() / facingLen; + horizDirZ = facing.getZ() / facingLen; + } } else { - direction.normalize(); + horizDirX = dx / horizontalLen; + horizDirZ = dz / horizontalLen; } + // Apply a slight reduction when airborne so it's weaker than grounded, but not drastically + double airMultiplier = player.isOnGround() ? 1.0 : 0.8; + Vector velocity = new Vector( - direction.getX() * FB_VELOCITY_HORIZONTAL_MULTIPLICATIVE * factor, + horizDirX * FB_VELOCITY_HORIZONTAL_MULTIPLICATIVE * factor * airMultiplier, FB_VELOCITY_VERTICAL_MULTIPLICATIVE * factor, - direction.getZ() * FB_VELOCITY_HORIZONTAL_MULTIPLICATIVE * factor + horizDirZ * FB_VELOCITY_HORIZONTAL_MULTIPLICATIVE * factor * airMultiplier ); player.setVelocity(velocity); From 21cef130859f1d4201031c64e5f6f28bcbd9fb9a Mon Sep 17 00:00:00 2001 From: MISHA <208148594+lokspel@users.noreply.github.com> Date: Mon, 9 Mar 2026 12:56:58 +0400 Subject: [PATCH 21/31] fix: update CopyGui synchronously to prevent duplicate icons (#299) * fix: prevent false firework cooldown and optimize by using EntitySpawnEvent * chore: remove unused imports * fix: include FFA arenas in enabled arena count * fix: update CopyGui synchronously to prevent duplicate icons --- .../manager/gui/setup/arena/arenasettings/normal/CopyGui.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/dev/nandi0813/practice/manager/gui/setup/arena/arenasettings/normal/CopyGui.java b/core/src/main/java/dev/nandi0813/practice/manager/gui/setup/arena/arenasettings/normal/CopyGui.java index 2ac60c86..c62018fb 100644 --- a/core/src/main/java/dev/nandi0813/practice/manager/gui/setup/arena/arenasettings/normal/CopyGui.java +++ b/core/src/main/java/dev/nandi0813/practice/manager/gui/setup/arena/arenasettings/normal/CopyGui.java @@ -50,7 +50,7 @@ public void build() { @Override public void update() { - Bukkit.getScheduler().runTaskAsynchronously(ZonePractice.getInstance(), () -> + Bukkit.getScheduler().runTask(ZonePractice.getInstance(), () -> { icons.clear(); List copies = arena.getCopies(); From ec924a47e3da42a9fb6de63cf2c3be53ca12ea32 Mon Sep 17 00:00:00 2001 From: Nandor Dukat Date: Mon, 9 Mar 2026 10:10:00 +0100 Subject: [PATCH 22/31] fixed chunk loading and arena copy action bar coloring --- .../arena/arenas/interfaces/BasicArena.java | 16 +++++++----- .../module/interfaces/ArenaCopyUtil.java | 4 +-- .../dev/nandi0813/practice/util/Cuboid.java | 10 +++---- .../practice_1_8_8/interfaces/ArenaUtil.java | 26 +++++++++++++------ .../practice_modern/interfaces/ArenaUtil.java | 20 ++++++++++---- .../listener/ArenaListener.java | 12 +++------ 6 files changed, 54 insertions(+), 34 deletions(-) diff --git a/core/src/main/java/dev/nandi0813/practice/manager/arena/arenas/interfaces/BasicArena.java b/core/src/main/java/dev/nandi0813/practice/manager/arena/arenas/interfaces/BasicArena.java index 4ec481ee..55a49464 100644 --- a/core/src/main/java/dev/nandi0813/practice/manager/arena/arenas/interfaces/BasicArena.java +++ b/core/src/main/java/dev/nandi0813/practice/manager/arena/arenas/interfaces/BasicArena.java @@ -95,12 +95,16 @@ public boolean teleport(Player player) { public void loadChunks() { if (this.cuboid != null) { - Bukkit.getScheduler().runTask(ZonePractice.getInstance(), () -> - ClassImport.getClasses().getArenaUtil().loadArenaChunks(this)); - - if (ArenaManager.LOAD_CHUNKS) { - ArenaManager.LOADED_CHUNKS.addAll(this.cuboid.getChunks()); - } + Bukkit.getScheduler().runTask(ZonePractice.getInstance(), () -> { + ClassImport.getClasses().getArenaUtil().loadArenaChunks(this); + + if (ArenaManager.LOAD_CHUNKS) { + // Collect only chunks that are already loaded — this avoids + // synchronous chunk loading on the main thread which caused + // server hangs of 10-15+ seconds. + ArenaManager.LOADED_CHUNKS.addAll(this.cuboid.getChunks()); + } + }); } } diff --git a/core/src/main/java/dev/nandi0813/practice/module/interfaces/ArenaCopyUtil.java b/core/src/main/java/dev/nandi0813/practice/module/interfaces/ArenaCopyUtil.java index c2c0f049..95f32c89 100644 --- a/core/src/main/java/dev/nandi0813/practice/module/interfaces/ArenaCopyUtil.java +++ b/core/src/main/java/dev/nandi0813/practice/module/interfaces/ArenaCopyUtil.java @@ -234,8 +234,8 @@ public void run() { if (finalActionBar != null) { finalActionBar.setMessage(LanguageManager.getString("ARENA.ACTION-BAR-MSG") .replace("%arena%", Common.serializeNormalToMMString(arenaCopy.getMainArena().getDisplayName())) - .replace("%progress_bar%", StatisticUtil.getProgressBar(progress)) - .replace("%progress_percent%", String.valueOf(progress))); + .replace("%progress_bar%", Common.serializeNormalToMMString(StatisticUtil.getProgressBar(progress))) + .replace("%progress_percent%", Common.serializeNormalToMMString(String.valueOf(progress)))); } Location newLoc = new Location(copyWorld, originLoc.getX(), originLoc.getY(), originLoc.getZ()).clone().subtract(reference).add(newLocation); diff --git a/core/src/main/java/dev/nandi0813/practice/util/Cuboid.java b/core/src/main/java/dev/nandi0813/practice/util/Cuboid.java index 28f7e9e6..08a67eb9 100644 --- a/core/src/main/java/dev/nandi0813/practice/util/Cuboid.java +++ b/core/src/main/java/dev/nandi0813/practice/util/Cuboid.java @@ -554,12 +554,12 @@ public List getChunks() { for (int x = x1; x <= x2; x++) { for (int z = z1; z <= z2; z++) { - - if (!w.isChunkLoaded(x, z)) { - w.loadChunk(x, z); + // Only collect already-loaded chunks. Chunk loading is handled + // separately by ArenaUtil.loadArenaChunks() to avoid blocking + // the main server thread. + if (w.isChunkLoaded(x, z)) { + res.add(w.getChunkAt(x, z)); } - - res.add(w.getChunkAt(x, z)); } } diff --git a/spigot_1_8_8/src/main/java/dev/nandi0813/practice_1_8_8/interfaces/ArenaUtil.java b/spigot_1_8_8/src/main/java/dev/nandi0813/practice_1_8_8/interfaces/ArenaUtil.java index dae82d27..395f77da 100644 --- a/spigot_1_8_8/src/main/java/dev/nandi0813/practice_1_8_8/interfaces/ArenaUtil.java +++ b/spigot_1_8_8/src/main/java/dev/nandi0813/practice_1_8_8/interfaces/ArenaUtil.java @@ -4,7 +4,6 @@ import dev.nandi0813.practice.manager.ladder.abstraction.Ladder; import dev.nandi0813.practice.manager.ladder.abstraction.normal.NormalLadder; import dev.nandi0813.practice.util.BasicItem; -import org.bukkit.Chunk; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.entity.ArmorStand; @@ -84,14 +83,25 @@ public void loadArenaChunks(BasicArena arena) { // 1.8.8 has no async chunk-load API — stagger each chunk one tick apart so // the server never freezes trying to load all chunks in a single tick. org.bukkit.plugin.Plugin plugin = dev.nandi0813.practice.ZonePractice.getInstance(); + org.bukkit.World world = arena.getCuboid().getWorld(); + if (world == null) return; + + int minCX = arena.getCuboid().getLowerX() >> 4; + int maxCX = arena.getCuboid().getUpperX() >> 4; + int minCZ = arena.getCuboid().getLowerZ() >> 4; + int maxCZ = arena.getCuboid().getUpperZ() >> 4; + long delay = 0; - for (Chunk chunk : arena.getCuboid().getChunks()) { - final Chunk c = chunk; - org.bukkit.Bukkit.getScheduler().runTaskLater(plugin, () -> { - if (!c.isLoaded()) { - c.load(true); - } - }, delay++); + for (int cx = minCX; cx <= maxCX; cx++) { + for (int cz = minCZ; cz <= maxCZ; cz++) { + final int x = cx; + final int z = cz; + org.bukkit.Bukkit.getScheduler().runTaskLater(plugin, () -> { + if (!world.isChunkLoaded(x, z)) { + world.loadChunk(x, z); + } + }, delay++); + } } } diff --git a/spigot_modern/src/main/java/dev/nandi0813/practice_modern/interfaces/ArenaUtil.java b/spigot_modern/src/main/java/dev/nandi0813/practice_modern/interfaces/ArenaUtil.java index 54a6b917..f1d629c8 100644 --- a/spigot_modern/src/main/java/dev/nandi0813/practice_modern/interfaces/ArenaUtil.java +++ b/spigot_modern/src/main/java/dev/nandi0813/practice_modern/interfaces/ArenaUtil.java @@ -4,7 +4,6 @@ import dev.nandi0813.practice.manager.ladder.abstraction.Ladder; import dev.nandi0813.practice.manager.ladder.abstraction.normal.NormalLadder; import dev.nandi0813.practice.util.BasicItem; -import org.bukkit.Chunk; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.entity.ArmorStand; @@ -71,12 +70,23 @@ public boolean requiresSupport(Block block) { @Override public void loadArenaChunks(BasicArena arena) { if (arena.getCuboid() == null) return; - // getChunkAtAsync schedules real async chunk loading on the I/O thread — - // no main-thread stall and no need to call chunk.load() manually. org.bukkit.World world = arena.getCuboid().getWorld(); if (world == null) return; - for (Chunk chunk : arena.getCuboid().getChunks()) { - world.getChunkAtAsync(chunk.getX(), chunk.getZ()); + + // Calculate chunk coordinate range directly from the cuboid bounds + // instead of calling getChunks() which synchronously loads all chunks. + int minCX = arena.getCuboid().getLowerX() >> 4; + int maxCX = arena.getCuboid().getUpperX() >> 4; + int minCZ = arena.getCuboid().getLowerZ() >> 4; + int maxCZ = arena.getCuboid().getUpperZ() >> 4; + + org.bukkit.plugin.Plugin plugin = dev.nandi0813.practice.ZonePractice.getInstance(); + for (int cx = minCX; cx <= maxCX; cx++) { + for (int cz = minCZ; cz <= maxCZ; cz++) { + // addPluginChunkTicket loads the chunk asynchronously if needed + // and prevents it from being unloaded — no main-thread stall. + world.addPluginChunkTicket(cx, cz, plugin); + } } } diff --git a/spigot_modern/src/main/java/dev/nandi0813/practice_modern/listener/ArenaListener.java b/spigot_modern/src/main/java/dev/nandi0813/practice_modern/listener/ArenaListener.java index 6355a45d..797a489a 100644 --- a/spigot_modern/src/main/java/dev/nandi0813/practice_modern/listener/ArenaListener.java +++ b/spigot_modern/src/main/java/dev/nandi0813/practice_modern/listener/ArenaListener.java @@ -3,8 +3,6 @@ import dev.nandi0813.practice.ZonePractice; import dev.nandi0813.practice.manager.arena.util.ArenaWorldUtil; import dev.nandi0813.practice.manager.server.ServerManager; -import org.bukkit.Bukkit; -import org.bukkit.World; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.world.ChunkUnloadEvent; @@ -19,12 +17,10 @@ public class ArenaListener implements Listener { public void onChunkUnload(ChunkUnloadEvent e) { if (LOAD_CHUNKS) { if (LOADED_CHUNKS.contains(e.getChunk())) { - final World world = e.getChunk().getWorld(); - final int cx = e.getChunk().getX(); - final int cz = e.getChunk().getZ(); - Bukkit.getScheduler().runTask(ZonePractice.getInstance(), () -> - world.getChunkAtAsync(cx, cz) - ); + // Use addPluginChunkTicket to force-keep the chunk loaded. + // This is safe from recursion (unlike getChunkAtAsync which can + // trigger chunk scheduling → more unloads → StackOverflowError). + e.getChunk().addPluginChunkTicket(ZonePractice.getInstance()); } } } From 236d9c62e4ca9aed9c9ba38f2303ad0549cbae1d Mon Sep 17 00:00:00 2001 From: Nandor Dukat Date: Mon, 9 Mar 2026 10:24:22 +0100 Subject: [PATCH 23/31] fixed event GUIs to show every icon type correctly --- .../practice/manager/fight/event/interfaces/Event.java | 2 +- .../main/java/dev/nandi0813/practice/manager/gui/GUIItem.java | 2 ++ .../dev/nandi0813/practice/manager/gui/guis/EventHostGui.java | 3 +-- .../practice/manager/gui/setup/event/EventSummaryGui.java | 3 +-- .../manager/gui/setup/event/eventsettings/EventMainGui.java | 3 +-- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/dev/nandi0813/practice/manager/fight/event/interfaces/Event.java b/core/src/main/java/dev/nandi0813/practice/manager/fight/event/interfaces/Event.java index 68810711..44494cc7 100644 --- a/core/src/main/java/dev/nandi0813/practice/manager/fight/event/interfaces/Event.java +++ b/core/src/main/java/dev/nandi0813/practice/manager/fight/event/interfaces/Event.java @@ -279,7 +279,7 @@ public boolean canDisplay() { @Override public GUIItem getSpectatorMenuItem() { return GUIFile.getGuiItem("GUIS.SPECTATOR-MENU.ICONS.EVENT-ICON") - .setMaterial(eventData.getIcon().getMaterial()).setDamage(eventData.getIcon().getDamage()).replace("%event_type%", type.getName()) + .setBaseItem(eventData.getIcon().get()).replace("%event_type%", type.getName()) .replace("%event_duration%", this.getDurationRunnable().getFormattedTime()) .replace("%players%", String.valueOf(players.size())).replace("%spectators%", String.valueOf(spectators.size())); diff --git a/core/src/main/java/dev/nandi0813/practice/manager/gui/GUIItem.java b/core/src/main/java/dev/nandi0813/practice/manager/gui/GUIItem.java index 01d6c48f..3e7eda19 100644 --- a/core/src/main/java/dev/nandi0813/practice/manager/gui/GUIItem.java +++ b/core/src/main/java/dev/nandi0813/practice/manager/gui/GUIItem.java @@ -98,6 +98,8 @@ public GUIItem(ItemStack itemStack) { this.amount = itemStack.getAmount(); this.damage = itemStack.getDurability(); this.material = itemStack.getType(); + // Preserve the full ItemStack so special meta (e.g. PotionMeta) is not lost + this.baseItemStack = itemStack.clone(); } public GUIItem setGlowing(boolean glowing) { diff --git a/core/src/main/java/dev/nandi0813/practice/manager/gui/guis/EventHostGui.java b/core/src/main/java/dev/nandi0813/practice/manager/gui/guis/EventHostGui.java index 5a25b347..7b7a9fa9 100644 --- a/core/src/main/java/dev/nandi0813/practice/manager/gui/guis/EventHostGui.java +++ b/core/src/main/java/dev/nandi0813/practice/manager/gui/guis/EventHostGui.java @@ -9,7 +9,6 @@ import dev.nandi0813.practice.manager.gui.GUIType; import dev.nandi0813.practice.manager.profile.Profile; import dev.nandi0813.practice.manager.profile.ProfileManager; -import dev.nandi0813.practice.module.interfaces.ItemCreateUtil; import dev.nandi0813.practice.util.Common; import dev.nandi0813.practice.util.InventoryUtil; import lombok.Getter; @@ -46,7 +45,7 @@ public void update() { int slot = gui.get(1).firstEmpty(); eventSlots.put(slot, eventType); - gui.get(1).setItem(slot, ItemCreateUtil.hideItemFlags(EventManager.getInstance().getEventData().get(eventType).getIcon().get())); + gui.get(1).setItem(slot, EventManager.getInstance().getEventData().get(eventType).getIcon().get()); } } } diff --git a/core/src/main/java/dev/nandi0813/practice/manager/gui/setup/event/EventSummaryGui.java b/core/src/main/java/dev/nandi0813/practice/manager/gui/setup/event/EventSummaryGui.java index 790754d8..6224597f 100644 --- a/core/src/main/java/dev/nandi0813/practice/manager/gui/setup/event/EventSummaryGui.java +++ b/core/src/main/java/dev/nandi0813/practice/manager/gui/setup/event/EventSummaryGui.java @@ -46,8 +46,7 @@ public void update() { .replace("%state%", eventData.isEnabled() ? GUIFile.getString("GUIS.SETUP.EVENT.EVENT-MANAGER.ICONS.EVENT-ICON.STATUS-NAMES.ENABLED") : GUIFile.getString("GUIS.SETUP.EVENT.EVENT-MANAGER.ICONS.EVENT-ICON.STATUS-NAMES.DISABLED")) - .setMaterial(eventData.getIcon().getMaterial()) - .setDamage(eventData.getIcon().getDamage()) + .setBaseItem(eventData.getIcon().get()) .get(); int slot = eventData.getType().getGuiSlot(); diff --git a/core/src/main/java/dev/nandi0813/practice/manager/gui/setup/event/eventsettings/EventMainGui.java b/core/src/main/java/dev/nandi0813/practice/manager/gui/setup/event/eventsettings/EventMainGui.java index 7a4c6436..1115950d 100644 --- a/core/src/main/java/dev/nandi0813/practice/manager/gui/setup/event/eventsettings/EventMainGui.java +++ b/core/src/main/java/dev/nandi0813/practice/manager/gui/setup/event/eventsettings/EventMainGui.java @@ -55,8 +55,7 @@ public void update() { inventory.setItem(10, GUIFile.getGuiItem("GUIS.SETUP.EVENT.EVENT-MAIN.ICONS.EVENT-NAME") .replace("%eventName%", eventData.getType().getName()) - .setMaterial(eventData.getIcon().getMaterial()) - .setDamage(eventData.getIcon().getDamage()) + .setBaseItem(eventData.getIcon().get()) .get()); inventory.setItem(11, eventData.isEnabled() ? From cc3e20313872ce65c67ee8f26417f6520680c83d Mon Sep 17 00:00:00 2001 From: Nandor Dukat Date: Mon, 9 Mar 2026 10:35:44 +0100 Subject: [PATCH 24/31] changed fireball fight velocity settings in config --- core/src/main/resources/modern/config.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/main/resources/modern/config.yml b/core/src/main/resources/modern/config.yml index eeaf9797..e129755c 100644 --- a/core/src/main/resources/modern/config.yml +++ b/core/src/main/resources/modern/config.yml @@ -205,15 +205,15 @@ MATCH-SETTINGS: FIREWORK-ROCKET: COOLDOWN: 1 # Cooldown in seconds for using firework rockets with elytra. Default is 1 second. FIREBALL-FIGHT: # These are multiplier values, so please test what is optimal for you and don't change them too much at once or the difference will multiply. - FIREBALL-SPEED: 1.2 - FIREBALL-YIELD: 1.6 + FIREBALL-SPEED: 1.0 + FIREBALL-YIELD: 1.8 EXPLOSION: TNT: - HORIZONTAL: 1.3 - VERTICAL: 1.2 - FIREBALL: HORIZONTAL: 1.4 VERTICAL: 1.3 + FIREBALL: + HORIZONTAL: 1.6 + VERTICAL: 1.4 BOXING: CUSTOM-ATTACK-COOLDOWN: ENABLED: true From 0b2d7e1cd28bedf42e71ecf10898d33b857d36be Mon Sep 17 00:00:00 2001 From: Nandor Dukat Date: Mon, 9 Mar 2026 10:42:04 +0100 Subject: [PATCH 25/31] implemented new fireball fight feature that lets players destroy placed blocks by fireballs --- .../Items/FireballBlockDestroyItem.java | 35 +++++++++++++++++++ .../laddersettings/Settings/SettingType.java | 1 + .../laddersettings/Settings/SettingsGui.java | 3 ++ .../manager/ladder/enums/LadderType.java | 1 + .../manager/ladder/type/FireballFight.java | 31 ++++++++++++++-- core/src/main/resources/1.8.8/guis.yml | 22 +++++++++++- core/src/main/resources/modern/guis.yml | 22 +++++++++++- 7 files changed, 111 insertions(+), 4 deletions(-) create mode 100644 core/src/main/java/dev/nandi0813/practice/manager/gui/setup/ladder/laddersettings/Settings/Items/FireballBlockDestroyItem.java diff --git a/core/src/main/java/dev/nandi0813/practice/manager/gui/setup/ladder/laddersettings/Settings/Items/FireballBlockDestroyItem.java b/core/src/main/java/dev/nandi0813/practice/manager/gui/setup/ladder/laddersettings/Settings/Items/FireballBlockDestroyItem.java new file mode 100644 index 00000000..9355943b --- /dev/null +++ b/core/src/main/java/dev/nandi0813/practice/manager/gui/setup/ladder/laddersettings/Settings/Items/FireballBlockDestroyItem.java @@ -0,0 +1,35 @@ +package dev.nandi0813.practice.manager.gui.setup.ladder.laddersettings.Settings.Items; + +import dev.nandi0813.practice.manager.backend.GUIFile; +import dev.nandi0813.practice.manager.gui.setup.ladder.laddersettings.Settings.SettingItem; +import dev.nandi0813.practice.manager.gui.setup.ladder.laddersettings.Settings.SettingType; +import dev.nandi0813.practice.manager.gui.setup.ladder.laddersettings.Settings.SettingsGui; +import dev.nandi0813.practice.manager.ladder.abstraction.normal.NormalLadder; +import dev.nandi0813.practice.manager.ladder.type.FireballFight; +import org.bukkit.event.inventory.InventoryClickEvent; + +public class FireballBlockDestroyItem extends SettingItem { + + private final FireballFight fireballFight; + + public FireballBlockDestroyItem(SettingsGui settingsGui, NormalLadder ladder) { + super(settingsGui, SettingType.FIREBALL_BLOCK_DESTROY, ladder); + this.fireballFight = (FireballFight) ladder; + } + + @Override + public void updateItemStack() { + if (fireballFight.isFireballBlockDestroy()) { + this.guiItem = GUIFile.getGuiItem("GUIS.SETUP.LADDER.SETTINGS.ICONS.FIREBALL-BLOCK-DESTROY.ENABLED").setGlowing(true); + } else { + this.guiItem = GUIFile.getGuiItem("GUIS.SETUP.LADDER.SETTINGS.ICONS.FIREBALL-BLOCK-DESTROY.DISABLED"); + } + } + + @Override + public void clickEvent(InventoryClickEvent e) { + fireballFight.setFireballBlockDestroy(!fireballFight.isFireballBlockDestroy()); + this.build(true); + } +} + diff --git a/core/src/main/java/dev/nandi0813/practice/manager/gui/setup/ladder/laddersettings/Settings/SettingType.java b/core/src/main/java/dev/nandi0813/practice/manager/gui/setup/ladder/laddersettings/Settings/SettingType.java index b640beb7..cad81ba8 100644 --- a/core/src/main/java/dev/nandi0813/practice/manager/gui/setup/ladder/laddersettings/Settings/SettingType.java +++ b/core/src/main/java/dev/nandi0813/practice/manager/gui/setup/ladder/laddersettings/Settings/SettingType.java @@ -18,6 +18,7 @@ public enum SettingType { RESPAWN_TIME, BOXING_HITS, FIREBALL_COOLDOWN, + FIREBALL_BLOCK_DESTROY, SKYWARS_LOOT, TEMP_BUILD_DELAY, BUILD, diff --git a/core/src/main/java/dev/nandi0813/practice/manager/gui/setup/ladder/laddersettings/Settings/SettingsGui.java b/core/src/main/java/dev/nandi0813/practice/manager/gui/setup/ladder/laddersettings/Settings/SettingsGui.java index a7a55793..cbc1e8c3 100644 --- a/core/src/main/java/dev/nandi0813/practice/manager/gui/setup/ladder/laddersettings/Settings/SettingsGui.java +++ b/core/src/main/java/dev/nandi0813/practice/manager/gui/setup/ladder/laddersettings/Settings/SettingsGui.java @@ -191,6 +191,9 @@ private void addItems() { if (settingTypes.contains(SettingType.FIREBALL_COOLDOWN)) settingItems.add(new FireballCooldownItem(this, ladder)); + if (settingTypes.contains(SettingType.FIREBALL_BLOCK_DESTROY)) + settingItems.add(new FireballBlockDestroyItem(this, ladder)); + if (settingTypes.contains(SettingType.SKYWARS_LOOT)) settingItems.add(new SkywarsLootItem(this, (SkyWars) ladder)); diff --git a/core/src/main/java/dev/nandi0813/practice/manager/ladder/enums/LadderType.java b/core/src/main/java/dev/nandi0813/practice/manager/ladder/enums/LadderType.java index 03c5172e..3634c5e0 100644 --- a/core/src/main/java/dev/nandi0813/practice/manager/ladder/enums/LadderType.java +++ b/core/src/main/java/dev/nandi0813/practice/manager/ladder/enums/LadderType.java @@ -147,6 +147,7 @@ public enum LadderType { .withPearlSettings() .withBuildSettings() .withSetting(SettingType.FIREBALL_COOLDOWN) + .withSetting(SettingType.FIREBALL_BLOCK_DESTROY) ), BRIDGES(LadderTypeConfig.builder( diff --git a/core/src/main/java/dev/nandi0813/practice/manager/ladder/type/FireballFight.java b/core/src/main/java/dev/nandi0813/practice/manager/ladder/type/FireballFight.java index 42b79600..60ac9cfa 100644 --- a/core/src/main/java/dev/nandi0813/practice/manager/ladder/type/FireballFight.java +++ b/core/src/main/java/dev/nandi0813/practice/manager/ladder/type/FireballFight.java @@ -47,7 +47,9 @@ public class FireballFight extends BedFight implements CustomConfig, LadderHandle { private double fireballCooldown; + private boolean fireballBlockDestroy; private static final String FIREBALL_COOLDOWN_PATH = "fireball-cooldown"; + private static final String FIREBALL_BLOCK_DESTROY_PATH = "fireball-block-destroy"; public FireballFight(String name, LadderType type) { super(name, type); @@ -61,6 +63,7 @@ public String getRespawnLanguagePath() { @Override public void setCustomConfig(YamlConfiguration config) { config.set(FIREBALL_COOLDOWN_PATH, fireballCooldown); + config.set(FIREBALL_BLOCK_DESTROY_PATH, fireballBlockDestroy); } @Override @@ -71,6 +74,8 @@ public void getCustomConfig(YamlConfiguration config) { this.fireballCooldown = 1.5; } else this.fireballCooldown = 1.5; + + this.fireballBlockDestroy = config.getBoolean(FIREBALL_BLOCK_DESTROY_PATH, false); } @Override @@ -263,8 +268,30 @@ private static void onEntityExplode(@NotNull EntityExplodeEvent e) { Entity entity = e.getEntity(); if (entity.hasMetadata(FIREBALL_FIGHT_FIREBALL)) { - // No block destruction from fireball — but don't cancel so the sound plays naturally. - e.blockList().clear(); + MetadataValue fbMv = BlockUtil.getMetadata(entity, FIREBALL_FIGHT_FIREBALL); + if (ListenerUtil.checkMetaData(fbMv)) { + e.blockList().clear(); + return; + } + + if (!(fbMv.value() instanceof Match match)) { + e.blockList().clear(); + return; + } + + if (!(match.getLadder() instanceof FireballFight fireballFight) || !fireballFight.isFireballBlockDestroy()) { + // Setting disabled — no block destruction from fireball, but don't cancel so the sound plays. + e.blockList().clear(); + return; + } + + if (!match.getCurrentRound().getRoundStatus().equals(RoundStatus.LIVE)) { + e.blockList().clear(); + return; + } + + // Setting enabled — only destroy player-placed blocks, not arena blocks. + e.blockList().removeIf(block -> !block.hasMetadata(PLACED_IN_FIGHT)); } else if (entity.hasMetadata(FIREBALL_FIGHT_TNT) && entity.hasMetadata(FIREBALL_FIGHT_TNT_SHOOTER)) { MetadataValue mv = BlockUtil.getMetadata(entity, FIREBALL_FIGHT_TNT); if (ListenerUtil.checkMetaData(mv)) return; diff --git a/core/src/main/resources/1.8.8/guis.yml b/core/src/main/resources/1.8.8/guis.yml index df6040c0..f27a1582 100644 --- a/core/src/main/resources/1.8.8/guis.yml +++ b/core/src/main/resources/1.8.8/guis.yml @@ -1,4 +1,4 @@ -VERSION: 11 +VERSION: 12 GENERAL-FILLER-ITEM: NAME: " " @@ -2130,6 +2130,26 @@ GUIS: - "&7placed during the match." - "" - "&e&lClick here &7to &aenable&7." + FIREBALL-BLOCK-DESTROY: + ENABLED: + NAME: "&7Fireball Block Destroy: &aEnabled" + MATERIAL: FIREBALL + LORE: + - "" + - "&7Fireball explosions will destroy" + - "&7blocks placed by players." + - "&8(Arena blocks are not affected)" + - "" + - "&e&lClick here &7to &cdisable&7." + DISABLED: + NAME: "&7Fireball Block Destroy: &cDisabled" + MATERIAL: FIREBALL + LORE: + - "" + - "&7Fireball explosions will not" + - "&7destroy any blocks." + - "" + - "&e&lClick here &7to &aenable&7." SPLEEF-SNOWBALL-MODE: ENABLED: NAME: "&7Snowball Mode: &aEnabled" diff --git a/core/src/main/resources/modern/guis.yml b/core/src/main/resources/modern/guis.yml index 6ce69d65..47c7852d 100644 --- a/core/src/main/resources/modern/guis.yml +++ b/core/src/main/resources/modern/guis.yml @@ -1,4 +1,4 @@ -VERSION: 11 +VERSION: 12 GENERAL-FILLER-ITEM: NAME: " " @@ -2075,6 +2075,26 @@ GUIS: - "&7placed during the match." - "" - "&e&lClick here &7to &aenable&7." + FIREBALL-BLOCK-DESTROY: + ENABLED: + NAME: "&7Fireball Block Destroy: &aEnabled" + MATERIAL: FIRE_CHARGE + LORE: + - "" + - "&7Fireball explosions will destroy" + - "&7blocks placed by players." + - "&8(Arena blocks are not affected)" + - "" + - "&e&lClick here &7to &cdisable&7." + DISABLED: + NAME: "&7Fireball Block Destroy: &cDisabled" + MATERIAL: FIRE_CHARGE + LORE: + - "" + - "&7Fireball explosions will not" + - "&7destroy any blocks." + - "" + - "&e&lClick here &7to &aenable&7." SPLEEF-SNOWBALL-MODE: ENABLED: NAME: "&7Snowball Mode: &aEnabled" From 6380ca1236a9b6dd0b719681abaffbef7e53ab8f Mon Sep 17 00:00:00 2001 From: Nandor Dukat Date: Mon, 9 Mar 2026 12:50:36 +0100 Subject: [PATCH 26/31] fixed NPE in PlayerItemCooldownEvent handling --- .../interfaces/ModernItemCooldownHandler.java | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/spigot_modern/src/main/java/dev/nandi0813/practice_modern/interfaces/ModernItemCooldownHandler.java b/spigot_modern/src/main/java/dev/nandi0813/practice_modern/interfaces/ModernItemCooldownHandler.java index b5955c5d..4d34ecda 100644 --- a/spigot_modern/src/main/java/dev/nandi0813/practice_modern/interfaces/ModernItemCooldownHandler.java +++ b/spigot_modern/src/main/java/dev/nandi0813/practice_modern/interfaces/ModernItemCooldownHandler.java @@ -32,7 +32,9 @@ public class ModernItemCooldownHandler implements ItemCooldownHandler { public void handleEnderPearlFFA(Player player, FightPlayer fightPlayer, int duration, boolean expBar, Cancellable event, String langKey) { if (player.hasCooldown(Material.ENDER_PEARL)) { - event.setCancelled(true); + if (event != null) { + event.setCancelled(true); + } } // If no cooldown: let the throw proceed; PlayerItemCooldownEvent will set the correct duration. } @@ -41,7 +43,9 @@ public void handleEnderPearlFFA(Player player, FightPlayer fightPlayer, int dura public void handleEnderPearlMatch(Player player, FightPlayer fightPlayer, int duration, boolean expBar, Cancellable event, String langKey) { if (player.hasCooldown(Material.ENDER_PEARL)) { - event.setCancelled(true); + if (event != null) { + event.setCancelled(true); + } } // If no cooldown: let the throw proceed; PlayerItemCooldownEvent will set the correct duration. } @@ -53,7 +57,9 @@ public void handleEnderPearlMatch(Player player, FightPlayer fightPlayer, int du @Override public void handleGoldenAppleFFA(Player player, int duration, Cancellable event, String langKey) { if (player.hasCooldown(Material.GOLDEN_APPLE)) { - event.setCancelled(true); + if (event != null) { + event.setCancelled(true); + } } else { player.setCooldown(Material.GOLDEN_APPLE, duration * 20); } @@ -62,7 +68,9 @@ public void handleGoldenAppleFFA(Player player, int duration, Cancellable event, @Override public void handleGoldenAppleMatch(Player player, int duration, Cancellable event, String langKey) { if (player.hasCooldown(Material.GOLDEN_APPLE)) { - event.setCancelled(true); + if (event != null) { + event.setCancelled(true); + } } else { player.setCooldown(Material.GOLDEN_APPLE, duration * 20); } @@ -77,7 +85,9 @@ public void handleFireworkRocketFFA(Player player, FightPlayer fightPlayer, int Cancellable event, String langKey) { Bukkit.getScheduler().runTaskLater(ZonePractice.getInstance(), () -> { if (player.hasCooldown(Material.FIREWORK_ROCKET)) { - event.setCancelled(true); + if (event != null) { + event.setCancelled(true); + } } else { player.setCooldown(Material.FIREWORK_ROCKET, duration * 20); } @@ -89,7 +99,9 @@ public void handleFireworkRocketMatch(Player player, FightPlayer fightPlayer, in Cancellable event, String langKey) { Bukkit.getScheduler().runTaskLater(ZonePractice.getInstance(), () -> { if (player.hasCooldown(Material.FIREWORK_ROCKET)) { - event.setCancelled(true); + if (event != null) { + event.setCancelled(true); + } } else { player.setCooldown(Material.FIREWORK_ROCKET, duration * 20); } From a4ddc68eddeb7ee01ccc5160d4b47c98456b958f Mon Sep 17 00:00:00 2001 From: Nandor Dukat Date: Mon, 9 Mar 2026 13:20:33 +0100 Subject: [PATCH 27/31] added kitdata to sumo event, fixed online player fetching for commands, changed event start sound to only play when under 5 seconds --- .../event/arguments/Events/SumoArg.java | 25 ++++++++++++++++++- .../command/practice/arguments/EloArg.java | 4 +-- .../command/practice/arguments/InfoArg.java | 2 +- .../command/practice/arguments/RankedArg.java | 12 ++++----- .../command/practice/arguments/ResetArg.java | 4 +-- .../practice/arguments/UnrankedArg.java | 8 +++--- .../command/statistics/StatisticsCommand.java | 2 +- .../fight/event/events/duel/sumo/Sumo.java | 10 ++++++++ .../event/events/duel/sumo/SumoData.java | 13 +++++++++- .../runnables/queue/QueueStartRunnable.java | 6 +++-- .../manager/server/ServerManager.java | 19 ++++++++++++++ core/src/main/resources/language.yml | 5 +++- 12 files changed, 89 insertions(+), 21 deletions(-) diff --git a/core/src/main/java/dev/nandi0813/practice/command/event/arguments/Events/SumoArg.java b/core/src/main/java/dev/nandi0813/practice/command/event/arguments/Events/SumoArg.java index 3469c7d1..096e9d85 100644 --- a/core/src/main/java/dev/nandi0813/practice/command/event/arguments/Events/SumoArg.java +++ b/core/src/main/java/dev/nandi0813/practice/command/event/arguments/Events/SumoArg.java @@ -6,6 +6,7 @@ import dev.nandi0813.practice.manager.fight.event.enums.EventType; import dev.nandi0813.practice.manager.fight.event.events.duel.sumo.SumoData; import dev.nandi0813.practice.manager.fight.event.util.EventUtil; +import dev.nandi0813.practice.module.interfaces.KitData; import dev.nandi0813.practice.util.Common; import org.bukkit.entity.Player; import org.bukkit.util.StringUtil; @@ -36,13 +37,34 @@ public static void run(Player player, String label, String[] args) { EventUtil.changeStatus(sumoData, player); else Common.sendMMMessage(player, LanguageManager.getString("COMMAND.EVENT.ARGUMENTS.SUMO.EVENT-ALREADY-ENABLED")); + return; } else if (args.length == 2 && args[1].equalsIgnoreCase("disable")) { if (sumoData.isEnabled()) EventUtil.changeStatus(sumoData, player); else Common.sendMMMessage(player, LanguageManager.getString("COMMAND.EVENT.ARGUMENTS.SUMO.EVENT-ALREADY-DISABLED")); - } else + return; + } else if (args.length == 2 && args[1].equalsIgnoreCase("help")) { + sendHelpMSG(player, label); + + return; + } + + // Checking if the arena is enabled, if it is, it will send a message to the player and return. + if (sumoData.isEnabled()) { + Common.sendMMMessage(player, LanguageManager.getString("COMMAND.EVENT.ARGUMENTS.SUMO.CANT-EDIT")); + return; + } + + // Setting the kit for the event. + if (args.length == 2 && args[1].equalsIgnoreCase("setkit")) { + KitData kitData = sumoData.getKitData(); + kitData.setKitData(player, true); + + Common.sendMMMessage(player, LanguageManager.getString("COMMAND.EVENT.ARGUMENTS.SUMO.KIT-SET")); + } else { sendHelpMSG(player, label); + } } private static void sendHelpMSG(Player player, String label) { @@ -58,6 +80,7 @@ public static List tabComplete(Player player, String[] args) { if (args.length == 2) { arguments.add("enable"); arguments.add("disable"); + arguments.add("setkit"); return StringUtil.copyPartialMatches(args[1], arguments, new ArrayList<>()); } diff --git a/core/src/main/java/dev/nandi0813/practice/command/practice/arguments/EloArg.java b/core/src/main/java/dev/nandi0813/practice/command/practice/arguments/EloArg.java index 61fdbd17..fc630944 100644 --- a/core/src/main/java/dev/nandi0813/practice/command/practice/arguments/EloArg.java +++ b/core/src/main/java/dev/nandi0813/practice/command/practice/arguments/EloArg.java @@ -115,7 +115,7 @@ public static void run(Player player, String label, String[] args) { public static void run(String label, String[] args) { if (args.length == 4 && args[1].equalsIgnoreCase("reset")) { - Profile target = ProfileManager.getInstance().getProfile(ServerManager.getInstance().getOfflinePlayers().get(args[2])); + Profile target = ProfileManager.getInstance().getProfile(ServerManager.getInstance().resolvePlayer(args[2])); if (target == null) { Common.sendConsoleMMMessage(LanguageManager.getString("COMMAND.PRACTICE.ARGUMENTS.ELO.TARGET-OFFLINE").replace("%target%", args[2])); return; @@ -145,7 +145,7 @@ public static void run(String label, String[] args) { .replace("%defaultElo%", String.valueOf(defaultElo))); } } else if (args.length == 5 && args[1].equalsIgnoreCase("set")) { - Profile target = ProfileManager.getInstance().getProfile(ServerManager.getInstance().getOfflinePlayers().get(args[2])); + Profile target = ProfileManager.getInstance().getProfile(ServerManager.getInstance().resolvePlayer(args[2])); if (target == null) { Common.sendConsoleMMMessage(LanguageManager.getString("COMMAND.PRACTICE.ARGUMENTS.ELO.TARGET-OFFLINE").replace("%target%", args[2])); return; diff --git a/core/src/main/java/dev/nandi0813/practice/command/practice/arguments/InfoArg.java b/core/src/main/java/dev/nandi0813/practice/command/practice/arguments/InfoArg.java index 89af72ce..21f79a08 100644 --- a/core/src/main/java/dev/nandi0813/practice/command/practice/arguments/InfoArg.java +++ b/core/src/main/java/dev/nandi0813/practice/command/practice/arguments/InfoArg.java @@ -28,7 +28,7 @@ public static void run(Player player, String label, String[] args) { return; } - Profile target = ProfileManager.getInstance().getProfile(ServerManager.getInstance().getOfflinePlayers().get(args[1])); + Profile target = ProfileManager.getInstance().getProfile(ServerManager.getInstance().resolvePlayer(args[1])); if (target == null) { Common.sendMMMessage(player, LanguageManager.getString("COMMAND.PRACTICE.ARGUMENTS.INFO.TARGET-NOT-FOUND").replace("%target%", args[1])); return; diff --git a/core/src/main/java/dev/nandi0813/practice/command/practice/arguments/RankedArg.java b/core/src/main/java/dev/nandi0813/practice/command/practice/arguments/RankedArg.java index ffcf446a..7129b3bf 100644 --- a/core/src/main/java/dev/nandi0813/practice/command/practice/arguments/RankedArg.java +++ b/core/src/main/java/dev/nandi0813/practice/command/practice/arguments/RankedArg.java @@ -24,7 +24,7 @@ public static void run(Player player, String label, String[] args) { return; } - final Profile target = ProfileManager.getInstance().getProfile(ServerManager.getInstance().getOfflinePlayers().get(args[2])); + final Profile target = ProfileManager.getInstance().getProfile(ServerManager.getInstance().resolvePlayer(args[2])); if (target == null) { Common.sendMMMessage(player, LanguageManager.getString("COMMAND.PRACTICE.ARGUMENTS.RANKED.TARGET-NOT-FOUND").replace("%target%", args[2])); return; @@ -51,7 +51,7 @@ public static void run(Player player, String label, String[] args) { return; } - final Profile target = ProfileManager.getInstance().getProfile(ServerManager.getInstance().getOfflinePlayers().get(args[2])); + final Profile target = ProfileManager.getInstance().getProfile(ServerManager.getInstance().resolvePlayer(args[2])); if (target == null) { Common.sendMMMessage(player, LanguageManager.getString("COMMAND.PRACTICE.ARGUMENTS.RANKED.TARGET-NOT-FOUND").replace("%target%", args[2])); return; @@ -81,7 +81,7 @@ public static void run(Player player, String label, String[] args) { return; } - final Profile target = ProfileManager.getInstance().getProfile(ServerManager.getInstance().getOfflinePlayers().get(args[2])); + final Profile target = ProfileManager.getInstance().getProfile(ServerManager.getInstance().resolvePlayer(args[2])); if (target == null) { Common.sendMMMessage(player, LanguageManager.getString("COMMAND.PRACTICE.ARGUMENTS.RANKED.TARGET-NOT-FOUND").replace("%target%", args[2])); return; @@ -122,7 +122,7 @@ public static void run(Player player, String label, String[] args) { return; } - final Profile target = ProfileManager.getInstance().getProfile(ServerManager.getInstance().getOfflinePlayers().get(args[2])); + final Profile target = ProfileManager.getInstance().getProfile(ServerManager.getInstance().resolvePlayer(args[2])); if (target == null) { Common.sendMMMessage(player, LanguageManager.getString("COMMAND.PRACTICE.ARGUMENTS.RANKED.TARGET-NOT-FOUND").replace("%target%", args[2])); return; @@ -146,7 +146,7 @@ public static void run(Player player, String label, String[] args) { public static void run(String label, String[] args) { if (args.length == 3 && args[1].equalsIgnoreCase("reset")) { - final Profile target = ProfileManager.getInstance().getProfile(ServerManager.getInstance().getOfflinePlayers().get(args[2])); + final Profile target = ProfileManager.getInstance().getProfile(ServerManager.getInstance().resolvePlayer(args[2])); if (target == null) { Common.sendConsoleMMMessage(LanguageManager.getString("COMMAND.PRACTICE.ARGUMENTS.RANKED.TARGET-NOT-FOUND").replace("%target%", args[2])); return; @@ -162,7 +162,7 @@ public static void run(String label, String[] args) { } else Common.sendConsoleMMMessage(LanguageManager.getString("COMMAND.PRACTICE.ARGUMENTS.RANKED.NO-GROUP").replace("%target%", target.getPlayer().getName())); } else if (args.length == 4 && args[1].equalsIgnoreCase("add")) { - final Profile target = ProfileManager.getInstance().getProfile(ServerManager.getInstance().getOfflinePlayers().get(args[2])); + final Profile target = ProfileManager.getInstance().getProfile(ServerManager.getInstance().resolvePlayer(args[2])); if (target == null) { Common.sendConsoleMMMessage(LanguageManager.getString("COMMAND.PRACTICE.ARGUMENTS.RANKED.TARGET-NOT-FOUND").replace("%target%", args[2])); return; diff --git a/core/src/main/java/dev/nandi0813/practice/command/practice/arguments/ResetArg.java b/core/src/main/java/dev/nandi0813/practice/command/practice/arguments/ResetArg.java index 61e8e1f7..91acaa9b 100644 --- a/core/src/main/java/dev/nandi0813/practice/command/practice/arguments/ResetArg.java +++ b/core/src/main/java/dev/nandi0813/practice/command/practice/arguments/ResetArg.java @@ -29,7 +29,7 @@ public static void run(Player player, String label, String[] args) { return; } - Profile target = ProfileManager.getInstance().getProfile(ServerManager.getInstance().getOfflinePlayers().get(args[1])); + Profile target = ProfileManager.getInstance().getProfile(ServerManager.getInstance().resolvePlayer(args[1])); if (target == null) { Common.sendMMMessage(player, LanguageManager.getString("COMMAND.PRACTICE.ARGUMENTS.RESET.TARGET-OFFLINE").replace("%target%", args[1])); return; @@ -60,7 +60,7 @@ public static void run(String label, String[] args) { return; } - Profile target = ProfileManager.getInstance().getProfile(ServerManager.getInstance().getOfflinePlayers().get(args[1])); + Profile target = ProfileManager.getInstance().getProfile(ServerManager.getInstance().resolvePlayer(args[1])); if (target == null) { Common.sendConsoleMMMessage(LanguageManager.getString("COMMAND.PRACTICE.ARGUMENTS.RESET.TARGET-OFFLINE").replace("%target%", args[1])); return; diff --git a/core/src/main/java/dev/nandi0813/practice/command/practice/arguments/UnrankedArg.java b/core/src/main/java/dev/nandi0813/practice/command/practice/arguments/UnrankedArg.java index a7881218..e541d2e4 100644 --- a/core/src/main/java/dev/nandi0813/practice/command/practice/arguments/UnrankedArg.java +++ b/core/src/main/java/dev/nandi0813/practice/command/practice/arguments/UnrankedArg.java @@ -24,7 +24,7 @@ public static void run(Player player, String label, String[] args) { return; } - final Profile target = ProfileManager.getInstance().getProfile(ServerManager.getInstance().getOfflinePlayers().get(args[2])); + final Profile target = ProfileManager.getInstance().getProfile(ServerManager.getInstance().resolvePlayer(args[2])); if (target == null) { Common.sendMMMessage(player, LanguageManager.getString("COMMAND.PRACTICE.ARGUMENTS.UNRANKED.TARGET-OFFLINE").replace("%target%", args[2])); return; @@ -51,7 +51,7 @@ public static void run(Player player, String label, String[] args) { return; } - final Profile target = ProfileManager.getInstance().getProfile(ServerManager.getInstance().getOfflinePlayers().get(args[2])); + final Profile target = ProfileManager.getInstance().getProfile(ServerManager.getInstance().resolvePlayer(args[2])); if (target == null) { Common.sendMMMessage(player, LanguageManager.getString("COMMAND.PRACTICE.ARGUMENTS.UNRANKED.TARGET-OFFLINE")); return; @@ -85,7 +85,7 @@ public static void run(Player player, String label, String[] args) { public static void run(String label, String[] args) { if (args.length == 3 && args[1].equalsIgnoreCase("reset")) { - final Profile target = ProfileManager.getInstance().getProfile(ServerManager.getInstance().getOfflinePlayers().get(args[2])); + final Profile target = ProfileManager.getInstance().getProfile(ServerManager.getInstance().resolvePlayer(args[2])); if (target == null) { Common.sendConsoleMMMessage(LanguageManager.getString("COMMAND.PRACTICE.ARGUMENTS.UNRANKED.TARGET-OFFLINE")); return; @@ -101,7 +101,7 @@ public static void run(String label, String[] args) { } else Common.sendConsoleMMMessage(LanguageManager.getString("COMMAND.PRACTICE.ARGUMENTS.UNRANKED.NO-GROUP").replace("%target%", target.getPlayer().getName())); } else if (args.length == 4 && args[1].equalsIgnoreCase("add")) { - final Profile target = ProfileManager.getInstance().getProfile(ServerManager.getInstance().getOfflinePlayers().get(args[2])); + final Profile target = ProfileManager.getInstance().getProfile(ServerManager.getInstance().resolvePlayer(args[2])); if (target == null) { Common.sendConsoleMMMessage(LanguageManager.getString("COMMAND.PRACTICE.ARGUMENTS.UNRANKED.TARGET-OFFLINE")); return; diff --git a/core/src/main/java/dev/nandi0813/practice/command/statistics/StatisticsCommand.java b/core/src/main/java/dev/nandi0813/practice/command/statistics/StatisticsCommand.java index 78b39869..d3d72cb2 100644 --- a/core/src/main/java/dev/nandi0813/practice/command/statistics/StatisticsCommand.java +++ b/core/src/main/java/dev/nandi0813/practice/command/statistics/StatisticsCommand.java @@ -52,7 +52,7 @@ public boolean onCommand(CommandSender sender, Command command, String label, St return false; } - Profile target = ProfileManager.getInstance().getProfile(ServerManager.getInstance().getOfflinePlayers().get(args[0])); + Profile target = ProfileManager.getInstance().getProfile(ServerManager.getInstance().resolvePlayer(args[0])); if (target == null) { Common.sendMMMessage(player, LanguageManager.getString("COMMAND.STATISTICS.TARGET-NOT-EXISTS").replace("%target%", args[0])); return false; diff --git a/core/src/main/java/dev/nandi0813/practice/manager/fight/event/events/duel/sumo/Sumo.java b/core/src/main/java/dev/nandi0813/practice/manager/fight/event/events/duel/sumo/Sumo.java index dfcf87b2..9b5b9512 100644 --- a/core/src/main/java/dev/nandi0813/practice/manager/fight/event/events/duel/sumo/Sumo.java +++ b/core/src/main/java/dev/nandi0813/practice/manager/fight/event/events/duel/sumo/Sumo.java @@ -1,10 +1,12 @@ package dev.nandi0813.practice.manager.fight.event.events.duel.sumo; +import dev.nandi0813.practice.manager.fight.event.events.duel.brackets.BracketsData; import dev.nandi0813.practice.manager.fight.event.events.duel.interfaces.DuelEvent; import dev.nandi0813.practice.module.util.ClassImport; import dev.nandi0813.practice.util.playerutil.PlayerUtil; import org.bukkit.Location; import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; public class Sumo extends DuelEvent { @@ -12,11 +14,19 @@ public Sumo(Object starter, SumoData sumoData) { super(starter, sumoData, "COMMAND.EVENT.ARGUMENTS.SUMO"); } + @Override + public SumoData getEventData() { + return (SumoData) eventData; + } + @Override public void teleport(Player player, Location location) { ClassImport.getClasses().getPlayerUtil().clearInventory(player); PlayerUtil.setFightPlayer(player); + player.teleport(location); + + this.getEventData().getKitData().loadKitData(player, true); } } diff --git a/core/src/main/java/dev/nandi0813/practice/manager/fight/event/events/duel/sumo/SumoData.java b/core/src/main/java/dev/nandi0813/practice/manager/fight/event/events/duel/sumo/SumoData.java index 68a86125..f92e6b64 100644 --- a/core/src/main/java/dev/nandi0813/practice/manager/fight/event/events/duel/sumo/SumoData.java +++ b/core/src/main/java/dev/nandi0813/practice/manager/fight/event/events/duel/sumo/SumoData.java @@ -2,26 +2,37 @@ import dev.nandi0813.practice.manager.fight.event.enums.EventType; import dev.nandi0813.practice.manager.fight.event.events.duel.interfaces.DuelEventData; +import dev.nandi0813.practice.module.interfaces.KitData; +import dev.nandi0813.practice.module.util.ClassImport; +import lombok.Getter; import java.io.IOException; +@Getter public class SumoData extends DuelEventData { + protected final KitData kitData; + public SumoData() { super(EventType.SUMO); + this.kitData = ClassImport.createKitData(); } @Override protected void setCustomData() { + kitData.saveData(config, "kit"); } @Override protected void getCustomData() { + kitData.getData(config, "kit"); } @Override protected void enable() throws IOException { - if (spawns.size() != 2) { + if (!kitData.isSet()) { + throw new IOException("Kit data is not set."); + } else if (spawns.size() != 2) { throw new IOException("Spawn positions are not set. Or not equal to 2. Current size: " + spawns.size()); } } diff --git a/core/src/main/java/dev/nandi0813/practice/manager/fight/event/runnables/queue/QueueStartRunnable.java b/core/src/main/java/dev/nandi0813/practice/manager/fight/event/runnables/queue/QueueStartRunnable.java index 5a7fb07b..4d651190 100644 --- a/core/src/main/java/dev/nandi0813/practice/manager/fight/event/runnables/queue/QueueStartRunnable.java +++ b/core/src/main/java/dev/nandi0813/practice/manager/fight/event/runnables/queue/QueueStartRunnable.java @@ -32,8 +32,10 @@ public void run() { .replace("%secondName%", (seconds == 1 ? LanguageManager.getString("SECOND-NAME.1SEC") : LanguageManager.getString("SECOND-NAME.1 offlinePlayers = new HashMap<>(); // All the player that has ever been on the server is here. + @Getter private final List onlineStaffs = new ArrayList<>(); @@ -212,6 +213,24 @@ public void alertPlayers(String permission, String message) { }); } + /** + * Resolves a player by name. First checks currently online players (exact match), + * then falls back to the offlinePlayers map. This ensures that online players are + * always found even if the offlinePlayers map is in an inconsistent state due to + * the async loading in {@link #loadOfflinePlayers()}. + * + * @param name the player name to look up + * @return the matching OfflinePlayer, or {@code null} if not found + */ + public OfflinePlayer resolvePlayer(String name) { + // First try online players — this is always reliable + Player online = Bukkit.getPlayerExact(name); + if (online != null) return online; + + // Fall back to the offline map + return offlinePlayers.get(name); + } + public static void runConsoleCommand(String command) { if (!ZonePractice.getInstance().isEnabled()) { return; diff --git a/core/src/main/resources/language.yml b/core/src/main/resources/language.yml index d6286e24..64c6aa77 100644 --- a/core/src/main/resources/language.yml +++ b/core/src/main/resources/language.yml @@ -1,4 +1,4 @@ -VERSION: 18 +VERSION: 19 CONSOLE-NAME: "Console" CANT-USE-CONSOLE: "You can't use this command from the console." @@ -546,6 +546,7 @@ COMMAND: - "Setup Commands:" - " » Enable the event.'>/%label% sumo enable" - " » Disable the event.'>/%label% sumo disable" + - " » Set the kit for the event.'>/%label% brackets setkit" - "" - "Note: Use /setup event GUI to get the" - "Event Wand for corner & spawn position setup." @@ -565,6 +566,8 @@ COMMAND: NO-WINNER: "No one won the event in time." STARTED-SPECTATING: "%spectator% started spectating the event." PLAYER-OUT: "%player% is out of the game." + CANT-EDIT: "You cannot edit an enabled Event." + KIT-SET: "You successfully set the kit for the sumo event." TNTTAG: COMMAND-HELP: - "---------------------------------------" From 24f75c9763a16cbe20fa5b3e8d08f254a4f8f031 Mon Sep 17 00:00:00 2001 From: Nandor Dukat Date: Mon, 9 Mar 2026 13:25:22 +0100 Subject: [PATCH 28/31] added duel right-click to player --- .../manager/inventory/InventoryListener.java | 30 +++++++++++++------ core/src/main/resources/1.8.8/config.yml | 3 +- core/src/main/resources/modern/config.yml | 3 +- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/dev/nandi0813/practice/manager/inventory/InventoryListener.java b/core/src/main/java/dev/nandi0813/practice/manager/inventory/InventoryListener.java index adb520c7..2236bc63 100644 --- a/core/src/main/java/dev/nandi0813/practice/manager/inventory/InventoryListener.java +++ b/core/src/main/java/dev/nandi0813/practice/manager/inventory/InventoryListener.java @@ -1,5 +1,6 @@ package dev.nandi0813.practice.manager.inventory; +import dev.nandi0813.practice.manager.backend.ConfigManager; import dev.nandi0813.practice.manager.inventory.inventories.StaffInventory; import dev.nandi0813.practice.manager.inventory.inventoryitem.InvItem; import dev.nandi0813.practice.manager.inventory.inventoryitem.staffitems.CheckInventoryInvItem; @@ -88,19 +89,30 @@ public void onPlayerInteractWithEntity(PlayerInteractEntityEvent e) { return; } - if (!profile.isStaffMode()) return; - if (!(e.getRightClicked() instanceof Player)) return; + if (!(e.getRightClicked() instanceof Player target)) return; - Inventory inventory = InventoryManager.getInstance().getPlayerInventory(player); - if (inventory == null) return; + // Staff mode: check inventory item + if (profile.isStaffMode()) { + Inventory inventory = InventoryManager.getInstance().getPlayerInventory(player); + if (inventory == null) return; - if (inventory instanceof StaffInventory) { - ItemStack itemInHand = ClassImport.getClasses().getPlayerUtil().getPlayerMainHand(player); - InvItem invItem = inventory.getInvItem(itemInHand.getItemMeta().getDisplayName(), itemInHand.getType()); + if (inventory instanceof StaffInventory) { + ItemStack itemInHand = ClassImport.getClasses().getPlayerUtil().getPlayerMainHand(player); + InvItem invItem = inventory.getInvItem(itemInHand.getItemMeta().getDisplayName(), itemInHand.getType()); - if (invItem instanceof CheckInventoryInvItem checkInventoryInvItem) { - checkInventoryInvItem.handleClickEvent(player, (Player) e.getRightClicked()); + if (invItem instanceof CheckInventoryInvItem checkInventoryInvItem) { + checkInventoryInvItem.handleClickEvent(player, target); + } } + return; + } + + // Right-click-to-duel: when in lobby, right-clicking a player sends a duel request + if (ConfigManager.getBoolean("MATCH-SETTINGS.DUEL.RIGHT-CLICK-TO-DUEL") + && profile.getStatus().equals(ProfileStatus.LOBBY) + && !profile.isParty() + && player.hasPermission("zpp.duel")) { + player.performCommand("duel " + target.getName()); } } diff --git a/core/src/main/resources/1.8.8/config.yml b/core/src/main/resources/1.8.8/config.yml index fc39340b..12bd77ff 100644 --- a/core/src/main/resources/1.8.8/config.yml +++ b/core/src/main/resources/1.8.8/config.yml @@ -1,4 +1,4 @@ -VERSION: 15 +VERSION: 16 # Mysql database setup. MYSQL-DATABASE: @@ -192,6 +192,7 @@ MATCH-SETTINGS: - "REGENERATION::20::2" REMOVE-EMPTY-BOTTLE: true # Removes empty potion bottles after the player use it. DUEL: + RIGHT-CLICK-TO-DUEL: true # When enabled, players can right-click another player in the lobby to send them a duel request. INVITATION-EXPIRY: 60 # After the seconds, the duel request will expire. ROUND-SELECTOR: MAX: 10 diff --git a/core/src/main/resources/modern/config.yml b/core/src/main/resources/modern/config.yml index e129755c..ebaf26ce 100644 --- a/core/src/main/resources/modern/config.yml +++ b/core/src/main/resources/modern/config.yml @@ -1,4 +1,4 @@ -VERSION: 15 +VERSION: 16 # Mysql database setup. MYSQL-DATABASE: @@ -195,6 +195,7 @@ MATCH-SETTINGS: - "REGENERATION::20::2" REMOVE-EMPTY-BOTTLE: true # Removes empty potion bottles after the player use it. DUEL: + RIGHT-CLICK-TO-DUEL: true # When enabled, players can right-click another player in the lobby to send them a duel request. INVITATION-EXPIRY: 60 # After the seconds, the duel request will expire. ROUND-SELECTOR: MAX: 10 From e03e8267f762a5fe12631d8cf4b4c6d621c710fb Mon Sep 17 00:00:00 2001 From: Nandor Dukat Date: Mon, 9 Mar 2026 13:29:52 +0100 Subject: [PATCH 29/31] changed ladder slot reading from config so it doesn't reset every time there is a change in the config.yml --- .../gui/guis/queue/QueueSelectorGui.java | 17 +++- core/src/main/resources/1.8.8/config.yml | 70 ++++++++--------- core/src/main/resources/modern/config.yml | 78 +++++++++---------- 3 files changed, 88 insertions(+), 77 deletions(-) diff --git a/core/src/main/java/dev/nandi0813/practice/manager/gui/guis/queue/QueueSelectorGui.java b/core/src/main/java/dev/nandi0813/practice/manager/gui/guis/queue/QueueSelectorGui.java index b28de2b3..0e88227c 100644 --- a/core/src/main/java/dev/nandi0813/practice/manager/gui/guis/queue/QueueSelectorGui.java +++ b/core/src/main/java/dev/nandi0813/practice/manager/gui/guis/queue/QueueSelectorGui.java @@ -25,6 +25,7 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -379,13 +380,23 @@ public void handleClickEvent(InventoryClickEvent e) { // ------------------------------------------------------------------------- private Map getTempLadderSlots(final String path, int size) { - final Map tempLadderSlots = new HashMap<>(); + final Map tempLadderSlots = new LinkedHashMap<>(); + + for (String entry : ConfigManager.getList(path)) { + String[] parts = entry.split("::"); + if (parts.length != 2) continue; + + String ladderName = parts[0].trim(); + int slot; + try { + slot = Integer.parseInt(parts[1].trim()); + } catch (NumberFormatException e) { + continue; + } - for (String ladderName : ConfigManager.getConfigList(path)) { NormalLadder ladder = LadderManager.getInstance().getLadder(ladderName); if (ladder != null && isValidLadder(ladder) && ladder.getMatchTypes().contains(MatchType.DUEL)) { - int slot = ConfigManager.getInt(path + "." + ladderName); if (slot >= 0 && slot < size) { tempLadderSlots.put(ladder, slot); diff --git a/core/src/main/resources/1.8.8/config.yml b/core/src/main/resources/1.8.8/config.yml index 12bd77ff..e4bbb80d 100644 --- a/core/src/main/resources/1.8.8/config.yml +++ b/core/src/main/resources/1.8.8/config.yml @@ -39,30 +39,30 @@ QUEUE: SIZE: 4 GO-TO-SECOND-CATEGORY-SLOT: 35 LADDERS: - BATTLERUSH: 10 - BEDWARS: 11 - BOXING: 12 - BRIDGES: 14 - FIREBALL: 15 - GAPPLE: 16 - NODEBUFF: 20 - COMBO: 21 - SUMO: 22 - BUILDUHC: 23 - PEARLFIGHT: 24 + - "BATTLERUSH::10" + - "BEDWARS::11" + - "BOXING::12" + - "BRIDGES::14" + - "FIREBALL::15" + - "GAPPLE::16" + - "NODEBUFF::20" + - "COMBO::21" + - "SUMO::22" + - "BUILDUHC::23" + - "PEARLFIGHT::24" SECOND-CATEGORY: ENABLED: true BACK-TO-FIRST-CATEGORY-SLOT: 35 SIZE: 4 LADDERS: - ARCHER: 10 - AXE: 11 - DEBUFF: 12 - SG: 14 - SKYWARS: 15 - SOUP: 16 - SPLEEF: 20 - VANILLA: 24 + - "ARCHER::10" + - "AXE::11" + - "DEBUFF::12" + - "SG::14" + - "SKYWARS::15" + - "SOUP::16" + - "SPLEEF::20" + - "VANILLA::24" RANKED: MAX-QUEUE-TIME: 300 # In sec. GUI-UPDATE-MINUTE: 1 @@ -77,27 +77,27 @@ QUEUE: SIZE: 4 GO-TO-SECOND-CATEGORY-SLOT: 35 LADDERS: - BATTLERUSH: 10 - BEDWARS: 11 - BOXING: 12 - BRIDGES: 14 - FIREBALL: 15 - GAPPLE: 16 - NODEBUFF: 20 - COMBO: 21 - SUMO: 22 - BUILDUHC: 23 - PEARLFIGHT: 24 + - "BATTLERUSH::10" + - "BEDWARS::11" + - "BOXING::12" + - "BRIDGES::14" + - "FIREBALL::15" + - "GAPPLE::16" + - "NODEBUFF::20" + - "COMBO::21" + - "SUMO::22" + - "BUILDUHC::23" + - "PEARLFIGHT::24" SECOND-CATEGORY: ENABLED: true BACK-TO-FIRST-CATEGORY-SLOT: 18 SIZE: 3 LADDERS: - DEBUFF: 11 - SG: 12 - SKYWARS: 13 - SOUP: 14 - SPLEEF: 15 + - "DEBUFF::11" + - "SG::12" + - "SKYWARS::13" + - "SOUP::14" + - "SPLEEF::15" # # Admin settings ADMIN-SETTINGS: diff --git a/core/src/main/resources/modern/config.yml b/core/src/main/resources/modern/config.yml index ebaf26ce..f301b0e3 100644 --- a/core/src/main/resources/modern/config.yml +++ b/core/src/main/resources/modern/config.yml @@ -39,32 +39,32 @@ QUEUE: SIZE: 5 GO-TO-SECOND-CATEGORY-SLOT: 44 LADDERS: - BATTLERUSH: 10 - BEDWARS: 11 - BOXING: 12 - BRIDGES: 14 - FIREBALL: 15 - GAPPLE: 16 - NODEBUFF: 20 - MACE: 21 - SUMO: 22 - BUILDUHC: 23 - PEARLFIGHT: 24 - CRYSTAL: 30 - SPEAR: 32 + - "BATTLERUSH::10" + - "BEDWARS::11" + - "BOXING::12" + - "BRIDGES::14" + - "FIREBALL::15" + - "GAPPLE::16" + - "NODEBUFF::20" + - "MACE::21" + - "SUMO::22" + - "BUILDUHC::23" + - "PEARLFIGHT::24" + - "CRYSTAL::30" + - "SPEAR::32" SECOND-CATEGORY: ENABLED: true BACK-TO-FIRST-CATEGORY-SLOT: 35 SIZE: 4 LADDERS: - ARCHER: 10 - AXE: 11 - DEBUFF: 12 - SG: 14 - SKYWARS: 15 - SOUP: 16 - SPLEEF: 20 - VANILLA: 24 + - "ARCHER::10" + - "AXE::11" + - "DEBUFF::12" + - "SG::14" + - "SKYWARS::15" + - "SOUP::16" + - "SPLEEF::20" + - "VANILLA::24" RANKED: MAX-QUEUE-TIME: 300 # In sec. GUI-UPDATE-MINUTE: 1 @@ -79,29 +79,29 @@ QUEUE: SIZE: 5 GO-TO-SECOND-CATEGORY-SLOT: 44 LADDERS: - BATTLERUSH: 10 - BEDWARS: 11 - BOXING: 12 - BRIDGES: 14 - FIREBALL: 15 - GAPPLE: 16 - NODEBUFF: 20 - MACE: 21 - SUMO: 22 - BUILDUHC: 23 - PEARLFIGHT: 24 - CRYSTAL: 30 - SPEAR: 32 + - "BATTLERUSH::10" + - "BEDWARS::11" + - "BOXING::12" + - "BRIDGES::14" + - "FIREBALL::15" + - "GAPPLE::16" + - "NODEBUFF::20" + - "MACE::21" + - "SUMO::22" + - "BUILDUHC::23" + - "PEARLFIGHT::24" + - "CRYSTAL::30" + - "SPEAR::32" SECOND-CATEGORY: ENABLED: true BACK-TO-FIRST-CATEGORY-SLOT: 18 SIZE: 3 LADDERS: - DEBUFF: 11 - SG: 12 - SKYWARS: 13 - SOUP: 14 - SPLEEF: 15 + - "DEBUFF::11" + - "SG::12" + - "SKYWARS::13" + - "SOUP::14" + - "SPLEEF::15" # # Admin settings ADMIN-SETTINGS: From e1d7482d9fcd3496264ec4cadcebddf4689964df Mon Sep 17 00:00:00 2001 From: Nandor Dukat Date: Mon, 9 Mar 2026 13:31:32 +0100 Subject: [PATCH 30/31] changed version to 6.4.6-SNAPSHOT --- core/pom.xml | 2 +- distribution/pom.xml | 4 ++-- pom.xml | 2 +- spigot_1_8_8/pom.xml | 2 +- spigot_modern/pom.xml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 13523a0e..94d3bd0b 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -7,7 +7,7 @@ dev.nandi0813 practice-parent - 6.4.5-SNAPSHOT + 6.4.6-SNAPSHOT practice-core diff --git a/distribution/pom.xml b/distribution/pom.xml index 5dd03c02..557c0d91 100644 --- a/distribution/pom.xml +++ b/distribution/pom.xml @@ -10,7 +10,7 @@ dev.nandi0813 practice-parent - 6.4.5-SNAPSHOT + 6.4.6-SNAPSHOT @@ -46,7 +46,7 @@ clean install - ZonePractice Pro v6.4.5-SNAPSHOT + ZonePractice Pro v6.4.6-SNAPSHOT diff --git a/pom.xml b/pom.xml index b9fb2b88..e08fd06d 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ dev.nandi0813 practice-parent - 6.4.5-SNAPSHOT + 6.4.6-SNAPSHOT pom ZonePractice Pro diff --git a/spigot_1_8_8/pom.xml b/spigot_1_8_8/pom.xml index 02d8db70..97a978d7 100644 --- a/spigot_1_8_8/pom.xml +++ b/spigot_1_8_8/pom.xml @@ -7,7 +7,7 @@ dev.nandi0813 practice-parent - 6.4.5-SNAPSHOT + 6.4.6-SNAPSHOT practice-spigot_1_8_8 diff --git a/spigot_modern/pom.xml b/spigot_modern/pom.xml index 2df95872..6b4f54e7 100644 --- a/spigot_modern/pom.xml +++ b/spigot_modern/pom.xml @@ -7,7 +7,7 @@ dev.nandi0813 practice-parent - 6.4.5-SNAPSHOT + 6.4.6-SNAPSHOT practice-spigot_modern From 1460a77416a04f8853f0ac6722618177692dfa53 Mon Sep 17 00:00:00 2001 From: Nandor Dukat Date: Mon, 9 Mar 2026 13:35:15 +0100 Subject: [PATCH 31/31] changed ladder slots path in config.yml --- .../manager/gui/guis/queue/QueueSelectorGui.java | 4 ++-- core/src/main/resources/1.8.8/config.yml | 10 +++++----- core/src/main/resources/language.yml | 2 +- core/src/main/resources/modern/config.yml | 10 +++++----- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/dev/nandi0813/practice/manager/gui/guis/queue/QueueSelectorGui.java b/core/src/main/java/dev/nandi0813/practice/manager/gui/guis/queue/QueueSelectorGui.java index 0e88227c..8081c6eb 100644 --- a/core/src/main/java/dev/nandi0813/practice/manager/gui/guis/queue/QueueSelectorGui.java +++ b/core/src/main/java/dev/nandi0813/practice/manager/gui/guis/queue/QueueSelectorGui.java @@ -102,7 +102,7 @@ private void doUpdate() { ConfigManager.getInt(queuePath + ".FIRST-CATEGORY.SIZE"), GUIFile.getGuiItem(guiPath + ".FIRST-CATEGORY.ICONS.FILLER-ITEM").get(), GUIFile.getGuiItem(guiPath + ".FIRST-CATEGORY.ICONS.LADDER"), - queuePath + ".FIRST-CATEGORY.LADDERS", + queuePath + ".FIRST-CATEGORY.LADDER-SLOTS", firstCategoryLadderSlots, secondCategoryEnabled ? ConfigManager.getInt(queuePath + ".FIRST-CATEGORY.GO-TO-SECOND-CATEGORY-SLOT") : -1, secondCategoryEnabled ? GUIFile.getGuiItem(guiPath + ".FIRST-CATEGORY.ICONS.GO-TO-SECOND-CATEGORY").get() : null @@ -115,7 +115,7 @@ private void doUpdate() { ConfigManager.getInt(queuePath + ".SECOND-CATEGORY.SIZE"), GUIFile.getGuiItem(guiPath + ".SECOND-CATEGORY.ICONS.FILLER-ITEM").get(), GUIFile.getGuiItem(guiPath + ".SECOND-CATEGORY.ICONS.LADDER"), - queuePath + ".SECOND-CATEGORY.LADDERS", + queuePath + ".SECOND-CATEGORY.LADDER-SLOTS", secondCategoryLadderSlots, ConfigManager.getInt(queuePath + ".SECOND-CATEGORY.BACK-TO-FIRST-CATEGORY-SLOT"), GUIFile.getGuiItem(guiPath + ".SECOND-CATEGORY.ICONS.GO-BACK-TO-FIRST-CATEGORY").get() diff --git a/core/src/main/resources/1.8.8/config.yml b/core/src/main/resources/1.8.8/config.yml index e4bbb80d..7d49d82d 100644 --- a/core/src/main/resources/1.8.8/config.yml +++ b/core/src/main/resources/1.8.8/config.yml @@ -1,4 +1,4 @@ -VERSION: 16 +VERSION: 17 # Mysql database setup. MYSQL-DATABASE: @@ -38,7 +38,7 @@ QUEUE: FIRST-CATEGORY: SIZE: 4 GO-TO-SECOND-CATEGORY-SLOT: 35 - LADDERS: + LADDER-SLOTS: - "BATTLERUSH::10" - "BEDWARS::11" - "BOXING::12" @@ -54,7 +54,7 @@ QUEUE: ENABLED: true BACK-TO-FIRST-CATEGORY-SLOT: 35 SIZE: 4 - LADDERS: + LADDER-SLOTS: - "ARCHER::10" - "AXE::11" - "DEBUFF::12" @@ -76,7 +76,7 @@ QUEUE: FIRST-CATEGORY: SIZE: 4 GO-TO-SECOND-CATEGORY-SLOT: 35 - LADDERS: + LADDER-SLOTS: - "BATTLERUSH::10" - "BEDWARS::11" - "BOXING::12" @@ -92,7 +92,7 @@ QUEUE: ENABLED: true BACK-TO-FIRST-CATEGORY-SLOT: 18 SIZE: 3 - LADDERS: + LADDER-SLOTS: - "DEBUFF::11" - "SG::12" - "SKYWARS::13" diff --git a/core/src/main/resources/language.yml b/core/src/main/resources/language.yml index 64c6aa77..723b0471 100644 --- a/core/src/main/resources/language.yml +++ b/core/src/main/resources/language.yml @@ -546,7 +546,7 @@ COMMAND: - "Setup Commands:" - " » Enable the event.'>/%label% sumo enable" - " » Disable the event.'>/%label% sumo disable" - - " » Set the kit for the event.'>/%label% brackets setkit" + - " » Set the kit for the event.'>/%label% sumo setkit" - "" - "Note: Use /setup event GUI to get the" - "Event Wand for corner & spawn position setup." diff --git a/core/src/main/resources/modern/config.yml b/core/src/main/resources/modern/config.yml index f301b0e3..42840ddf 100644 --- a/core/src/main/resources/modern/config.yml +++ b/core/src/main/resources/modern/config.yml @@ -1,4 +1,4 @@ -VERSION: 16 +VERSION: 17 # Mysql database setup. MYSQL-DATABASE: @@ -38,7 +38,7 @@ QUEUE: FIRST-CATEGORY: SIZE: 5 GO-TO-SECOND-CATEGORY-SLOT: 44 - LADDERS: + LADDER-SLOTS: - "BATTLERUSH::10" - "BEDWARS::11" - "BOXING::12" @@ -56,7 +56,7 @@ QUEUE: ENABLED: true BACK-TO-FIRST-CATEGORY-SLOT: 35 SIZE: 4 - LADDERS: + LADDER-SLOTS: - "ARCHER::10" - "AXE::11" - "DEBUFF::12" @@ -78,7 +78,7 @@ QUEUE: FIRST-CATEGORY: SIZE: 5 GO-TO-SECOND-CATEGORY-SLOT: 44 - LADDERS: + LADDER-SLOTS: - "BATTLERUSH::10" - "BEDWARS::11" - "BOXING::12" @@ -96,7 +96,7 @@ QUEUE: ENABLED: true BACK-TO-FIRST-CATEGORY-SLOT: 18 SIZE: 3 - LADDERS: + LADDER-SLOTS: - "DEBUFF::11" - "SG::12" - "SKYWARS::13"