fix: close two unbounded map leaks that survived player quit
Обе карты росли без границы и никогда не освобождались. 1. MagicListener.lastCast / lastCastTime Писались на каждом применении магии (recordCast) и не удалялись НИКОГДА — ни на выходе, ни на смерти. Каждый игрок, хоть раз применивший магию, навсегда оставлял две записи. Данные копеечные, но карты растут без границы весь срок жизни сервера. Очистка вынесена в общий forget(UUID), который зовут и onQuit, и onDeath. 2. TensionManager.battleEnemies На выходе не удалялся. Расчёт был на то, что запись исчезнет сама через BATTLE_EXPIRE_MS, но эта проверка требует непустого lastCombat — а он удалялся в той же строке выше. В итоге запись жила вечно, если в бою был противник, который не умирает: босс или запертый моб на ферме. Множество не пустеет, lastCombat == null, условие истечения не проверяется, и tickBattle обходит эту запись каждый тик до конца жизни сервера. Дополнительно tickBattle теперь снимает боевую запись и при lastCombat == null, а не только по таймауту. Это закрывает не только выход, но и любую другую рассинхронизацию: без боя и без отметки времени бой не бой.
This commit is contained in:
@@ -225,17 +225,29 @@ public class MagicListener implements Listener {
|
||||
return dmg * 0.7;
|
||||
}
|
||||
|
||||
/**
|
||||
* Забыть всё состояние игрока. Вызывается и на выходе, и на смерти.
|
||||
*
|
||||
* <p>Про lastCast: эти две карты писались на каждом применении магии и
|
||||
* НИКОГДА не удалялись. Каждый игрок, хоть раз применивший магию, навсегда
|
||||
* оставлял две записи — на весь срок жизни сервера. Данные там копеечные,
|
||||
* но карты растут без границы, и их приходится обходить каждый тик.
|
||||
*/
|
||||
private void forget(UUID id) {
|
||||
var task = shieldTasks.remove(id);
|
||||
if (task != null) task.cancel();
|
||||
shieldEnd.remove(id);
|
||||
lastCast.remove(id);
|
||||
lastCastTime.remove(id);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onQuit(org.bukkit.event.player.PlayerQuitEvent e) {
|
||||
var task = shieldTasks.remove(e.getPlayer().getUniqueId());
|
||||
if (task != null) task.cancel();
|
||||
shieldEnd.remove(e.getPlayer().getUniqueId());
|
||||
forget(e.getPlayer().getUniqueId());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onDeath(org.bukkit.event.entity.PlayerDeathEvent e) {
|
||||
var task = shieldTasks.remove(e.getEntity().getUniqueId());
|
||||
if (task != null) task.cancel();
|
||||
shieldEnd.remove(e.getEntity().getUniqueId());
|
||||
forget(e.getEntity().getUniqueId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,7 +212,11 @@ public class TensionManager implements Listener {
|
||||
|| (ent instanceof org.bukkit.entity.LivingEntity le && le.isDead());
|
||||
});
|
||||
final Long last = lastCombat.get(pid);
|
||||
if (set.isEmpty() || (last != null && now - last > BATTLE_EXPIRE_MS)) {
|
||||
// lastCombat == null означает, что бой не начинался или уже вышел из
|
||||
// состояния. Множество при этом может быть непустым (противник жив),
|
||||
// и тогда условие истечения по времени не проверяется вовсе — запись
|
||||
// остаётся навсегда. Без боя и без отметки времени бой не бой.
|
||||
if (last == null || set.isEmpty() || (now - last > BATTLE_EXPIRE_MS)) {
|
||||
if (drop == null) {
|
||||
drop = new java.util.ArrayList<>(4);
|
||||
}
|
||||
@@ -281,6 +285,14 @@ public class TensionManager implements Listener {
|
||||
tp.remove(id);
|
||||
lastCombat.remove(id);
|
||||
lastPct.remove(id);
|
||||
// Утечка: battleEnemies на выходе не чистился. Расчёт на то, что запись
|
||||
// исчезнет сама через BATTLE_EXPIRE_MS, но этот путь требует непустого
|
||||
// lastCombat — а его мы только что удалили строкой выше. В итоге запись
|
||||
// могла жить вечно: пока в бою есть противник, который не умирает
|
||||
// (босс на ферме, запертый моб), множество не пустеет, lastCombat равен
|
||||
// null, и условие истечения не проверяется. Дальше tickBattle обходит
|
||||
// эту запись каждый тик до конца жизни сервера.
|
||||
battleEnemies.remove(id);
|
||||
AbilityCooldown.clearAll(e.getPlayer());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user