Keep ability cooldowns across logout

Dimension change and teleport were already fixed, but disconnect still cleared
cooldowns and login cleared them again, and they were never written to NBT - so
relogging reset every cooldown. They are absolute world-time deadlines, so they
now persist and are restored on load.
This commit is contained in:
KAMKEEL committed 2026-08-11 03:24:19 -04:00
1 parent aaf6734bd9
commit 3565c66cf8
1 file changed
+39 -5
@@ -832,9 +832,8 @@ public class PlayerAbilityData extends AbstractDataAbilities implements IPlayerA
currentTarget = null;
lastAbilityActivationTime = -1;
// Clear cooldowns (fresh start after relog)
cooldownEndTime = 0;
resetAllPerAbilityCooldowns();
// Cooldowns are absolute world-time deadlines and are restored from NBT, so relogging
// must not clear them or it becomes a way to bypass them.
interruptCooldownRolled = false;
// Reset sync tracking so next sync sends fresh state
@@ -852,9 +851,10 @@ public class PlayerAbilityData extends AbstractDataAbilities implements IPlayerA
if (currentAbility != null && currentAbility.isExecuting()) {
interruptCurrentAbility(null, 0);
}
// Clear any remaining transient state (cooldowns, chains, locks)
// Clear any remaining transient state (chains, locks)
// Don't clear toggles — they were already saved to NBT by this point
onEntityReconstructed(true, false);
// Don't clear cooldowns — they persist across sessions
onEntityReconstructed(false, false);
}
// ═══════════════════════════════════════════════════════════════════
@@ -1048,6 +1048,22 @@ public class PlayerAbilityData extends AbstractDataAbilities implements IPlayerA
toggleList.appendTag(toggleNbt);
}
compound.setTag("ActiveToggles", toggleList);
// Cooldowns are absolute world-time deadlines, so they stay meaningful across sessions
NBTTagCompound cooldowns = new NBTTagCompound();
cooldowns.setLong("GlobalEnd", cooldownEndTime);
cooldowns.setInteger("GlobalDuration", globalCooldownDuration);
NBTTagList perAbilityList = new NBTTagList();
for (Map.Entry<String, Long> entry : perAbilityCooldownEndTimes.entrySet()) {
NBTTagCompound cooldownNbt = new NBTTagCompound();
cooldownNbt.setString("Key", entry.getKey());
cooldownNbt.setLong("End", entry.getValue());
Integer duration = perAbilityCooldownDurations.get(entry.getKey());
cooldownNbt.setInteger("Duration", duration == null ? 0 : duration);
perAbilityList.appendTag(cooldownNbt);
}
cooldowns.setTag("PerAbility", perAbilityList);
compound.setTag("AbilityCooldowns", cooldowns);
}
public void readFromNBT(NBTTagCompound compound) {
@@ -1082,6 +1098,24 @@ public class PlayerAbilityData extends AbstractDataAbilities implements IPlayerA
setToggleEntryDirect(key, state);
}
}
if (compound.hasKey("AbilityCooldowns")) {
cooldownEndTime = 0;
globalCooldownDuration = 0;
resetAllPerAbilityCooldowns();
NBTTagCompound cooldowns = compound.getCompoundTag("AbilityCooldowns");
cooldownEndTime = cooldowns.getLong("GlobalEnd");
globalCooldownDuration = cooldowns.getInteger("GlobalDuration");
NBTTagList perAbilityList = cooldowns.getTagList("PerAbility", 10); // 10 = TAG_COMPOUND
for (int i = 0; i < perAbilityList.tagCount(); i++) {
NBTTagCompound entry = perAbilityList.getCompoundTagAt(i);
String key = entry.getString("Key");
if (key == null || key.isEmpty()) continue;
perAbilityCooldownEndTimes.put(key, entry.getLong("End"));
perAbilityCooldownDurations.put(key, entry.getInteger("Duration"));
}
}
}
/**