Skip to content
Open

Dev #567

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
dcd670a
change
Nandi0813 Apr 18, 2026
c66c623
execute extra items commands as player instead of console (#493)
Yow-sef Apr 20, 2026
5807e95
added hologram teleport command
Nandi0813 Apr 20, 2026
14b8b7b
fixed possible NPE
Nandi0813 Apr 20, 2026
5e3c6de
feat add allow-lobby-interact config option (#509)
lokspel Apr 28, 2026
f370ed6
feat(ladder): add hearts setting and enforce ladder max health in fights
Nandi0813 May 15, 2026
f885057
feat(arena): add party FFA center setting and update related commands
Nandi0813 May 15, 2026
9a3bfd5
feat(settings): update ladder settings GUI to support additional layo…
Nandi0813 May 15, 2026
a8aa75c
Merge branch 'dev' of https://github.com/ZoneDevelopement/ZonePractic…
Nandi0813 May 15, 2026
d21a16c
Missing pearl configuration & Update dependencies (26.1.2 is supporte…
lokspel May 17, 2026
ec0892f
Refactor tab integration (#527)
lokspel May 17, 2026
20d6c40
fix: fix profile worldtime default value and handle offline players i…
lokspel May 18, 2026
daa22b9
fix: add missing break statements in PartySettingsGui switch (#530)
lokspel May 18, 2026
212d419
fix: not-won round symbols rendering as filled on scoreboard (#532)
lokspel May 18, 2026
d3aa99a
Prevent throwing ender peal if cooldown is 0.0 in the start of the ma…
lokspel May 18, 2026
480bf3f
Fixes (#534)
lokspel May 18, 2026
6b0e18e
Fixes (#537)
lokspel May 18, 2026
f032f31
refactor: remove telemetry logger references from multiple classes
Nandi0813 May 19, 2026
1a51ec6
refactor: simplify arena side build limit check and update player joi…
Nandi0813 May 19, 2026
f659742
fixed possible deadlock
Nandi0813 May 19, 2026
22c53a1
Full MiniMessage support (#544)
lokspel May 19, 2026
6987723
fix: sword ladder was not loading by default (#545)
lokspel May 19, 2026
b704889
Fix sidebar (#547)
lokspel May 19, 2026
781c07c
Fix sidebar (#548)
lokspel May 19, 2026
eaa42b5
Hearts are customizable now and UGLY text (#550)
lokspel May 19, 2026
3b45c45
fix: correct typo in Sumo Rackets GUI item path
Nandi0813 May 20, 2026
8c4e1c8
refactor: add mini message display name methods for item meta and upd…
Nandi0813 May 20, 2026
e8efc55
refactor: update player head retrieval to use PlayerProfile for bette…
Nandi0813 May 20, 2026
2bf5e44
feat: add configuration options for icon editing and custom kit prese…
Nandi0813 May 20, 2026
35008d7
fix: prevent ender pearl usage when match round is not live and strea…
Nandi0813 May 20, 2026
32f9e3b
chore: remove telemetry configuration options from config.yml
Nandi0813 May 20, 2026
7ab2acf
Fix colors (#560)
lokspel May 23, 2026
23d5ea7
Fixes (#562)
lokspel May 25, 2026
1d970c6
fix: preserve custom config keys on BoostedYAML version update + Refa…
lokspel May 25, 2026
c547426
Merge branch 'dev' of https://github.com/ZoneDevelopement/ZonePractic…
Nandi0813 May 25, 2026
72da32d
changed version to 7.6.0-SNAPSHOT
Nandi0813 May 25, 2026
e06f3f0
Add player-caused explosion death message (crystals/TNT) with killer …
lokspel May 26, 2026
a286983
fix: prevent settings being overwritten with defaults on server start…
lokspel May 26, 2026
d702b49
GITBOOK-120: No subject
Nandi0813 May 26, 2026
2a03542
GITBOOK-120: No subject
Nandi0813 May 26, 2026
1ca8083
Merge branch 'dev' of https://github.com/ZoneDevelopement/ZonePractic…
Nandi0813 May 26, 2026
1fb9213
removed gitbook unnecessary
Nandi0813 May 26, 2026
52a8654
gitbook fix
Nandi0813 May 26, 2026
dd6268c
Update gitbook (#566)
lokspel May 27, 2026
fa2a865
fix: remove entities during rollback and arena copy deletion (#568)
lokspel May 27, 2026
34bbfa0
fixes for docs
Nandi0813 May 27, 2026
c841a50
updated supported versions
Nandi0813 May 27, 2026
9bddee3
fix: queue timeout not resetting player status and wrong time unit in…
lokspel May 27, 2026
752dc9f
fix: cancel queue and reset player state on world change (#571)
lokspel May 27, 2026
6b72292
fix: restore support-dependent blocks (vines, etc.) broken by physics…
lokspel May 27, 2026
6c446b4
Fix NPE (#574)
lokspel May 30, 2026
4eb19b8
refactor: remove unused comment in NormalArena class
Nandi0813 May 30, 2026
e131ec9
Merge branch 'dev' of https://github.com/ZoneDevelopement/ZonePractic…
Nandi0813 May 30, 2026
4b152ad
fix CPS memory leak, cleanup matchPlayers on player remove (#575)
lokspel May 30, 2026
65e96bf
feat: add global toggle for join message in config.yml (#578)
lokspel May 31, 2026
9c13dc4
Fix sidebar race condition (#579)
lokspel Jun 1, 2026
6572e87
fix: persist buildMax via setData() on wand change and skip height ch…
lokspel Jun 1, 2026
30bb4f8
feat: add %playerNameOnly% / %enemyNameOnly% scoreboard placeholders …
lokspel Jun 1, 2026
0fa32ef
fix: add null checks for profile in PlayerHider to prevent NPE on pla…
lokspel Jun 1, 2026
635fe5a
Fix round end in PartyFFA (#584)
lokspel Jun 2, 2026
7064430
Refactor Profile (#585)
lokspel Jun 2, 2026
9abc884
Fix NPE in onPlayerChooseKit when matchFightPlayer is null (#588)
lokspel Jun 3, 2026
a74d18a
Fix Colors in Tab (#590)
lokspel Jun 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ For detailed guides on setup, configuration, and feature usage, please visit our

## Supported Versions

- Primary targets: **1.20.6 / 1.21.X**
- Primary targets: **1.21.11 / 26.1.2**
- Actual supported versions are detected at runtime via the `VersionChecker`
- The plugin automatically disables itself on unsupported versions

Expand Down Expand Up @@ -345,4 +345,4 @@ Copyright © **ZonePractice contributors**

## Contact

For issues, feature requests or contributions, use the project’s GitHub issue tracker.
For issues, feature requests or contributions, use the project’s GitHub issue tracker.
27 changes: 27 additions & 0 deletions SUMMARY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Table of contents

* [✌️ ZonePractice Pro](README.md)

## Overview

* [💡 Getting Started](overview/getting-started.md)

## Setup Guides

* [⚔️ Ladder Setup](setup-guides/ladder-setup.md)
* [🧊 Arena Setup](setup-guides/arena-setup.md)
* [🎮 Event Setup](setup-guides/event-setup.md)
* [✡️ Hologram Setup](setup-guides/hologram-setup.md)

## Informations

* [📜 Configuration Files](informations/configuration-files.md)
* [⚙️ Commands](informations/commands.md)
* [📚 Permissions](informations/permissions.md)

## Extra

* [9️⃣ Modern version Support Informations](extra/modern-version-support-informations.md)
* [🧱 Placeholder API](extra/placeholder-api.md)
* [🖥️ For Developers](extra/for-developers.md)
* [❔ Support](extra/support.md)
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ public void onEnable() {
faststats_metrics.ready();

if (VersionChecker.getBukkitVersion() == null) {
Common.sendConsoleMMMessage("<red>Unsupported server version! Please use 1.20.6 or 1.21.X");
Common.sendConsoleMMMessage("<red>Unsupported server version! Please use 1.21.11 or 26.1.2");
Bukkit.getPluginManager().disablePlugin(this);
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,8 @@ public void onEntityDamage(EntityDamageEvent e) {
Location lobbyLocation = ServerManager.getLobby();
if (lobbyLocation != null)
player.teleport(lobbyLocation);
e.setCancelled(true);
}

e.setCancelled(true);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,12 @@ public class PlayerJoin implements Listener {
public void onPlayerJoin(PlayerJoinEvent e) {
final Player player = e.getPlayer();
final UUID uuid = player.getUniqueId();
e.joinMessage(Component.empty());

Profile profile = ProfileManager.getInstance().getProfile(player);

// Suppress default join message if disabled in config
if (!ConfigManager.getBoolean("PLAYER.JOIN-MESSAGE"))
e.joinMessage(Component.empty());

if (profile == null)
profile = ProfileManager.getInstance().newProfile(player, uuid);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ public void onPlayerQuit(PlayerQuitEvent e) {

MessageCommand.latestMessage.remove(player);
MessageCommand.latestMessage.values().removeIf(target -> target.equals(player));
ProfileManager.getInstance().clearPlayerReference(player);

final Profile profile = ProfileManager.getInstance().getProfile(player);
final Party party = PartyManager.getInstance().getParty(player);
Expand Down Expand Up @@ -68,6 +67,8 @@ public void onPlayerQuit(PlayerQuitEvent e) {
ProfileManager.getInstance().demoteOfflineProfile(uuid), 40L);
}
}

ProfileManager.getInstance().clearPlayerReference(player);
}

@EventHandler(priority = EventPriority.LOWEST)
Expand All @@ -83,20 +84,24 @@ public void onPlayerKick(PlayerKickEvent e) {

MessageCommand.latestMessage.remove(player);
MessageCommand.latestMessage.values().removeIf(target -> target.equals(player));
ProfileManager.getInstance().clearPlayerReference(player);

MatchManager.getInstance().invalidateRematchByPlayer(player);

Profile profile = ProfileManager.getInstance().getProfile(player);
if (profile != null) {
profile.getActionBar().resetForReconnect();
profile.setLastJoin(System.currentTimeMillis());

if (ZonePractice.getInstance().isEnabled()) {
UUID uuid = player.getUniqueId();
Bukkit.getScheduler().runTaskLater(ZonePractice.getInstance(), () ->
ProfileManager.getInstance().demoteOfflineProfile(uuid), 40L);
Bukkit.getScheduler().runTaskLater(ZonePractice.getInstance(), () -> {
profile.setStatus(ProfileStatus.OFFLINE);
ProfileManager.getInstance().demoteOfflineProfile(uuid);
}, 40L);
}
}

ProfileManager.getInstance().clearPlayerReference(player);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
@Setter
public abstract class NormalArena extends BasicArena implements DisplayName {

// Not used yet
protected boolean portalProtection = false;
protected int portalProtectionValue;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,9 @@ public void run() {
}

protected void deleteNormal(final String arena, final Cuboid cuboid) {
// Remove all non-player entities before deleting blocks
removeNonPlayerEntities(cuboid);

// OPTIMIZATION: Use iterator directly instead of creating a full list in memory
final Iterator<Block> iterator = cuboid.iterator();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,9 @@ private void handleBuildMax(Player player, DisplayArena arena, Action action) {
if (action.name().contains("LEFT")) {
arena.setBuildMax(false);
arena.setBuildMaxValue(ConfigManager.getInt("MATCH-SETTINGS.BUILD-LIMIT-DEFAULT"));
Common.sendMMMessage(player, "<red>Build Height Limit disabled for " + arena.getName());
arena.setData();
Common.sendMMMessage(player, "<red>Build Height Limit disabled for " + arena.getName() +
" <gray>(now relative: spawn Y + " + ConfigManager.getInt("MATCH-SETTINGS.BUILD-LIMIT-DEFAULT") + " blocks — use right-click to set an absolute limit)");
return;
}

Expand All @@ -341,6 +343,7 @@ private void handleBuildMax(Player player, DisplayArena arena, Action action) {

arena.setBuildMaxValue(pos.getBlockY());
arena.setBuildMax(true);
arena.setData();

Common.sendMMMessage(player, LanguageManager.getString("COMMAND.ARENA.ARGUMENTS.SETBUILDMAX.SET-BUILDMAX")
.replace("%arena%", arena.getName())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,19 +151,21 @@ public static boolean changeStatus(Player player, DisplayArena arena) {
}
}

arena.setEnabled(!arena.isEnabled());
arena.setData();

if (arena instanceof FFAArena) {
FFA ffa = ((FFAArena) arena).getFfa();

if (ffa != null) {
if (arena.isEnabled()) {
ffa.close("");
} else {
ffa.open();
} else {
ffa.close("");
}
}
}

arena.setEnabled(!arena.isEnabled());
return true;
}

Expand Down Expand Up @@ -233,16 +235,16 @@ public static boolean containsDestroyableBlock(Ladder ladder, Block block) {

public static boolean requiresSupport(Block block) {
Material type = block.getType();
return org.bukkit.Tag.FLOWERS.isTagged(type)
|| org.bukkit.Tag.SAPLINGS.isTagged(type)
|| org.bukkit.Tag.CROPS.isTagged(type)
|| org.bukkit.Tag.WALL_POST_OVERRIDE.isTagged(type) // torches, signs on walls, etc.
return Tag.FLOWERS.isTagged(type)
|| Tag.SAPLINGS.isTagged(type)
|| Tag.CROPS.isTagged(type)
|| Tag.WALL_POST_OVERRIDE.isTagged(type) // torches, signs on walls, etc.
|| type == Material.DEAD_BUSH
|| type == Material.SHORT_GRASS
|| type == Material.TALL_GRASS
|| type == Material.FERN
|| type == Material.LARGE_FERN
|| type == Material.VINE
|| type.name().equals("VINE") || type.name().contains("_VINE") || type.name().contains("_VINES")
|| type == Material.SUGAR_CANE
|| type == Material.CACTUS
|| type == Material.SNOW
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,14 @@ public void onPlayerInteract(PlayerInteractEvent e) {

FFA ffa = FFAManager.getInstance().getFFAByPlayer(player);
if (ffa == null) return;

// Prevent interaction while waiting for kit selection
if (ffa.isPlayerWaitingForKitSelection(player)) {
ffa.playerSelectKit(player, player.getInventory().getHeldItemSlot());
e.setCancelled(true);
return;
}

if (!action.equals(Action.RIGHT_CLICK_AIR) && !action.equals(Action.RIGHT_CLICK_BLOCK)) return;

Block clickedBlock = e.getClickedBlock();
Expand Down Expand Up @@ -220,7 +220,7 @@ public void onBlockBreak(BlockBreakEvent e) {
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())) {
if (ffa.getArena().isBuildMax() && e.getBlock().getLocation().getY() >= ListenerUtil.getCalculatedBuildLimit(ffa.getArena())) {
Common.sendMMMessage(player, LanguageManager.getString("FFA.GAME.CANT-BUILD-OVER-LIMIT"));
e.setCancelled(true);
return;
Expand Down Expand Up @@ -272,7 +272,7 @@ public void onBlockPlace(BlockPlaceEvent e) {
return;
}

if (block.getLocation().getY() >= ListenerUtil.getCalculatedBuildLimit(arena)) {
if (arena.isBuildMax() && block.getLocation().getY() >= ListenerUtil.getCalculatedBuildLimit(arena)) {
Common.sendMMMessage(player, LanguageManager.getString("FFA.GAME.CANT-BUILD-OVER-LIMIT"));
e.setCancelled(true);
}
Expand Down Expand Up @@ -302,7 +302,7 @@ public void onBucketEmpty(PlayerBucketEmptyEvent e) {
return;
}

if (block.getRelative(e.getBlockFace()).getLocation().getY() >= ListenerUtil.getCalculatedBuildLimit(ffa.getArena())) {
if (ffa.getArena().isBuildMax() && block.getRelative(e.getBlockFace()).getLocation().getY() >= ListenerUtil.getCalculatedBuildLimit(ffa.getArena())) {
Common.sendMMMessage(player, LanguageManager.getString("FFA.GAME.CANT-BUILD-OVER-LIMIT"));
e.setCancelled(true);
}
Expand Down Expand Up @@ -389,6 +389,9 @@ public void onPlayerDeath(PlayerDeathEvent e) {
Player killer = resolveKiller(player, ffa, damageSource);

DeathCause cause = FightUtil.convert(damageSource.getDamageType());
if (cause == DeathCause.EXPLOSION && killer != null && !killer.equals(player)) {
cause = DeathCause.EXPLOSION_BY_PLAYER;
}
ffa.killPlayer(player, killer, cause.getMessage().replace("%killer%", killer != null ? killer.getName() : "Unknown"));

if (killer != null && !killer.equals(player)) {
Expand Down Expand Up @@ -484,4 +487,4 @@ public void onEntityDamage(EntityDamageEvent e) {
}
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,32 @@ public void onBlockPlace(BlockPlaceEvent event) {
}
}

/**
* Captures support-dependent blocks for rollback before physics removes them.
* <p>
* Uses the <b>source block</b> of the physics event (the block that changed,
* triggering neighbor physics) to look up the owning fight — O(1) check instead
* of scanning all active fights for every affected neighbor.
* <p>
* At LOWEST priority the affected block still holds its original material,
* so we snapshot it before physics turns it to air.
*/
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onBlockPhysics(BlockPhysicsEvent e) {
Block block = e.getBlock();

if (!ArenaUtil.requiresSupport(block)) return;
if (BlockUtil.hasMetadata(block, PLACED_IN_FIGHT)) return;

Block source = e.getSourceBlock();
if (source == null) return;

Spectatable spectatable = getByBlock(source);
if (spectatable == null || !spectatable.isBuild()) return;

spectatable.getFightChange().addArenaBlockChange(new ChangedBlock(block));
}

@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onBlockIgnite(BlockIgniteEvent event) {
Block ignitedBlock = event.getBlock();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ public void startMatch() {
for (Player online : Bukkit.getOnlinePlayers()) {
if (!this.players.contains(online)) {
PlayerHider.getInstance().hidePlayer(player, online, true);
PlayerHider.getInstance().hidePlayer(online, player, false);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ protected boolean isWithinBuildLimits(Block block, Match match, Player player) {
// Check height limit
// Note: The limit represents the maximum Y coordinate blocks can reach (top of block)
// Since blocks occupy Y to Y+1, we check if the block's position (bottom) is >= limit
if (block.getLocation().getY() >= ListenerUtil.getCalculatedBuildLimit(match.getArena())) {
if (match.getArena().isBuildMax() && block.getLocation().getY() >= ListenerUtil.getCalculatedBuildLimit(match.getArena())) {
Common.sendMMMessage(player, LanguageManager.getString("MATCH.CANT-BUILD-OVER-LIMIT"));
return false;
}
Expand Down Expand Up @@ -691,8 +691,12 @@ public void onPlayerDeath(PlayerDeathEvent e) {
}

DeathCause cause = FightUtil.convert(damageSource.getDamageType());
if (cause == DeathCause.EXPLOSION && killer != null && !killer.equals(player)) {
cause = DeathCause.EXPLOSION_BY_PLAYER;
}
DeathCause finalCause = cause;
Bukkit.getScheduler().runTaskLater(ZonePractice.getInstance(), () ->
match.killPlayer(player, killer, cause.getMessage().replace("%killer%", killer != null ? killer.getName() : "Unknown")), 1L);
match.killPlayer(player, killer, finalCause.getMessage().replace("%killer%", killer != null ? killer.getName() : "Unknown")), 1L);

if (killer != null) {
Statistic statistic = match.getCurrentStat(killer);
Expand Down Expand Up @@ -898,4 +902,4 @@ private static boolean shouldDelegateExplosiveByEntityDamageToLadder(EntityDamag
return ladderType == LadderType.FIREBALL_FIGHT || ladderType == LadderType.TNT_SUMO;
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ public void onPlayerChooseKit(PlayerInteractEvent e) {
if (match == null) return;

MatchFightPlayer matchFightPlayer = match.getMatchPlayers().get(player);
if (matchFightPlayer == null) return;

if (!matchFightPlayer.isHasChosenKit()) {
e.setCancelled(true);

Expand All @@ -108,6 +110,8 @@ public void onPlayerChooseKit(InventoryClickEvent e) {
if (match == null) return;

MatchFightPlayer matchFightPlayer = match.getMatchPlayers().get(player);
if (matchFightPlayer == null) return;

if (!matchFightPlayer.isHasChosenKit()) {
e.setCancelled(true);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import dev.nandi0813.practice.manager.fight.match.util.TeamUtil;
import dev.nandi0813.practice.manager.fight.match.util.TempKillPlayer;
import dev.nandi0813.practice.manager.inventory.InventoryManager;
import dev.nandi0813.practice.manager.nametag.NametagManager;
import dev.nandi0813.practice.manager.ladder.abstraction.Ladder;
import dev.nandi0813.practice.manager.ladder.abstraction.interfaces.DeathResult;
import dev.nandi0813.practice.manager.ladder.abstraction.normal.NormalLadder;
Expand Down Expand Up @@ -61,6 +62,10 @@ public Duel(Ladder ladder, Arena arena, List<Player> players, boolean ranked, in
} else {
this.player2 = null;
}

NametagManager.getInstance().setNametag(player1, TeamEnum.TEAM1.getPrefix(), TeamEnum.TEAM1.getNameColor(), TeamEnum.TEAM1.getSuffix(), 20);
if (player2 != null)
NametagManager.getInstance().setNametag(player2, TeamEnum.TEAM2.getPrefix(), TeamEnum.TEAM2.getNameColor(), TeamEnum.TEAM2.getSuffix(), 21);
}

@Override
Expand Down Expand Up @@ -190,6 +195,7 @@ public void removePlayer(Player player, boolean quit) {
if (!players.contains(player)) return;

players.remove(player);
matchPlayers.remove(player);
MatchManager.getInstance().getPlayerMatches().remove(player);

// Only process quit logic if the match hasn't ended yet
Expand Down
Loading
Loading