(Right-click to remove)");
if (labelStand != null) {
markers.add(labelStand);
}
@@ -108,7 +108,7 @@ private Mannequin createMarker(@NotNull Location location, @NotNull String name)
mannequin.setGravity(false);
mannequin.setCanPickupItems(false);
mannequin.setCustomNameVisible(true);
- mannequin.customName(Component.text(dev.nandi0813.practice.util.StringUtil.CC(name)));
+ mannequin.customName(dev.nandi0813.practice.util.Common.deserializeMiniMessage(name));
mannequin.setAI(false);
mannequin.setCollidable(false);
mannequin.setSilent(true);
@@ -152,7 +152,7 @@ private Mannequin createLabelOnly(Location location, String text) {
labelStand.setGravity(false);
labelStand.setCanPickupItems(false);
labelStand.setCustomNameVisible(true);
- labelStand.customName(Component.text(dev.nandi0813.practice.util.StringUtil.CC(text)));
+ labelStand.customName(dev.nandi0813.practice.util.Common.deserializeMiniMessage(text));
labelStand.setAI(false);
labelStand.setCollidable(false);
labelStand.setSilent(true);
@@ -319,7 +319,7 @@ public boolean removeMarker(Mannequin mannequin, DisplayArena arena) {
* or were not properly removed due to timing issues.
*
* Orphaned markers are identified by:
- * - Having a custom name starting with "&c&l" (our marker naming pattern)
+ * - Having a custom name starting with "" (our marker naming pattern)
* - Being in the arenas world
* - Not being tracked in our current marker lists
*
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 07e3f4175..004cbf47d 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
@@ -1,5 +1,6 @@
package dev.nandi0813.practice.manager.arena.util;
+import dev.nandi0813.practice.manager.arena.ArenaManager;
import dev.nandi0813.practice.manager.arena.arenas.Arena;
import dev.nandi0813.practice.manager.arena.arenas.ArenaCopy;
import dev.nandi0813.practice.manager.arena.arenas.FFAArena;
@@ -146,7 +147,7 @@ public static boolean changeStatus(Player player, DisplayArena arena) {
List editors = new ArrayList<>(setupManager.getPlayersSettingUpArena(arena));
for (Player editor : editors) {
setupManager.stopSetup(editor);
- editor.sendMessage(Common.colorize("&cSetup mode force ended because the arena has been &aENABLED&c!"));
+ Common.sendMMMessage(editor, "Setup mode force ended because the arena has been ENABLED!");
}
}
@@ -273,6 +274,9 @@ public static void loadArenaChunks(BasicArena arena) {
org.bukkit.plugin.Plugin plugin = dev.nandi0813.practice.ZonePractice.getInstance();
for (int cx = minCX; cx <= maxCX; cx++) {
for (int cz = minCZ; cz <= maxCZ; cz++) {
+ if (ArenaManager.LOAD_CHUNKS) {
+ ArenaManager.LOADED_CHUNK_KEYS.add(chunkKey(cx, cz));
+ }
// addPluginChunkTicket loads the chunk asynchronously if needed
// and prevents it from being unloaded — no main-thread stall.
world.addPluginChunkTicket(cx, cz, plugin);
@@ -280,6 +284,10 @@ public static void loadArenaChunks(BasicArena arena) {
}
}
+ public static long chunkKey(int cx, int cz) {
+ return ((long) cx << 32) ^ (cz & 0xffffffffL);
+ }
+
public static void setMannequinItemInHand(Mannequin mannequin, ItemStack item, boolean rightHand) {
if (mannequin == null) return;
if (mannequin.getEquipment() == null) return;
diff --git a/core/src/main/java/dev/nandi0813/practice/manager/backend/ConfigManager.java b/core/src/main/java/dev/nandi0813/practice/manager/backend/ConfigManager.java
index fcdc34a15..192f9031c 100644
--- a/core/src/main/java/dev/nandi0813/practice/manager/backend/ConfigManager.java
+++ b/core/src/main/java/dev/nandi0813/practice/manager/backend/ConfigManager.java
@@ -3,7 +3,6 @@
import dev.nandi0813.practice.ZonePractice;
import dev.nandi0813.practice.manager.gui.GUIItem;
import dev.nandi0813.practice.util.Common;
-import dev.nandi0813.practice.util.StringUtil;
import lombok.Getter;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
@@ -51,7 +50,7 @@ public static Object get(String loc) {
public static String getString(String loc) {
String s = config.getString(loc);
if (s != null)
- return StringUtil.CC(s);
+ return s;
return "";
}
@@ -80,7 +79,7 @@ public static Set getConfigSectionKeys(String loc) {
}
public static List getList(String loc) {
- return StringUtil.CC(getConfig().getStringList(loc));
+ return getConfig().getStringList(loc);
}
public static Set getConfigList(String loc) {
diff --git a/core/src/main/java/dev/nandi0813/practice/manager/backend/GUIFile.java b/core/src/main/java/dev/nandi0813/practice/manager/backend/GUIFile.java
index ed52dbe0d..f00e47d4f 100644
--- a/core/src/main/java/dev/nandi0813/practice/manager/backend/GUIFile.java
+++ b/core/src/main/java/dev/nandi0813/practice/manager/backend/GUIFile.java
@@ -3,7 +3,6 @@
import dev.nandi0813.practice.ZonePractice;
import dev.nandi0813.practice.manager.gui.GUIItem;
import dev.nandi0813.practice.util.Common;
-import dev.nandi0813.practice.util.StringUtil;
import lombok.Getter;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
@@ -42,7 +41,7 @@ public static void reload() {
}
public static String getString(String loc) {
- return StringUtil.CC(config.getString(loc.toUpperCase()));
+ return config.getString(loc.toUpperCase());
}
public static int getInt(String loc) {
@@ -50,7 +49,7 @@ public static int getInt(String loc) {
}
public static List getStringList(String loc) {
- return StringUtil.CC(config.getStringList(loc.toUpperCase()));
+ return config.getStringList(loc.toUpperCase());
}
public static GUIItem getGuiItem(String loc) {
diff --git a/core/src/main/java/dev/nandi0813/practice/manager/backend/MysqlManager.java b/core/src/main/java/dev/nandi0813/practice/manager/backend/MysqlManager.java
index 0cb512021..dab3cd820 100644
--- a/core/src/main/java/dev/nandi0813/practice/manager/backend/MysqlManager.java
+++ b/core/src/main/java/dev/nandi0813/practice/manager/backend/MysqlManager.java
@@ -56,6 +56,9 @@ public enum MysqlManager {
"rankedWins=VALUES(rankedWins), rankedLosses=VALUES(rankedLosses), rankedWinStreak=VALUES(rankedWinStreak), " +
"rankedBestWinStreak=VALUES(rankedBestWinStreak), rankedLoseStreak=VALUES(rankedLoseStreak), " +
"rankedBestLoseStreak=VALUES(rankedBestLoseStreak), elo=VALUES(elo), `rank`=VALUES(`rank`), kills=VALUES(kills), deaths=VALUES(deaths);";
+ private static final int RETRYABLE_WRITE_MAX_ATTEMPTS = 3;
+ private static final long RETRYABLE_WRITE_BASE_DELAY_MS = 40L;
+ private static final Object[] PROFILE_WRITE_LOCKS = createLockStripes(64);
public static void openConnection() {
if (!ConfigManager.getBoolean("MYSQL-DATABASE.ENABLED")) return;
@@ -71,7 +74,7 @@ public static void openConnection() {
try {
// Explicitly load the MariaDB driver to ensure it's available for JDBC DriverManager
- Class.forName("org.mariadb.jdbc.Driver");
+ Class.forName("dev.nandi0813.practice.dependencies.mariadb.Driver");
HikariConfig config = new HikariConfig();
config.setJdbcUrl(url);
@@ -150,18 +153,15 @@ public static Connection getConnection() throws SQLException {
}
public static CompletableFuture saveProfileAsync(Profile profile) {
- if (!isConnected(true)) {
+ if (profile == null || !isConnected(true)) {
return CompletableFuture.completedFuture(null);
}
- return CompletableFuture.runAsync(() -> {
- try (Connection connection = getConnection()) {
+ return CompletableFuture.runAsync(() ->
+ executeProfileWriteWithRetry(profile.getUuid(), "[MySQL] save profile", connection -> {
saveGlobalStats(connection, profile);
saveLadderStats(connection, profile);
- } catch (SQLException e) {
- Common.sendConsoleMMMessage("Error: " + e.getMessage());
- }
- }, getExecutor());
+ }), getExecutor());
}
public static CompletableFuture saveProfilesAsync(Collection profiles) {
@@ -170,6 +170,7 @@ public static CompletableFuture saveProfilesAsync(Collection prof
}
CompletableFuture>[] futures = profiles.stream()
+ .filter(Objects::nonNull)
.map(MysqlManager::saveProfileAsync)
.toArray(CompletableFuture[]::new);
return CompletableFuture.allOf(futures);
@@ -180,18 +181,21 @@ public static void saveProfilesBlocking(Collection profiles) {
return;
}
- try (Connection connection = getConnection()) {
- int saved = 0;
- for (Profile profile : profiles) {
+ int saved = 0;
+ for (Profile profile : profiles) {
+ if (profile == null) {
+ continue;
+ }
+
+ if (executeProfileWriteWithRetry(profile.getUuid(), "[MySQL] save profile", connection -> {
saveGlobalStats(connection, profile);
saveLadderStats(connection, profile);
+ })) {
saved++;
}
-
- Common.sendConsoleMMMessage("MySQL shutdown flush completed: " + saved + " profiles saved.");
- } catch (SQLException e) {
- Common.sendConsoleMMMessage("Error: " + e.getMessage());
}
+
+ Common.sendConsoleMMMessage("MySQL shutdown flush completed: " + saved + " profiles saved.");
}
public static CompletableFuture loadProfileAsync(Profile profile) {
@@ -225,8 +229,8 @@ public static CompletableFuture deleteProfileStatsAsync(UUID uuid) {
return CompletableFuture.completedFuture(null);
}
- return CompletableFuture.runAsync(() -> {
- try (Connection connection = getConnection()) {
+ return CompletableFuture.runAsync(() ->
+ executeProfileWriteWithRetry(uuid, "[MySQL] delete profile stats", connection -> {
try (PreparedStatement stmt = connection.prepareStatement("DELETE FROM global_stats WHERE uuid=?;")) {
stmt.setString(1, uuid.toString());
stmt.executeUpdate();
@@ -236,10 +240,7 @@ public static CompletableFuture deleteProfileStatsAsync(UUID uuid) {
stmt.setString(1, uuid.toString());
stmt.executeUpdate();
}
- } catch (SQLException e) {
- Common.sendConsoleMMMessage("Error: " + e.getMessage());
- }
- }, getExecutor());
+ }), getExecutor());
}
public static CompletableFuture deleteLadderStatsAsync(String ladderName) {
@@ -247,15 +248,13 @@ public static CompletableFuture deleteLadderStatsAsync(String ladderName)
return CompletableFuture.completedFuture(null);
}
- return CompletableFuture.runAsync(() -> {
- try (Connection connection = getConnection();
- PreparedStatement stmt = connection.prepareStatement("DELETE FROM ladder_stats WHERE ladder=?;")) {
- stmt.setString(1, ladderName);
- stmt.executeUpdate();
- } catch (SQLException e) {
- Common.sendConsoleMMMessage("Error: " + e.getMessage());
- }
- }, getExecutor());
+ return CompletableFuture.runAsync(() ->
+ executeWriteWithRetry("[MySQL] delete ladder stats", connection -> {
+ try (PreparedStatement stmt = connection.prepareStatement("DELETE FROM ladder_stats WHERE ladder=?;")) {
+ stmt.setString(1, ladderName);
+ stmt.executeUpdate();
+ }
+ }), getExecutor());
}
private static void initDB() throws IOException, SQLException {
@@ -398,10 +397,14 @@ private static void saveLadderStats(Connection connection, Profile profile) thro
return;
}
+ List> orderedLadderStats =
+ new ArrayList<>(profile.getStats().getLadderStats().entrySet());
+ orderedLadderStats.sort(Comparator.comparing(entry -> entry.getKey().getName(), String.CASE_INSENSITIVE_ORDER));
+
try (PreparedStatement stmt = connection.prepareStatement(LADDER_STATS_UPSERT)) {
String username = profile.getPlayer().getName() != null ? profile.getPlayer().getName() : profile.getUuid().toString();
- for (Map.Entry entry : profile.getStats().getLadderStats().entrySet()) {
+ for (Map.Entry entry : orderedLadderStats) {
NormalLadder ladder = entry.getKey();
LadderStats ladderStats = entry.getValue();
@@ -431,6 +434,73 @@ private static void saveLadderStats(Connection connection, Profile profile) thro
}
}
+ @FunctionalInterface
+ private interface SqlWriteOperation {
+ void run(Connection connection) throws SQLException;
+ }
+
+ private static boolean executeProfileWriteWithRetry(UUID uuid, String operation, SqlWriteOperation writeOperation) {
+ if (uuid == null) {
+ return executeWriteWithRetry(operation, writeOperation);
+ }
+
+ synchronized (getProfileWriteLock(uuid)) {
+ return executeWriteWithRetry(operation, writeOperation);
+ }
+ }
+
+ private static boolean executeWriteWithRetry(String operation, SqlWriteOperation writeOperation) {
+ int attempt = 1;
+ while (attempt <= RETRYABLE_WRITE_MAX_ATTEMPTS) {
+ try (Connection connection = getConnection()) {
+ writeOperation.run(connection);
+ return true;
+ } catch (SQLException e) {
+ if (isRetryableWriteError(e) && attempt < RETRYABLE_WRITE_MAX_ATTEMPTS) {
+ sleepBeforeRetry(attempt);
+ attempt++;
+ continue;
+ }
+ Common.sendConsoleMMMessage("" + operation + " error: " + e.getMessage());
+ return false;
+ }
+ }
+ return false;
+ }
+
+ private static boolean isRetryableWriteError(SQLException e) {
+ for (SQLException sqlException = e; sqlException != null; sqlException = sqlException.getNextException()) {
+ int errorCode = sqlException.getErrorCode();
+ String sqlState = sqlException.getSQLState();
+ if (errorCode == 1213 || errorCode == 1205 || "40001".equals(sqlState) || "41000".equals(sqlState)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static void sleepBeforeRetry(int attempt) {
+ long delay = RETRYABLE_WRITE_BASE_DELAY_MS * attempt
+ + ThreadLocalRandom.current().nextLong(20L, 81L);
+ try {
+ Thread.sleep(delay);
+ } catch (InterruptedException interruptedException) {
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ private static Object getProfileWriteLock(UUID uuid) {
+ return PROFILE_WRITE_LOCKS[Math.floorMod(uuid.hashCode(), PROFILE_WRITE_LOCKS.length)];
+ }
+
+ private static Object[] createLockStripes(int size) {
+ Object[] locks = new Object[size];
+ for (int i = 0; i < size; i++) {
+ locks[i] = new Object();
+ }
+ return locks;
+ }
+
private static void loadGlobalStats(Connection connection, Profile profile) throws SQLException {
try (PreparedStatement stmt = connection.prepareStatement("SELECT * FROM global_stats WHERE uuid=? LIMIT 1;")) {
stmt.setString(1, profile.getUuid().toString());
@@ -525,17 +595,14 @@ public static void saveMatchHistoryAsync(UUID playerUuid, UUID opponentUuid,
UUID winnerUuid, int matchDuration, long playedAt) {
if (!isConnected(false)) return;
- CompletableFuture.runAsync(() -> {
- try (Connection conn = getConnection()) {
- insertMatchHistoryRow(conn, playerUuid, opponentUuid, playerName, opponentName,
- kitName, arenaName, playerScore, opponentScore,
- playerFinalHealth, opponentFinalHealth, winnerUuid, matchDuration, playedAt);
- pruneMatchHistory(conn, playerUuid);
- pruneMatchHistory(conn, opponentUuid);
- } catch (SQLException e) {
- Common.sendConsoleMMMessage("[MatchHistory] MySQL save error: " + e.getMessage());
- }
- }, getExecutor());
+ CompletableFuture.runAsync(() ->
+ executeWriteWithRetry("[MatchHistory] MySQL save", conn -> {
+ insertMatchHistoryRow(conn, playerUuid, opponentUuid, playerName, opponentName,
+ kitName, arenaName, playerScore, opponentScore,
+ playerFinalHealth, opponentFinalHealth, winnerUuid, matchDuration, playedAt);
+ pruneMatchHistory(conn, playerUuid);
+ pruneMatchHistory(conn, opponentUuid);
+ }), getExecutor());
}
/**
diff --git a/core/src/main/java/dev/nandi0813/practice/manager/division/Division.java b/core/src/main/java/dev/nandi0813/practice/manager/division/Division.java
index 4c0c0545c..306d28c7d 100644
--- a/core/src/main/java/dev/nandi0813/practice/manager/division/Division.java
+++ b/core/src/main/java/dev/nandi0813/practice/manager/division/Division.java
@@ -1,6 +1,7 @@
package dev.nandi0813.practice.manager.division;
import dev.nandi0813.practice.ZonePractice;
+import dev.nandi0813.practice.util.StringUtil;
import lombok.Getter;
import net.kyori.adventure.text.Component;
import org.bukkit.Material;
@@ -48,11 +49,11 @@ public boolean isValid() {
}
public Component getComponentFullName() {
- return ZonePractice.getMiniMessage().deserialize(fullName);
+ return ZonePractice.getMiniMessage().deserialize(StringUtil.legacyToMiniMessage(fullName));
}
public Component getComponentShortName() {
- return ZonePractice.getMiniMessage().deserialize(shortName);
+ return ZonePractice.getMiniMessage().deserialize(StringUtil.legacyToMiniMessage(shortName));
}
}
diff --git a/core/src/main/java/dev/nandi0813/practice/manager/fight/event/EventManager.java b/core/src/main/java/dev/nandi0813/practice/manager/fight/event/EventManager.java
index be7c2c56a..e3518a56a 100644
--- a/core/src/main/java/dev/nandi0813/practice/manager/fight/event/EventManager.java
+++ b/core/src/main/java/dev/nandi0813/practice/manager/fight/event/EventManager.java
@@ -37,7 +37,6 @@
import dev.nandi0813.practice.util.Common;
import dev.nandi0813.practice.util.KitData;
import dev.nandi0813.practice.util.StartUpCallback;
-import dev.nandi0813.practice.util.StringUtil;
import lombok.Getter;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
@@ -313,10 +312,6 @@ private Component toTitleComponent(String line) {
return Component.empty();
}
- if (line.contains("&") || line.contains("§")) {
- line = StringUtil.legacyColorToMiniMessage(line);
- }
-
return Common.deserializeMiniMessage(line);
}
diff --git a/core/src/main/java/dev/nandi0813/practice/manager/fight/event/enums/EventType.java b/core/src/main/java/dev/nandi0813/practice/manager/fight/event/enums/EventType.java
index 25c4a1da4..8039baab1 100644
--- a/core/src/main/java/dev/nandi0813/practice/manager/fight/event/enums/EventType.java
+++ b/core/src/main/java/dev/nandi0813/practice/manager/fight/event/enums/EventType.java
@@ -15,7 +15,7 @@ public enum EventType {
LMS(
10,
ConfigManager.getString("EVENT.LMS.NAME"),
- ItemCreateUtil.createItem("&eLast Man Standing", Material.DIAMOND_SWORD),
+ ItemCreateUtil.createItem("Last Man Standing", Material.DIAMOND_SWORD),
15,
LanguageManager.getString("COMMAND.EVENT.ARGUMENTS.LMS.BROADCAST"),
ConfigManager.getList("EVENT.LMS.WINNER-COMMAND"),
@@ -29,7 +29,7 @@ public enum EventType {
OITC(
11,
ConfigManager.getString("EVENT.OITC.NAME"),
- ItemCreateUtil.createItem("&6One In The Chamber", Material.BOW),
+ ItemCreateUtil.createItem("One In The Chamber", Material.BOW),
15,
LanguageManager.getString("COMMAND.EVENT.ARGUMENTS.OITC.BROADCAST"),
ConfigManager.getList("EVENT.OITC.WINNER-COMMAND"),
@@ -43,7 +43,7 @@ public enum EventType {
TNTTAG(
12,
ConfigManager.getString("EVENT.TNTTAG.NAME"),
- ItemCreateUtil.createItem("&cTNT Tag", Material.TNT),
+ ItemCreateUtil.createItem("TNT Tag", Material.TNT),
15,
LanguageManager.getString("COMMAND.EVENT.ARGUMENTS.TNTTAG.BROADCAST"),
ConfigManager.getList("EVENT.TNTTAG.WINNER-COMMAND"),
@@ -57,7 +57,7 @@ public enum EventType {
BRACKETS(
13,
ConfigManager.getString("EVENT.BRACKETS.NAME"),
- ItemCreateUtil.createItem("&aBrackets", Material.POTION, Short.valueOf("34")),
+ ItemCreateUtil.createItem("Brackets", Material.POTION, Short.valueOf("34")),
15,
LanguageManager.getString("COMMAND.EVENT.ARGUMENTS.BRACKETS.BROADCAST"),
ConfigManager.getList("EVENT.BRACKETS.WINNER-COMMAND"),
@@ -71,7 +71,7 @@ public enum EventType {
SUMO(
14,
ConfigManager.getString("EVENT.SUMO.NAME"),
- ItemCreateUtil.createItem("&6Sumo", Material.STICK),
+ ItemCreateUtil.createItem("Sumo", Material.STICK),
15,
LanguageManager.getString("COMMAND.EVENT.ARGUMENTS.SUMO.BROADCAST"),
ConfigManager.getList("EVENT.SUMO.WINNER-COMMAND"),
@@ -85,7 +85,7 @@ public enum EventType {
SPLEGG(
15,
ConfigManager.getString("EVENT.SPLEGG.NAME"),
- ItemCreateUtil.createItem("&bSplegg", Material.EGG),
+ ItemCreateUtil.createItem("Splegg", Material.EGG),
15,
LanguageManager.getString("COMMAND.EVENT.ARGUMENTS.SPLEGG.BROADCAST"),
ConfigManager.getList("EVENT.SPLEGG.WINNER-COMMAND"),
@@ -99,7 +99,7 @@ public enum EventType {
JUGGERNAUT(
16,
ConfigManager.getString("EVENT.JUGGERNAUT.NAME"),
- ItemCreateUtil.createItem("&6Juggernaut", Material.GOLDEN_APPLE),
+ ItemCreateUtil.createItem("Juggernaut", Material.GOLDEN_APPLE),
15,
LanguageManager.getString("COMMAND.EVENT.ARGUMENTS.JUGGERNAUT.BROADCAST"),
ConfigManager.getList("EVENT.JUGGERNAUT.WINNER-COMMAND"),
diff --git a/core/src/main/java/dev/nandi0813/practice/manager/fight/event/events/ffa/interfaces/FFAEvent.java b/core/src/main/java/dev/nandi0813/practice/manager/fight/event/events/ffa/interfaces/FFAEvent.java
index 52795e26c..127625c00 100644
--- a/core/src/main/java/dev/nandi0813/practice/manager/fight/event/events/ffa/interfaces/FFAEvent.java
+++ b/core/src/main/java/dev/nandi0813/practice/manager/fight/event/events/ffa/interfaces/FFAEvent.java
@@ -35,7 +35,7 @@ public FFAEvent(Object starter, EventData eventData, String languagePath) {
public void teleport(final Player player) {
// Safety check: ensure spawns list is not empty
if (eventData.getSpawns() == null || eventData.getSpawns().isEmpty()) {
- player.sendMessage(Common.colorize("&cError: No spawn points configured for this event!"));
+ Common.sendMMMessage(player, "Error: No spawn points configured for this event!");
return;
}
@@ -49,7 +49,7 @@ public void teleport(final Player player) {
// Safety check: ensure spawn location is valid
if (spawnLocation == null || spawnLocation.getWorld() == null) {
- player.sendMessage(Common.colorize("&cError: Invalid spawn location!"));
+ Common.sendMMMessage(player, "Error: Invalid spawn location!");
return;
}
diff --git a/core/src/main/java/dev/nandi0813/practice/manager/fight/event/events/onevsall/tnttag/TNTTagListener.java b/core/src/main/java/dev/nandi0813/practice/manager/fight/event/events/onevsall/tnttag/TNTTagListener.java
index 387ab1274..69ae30680 100644
--- a/core/src/main/java/dev/nandi0813/practice/manager/fight/event/events/onevsall/tnttag/TNTTagListener.java
+++ b/core/src/main/java/dev/nandi0813/practice/manager/fight/event/events/onevsall/tnttag/TNTTagListener.java
@@ -70,7 +70,7 @@ public void onPlayerQuit(Event event, PlayerQuitEvent e) {
continue;
}
- tntTag.sendMessage("&cSince " + player.getName() + " left the game, the new IT will be " + eventPlayer.getName() + ".", true);
+ tntTag.sendMessage("Since " + player.getName() + " left the game, the new IT will be " + eventPlayer.getName() + ".", true);
tntTag.setTag(null, eventPlayer);
break;
}
diff --git a/core/src/main/java/dev/nandi0813/practice/manager/fight/event/setup/EventSetupListener.java b/core/src/main/java/dev/nandi0813/practice/manager/fight/event/setup/EventSetupListener.java
index f5b5bd908..845eefd90 100644
--- a/core/src/main/java/dev/nandi0813/practice/manager/fight/event/setup/EventSetupListener.java
+++ b/core/src/main/java/dev/nandi0813/practice/manager/fight/event/setup/EventSetupListener.java
@@ -71,7 +71,7 @@ public void onInteract(PlayerInteractEvent event) {
EventData eventData = session.getEventData();
if (eventData == null) {
- player.sendMessage(Common.colorize("&cEvent not found!"));
+ Common.sendMMMessage(player, "Event not found!");
setupManager.stopSetup(player);
return;
}
@@ -193,7 +193,7 @@ public void onPlayerInteractAtEntity(PlayerInteractAtEntityEvent event) {
if (spawnIndex < 0 || spawnIndex >= eventData.getSpawns().size()) {
markerManager.updateMarkers(eventData);
updateGui(eventData);
- player.sendMessage(Common.colorize("&cThis spawn marker is outdated. Markers have been refreshed."));
+ Common.sendMMMessage(player, "This spawn marker is outdated. Markers have been refreshed.");
return;
}
@@ -202,7 +202,7 @@ public void onPlayerInteractAtEntity(PlayerInteractAtEntityEvent event) {
updateGui(eventData);
scheduleSave(eventData);
- player.sendMessage(Common.colorize("&aRemoved spawn point #" + (spawnIndex + 1) + ". Remaining: " + eventData.getSpawns().size()));
+ Common.sendMMMessage(player, "Removed spawn point #" + (spawnIndex + 1) + ". Remaining: " + eventData.getSpawns().size());
}
// Prevent damage to marker mannequins
@@ -244,9 +244,9 @@ public void onMarkerDamage(EntityDamageByEntityEvent event) {
EventSpawnMarkerManager.getInstance().updateMarkers(eventData);
updateGui(eventData);
scheduleSave(eventData);
- player.sendMessage(Common.colorize("&cRemoved last spawn point. Remaining: " + index));
+ Common.sendMMMessage(player, "Removed last spawn point. Remaining: " + index);
} else {
- player.sendMessage(Common.colorize("&cNo spawn points to remove."));
+ Common.sendMMMessage(player, "No spawn points to remove.");
}
}
@@ -270,7 +270,7 @@ private void handleModeSwitch(Player player, EventWandSetupManager.SetupSession
}
setupManager.updateWand(player);
- player.sendMessage(Common.colorize("&eSwitched to mode: &f" + session.getCurrentMode().getDisplayName()));
+ Common.sendMMMessage(player, "Switched to mode: " + session.getCurrentMode().getDisplayName());
}
private void handleCornerSelection(Player player, EventData eventData, Action action, PlayerInteractEvent event) {
@@ -281,7 +281,7 @@ private void handleCornerSelection(Player player, EventData eventData, Action ac
Block targetBlock = event.getClickedBlock();
if (targetBlock == null || targetBlock.getType().equals(Material.AIR)) {
- player.sendMessage(Common.colorize("&cBlock location cannot be found!"));
+ Common.sendMMMessage(player, "Block location cannot be found!");
return;
}
@@ -313,7 +313,7 @@ private void handleSpawnPoints(Player player, EventData eventData, Action action
}
if (eventData.getCuboid() == null) {
- player.sendMessage(Common.colorize("&cYou must set corners first!"));
+ Common.sendMMMessage(player, "You must set corners first!");
return;
}
@@ -325,7 +325,7 @@ private void handleSpawnPoints(Player player, EventData eventData, Action action
Location spawnLoc = getSnappedLocation(block, player);
if (!eventData.getCuboid().contains(spawnLoc)) {
- player.sendMessage(Common.colorize("&cSpawn point must be within the event cuboid!"));
+ Common.sendMMMessage(player, "Spawn point must be within the event cuboid!");
return;
}
@@ -333,7 +333,7 @@ private void handleSpawnPoints(Player player, EventData eventData, Action action
eventData.addSpawn(spawnLoc);
} catch (IllegalStateException ex) {
// Ignore duplicate/invalid add attempts and show the setup error to the player.
- player.sendMessage(Common.colorize("&c" + ex.getMessage()));
+ Common.sendMMMessage(player, "" + ex.getMessage());
return;
}
@@ -341,7 +341,7 @@ private void handleSpawnPoints(Player player, EventData eventData, Action action
updateGui(eventData);
scheduleSave(eventData);
- player.sendMessage(Common.colorize("&aAdded spawn point #" + eventData.getSpawns().size() + " at your location."));
+ Common.sendMMMessage(player, "Added spawn point #" + eventData.getSpawns().size() + " at your location.");
}
// Left-click anywhere to remove the last spawn point
else if (action == Action.LEFT_CLICK_AIR || action == Action.LEFT_CLICK_BLOCK) {
@@ -351,9 +351,9 @@ else if (action == Action.LEFT_CLICK_AIR || action == Action.LEFT_CLICK_BLOCK) {
EventSpawnMarkerManager.getInstance().updateMarkers(eventData);
updateGui(eventData);
scheduleSave(eventData);
- player.sendMessage(Common.colorize("&cRemoved last spawn point. Remaining: " + index));
+ Common.sendMMMessage(player, "Removed last spawn point. Remaining: " + index);
} else {
- player.sendMessage(Common.colorize("&cNo spawn points to remove."));
+ Common.sendMMMessage(player, "No spawn points to remove.");
}
}
}
@@ -366,19 +366,19 @@ private void handleToggleStatus(Player player, EventData eventData, Action actio
try {
if (eventData.isEnabled()) {
eventData.setEnabled(false);
- player.sendMessage(Common.colorize("&cDisabled event: &e" + eventData.getType().getName()));
+ Common.sendMMMessage(player, "Disabled event: " + eventData.getType().getName());
} else {
if (eventData.getCuboidLoc1() == null || eventData.getCuboidLoc2() == null) {
- player.sendMessage(Common.colorize("&cYou must set both corners first!"));
+ Common.sendMMMessage(player, "You must set both corners first!");
return;
}
if (eventData.getSpawns().isEmpty()) {
- player.sendMessage(Common.colorize("&cYou must set at least one spawn point!"));
+ Common.sendMMMessage(player, "You must set at least one spawn point!");
return;
}
eventData.setEnabled(true);
- player.sendMessage(Common.colorize("&aEnabled event: &e" + eventData.getType().getName()));
+ Common.sendMMMessage(player, "Enabled event: " + eventData.getType().getName());
// End setup mode for all players currently setting up this event
List playersSettingUp = new ArrayList<>(setupManager.getPlayersSettingUpEvent(eventData));
@@ -390,7 +390,7 @@ private void handleToggleStatus(Player player, EventData eventData, Action actio
updateGui(eventData);
scheduleSave(eventData);
} catch (Exception e) {
- player.sendMessage(Common.colorize("&cError toggling status: " + e.getMessage()));
+ Common.sendMMMessage(player, "Error toggling status: " + e.getMessage());
}
}
diff --git a/core/src/main/java/dev/nandi0813/practice/manager/fight/event/setup/EventSetupMode.java b/core/src/main/java/dev/nandi0813/practice/manager/fight/event/setup/EventSetupMode.java
index f7abc938a..134dccd27 100644
--- a/core/src/main/java/dev/nandi0813/practice/manager/fight/event/setup/EventSetupMode.java
+++ b/core/src/main/java/dev/nandi0813/practice/manager/fight/event/setup/EventSetupMode.java
@@ -6,18 +6,18 @@
public enum EventSetupMode {
CORNERS("Corner Selection", new String[]{
- "&b Left Click: &fSet Corner 1",
- "&b Right Click: &fSet Corner 2"
+ " Left Click: Set Corner 1",
+ " Right Click: Set Corner 2"
}),
SPAWN_POINTS("Spawn Points", new String[]{
- "&b Right Click Block: &fAdd Spawn Point",
- "&b Right Click Armor Stand: &fRemove That Spawn",
- "&b Left Click (Anywhere): &fRemove Last Spawn"
+ " Right Click Block: Add Spawn Point",
+ " Right Click Armor Stand: Remove That Spawn",
+ " Left Click (Anywhere): Remove Last Spawn"
}),
TOGGLE_STATUS("Event Status", new String[]{
- "&b Right Click: &fEnable Event",
+ " Right Click: Enable Event",
});
private final String displayName;
diff --git a/core/src/main/java/dev/nandi0813/practice/manager/fight/event/setup/EventSpawnMarkerManager.java b/core/src/main/java/dev/nandi0813/practice/manager/fight/event/setup/EventSpawnMarkerManager.java
index 2b8156d6f..c9b1e2793 100644
--- a/core/src/main/java/dev/nandi0813/practice/manager/fight/event/setup/EventSpawnMarkerManager.java
+++ b/core/src/main/java/dev/nandi0813/practice/manager/fight/event/setup/EventSpawnMarkerManager.java
@@ -5,7 +5,6 @@
import dev.nandi0813.practice.util.Common;
import dev.nandi0813.practice.util.ItemCreateUtil;
import lombok.Getter;
-import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.EntityType;
@@ -73,7 +72,7 @@ public void showMarkers(EventData eventData) {
if (!spawns.isEmpty()) {
int index = 0;
for (Location spawnLoc : spawns) {
- Mannequin marker = createMarker(spawnLoc, "&c&lSpawn #" + (index + 1));
+ Mannequin marker = createMarker(spawnLoc, "Spawn #" + (index + 1));
if (marker != null) {
markers.add(marker);
// Track this main marker to its spawn index
@@ -110,7 +109,7 @@ private Mannequin createMarker(Location location, String name) {
mannequin.setGravity(false);
mannequin.setCanPickupItems(false);
mannequin.setCustomNameVisible(true);
- mannequin.customName(Component.text(Common.colorize(name)));
+ mannequin.customName(Common.deserializeMiniMessage(name));
mannequin.setAI(false);
mannequin.setCollidable(false);
mannequin.setSilent(true);
@@ -123,7 +122,7 @@ private Mannequin createMarker(Location location, String name) {
mannequin.teleport(facingLoc);
// Give it a sword to hold (to make it more visible)
- ItemStack sword = ItemCreateUtil.createItem("&cSpawn Marker", org.bukkit.Material.DIAMOND_SWORD);
+ ItemStack sword = ItemCreateUtil.createItem("Spawn Marker", org.bukkit.Material.DIAMOND_SWORD);
mannequin.getEquipment().setItemInMainHand(sword);
// Make it invulnerable and non-persistent.
@@ -145,7 +144,7 @@ private Mannequin createLabelOnly(Location location) {
labelStand.setGravity(false);
labelStand.setCanPickupItems(false);
labelStand.setCustomNameVisible(true);
- labelStand.customName(Component.text(Common.colorize("&7(Right-click to remove)")));
+ labelStand.customName(Common.deserializeMiniMessage("(Right-click to remove)"));
labelStand.setAI(false);
labelStand.setCollidable(false);
labelStand.setSilent(true);
diff --git a/core/src/main/java/dev/nandi0813/practice/manager/fight/event/setup/EventWandSetupManager.java b/core/src/main/java/dev/nandi0813/practice/manager/fight/event/setup/EventWandSetupManager.java
index 4bee54170..ef2d507c7 100644
--- a/core/src/main/java/dev/nandi0813/practice/manager/fight/event/setup/EventWandSetupManager.java
+++ b/core/src/main/java/dev/nandi0813/practice/manager/fight/event/setup/EventWandSetupManager.java
@@ -80,7 +80,7 @@ public void startSetup(Player player, EventData eventData) {
// Show spawn position markers
EventSpawnMarkerManager.getInstance().showMarkers(eventData);
- player.sendMessage(Common.colorize("&aSetup mode started for event: &e" + eventData.getType().getName() + "&a."));
+ Common.sendMMMessage(player, "Setup mode started for event: " + eventData.getType().getName() + ".");
}
public void stopSetup(Player player) {
@@ -101,7 +101,7 @@ public void stopSetup(Player player) {
EventSpawnMarkerManager.getInstance().clearMarkers(eventData);
}
- player.sendMessage(Common.colorize("&cSetup mode ended for event: &c" + eventData.getType().getName() + "."));
+ Common.sendMMMessage(player, "Setup mode ended for event: " + eventData.getType().getName() + ".");
}
public SetupSession getSession(Player player) {
@@ -153,24 +153,24 @@ public void updateWand(Player player) {
ItemMeta meta = wand.getItemMeta();
EventSetupMode mode = session.getCurrentMode();
- meta.displayName(Common.legacyToComponent(Common.colorize("&6Event Wand &7(&e" + mode.getDisplayName() + "&7)")));
+ meta.displayName(Common.legacyToComponent(Common.colorize("Event Wand (" + mode.getDisplayName() + ")")));
List lore = new ArrayList<>();
- lore.add(Common.colorize("&7Editing: &a" + eventData.getType().getName()));
+ lore.add(Common.colorize("Editing: " + eventData.getType().getName()));
lore.add("");
- lore.add(Common.colorize("&eCurrent Mode: &f" + mode.getDisplayName()));
+ lore.add(Common.colorize("Current Mode: " + mode.getDisplayName()));
lore.add("");
- lore.add(Common.colorize("&7Controls:"));
+ lore.add(Common.colorize("Controls:"));
for (String line : mode.getDescription()) {
lore.add(Common.colorize(line));
}
lore.add("");
- lore.add(Common.colorize("&dShift + Left: &7Next Mode"));
- lore.add(Common.colorize("&dShift + Right: &7Prev Mode"));
+ lore.add(Common.colorize("Shift + Left: Next Mode"));
+ lore.add(Common.colorize("Shift + Right: Prev Mode"));
lore.add("");
- lore.add(Common.colorize("&cDrop (Q): &7Exit Setup"));
+ lore.add(Common.colorize("Drop (Q): Exit Setup"));
meta.lore(lore.stream().map(Common::legacyToComponent).collect(Collectors.toList()));
wand.setItemMeta(meta);
diff --git a/core/src/main/java/dev/nandi0813/practice/manager/fight/ffa/FFAListener.java b/core/src/main/java/dev/nandi0813/practice/manager/fight/ffa/FFAListener.java
index 21846997b..8f1fccd17 100644
--- a/core/src/main/java/dev/nandi0813/practice/manager/fight/ffa/FFAListener.java
+++ b/core/src/main/java/dev/nandi0813/practice/manager/fight/ffa/FFAListener.java
@@ -217,14 +217,7 @@ public void onBlockBreak(BlockBreakEvent e) {
Block block = e.getBlock();
- // Blocks placed during the fight — allow breaking (tracking done by BuildListener)
- if (BlockUtil.hasMetadata(block, PLACED_IN_FIGHT)) {
- Object mv = BlockUtil.getMetadata(block, PLACED_IN_FIGHT, Object.class);
- if (ListenerUtil.checkMetaData(mv)) {
- e.setCancelled(true);
- }
- return;
- }
+ if (ListenerUtil.handlePlacedInFightBlock(block, e)) return;
// For natural arena blocks or destroyable blocks, check build limits
if (e.getBlock().getLocation().getY() >= ListenerUtil.getCalculatedBuildLimit(ffa.getArena())) {
@@ -398,8 +391,11 @@ public void onPlayerDeath(PlayerDeathEvent e) {
DeathCause cause = FightUtil.convert(damageSource.getDamageType());
ffa.killPlayer(player, killer, cause.getMessage().replace("%killer%", killer != null ? killer.getName() : "Unknown"));
- if (killer != null) {
- Statistic statistic = ffa.getStatistics().get(killer);
+ if (killer != null && !killer.equals(player)) {
+ Statistic statistic = ffa.getStatistics().computeIfAbsent(
+ killer,
+ p -> new Statistic(ProfileManager.getInstance().getUuids().get(p))
+ );
statistic.setKills(statistic.getKills() + 1);
}
}
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 4b7010f66..4e7d93520 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
@@ -20,9 +20,9 @@
import dev.nandi0813.practice.manager.profile.ProfileManager;
import dev.nandi0813.practice.manager.profile.enums.ProfileStatus;
import dev.nandi0813.practice.manager.spectator.SpectatorManager;
-import dev.nandi0813.practice.telemetry.transport.stats.PracticeStatsTelemetryLogger;
import dev.nandi0813.practice.util.Common;
import dev.nandi0813.practice.util.Cuboid;
+import dev.nandi0813.practice.util.LastAttackerTracker;
import dev.nandi0813.practice.util.entityhider.PlayerHider;
import dev.nandi0813.practice.util.fightmapchange.FightChangeOptimized;
import dev.nandi0813.practice.util.interfaces.Spectatable;
@@ -56,11 +56,7 @@ public class FFA implements Spectatable, dev.nandi0813.api.Interface.FFA {
private boolean open;
/** Tracks the last player that dealt damage to another player, for void-kill attribution. */
- private final Map lastAttackerMap = new HashMap<>();
- /** Timestamp (ms) of the last attacker hit, keyed by victim UUID. */
- private final Map lastAttackerTime = new HashMap<>();
- /** How long (ms) a last-attacker is considered valid for void attribution. */
- private static final long LAST_ATTACKER_EXPIRY_MS = 4_000L;
+ private final LastAttackerTracker lastAttackerTracker = new LastAttackerTracker();
public FFA(FFAArena arena) {
this.arena = arena;
@@ -145,7 +141,7 @@ public void addPlayer(Player player, NormalLadder ladder) {
teleportPlayer(player);
this.sendMessage(LanguageManager.getString("FFA.GAME.PLAYER-JOIN").replace("%player%", player.getName()), true);
- PlayerUtil.setFightPlayer(player);
+ PlayerUtil.setFightPlayer(player, ladder);
this.addPlayerToBelowName(player);
// Show kit chooser or apply default kit
@@ -162,7 +158,7 @@ public void changePlayerLadder(Player player, NormalLadder ladder) {
return;
players.put(player, ladder);
- PlayerUtil.setFightPlayer(player);
+ PlayerUtil.setFightPlayer(player, ladder);
KitUtil.loadDefaultLadderKit(player, TeamEnum.FFA, ladder);
dev.nandi0813.practice.manager.fight.util.PlayerUtil.setAttackSpeed(player, ladder.getAttackCooldownModifier());
}
@@ -225,7 +221,7 @@ public void killPlayer(Player player, Player killer, String deathMessage) {
// check whether a recent attacker should be credited instead.
if (killer == null) {
Player lastAttacker = getLastAttacker(player);
- if (lastAttacker != null && deathMessage != null
+ if (lastAttacker != null && !lastAttacker.equals(player) && deathMessage != null
&& deathMessage.equals(dev.nandi0813.practice.manager.fight.util.DeathCause.VOID.getMessage())) {
killer = lastAttacker;
deathMessage = dev.nandi0813.practice.manager.fight.util.DeathCause.VOID_BY_PLAYER
@@ -237,12 +233,10 @@ public void killPlayer(Player player, Player killer, String deathMessage) {
fightPlayers.get(player).die(deathMessage, statistics.get(player));
Profile deadProfile = fightPlayers.get(player).getProfile();
deadProfile.getStats().getLadderStat(players.get(player)).increaseDeaths();
- PracticeStatsTelemetryLogger.markDirty(deadProfile);
- if (killer != null) {
+ if (killer != null && !killer.equals(player)) {
Profile killerProfile = fightPlayers.get(killer).getProfile();
killerProfile.getStats().getLadderStat(players.get(killer)).increaseKills();
- PracticeStatsTelemetryLogger.markDirty(killerProfile);
playDeathEffect(killer, player);
@@ -258,7 +252,7 @@ public void killPlayer(Player player, Player killer, String deathMessage) {
if (arena.isLobbyAfterDeath()) {
this.removePlayer(player);
} else {
- PlayerUtil.setFightPlayer(player);
+ PlayerUtil.setFightPlayer(player, players.get(player));
applySelectedOrDefaultKit(player);
dev.nandi0813.practice.manager.fight.util.PlayerUtil.setAttackSpeed(player, players.get(player).getAttackCooldownModifier());
@@ -281,30 +275,15 @@ private void applySelectedOrDefaultKit(Player player) {
}
private void playDeathEffect(Player killer, Player victim) {
- if (killer == null || victim == null) {
- return;
- }
-
- try {
- Profile killerProfile = fightPlayers.containsKey(killer)
- ? fightPlayers.get(killer).getProfile()
- : ProfileManager.getInstance().getProfile(killer);
+ if (killer == null || victim == null) return;
- if (killerProfile == null || killerProfile.getCosmeticsData() == null) {
- return;
- }
-
- var deathEffect = killerProfile.getCosmeticsData().getDeathEffect();
- if (deathEffect == null) {
- return;
- }
+ Profile killerProfile = fightPlayers.containsKey(killer)
+ ? fightPlayers.get(killer).getProfile()
+ : ProfileManager.getInstance().getProfile(killer);
- List viewers = new ArrayList<>(players.keySet());
- viewers.addAll(spectators);
- deathEffect.play(victim.getLocation(), viewers);
- } catch (Exception ignored) {
- // Cosmetic effects should never break FFA kill handling.
- }
+ List viewers = new ArrayList<>(players.keySet());
+ viewers.addAll(spectators);
+ Common.playDeathEffect(killerProfile, victim.getLocation(), viewers);
}
private void applyHealthResetOnKill(Player killer) {
@@ -322,10 +301,7 @@ private void applyHealthResetOnKill(Player killer) {
* Called from damage listeners so void deaths can be attributed correctly.
*/
public void recordAttack(Player victim, Player attacker) {
- if (victim == attacker) return;
-
- lastAttackerMap.put(victim.getUniqueId(), attacker.getUniqueId());
- lastAttackerTime.put(victim.getUniqueId(), System.currentTimeMillis());
+ lastAttackerTracker.recordAttack(victim, attacker);
}
/**
@@ -333,14 +309,7 @@ public void recordAttack(Player victim, Player attacker) {
* or {@code null} if there is none.
*/
public @org.jetbrains.annotations.Nullable Player getLastAttacker(Player victim) {
- Long time = lastAttackerTime.get(victim.getUniqueId());
- if (time == null || System.currentTimeMillis() - time > LAST_ATTACKER_EXPIRY_MS) return null;
- UUID attackerUuid = lastAttackerMap.get(victim.getUniqueId());
- if (attackerUuid == null) return null;
- for (Player p : players.keySet()) {
- if (attackerUuid.equals(p.getUniqueId())) return p;
- }
- return null;
+ return lastAttackerTracker.getLastAttacker(victim, players.keySet());
}
public void teleportPlayer(Player player) {
@@ -348,14 +317,7 @@ public void teleportPlayer(Player player) {
}
public void sendMessage(String message, boolean spectator) {
- for (Player player : players.keySet()) {
- Common.sendMMMessage(player, message);
- }
- if (spectator) {
- for (Player spectatorPlayer : spectators) {
- Common.sendMMMessage(spectatorPlayer, message);
- }
- }
+ Common.sendMessage(players.keySet(), spectators, message, spectator);
}
private void teleportStuckSpectatorsAfterRollback() {
diff --git a/core/src/main/java/dev/nandi0813/practice/manager/fight/listener/BuildListener.java b/core/src/main/java/dev/nandi0813/practice/manager/fight/listener/BuildListener.java
index 1f9fe5973..6a0b16ce3 100644
--- a/core/src/main/java/dev/nandi0813/practice/manager/fight/listener/BuildListener.java
+++ b/core/src/main/java/dev/nandi0813/practice/manager/fight/listener/BuildListener.java
@@ -70,9 +70,6 @@
*/
public class BuildListener implements Listener {
- // =========================================================================
- // HELPERS — shared by all subclasses
- // =========================================================================
private final Map setFuseTick = new HashMap<>();
@@ -251,9 +248,7 @@ private static void filterAndTrackExplosionBlocks(List blockList, Spectat
}
}
- // =========================================================================
// PLAYER-DRIVEN BLOCK EVENTS (merged from BuildBlockListener)
- // =========================================================================
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onBlockBreak(BlockBreakEvent e) {
@@ -392,9 +387,7 @@ public void onPlayerInteract(PlayerInteractEvent event) {
spectatable.getFightChange().trackFirePosition(target);
}
- // =========================================================================
// EXPLOSIONS
- // =========================================================================
/**
* Tracks every block stacked directly above {@code base} that requires solid
@@ -484,9 +477,6 @@ public void onEntityExplode(EntityExplodeEvent e) {
handleExplosion(e, e.blockList(), spectatable);
}
- // =========================================================================
- // TNT ENTITY SPAWN (fallback — used as-is on 1.8.8; still fires on modern)
- // =========================================================================
/**
* Tracks a newly spawned {@link TNTPrimed} entity for rollback and applies
@@ -577,9 +567,7 @@ protected boolean isTntBlockAlreadyTracked() {
return true;
}
- // =========================================================================
// PISTONS
- // =========================================================================
@EventHandler
public void onBlockPistonExtend(BlockPistonExtendEvent e) {
@@ -609,9 +597,6 @@ public void onBlockPistonRetract(BlockPistonRetractEvent e) {
}
}
- // =========================================================================
- // BLOCK FORM (cobblestone / obsidian generators, ice, etc.)
- // =========================================================================
/**
* Tracks blocks that turn to dirt when another block forms on top (e.g., grass
@@ -648,9 +633,7 @@ public void onBlockForm(BlockFormEvent e) {
}
}
- // =========================================================================
// LIQUID SOURCE — bucket placement
- // =========================================================================
/**
* Captures the block that will become the liquid source BEFORE the bucket is emptied.
@@ -676,9 +659,7 @@ public void onBucketEmpty(PlayerBucketEmptyEvent e) {
BlockUtil.setMetadata(liquidSourceBlock, PLACED_IN_FIGHT, spectatable);
}
- // =========================================================================
// LIQUID FLOW
- // =========================================================================
/**
* Tracks blocks that turn to dirt when lava flows on top (e.g., grass
@@ -755,9 +736,6 @@ public void onBlockFromTo(BlockFromToEvent e) {
}
}
- // =========================================================================
- // BLOCK SPREAD (fire, mushrooms, etc.)
- // =========================================================================
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onBlockSpread(BlockSpreadEvent e) {
@@ -794,9 +772,6 @@ public void onBlockSpread(BlockSpreadEvent e) {
});
}
- // =========================================================================
- // BLOCK FADE (grass → dirt, ice melt, etc.)
- // =========================================================================
/**
* Tracks blocks that fade to another type (e.g. grass/mycelium turning to dirt when
@@ -816,9 +791,6 @@ public void onBlockFade(BlockFadeEvent e) {
new ChangedBlock(block, block.getType()));
}
- // =========================================================================
- // BLOCK BURN (fire destroying blocks)
- // =========================================================================
/**
* Tracks blocks destroyed by fire so they are restored during rollback.
@@ -904,9 +876,7 @@ private void trackAdjacentFire(Block center, Spectatable spectatable) {
}
}
- // =========================================================================
// FALLING BLOCKS (sand, gravel, concrete powder, anvils, etc.)
- // =========================================================================
/**
* Tracks falling blocks for rollback. Runs at LOWEST so the block in the world
diff --git a/core/src/main/java/dev/nandi0813/practice/manager/fight/listener/EPCountdownListener.java b/core/src/main/java/dev/nandi0813/practice/manager/fight/listener/EPCountdownListener.java
index 34d4ab44a..4b08f3543 100644
--- a/core/src/main/java/dev/nandi0813/practice/manager/fight/listener/EPCountdownListener.java
+++ b/core/src/main/java/dev/nandi0813/practice/manager/fight/listener/EPCountdownListener.java
@@ -5,7 +5,6 @@
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.manager.fight.util.ModernItemCooldownHandler;
import io.papermc.paper.event.player.PlayerItemCooldownEvent;
import org.bukkit.Material;
import org.bukkit.entity.EnderPearl;
@@ -39,6 +38,11 @@ public void onEnderPearlCooldownSet(PlayerItemCooldownEvent e) {
Match match = MatchManager.getInstance().getLiveMatchByPlayer(player);
if (match != null) {
+ if (!match.getCurrentRound().getRoundStatus().equals(RoundStatus.LIVE)) {
+ e.setCancelled(true);
+ return;
+ }
+
double duration = match.getLadder().getEnderPearlCooldown();
if (duration <= 0) {
e.setCancelled(true);
@@ -49,7 +53,7 @@ public void onEnderPearlCooldownSet(PlayerItemCooldownEvent e) {
}
FFA ffa = FFAManager.getInstance().getFFAByPlayer(player);
- if (ffa != null) {
+ if (ffa != null && ffa.getPlayers().containsKey(player)) {
double duration = ffa.getPlayers().get(player).getEnderPearlCooldown();
if (duration <= 0) {
e.setCancelled(true);
@@ -91,28 +95,15 @@ public void onProjectileShoot(ProjectileLaunchEvent e) {
FFA ffa = FFAManager.getInstance().getFFAByPlayer(player);
if (ffa != null) {
- double duration = ffa.getPlayers().get(player).getEnderPearlCooldown();
- if (duration <= 0) {
- return;
- }
-
- ModernItemCooldownHandler.handleEnderPearl(player, duration, e);
return;
}
Match match = MatchManager.getInstance().getLiveMatchByPlayer(player);
if (match != null) {
- double duration = match.getLadder().getEnderPearlCooldown();
- if (duration <= 0) {
- return;
- }
-
if (!match.getCurrentRound().getRoundStatus().equals(RoundStatus.LIVE)) {
e.setCancelled(true);
return;
}
-
- ModernItemCooldownHandler.handleEnderPearl(player, duration, e);
}
}
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 c77eabb16..20228125a 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
@@ -31,9 +31,9 @@
import dev.nandi0813.practice.manager.profile.ProfileManager;
import dev.nandi0813.practice.manager.profile.enums.ProfileStatus;
import dev.nandi0813.practice.manager.spectator.SpectatorManager;
-import dev.nandi0813.practice.telemetry.transport.stats.PracticeStatsTelemetryLogger;
import dev.nandi0813.practice.util.Common;
import dev.nandi0813.practice.util.Cuboid;
+import dev.nandi0813.practice.util.LastAttackerTracker;
import dev.nandi0813.practice.util.PermanentConfig;
import dev.nandi0813.practice.util.StringUtil;
import dev.nandi0813.practice.util.entityhider.PlayerHider;
@@ -84,13 +84,9 @@ public abstract class Match extends BukkitRunnable implements Spectatable, dev.n
private final FightChangeOptimized fightChange;
/** Tracks the last player that dealt damage to another player, for void-kill attribution. */
- private final Map lastAttackerMap = new HashMap<>();
- /** Timestamp (ms) of the last attacker hit, keyed by victim UUID. */
- private final Map lastAttackerTime = new HashMap<>();
+ private final LastAttackerTracker lastAttackerTracker = new LastAttackerTracker();
/** Tracks whether a player's last registered death in this match was void-related. */
private final Map lastDeathWasVoid = new HashMap<>();
- /** How long (ms) a last-attacker is considered valid for void attribution. */
- private static final long LAST_ATTACKER_EXPIRY_MS = 4_000L;
/** True while the arena is being rolled back between rounds — players are frozen. */
@Getter
@@ -118,7 +114,7 @@ protected Match(final Ladder ladder, final Arena arena, final List playe
}
this.fightChange = new FightChangeOptimized(this);
- if (arena != null && arena.getSideBuildLimit() > 0)
+ if (arena.getSideBuildLimit() > 0)
this.sideBuildLimit = MatchUtil.getSideBuildLimitCube(this.arena.getCuboid().clone(), arena.getSideBuildLimit());
else
this.sideBuildLimit = null;
@@ -142,7 +138,7 @@ public void startMatch() {
this.allowSpectators = false;
}
- PlayerUtil.setFightPlayer(player);
+ PlayerUtil.setFightPlayer(player, this.ladder);
for (Player online : Bukkit.getOnlinePlayers()) {
if (!this.players.contains(online)) {
@@ -159,15 +155,7 @@ public void startMatch() {
}
public void sendMessage(String message, boolean spectator) {
- for (Player player : this.players) {
- Common.sendMMMessage(player, message);
- }
-
- if (spectator) {
- for (Player specPlayer : this.spectators) {
- Common.sendMMMessage(specPlayer, message);
- }
- }
+ Common.sendMessage(players, spectators, message, spectator);
}
public void entityVanish(Player player) {
@@ -204,8 +192,7 @@ public void entityVanish(Player player) {
* Called from damage listeners so void deaths can be attributed correctly.
*/
public void recordAttack(Player victim, Player attacker) {
- lastAttackerMap.put(victim.getUniqueId(), attacker.getUniqueId());
- lastAttackerTime.put(victim.getUniqueId(), System.currentTimeMillis());
+ lastAttackerTracker.recordAttack(victim, attacker);
}
/**
@@ -213,14 +200,7 @@ public void recordAttack(Player victim, Player attacker) {
* or {@code null} if there is none.
*/
public @org.jetbrains.annotations.Nullable Player getLastAttacker(Player victim) {
- Long time = lastAttackerTime.get(victim.getUniqueId());
- if (time == null || System.currentTimeMillis() - time > LAST_ATTACKER_EXPIRY_MS) return null;
- UUID attackerUuid = lastAttackerMap.get(victim.getUniqueId());
- if (attackerUuid == null) return null;
- for (Player p : players) {
- if (attackerUuid.equals(p.getUniqueId())) return p;
- }
- return null;
+ return lastAttackerTracker.getLastAttacker(victim, players);
}
public boolean wasLastDeathVoid(Player player) {
@@ -284,11 +264,9 @@ public void killPlayer(Player player, Player killer, String deathMessage) {
if (killer != null) {
Profile killerProfile = matchPlayers.get(killer).getProfile();
killerProfile.getStats().getLadderStat((NormalLadder) ladder).increaseKills();
- PracticeStatsTelemetryLogger.markDirty(killerProfile);
}
Profile deadProfile = matchPlayers.get(player).getProfile();
deadProfile.getStats().getLadderStat((NormalLadder) ladder).increaseDeaths();
- PracticeStatsTelemetryLogger.markDirty(deadProfile);
}
playDeathEffect(killer, player);
@@ -297,28 +275,13 @@ public void killPlayer(Player player, Player killer, String deathMessage) {
}
private void playDeathEffect(Player killer, Player victim) {
- if (killer == null || victim == null) {
- return;
- }
-
- try {
- Profile killerProfile = matchPlayers.containsKey(killer)
- ? matchPlayers.get(killer).getProfile()
- : ProfileManager.getInstance().getProfile(killer);
-
- if (killerProfile == null || killerProfile.getCosmeticsData() == null) {
- return;
- }
+ if (killer == null || victim == null) return;
- var deathEffect = killerProfile.getCosmeticsData().getDeathEffect();
- if (deathEffect == null) {
- return;
- }
+ Profile killerProfile = matchPlayers.containsKey(killer)
+ ? matchPlayers.get(killer).getProfile()
+ : ProfileManager.getInstance().getProfile(killer);
- deathEffect.play(victim.getLocation(), getPeople());
- } catch (Exception ignored) {
- // Cosmetic effects should never break combat flow.
- }
+ Common.playDeathEffect(killerProfile, victim.getLocation(), getPeople());
}
protected abstract void killPlayer(Player player, String deathMessage);
@@ -346,7 +309,13 @@ public void endMatch() {
removeSpectator(spectator);
// Reset the arena and only make it reusable after rollback completes.
- resetMap(() -> this.arena.setAvailable(true));
+ // Also defer live-match removal to the rollback callback so block event listeners
+ // can still resolve this match via cuboid lookup during the multi-tick rollback,
+ // preventing untracked block changes from leaking into the next match.
+ resetMap(() -> {
+ MatchManager.getInstance().getLiveMatches().remove(this);
+ this.arena.setAvailable(true);
+ });
this.cancel();
@@ -425,7 +394,7 @@ public void addSpectator(Player player, Player target, boolean teleport, boolean
return;
}
- if (this.status.equals(MatchStatus.OVER)) {
+ if (this.status.equals(MatchStatus.OVER) || this.players.isEmpty()) {
Common.sendMMMessage(player, LanguageManager.getString("SPECTATE.MATCH.MATCH-ENDED"));
return;
}
@@ -466,6 +435,7 @@ public void addSpectator(Player player, Player target, boolean teleport, boolean
}
Profile profile = ProfileManager.getInstance().getProfile(player);
+ profile.setStatus(ProfileStatus.SPECTATE);
if (profile.isStaffMode()) {
InventoryManager.getInstance().setStaffModeInventory(player);
diff --git a/core/src/main/java/dev/nandi0813/practice/manager/fight/match/Round.java b/core/src/main/java/dev/nandi0813/practice/manager/fight/match/Round.java
index a7d13c76a..672156725 100644
--- a/core/src/main/java/dev/nandi0813/practice/manager/fight/match/Round.java
+++ b/core/src/main/java/dev/nandi0813/practice/manager/fight/match/Round.java
@@ -73,7 +73,7 @@ public void startRound() {
for (Player player : match.getPlayers()) {
match.teleportPlayer(player);
- PlayerUtil.setFightPlayer(player);
+ PlayerUtil.setFightPlayer(player, match.getLadder());
MatchFightPlayer matchFightPlayer = match.getMatchPlayers().get(player);
matchFightPlayer.setKitChooserOrKit(match instanceof Team ? ((Team) match).getTeam(player) : TeamEnum.TEAM1);
diff --git a/core/src/main/java/dev/nandi0813/practice/manager/fight/match/enums/TeamEnum.java b/core/src/main/java/dev/nandi0813/practice/manager/fight/match/enums/TeamEnum.java
index 15e1f9bfe..88595881b 100644
--- a/core/src/main/java/dev/nandi0813/practice/manager/fight/match/enums/TeamEnum.java
+++ b/core/src/main/java/dev/nandi0813/practice/manager/fight/match/enums/TeamEnum.java
@@ -2,6 +2,7 @@
import dev.nandi0813.practice.ZonePractice;
import dev.nandi0813.practice.manager.backend.ConfigManager;
+import dev.nandi0813.practice.util.StringUtil;
import lombok.Getter;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
@@ -9,18 +10,18 @@
public enum TeamEnum {
TEAM1(
- ZonePractice.getMiniMessage().deserialize(ConfigManager.getConfig().getString("MATCH-SETTINGS.TEAMS.TEAM1.NAME")),
+ ZonePractice.getMiniMessage().deserialize(StringUtil.legacyToMiniMessage(ConfigManager.getConfig().getString("MATCH-SETTINGS.TEAMS.TEAM1.NAME"))),
ConfigManager.getConfig().getString("MATCH-SETTINGS.TEAMS.TEAM1.COLOR"),
- ZonePractice.getMiniMessage().deserialize(ConfigManager.getString("MATCH-SETTINGS.TEAMS.TEAM1.NAMETAG.PREFIX")),
+ ZonePractice.getMiniMessage().deserialize(StringUtil.legacyToMiniMessage(ConfigManager.getString("MATCH-SETTINGS.TEAMS.TEAM1.NAMETAG.PREFIX"))),
NamedTextColor.NAMES.valueOr(ConfigManager.getString("MATCH-SETTINGS.TEAMS.TEAM1.NAMETAG.NAME-COLOR").toLowerCase(), NamedTextColor.WHITE),
- ZonePractice.getMiniMessage().deserialize(ConfigManager.getString("MATCH-SETTINGS.TEAMS.TEAM1.NAMETAG.SUFFIX"))
+ ZonePractice.getMiniMessage().deserialize(StringUtil.legacyToMiniMessage(ConfigManager.getString("MATCH-SETTINGS.TEAMS.TEAM1.NAMETAG.SUFFIX")))
),
TEAM2(
- ZonePractice.getMiniMessage().deserialize(ConfigManager.getConfig().getString("MATCH-SETTINGS.TEAMS.TEAM2.NAME")),
+ ZonePractice.getMiniMessage().deserialize(StringUtil.legacyToMiniMessage(ConfigManager.getConfig().getString("MATCH-SETTINGS.TEAMS.TEAM2.NAME"))),
ConfigManager.getConfig().getString("MATCH-SETTINGS.TEAMS.TEAM2.COLOR"),
- ZonePractice.getMiniMessage().deserialize(ConfigManager.getString("MATCH-SETTINGS.TEAMS.TEAM2.NAMETAG.PREFIX")),
+ ZonePractice.getMiniMessage().deserialize(StringUtil.legacyToMiniMessage(ConfigManager.getString("MATCH-SETTINGS.TEAMS.TEAM2.NAMETAG.PREFIX"))),
NamedTextColor.NAMES.valueOr(ConfigManager.getString("MATCH-SETTINGS.TEAMS.TEAM2.NAMETAG.NAME-COLOR").toLowerCase(), NamedTextColor.WHITE),
- ZonePractice.getMiniMessage().deserialize(ConfigManager.getString("MATCH-SETTINGS.TEAMS.TEAM2.NAMETAG.SUFFIX"))
+ ZonePractice.getMiniMessage().deserialize(StringUtil.legacyToMiniMessage(ConfigManager.getString("MATCH-SETTINGS.TEAMS.TEAM2.NAMETAG.SUFFIX")))
),
FFA(
Component.empty(),
@@ -41,7 +42,7 @@ public enum TeamEnum {
private final Component suffix;
TeamEnum(Component name, String color, Component prefix, NamedTextColor nameColor, Component suffix) {
- this.name = ZonePractice.getMiniMessage().deserialize(color).append(name);
+ this.name = ZonePractice.getMiniMessage().deserialize(StringUtil.legacyToMiniMessage(color)).append(name);
this.color = color;
this.prefix = prefix;
@@ -54,7 +55,7 @@ public Component getNameComponent() {
}
public Component getColor() {
- return ZonePractice.getMiniMessage().deserialize(color);
+ return ZonePractice.getMiniMessage().deserialize(StringUtil.legacyToMiniMessage(color));
}
public String getNameMM() {
diff --git a/core/src/main/java/dev/nandi0813/practice/manager/fight/match/enums/WeightClass.java b/core/src/main/java/dev/nandi0813/practice/manager/fight/match/enums/WeightClass.java
index f88f9e65d..f09bb8794 100644
--- a/core/src/main/java/dev/nandi0813/practice/manager/fight/match/enums/WeightClass.java
+++ b/core/src/main/java/dev/nandi0813/practice/manager/fight/match/enums/WeightClass.java
@@ -2,7 +2,6 @@
import dev.nandi0813.practice.manager.backend.ConfigManager;
import dev.nandi0813.practice.util.Common;
-import dev.nandi0813.practice.util.StringUtil;
public enum WeightClass {
@@ -16,7 +15,7 @@ public enum WeightClass {
}
public String getName() {
- return StringUtil.CC(this.name);
+ return Common.mmToNormal(this.name);
}
public String getMMName() {
diff --git a/core/src/main/java/dev/nandi0813/practice/manager/fight/match/listener/LadderTypeListener.java b/core/src/main/java/dev/nandi0813/practice/manager/fight/match/listener/LadderTypeListener.java
index c043be5bf..106036234 100644
--- a/core/src/main/java/dev/nandi0813/practice/manager/fight/match/listener/LadderTypeListener.java
+++ b/core/src/main/java/dev/nandi0813/practice/manager/fight/match/listener/LadderTypeListener.java
@@ -62,7 +62,6 @@ public class LadderTypeListener implements Listener {
private static final int SKYWARS_KILLER_EXP_LEVEL_REWARD = 5;
private static final int SKYWARS_ENCHANT_LAPIS_AMOUNT = 3;
- // ========== HELPER METHODS ==========
/**
* Gets the match for a player if they are in MATCH status.
@@ -151,7 +150,7 @@ private static Match resolveProjectileMatch(Projectile projectile) {
return null;
}
- // ========== EVENT HANDLERS ==========
+ // EVENT HANDLERS
protected static void arrowDisplayHearth(Player shooter, Player target, double finalDamage, EntityDamageByEntityEvent event) {
if (!PermanentConfig.DISPLAY_ARROW_HIT) return;
@@ -339,14 +338,7 @@ public void onBlockBreak(BlockBreakEvent e) {
Block block = e.getBlock();
- // Blocks placed during the fight — allow breaking (tracking done by BuildListener)
- if (BlockUtil.hasMetadata(block, PLACED_IN_FIGHT)) {
- Object mv = BlockUtil.getMetadata(block, PLACED_IN_FIGHT, Object.class);
- if (ListenerUtil.checkMetaData(mv)) {
- e.setCancelled(true);
- }
- return;
- }
+ if (ListenerUtil.handlePlacedInFightBlock(block, e)) return;
// For natural arena blocks or destroyable blocks, check build limits
if (!isWithinBuildLimits(block, match, player)) {
diff --git a/core/src/main/java/dev/nandi0813/practice/manager/fight/match/listener/MatchLifecycleListener.java b/core/src/main/java/dev/nandi0813/practice/manager/fight/match/listener/MatchLifecycleListener.java
index f6ab538fc..a65c556cd 100644
--- a/core/src/main/java/dev/nandi0813/practice/manager/fight/match/listener/MatchLifecycleListener.java
+++ b/core/src/main/java/dev/nandi0813/practice/manager/fight/match/listener/MatchLifecycleListener.java
@@ -58,7 +58,10 @@ public void onMatchEnd(MatchEndEvent e) {
if (party != null)
party.setMatch(null);
- MatchManager.getInstance().getLiveMatches().remove(match);
+ // Live match removal is deferred to after rollback completes in Match.endMatch().
+ // This ensures block event listeners can still resolve the match via cuboid lookup
+ // during the multi-tick rollback window, preventing untracked block changes.
+ // MatchManager.getInstance().getLiveMatches().remove(match);
// Update GUIs
if (match instanceof Duel && ((Duel) match).isRanked())
diff --git a/core/src/main/java/dev/nandi0813/practice/manager/fight/match/type/duel/Duel.java b/core/src/main/java/dev/nandi0813/practice/manager/fight/match/type/duel/Duel.java
index f15f8058e..18ad4659a 100644
--- a/core/src/main/java/dev/nandi0813/practice/manager/fight/match/type/duel/Duel.java
+++ b/core/src/main/java/dev/nandi0813/practice/manager/fight/match/type/duel/Duel.java
@@ -29,6 +29,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.UUID;
@Getter
public class Duel extends Match implements Team {
@@ -108,9 +109,11 @@ public DuelRound getCurrentRound() {
@Override
public int getWonRounds(Player player) {
+ UUID playerUuid = player.getUniqueId();
int wonRounds = 0;
for (Round round : this.rounds.values()) {
- if (((DuelRound) round).getRoundWinner() == player)
+ Player winner = ((DuelRound) round).getRoundWinner();
+ if (winner != null && winner.getUniqueId().equals(playerUuid))
wonRounds++;
}
return wonRounds;
@@ -141,7 +144,7 @@ protected void killPlayer(Player player, String deathMessage) {
SoundManager.getInstance().getSound(SoundType.MATCH_PLAYER_TEMP_DEATH).play(this.getPeople());
});
dev.nandi0813.practice.manager.fight.util.PlayerUtil.clearInventory(player);
- player.setHealth(20);
+ PlayerUtil.healToMaxHealth(player);
break;
case ELIMINATED:
@@ -154,18 +157,18 @@ protected void killPlayer(Player player, String deathMessage) {
endRound = true;
SoundManager.getInstance().getSound(SoundType.MATCH_PLAYER_DEATH).play(this.getPeople());
dev.nandi0813.practice.manager.fight.util.PlayerUtil.clearInventory(player);
- player.setHealth(20);
+ PlayerUtil.healToMaxHealth(player);
} else if (isScoringLadder()) {
// Scoring ladder (like Boxing) - death doesn't end round
return;
} else {
// Default death behavior for standard ladders
this.getCurrentStat(player).end(true);
- PlayerUtil.setFightPlayer(player);
+ PlayerUtil.setFightPlayer(player, ladder);
if (ladder.isDropInventory())
addEntityChange(dev.nandi0813.practice.manager.fight.util.PlayerUtil.dropPlayerInventory(player));
dev.nandi0813.practice.manager.fight.util.PlayerUtil.clearInventory(player);
- player.setHealth(20);
+ PlayerUtil.healToMaxHealth(player);
SoundManager.getInstance().getSound(SoundType.MATCH_PLAYER_DEATH).play(this.getPeople());
endRound = true;
}
diff --git a/core/src/main/java/dev/nandi0813/practice/manager/fight/match/type/duel/DuelRound.java b/core/src/main/java/dev/nandi0813/practice/manager/fight/match/type/duel/DuelRound.java
index f92fc4d85..1427bad23 100644
--- a/core/src/main/java/dev/nandi0813/practice/manager/fight/match/type/duel/DuelRound.java
+++ b/core/src/main/java/dev/nandi0813/practice/manager/fight/match/type/duel/DuelRound.java
@@ -11,7 +11,6 @@
import dev.nandi0813.practice.manager.ladder.abstraction.normal.NormalLadder;
import dev.nandi0813.practice.manager.profile.Profile;
import dev.nandi0813.practice.manager.profile.statistics.LadderStats;
-import dev.nandi0813.practice.telemetry.transport.stats.PracticeStatsTelemetryLogger;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.entity.Player;
@@ -51,9 +50,6 @@ public void sendEndMessage(boolean endMatch) {
lLadderStats.increaseLosses(duel.isRanked());
loserProfile.getStats().increaseLoseStreak(normalLadder, duel.isRanked());
- PracticeStatsTelemetryLogger.markDirty(winnerProfile);
- PracticeStatsTelemetryLogger.markDirty(loserProfile);
-
if (duel.isRanked()) {
int eloChange = MatchUtil.getRandomElo();
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 7bf1000e7..1e801e096 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
@@ -69,6 +69,11 @@ public int getWonRounds(Player player) {
@Override
public void teleportPlayer(Player player) {
+ if (arena.getPartyFfaCenter() != null) {
+ player.teleport(arena.getPartyFfaCenter());
+ return;
+ }
+
int randomNum;
if (arena.getFfaPositions().isEmpty()) {
randomNum = new Random().nextInt(2);
@@ -97,7 +102,7 @@ protected void killPlayer(Player player, String deathMessage) {
SoundManager.getInstance().getSound(SoundType.MATCH_PLAYER_TEMP_DEATH).play(this.getPeople());
});
dev.nandi0813.practice.manager.fight.util.PlayerUtil.clearInventory(player);
- player.setHealth(20);
+ PlayerUtil.healToMaxHealth(player);
return;
case ELIMINATED:
@@ -110,7 +115,7 @@ protected void killPlayer(Player player, String deathMessage) {
this.getCurrentStat(player).end(true);
SoundManager.getInstance().getSound(SoundType.MATCH_PLAYER_DEATH).play(this.getPeople());
- PlayerUtil.setFightPlayer(player);
+ PlayerUtil.setFightPlayer(player, ladder);
if (ladder.isDropInventory())
addEntityChange(dev.nandi0813.practice.manager.fight.util.PlayerUtil.dropPlayerInventory(player));
diff --git a/core/src/main/java/dev/nandi0813/practice/manager/fight/match/type/playersvsplayers/PlayersVsPlayers.java b/core/src/main/java/dev/nandi0813/practice/manager/fight/match/type/playersvsplayers/PlayersVsPlayers.java
index 62e238152..338517eaf 100644
--- a/core/src/main/java/dev/nandi0813/practice/manager/fight/match/type/playersvsplayers/PlayersVsPlayers.java
+++ b/core/src/main/java/dev/nandi0813/practice/manager/fight/match/type/playersvsplayers/PlayersVsPlayers.java
@@ -73,7 +73,7 @@ protected void killPlayer(Player player, String deathMessage) {
SoundManager.getInstance().getSound(SoundType.MATCH_PLAYER_TEMP_DEATH).play(this.getPeople());
});
dev.nandi0813.practice.manager.fight.util.PlayerUtil.clearInventory(player);
- player.setHealth(20);
+ PlayerUtil.healToMaxHealth(player);
break;
case ELIMINATED:
@@ -89,7 +89,7 @@ protected void killPlayer(Player player, String deathMessage) {
MatchPlayerUtil.hidePlayerPartyGames(player, this.players);
dev.nandi0813.practice.manager.fight.util.PlayerUtil.clearInventory(player);
- player.setHealth(20);
+ PlayerUtil.healToMaxHealth(player);
} else if (isScoringLadder()) {
// Scoring ladder (like Boxing) - death doesn't end round
return;
@@ -98,7 +98,7 @@ protected void killPlayer(Player player, String deathMessage) {
this.getCurrentStat(player).end(true);
SoundManager.getInstance().getSound(SoundType.MATCH_PLAYER_DEATH).play(this.getPeople());
- PlayerUtil.setFightPlayer(player);
+ PlayerUtil.setFightPlayer(player, ladder);
if (ladder.isDropInventory())
addEntityChange(dev.nandi0813.practice.manager.fight.util.PlayerUtil.dropPlayerInventory(player));
@@ -123,7 +123,7 @@ protected void killPlayer(Player player, String deathMessage) {
MatchPlayerUtil.hidePlayerPartyGames(player, this.players);
dev.nandi0813.practice.manager.fight.util.PlayerUtil.clearInventory(player);
- player.setHealth(20);
+ PlayerUtil.healToMaxHealth(player);
}
break;
diff --git a/core/src/main/java/dev/nandi0813/practice/manager/fight/match/util/KnockbackUtil.java b/core/src/main/java/dev/nandi0813/practice/manager/fight/match/util/KnockbackUtil.java
index 9722dd991..c73d158c6 100644
--- a/core/src/main/java/dev/nandi0813/practice/manager/fight/match/util/KnockbackUtil.java
+++ b/core/src/main/java/dev/nandi0813/practice/manager/fight/match/util/KnockbackUtil.java
@@ -9,13 +9,20 @@ public enum KnockbackUtil {
public static void setPlayerKnockback(Entity target, Entity attacker, KnockbackType knockbackType) {
Vector currentVelocity = target.getVelocity().clone();
+ boolean targetOnGround = target.isOnGround();
- double horizontalScale = target.isOnGround()
+ double horizontalScale = targetOnGround
? knockbackType.getHorizontal()
: knockbackType.getAirhorizontal();
- double verticalScale = target.isOnGround()
+ double verticalScale = targetOnGround
? knockbackType.getVertical()
: knockbackType.getAirvertical();
+ double maxHorizontal = targetOnGround
+ ? knockbackType.getMaxHorizontal()
+ : knockbackType.getMaxAirhorizontal();
+ double maxVertical = targetOnGround
+ ? knockbackType.getMaxVertical()
+ : knockbackType.getMaxAirvertical();
Vector awayFromAttacker = target.getLocation().toVector().subtract(attacker.getLocation().toVector());
awayFromAttacker.setY(0);
@@ -42,6 +49,12 @@ public static void setPlayerKnockback(Entity target, Entity attacker, KnockbackT
if (appliedVertical < 0.08D) {
appliedVertical = 0.08D * verticalScale;
}
+ if (maxHorizontal > 0.0D) {
+ appliedHorizontal = Math.min(appliedHorizontal, maxHorizontal);
+ }
+ if (maxVertical > 0.0D) {
+ appliedVertical = Math.min(appliedVertical, maxVertical);
+ }
Vector adjustedVelocity = new Vector(
awayFromAttacker.getX() * appliedHorizontal,
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 dcf5077fb..d2f703407 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
@@ -1,11 +1,16 @@
package dev.nandi0813.practice.manager.fight.match.util;
import dev.nandi0813.practice.manager.backend.ConfigManager;
+import dev.nandi0813.practice.manager.fight.match.Match;
+import dev.nandi0813.practice.manager.fight.match.MatchManager;
import dev.nandi0813.practice.manager.fight.match.type.partyffa.PartyFFA;
import dev.nandi0813.practice.manager.fight.util.Stats.Statistic;
import dev.nandi0813.practice.manager.ladder.abstraction.Ladder;
import dev.nandi0813.practice.manager.ladder.enums.LadderType;
import dev.nandi0813.practice.manager.ladder.type.SkyWars;
+import dev.nandi0813.practice.manager.profile.Profile;
+import dev.nandi0813.practice.manager.profile.ProfileManager;
+import dev.nandi0813.practice.manager.profile.enums.ProfileStatus;
import dev.nandi0813.practice.util.Cuboid;
import dev.nandi0813.practice.util.NumberUtil;
import dev.nandi0813.practice.util.playerutil.PlayerUtil;
@@ -14,15 +19,25 @@
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
+import org.jetbrains.annotations.Nullable;
import java.util.*;
-public enum MatchUtil {
- ;
+public final class MatchUtil {
+ private MatchUtil() {
+ }
public static String getMatchID() {
return "match-" + System.currentTimeMillis() + NumberUtil.getRandomNumber(100, 999);
}
+ @Nullable
+ public static Match getMatchIfInMatch(Player player) {
+ Profile profile = ProfileManager.getInstance().getProfile(player);
+ if (profile == null || profile.getStatus() != ProfileStatus.MATCH)
+ return null;
+ return MatchManager.getInstance().getLiveMatchByPlayer(player);
+ }
+
public static boolean isLadderBedRelated(Ladder ladder) {
LadderType ladderType = ladder.getType();
return ladderType.equals(LadderType.BEDWARS)
diff --git a/core/src/main/java/dev/nandi0813/practice/manager/fight/match/util/TempKillPlayer.java b/core/src/main/java/dev/nandi0813/practice/manager/fight/match/util/TempKillPlayer.java
index 1dffe95a6..7d9cabe42 100644
--- a/core/src/main/java/dev/nandi0813/practice/manager/fight/match/util/TempKillPlayer.java
+++ b/core/src/main/java/dev/nandi0813/practice/manager/fight/match/util/TempKillPlayer.java
@@ -110,7 +110,7 @@ public void cancel(boolean setPlayer) {
dev.nandi0813.practice.manager.fight.util.PlayerUtil.setCollidesWithEntities(player, true);
match.teleportPlayer(player);
- PlayerUtil.setFightPlayer(player);
+ PlayerUtil.setFightPlayer(player, match.getLadder());
match.getMatchPlayers().get(player).setKitChooserOrKit(playerTeam);
}
diff --git a/core/src/main/java/dev/nandi0813/practice/manager/fight/util/KitSelectionHandler.java b/core/src/main/java/dev/nandi0813/practice/manager/fight/util/KitSelectionHandler.java
index 7e144d642..12d28f419 100644
--- a/core/src/main/java/dev/nandi0813/practice/manager/fight/util/KitSelectionHandler.java
+++ b/core/src/main/java/dev/nandi0813/practice/manager/fight/util/KitSelectionHandler.java
@@ -128,22 +128,6 @@ public void showKitChooserOrApplyKit(TeamEnum team) {
ItemStack[] inventory = kit.getInventory();
ItemStack[] armor = kit.getArmor();
- // Legacy safeguard: old kits may still have armor appended into inventory[36..39].
- if (inventory != null && inventory.length > 36) {
- if (armor == null) {
- armor = new ItemStack[]{
- inventory[36],
- inventory.length > 37 ? inventory[37] : null,
- inventory.length > 38 ? inventory[38] : null,
- inventory.length > 39 ? inventory[39] : null
- };
- }
-
- ItemStack[] trimmed = new ItemStack[36];
- System.arraycopy(inventory, 0, trimmed, 0, 36);
- inventory = trimmed;
- }
-
if (armor == null) {
armor = ladder.getKitData().getArmor();
}
diff --git a/core/src/main/java/dev/nandi0813/practice/manager/fight/util/ListenerUtil.java b/core/src/main/java/dev/nandi0813/practice/manager/fight/util/ListenerUtil.java
index b3d1ffe2e..8393f0df3 100644
--- a/core/src/main/java/dev/nandi0813/practice/manager/fight/util/ListenerUtil.java
+++ b/core/src/main/java/dev/nandi0813/practice/manager/fight/util/ListenerUtil.java
@@ -4,10 +4,30 @@
import dev.nandi0813.practice.manager.arena.arenas.interfaces.BasicArena;
import dev.nandi0813.practice.manager.fight.match.Match;
import dev.nandi0813.practice.manager.fight.match.enums.RoundStatus;
+import org.bukkit.block.Block;
import org.bukkit.entity.Player;
+import org.bukkit.event.block.BlockBreakEvent;
-public enum ListenerUtil {
- ;
+import static dev.nandi0813.practice.util.PermanentConfig.PLACED_IN_FIGHT;
+
+public final class ListenerUtil {
+ private ListenerUtil() {
+ }
+
+ /**
+ * If the block was placed during the fight, cancels the event when the
+ * metadata owner is null (stale/ orphaned data) and returns true.
+ * Returns false if the block is not a fight-placed block (caller should
+ * continue with normal block-break validation).
+ */
+ public static boolean handlePlacedInFightBlock(Block block, BlockBreakEvent e) {
+ if (!BlockUtil.hasMetadata(block, PLACED_IN_FIGHT)) return false;
+ Object mv = BlockUtil.getMetadata(block, PLACED_IN_FIGHT, Object.class);
+ if (checkMetaData(mv)) {
+ e.setCancelled(true);
+ }
+ return true;
+ }
public static boolean cancelEvent(Match match, Player player) {
if (match.getCurrentStat(player).isSet())
diff --git a/core/src/main/java/dev/nandi0813/practice/manager/fight/util/PlayerUtil.java b/core/src/main/java/dev/nandi0813/practice/manager/fight/util/PlayerUtil.java
index 4f5ad5bf6..7db45779b 100644
--- a/core/src/main/java/dev/nandi0813/practice/manager/fight/util/PlayerUtil.java
+++ b/core/src/main/java/dev/nandi0813/practice/manager/fight/util/PlayerUtil.java
@@ -1,6 +1,6 @@
package dev.nandi0813.practice.manager.fight.util;
-import dev.nandi0813.practice.util.StringUtil;
+import dev.nandi0813.practice.util.Common;
import net.kyori.adventure.text.Component;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
@@ -151,7 +151,7 @@ public static double getPlayerHealth(Player player) {
@SuppressWarnings("deprecation")
public static void setActiveInventoryTitle(Player player, String title) {
- player.getOpenInventory().setTitle(StringUtil.CC(title));
+ player.getOpenInventory().setTitle(Common.mmToNormal(title));
}
public static void setPlayerListName(Player player, Component component) {
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 51098374f..134ce0b71 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
@@ -124,14 +124,14 @@ public GUIItem setUnbreakable(boolean unbreakable) {
/**
* Parses a raw name/lore string into a {@link net.kyori.adventure.text.Component},
- * supporting all color formats: legacy {@code &c}, hex {@code RRGGBB},
- * Bungeecord hex {@code &x&R&R&G&G&B&B}, and MiniMessage tags {@code }.
+ * supporting all color formats: legacy {@code }, hex {@code RRGGBB},
+ * Bungeecord hex {@code &x&G&G}, and MiniMessage tags {@code }.
*/
private static net.kyori.adventure.text.Component parseColor(String raw) {
if (raw == null || raw.isEmpty()) return net.kyori.adventure.text.Component.empty();
// Explicitly mark italic as false so Minecraft's default item-name italic doesn't apply.
// Users can still opt back in by writing in their config.
- return ZonePractice.getMiniMessage().deserialize(StringUtil.translateColorsToMiniMessage(raw))
+ return ZonePractice.getMiniMessage().deserialize(StringUtil.legacyToMiniMessage(raw))
.decorationIfAbsent(net.kyori.adventure.text.format.TextDecoration.ITALIC,
net.kyori.adventure.text.format.TextDecoration.State.FALSE);
}
diff --git a/core/src/main/java/dev/nandi0813/practice/manager/gui/guis/MatchHistoryGui.java b/core/src/main/java/dev/nandi0813/practice/manager/gui/guis/MatchHistoryGui.java
index 8eefddec3..7e316a9b7 100644
--- a/core/src/main/java/dev/nandi0813/practice/manager/gui/guis/MatchHistoryGui.java
+++ b/core/src/main/java/dev/nandi0813/practice/manager/gui/guis/MatchHistoryGui.java
@@ -7,6 +7,7 @@
import dev.nandi0813.practice.manager.matchhistory.MatchHistoryEntry;
import dev.nandi0813.practice.util.Common;
import dev.nandi0813.practice.util.InventoryUtil;
+import dev.nandi0813.practice.util.ItemCreateUtil;
import dev.nandi0813.practice.util.StringUtil;
import net.kyori.adventure.text.Component;
import org.bukkit.Material;
@@ -15,10 +16,12 @@
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
-import org.bukkit.inventory.meta.SkullMeta;
import java.text.SimpleDateFormat;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.UUID;
import java.util.stream.Collectors;
/**
@@ -61,10 +64,10 @@ public void build() {
@Override
public void update() {
- // ── Read config ────────────────────────────────────────
+ // Read config
String rawTitle = GUIFile.getString("GUIS.MATCH-HISTORY.TITLE");
if (rawTitle == null || rawTitle.isEmpty())
- rawTitle = "&8Match History &7- &6%player%";
+ rawTitle = "Match History - %player%";
rawTitle = rawTitle.replace("%player%", targetName);
int size = GUIFile.getInt("GUIS.MATCH-HISTORY.SIZE");
@@ -73,14 +76,14 @@ public void update() {
boolean centerItems = getBooleanOrDefault("GUIS.MATCH-HISTORY.CENTER-ITEMS", true);
int configuredStart = GUIFile.getInt("GUIS.MATCH-HISTORY.START-SLOT"); // -1 means auto
- // ── Build inventory ────────────────────────────────────
+ // Build inventory
Inventory inventory = InventoryUtil.createInventory(rawTitle, size / 9);
// Fill with configurable glass pane
ItemStack filler = buildFillerItem();
for (int i = 0; i < size; i++) inventory.setItem(i, filler);
- // ── Determine where to place the match items ───────────
+ // Determine where to place the match items
int startSlot;
if (configuredStart >= 0) {
startSlot = configuredStart;
@@ -90,7 +93,7 @@ public void update() {
startSlot = 0;
}
- // ── Place match items ──────────────────────────────────
+ // Place match items
String materialStr = GUIFile.getString("GUIS.MATCH-HISTORY.MATCH-ITEM.MATERIAL");
Material material = Material.PAPER;
if (materialStr != null && !materialStr.isBlank()) {
@@ -110,8 +113,7 @@ public void update() {
gui.put(1, inventory);
}
- // ── Item builders ────────────────────────────────────────────
-
+ // Item builders
private ItemStack buildMatchItem(MatchHistoryEntry entry,
Material fallbackMaterial,
boolean usePlayerHead) {
@@ -130,7 +132,7 @@ private ItemStack buildMatchItem(MatchHistoryEntry entry,
: StringUtil.CC(GUIFile.getString(
"GUIS.MATCH-HISTORY.MESSAGES.LOSS"));
final String result = (rawResult == null || rawResult.isBlank())
- ? (won ? "§aWin" : draw ? "§eEquality" : "§cLoss")
+ ? (won ? "Win" : draw ? "Equality" : "Loss")
: rawResult;
double myHealth = getMyHealth(entry);
@@ -138,14 +140,14 @@ private ItemStack buildMatchItem(MatchHistoryEntry entry,
int myScore = getMyScore(entry);
int oppScore = getOpponentScore(entry);
- // ── Name ──────────────────────────────────────────────
+ // Name
String rawName = GUIFile.getString("GUIS.MATCH-HISTORY.MATCH-ITEM.NAME");
if (rawName == null || rawName.isBlank())
- rawName = "&eMatch vs &f%opponent%";
+ rawName = "Match vs %opponent%";
String displayName = applyPlaceholders(rawName, entry, oppName,
result, myScore, oppScore, myHealth, oppHealth, won, draw);
- // ── Lore ──────────────────────────────────────────────
+ // Lore
List loreCfg = GUIFile.getStringList("GUIS.MATCH-HISTORY.MATCH-ITEM.LORE");
if (loreCfg == null || loreCfg.isEmpty()) {
loreCfg = defaultLore();
@@ -156,7 +158,7 @@ private ItemStack buildMatchItem(MatchHistoryEntry entry,
.map(line -> Common.legacyToComponent(StringUtil.CC(line)))
.collect(Collectors.toList());
- // ── ItemStack ─────────────────────────────────────────
+ // ItemStack
ItemStack item;
if (usePlayerHead) {
item = buildSkull(oppUuid, oppName);
@@ -192,14 +194,8 @@ private ItemStack buildMatchItem(MatchHistoryEntry entry,
}
private ItemStack buildSkull(UUID uuid, String name) {
- ItemStack skull = new ItemStack(Material.PLAYER_HEAD);
- SkullMeta skullMeta = (SkullMeta) skull.getItemMeta();
- if (skullMeta != null) {
- OfflinePlayer op = org.bukkit.Bukkit.getOfflinePlayer(uuid);
- skullMeta.setOwningPlayer(op);
- skull.setItemMeta(skullMeta);
- }
- return skull;
+ OfflinePlayer op = org.bukkit.Bukkit.getOfflinePlayer(uuid);
+ return ItemCreateUtil.getPlayerHead(op);
}
private ItemStack buildFillerItem() {
@@ -218,8 +214,7 @@ private ItemStack buildFillerItem() {
return item;
}
- // ── Centering logic ──────────────────────────────────────────
-
+ // Centering logic
/**
* Computes the first slot so that {@code count} items are centred
* inside the inventory. Works row-by-row:
@@ -242,8 +237,7 @@ private int computeCenterStart(int size, int count) {
return startRow * 9 + startCol;
}
- // ── Placeholder helpers ──────────────────────────────────────
-
+ // Placeholder helpers
private String applyPlaceholders(String text, MatchHistoryEntry entry,
String oppName, String result,
int myScore, int oppScore,
@@ -266,8 +260,7 @@ private String formatHealth(double raw) {
return String.format("%.1f❤", raw / 2.0);
}
- // ── Perspective helpers ──────────────────────────────────────
-
+ // Perspective helpers
private boolean isViewer(MatchHistoryEntry e) {
return viewerUuid != null && e.getPlayerUuid().equals(viewerUuid);
}
@@ -296,25 +289,23 @@ private int getOpponentScore(MatchHistoryEntry e) {
return isViewer(e) ? e.getOpponentScore() : e.getPlayerScore();
}
- // ── Default lore ─────────────────────────────────────────────
-
+ // Default lore
private List defaultLore() {
List lore = new ArrayList<>();
- lore.add("&8&m--------------------");
- lore.add("&7Result: %result%");
- lore.add("&7Score: %score%");
- lore.add("&7Kit: &f%kit%");
- lore.add("&7Arena: &f%arena%");
- lore.add("&7Your Health: %player_health%");
- lore.add("&7Opponent Health: %opponent_health%");
- lore.add("&7Duration: &f%duration%");
- lore.add("&7Played: &f%date%");
- lore.add("&8&m--------------------");
+ lore.add("--------------------");
+ lore.add("Result: %result%");
+ lore.add("Score: %score%");
+ lore.add("Kit: %kit%");
+ lore.add("Arena: %arena%");
+ lore.add("Your Health: %player_health%");
+ lore.add("Opponent Health: %opponent_health%");
+ lore.add("Duration: %duration%");
+ lore.add("Played: %date%");
+ lore.add("--------------------");
return lore;
}
- // ── Utility ─────────────────────────────────────────────────
-
+ // Utility
private boolean getBooleanOrDefault(String path, boolean def) {
if (GUIFile.getConfig().isSet(path.toUpperCase()))
return GUIFile.getConfig().getBoolean(path.toUpperCase());
diff --git a/core/src/main/java/dev/nandi0813/practice/manager/gui/guis/arena/ArenaCreateGui.java b/core/src/main/java/dev/nandi0813/practice/manager/gui/guis/arena/ArenaCreateGui.java
index a902d4845..e5f79045e 100644
--- a/core/src/main/java/dev/nandi0813/practice/manager/gui/guis/arena/ArenaCreateGui.java
+++ b/core/src/main/java/dev/nandi0813/practice/manager/gui/guis/arena/ArenaCreateGui.java
@@ -61,7 +61,7 @@ public void update() {
inventory.setItem(16, null);
for (ArenaType type : ArenaType.values()) {
- ItemStack item = ItemCreateUtil.createItem("&e" + type.getName(), type.getIcon());
+ ItemStack item = ItemCreateUtil.createItem("