diff --git a/pom.xml b/pom.xml
index 4e17394ce..a90f5c38f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -11,7 +11,7 @@
UTF-8
chancesd
https://sonarcloud.io
- 4.0.9
+ 4.0.10
diff --git a/pvpmanager/build.gradle b/pvpmanager/build.gradle
index 3ae76f063..55b5c33f3 100644
--- a/pvpmanager/build.gradle
+++ b/pvpmanager/build.gradle
@@ -6,7 +6,7 @@ plugins {
}
group = 'me.chancesd.pvpmanager'
-version = '4.0.3'
+version = '4.0.10'
description = 'A powerful plugin to manage various PvP combat features'
dependencies {
diff --git a/pvpmanager/src/main/java/me/chancesd/pvpmanager/command/Newbie.java b/pvpmanager/src/main/java/me/chancesd/pvpmanager/command/Newbie.java
index b9b819c0a..d185cb890 100644
--- a/pvpmanager/src/main/java/me/chancesd/pvpmanager/command/Newbie.java
+++ b/pvpmanager/src/main/java/me/chancesd/pvpmanager/command/Newbie.java
@@ -126,6 +126,12 @@ public AddNewbieCommand(final PlayerManager ph) {
public void execute(final CommandSender sender, final String label, final List args) {
final Player targetPlayer = getArgument(args, ARG_PLAYER).getAsPlayer();
final CombatPlayer target = ph.get(targetPlayer);
+ // FIX: Prevent orphaned NewbieTask accumulation — skip if already protected.
+ if (target.isNewbie()) {
+ sender.sendMessage(ChatUtils.colorize(
+ Lang.PREFIX + " FFFF55" + target.getName() + " FFAAAAalready has newbie protection"));
+ return;
+ }
target.setNewbie(true);
sender.sendMessage(ChatUtils.colorize(Lang.PREFIX + " Added newbie protection to FFFF55" + target.getName()));
}
diff --git a/pvpmanager/src/main/java/me/chancesd/pvpmanager/player/CombatPlayer.java b/pvpmanager/src/main/java/me/chancesd/pvpmanager/player/CombatPlayer.java
index 3794ffe6f..9a076c5dc 100644
--- a/pvpmanager/src/main/java/me/chancesd/pvpmanager/player/CombatPlayer.java
+++ b/pvpmanager/src/main/java/me/chancesd/pvpmanager/player/CombatPlayer.java
@@ -130,6 +130,14 @@ public final void setNewbie(final boolean newbie) {
public final void setNewbie(final boolean newbie, final long time) {
if (newbie) {
+ // FIX: Cancel existing newbieTask before overwriting the reference.
+ // Without this, the old task stays in the ScheduledThreadPoolExecutor queue
+ // (potentially for hours) while holding a strong CombatPlayer -> CraftPlayer
+ // reference, causing each orphaned task to retain ~4.85 GB of heap in aggregate.
+ // Triggered by repeated /newbie add on a player already under protection.
+ if (newbieTask != null) {
+ newbieTask.cancel();
+ }
this.newbieTask = new NewbieTask(this, time);
} else if (this.newbie && newbieTask != null) {
newbieTask.cancel();
@@ -480,6 +488,8 @@ public PlayerData exportPlayerData() {
public final void cleanForRemoval() {
if (newbieTask != null) {
newbieTask.cancel();
+ // FIX: Null out after cancel so the reference is released immediately.
+ newbieTask = null;
}
if (nametag != null) {
nametag.cleanup();