- Хроники
- Interlude
- Исходники
- Присутствуют
- Сборка
- acis 409
Делюсь самым минимальным фиксом за всю историю L2j)
***Специальная благодарность к разработчикам ИИ!)
Баг: случайная смерть админа в режиме undying.
***Специальная благодарность к разработчикам ИИ!)
Баг: случайная смерть админа в режиме undying.
DEBUG (reduceHp): --- Entering reduceHp for Veronika ---
DEBUG (reduceHp): Initial HP: 636.28, Damage value: 394.0
DEBUG (reduceHp): isMortal: false, isInvul: false, isDead: false
DEBUG (reduceHp): Applying damage to HP. Initial _hp: 1051.4, value to subtract: 394.0
DEBUG (reduceHp): HP after subtraction: 657.4000000000001
DEBUG (reduceHp): Final HP after setHp call: 657.4000000000001
DEBUG (reduceHp): Checking for death condition. Current _hp: 657.4000000000001
DEBUG (reduceHp): --- Exiting reduceHp for Veronika ---
DEBUG (reduceHp): --- Entering reduceHp for Veronika ---
DEBUG (reduceHp): Initial HP: 657.4000000000001, Damage value: 248.0
DEBUG (reduceHp): isMortal: false, isInvul: false, isDead: false
DEBUG (reduceHp): Applying damage to HP. Initial _hp: 657.4000000000001, value to subtract: 248.0
DEBUG (reduceHp): HP after subtraction: 409.4000000000001
DEBUG (reduceHp): Final HP after setHp call: 409.4000000000001
DEBUG (reduceHp): Checking for death condition. Current _hp: 409.4000000000001
DEBUG (reduceHp): --- Exiting reduceHp for Veronika ---
DEBUG (reduceHp): --- Entering reduceHp for Veronika ---
DEBUG (reduceHp): --- Entering reduceHp for Veronika ---
DEBUG (reduceHp): Initial HP: 409.4000000000001, Damage value: 162.0
DEBUG (reduceHp): isMortal: false, isInvul: false, isDead: false
DEBUG (reduceHp): Applying damage to HP. Initial _hp: 409.4000000000001, value to subtract: 162.0
DEBUG (reduceHp): HP after subtraction: 247.4000000000001
DEBUG (reduceHp): Initial HP: 409.4000000000001, Damage value: 247.0
DEBUG (reduceHp): isMortal: false, isInvul: false, isDead: false
DEBUG (reduceHp): Applying damage to HP. Initial _hp: 247.4000000000001, value to subtract: 247.0
DEBUG (reduceHp): Final HP after setHp call: 247.4000000000001
DEBUG (reduceHp): HP after subtraction: 0.40000000000009095
DEBUG (reduceHp): Checking for death condition. Current _hp: 247.4000000000001
DEBUG (reduceHp): Final HP after setHp call: 0.40000000000009095
DEBUG (reduceHp): _hp is less than 0.5. Initiating death sequence.
DEBUG (reduceHp): Checking for death condition. Current _hp: 0.40000000000009095
DEBUG (reduceHp): _hp is less than 0.5. Initiating death sequence.
DEBUG (reduceHp): Calling doDie for Veronika.
DEBUG (reduceHp): Calling doDie for Veronika.
DEBUG (reduceHp): --- Exiting reduceHp for Veronika ---
DEBUG (reduceHp): Initial HP: 636.28, Damage value: 394.0
DEBUG (reduceHp): isMortal: false, isInvul: false, isDead: false
DEBUG (reduceHp): Applying damage to HP. Initial _hp: 1051.4, value to subtract: 394.0
DEBUG (reduceHp): HP after subtraction: 657.4000000000001
DEBUG (reduceHp): Final HP after setHp call: 657.4000000000001
DEBUG (reduceHp): Checking for death condition. Current _hp: 657.4000000000001
DEBUG (reduceHp): --- Exiting reduceHp for Veronika ---
DEBUG (reduceHp): --- Entering reduceHp for Veronika ---
DEBUG (reduceHp): Initial HP: 657.4000000000001, Damage value: 248.0
DEBUG (reduceHp): isMortal: false, isInvul: false, isDead: false
DEBUG (reduceHp): Applying damage to HP. Initial _hp: 657.4000000000001, value to subtract: 248.0
DEBUG (reduceHp): HP after subtraction: 409.4000000000001
DEBUG (reduceHp): Final HP after setHp call: 409.4000000000001
DEBUG (reduceHp): Checking for death condition. Current _hp: 409.4000000000001
DEBUG (reduceHp): --- Exiting reduceHp for Veronika ---
DEBUG (reduceHp): --- Entering reduceHp for Veronika ---
DEBUG (reduceHp): --- Entering reduceHp for Veronika ---
DEBUG (reduceHp): Initial HP: 409.4000000000001, Damage value: 162.0
DEBUG (reduceHp): isMortal: false, isInvul: false, isDead: false
DEBUG (reduceHp): Applying damage to HP. Initial _hp: 409.4000000000001, value to subtract: 162.0
DEBUG (reduceHp): HP after subtraction: 247.4000000000001
DEBUG (reduceHp): Initial HP: 409.4000000000001, Damage value: 247.0
DEBUG (reduceHp): isMortal: false, isInvul: false, isDead: false
DEBUG (reduceHp): Applying damage to HP. Initial _hp: 247.4000000000001, value to subtract: 247.0
DEBUG (reduceHp): Final HP after setHp call: 247.4000000000001
DEBUG (reduceHp): HP after subtraction: 0.40000000000009095
DEBUG (reduceHp): Checking for death condition. Current _hp: 247.4000000000001
DEBUG (reduceHp): Final HP after setHp call: 0.40000000000009095
DEBUG (reduceHp): _hp is less than 0.5. Initiating death sequence.
DEBUG (reduceHp): Checking for death condition. Current _hp: 0.40000000000009095
DEBUG (reduceHp): _hp is less than 0.5. Initiating death sequence.
DEBUG (reduceHp): Calling doDie for Veronika.
DEBUG (reduceHp): Calling doDie for Veronika.
DEBUG (reduceHp): --- Exiting reduceHp for Veronika ---
Найдите метод reduceHp в PlayerStatus.java
замените:
public final void reduceHp(double value, Creature attacker, boolean awake, boolean isDOT, boolean isHPConsumption, boolean ignoreCP)
на:
public final synchronized void reduceHp(double value, Creature attacker, boolean awake, boolean isDOT, boolean isHPConsumption, boolean ignoreCP)
замените:
public final void reduceHp(double value, Creature attacker, boolean awake, boolean isDOT, boolean isHPConsumption, boolean ignoreCP)
на:
public final synchronized void reduceHp(double value, Creature attacker, boolean awake, boolean isDOT, boolean isHPConsumption, boolean ignoreCP)
Проблема: Состояние гонки (Race Condition) или одновременное нанесение урона.
Судя по логам, происходит следующее:
У Вероники было 409.40 HP.
На нее почти одновременно или очень быстро друг за другом действуют два источника урона.
Первый вызов reduceHp (урон 162.0):
Получает Initial HP: 409.40.
Вычисляет новое HP: 409.40 - 162.0 = 247.40.
Устанавливает HP в 247.40.
Второй вызов reduceHp (урон 247.0) - происходит параллельно или очень быстро:
Здесь кроется проблема: Лог Initial HP: 409.4000000000001, Damage value: 247.0 показывает, что этот второй вызов, возможно, начал работу с "устаревшим" значением HP (409.40), которое еще не было обновлено глобально первым вызовом reduceHp (или же произошла другая рассинхронизация).
Однако, лог Applying damage to HP. Initial _hp: 247.4000000000001, value to subtract: 247.0 показывает, что внутри этого второго вызова, когда дошло дело до применения урона, переменная _hp уже отражала результат первого вызова.
Это приводит к тому, что 247.40 - 247.0 = 0.40.
Поскольку 0.40 меньше 0.5, срабатывает условие if (_hp < 0.5), и вызывается _actor.doDie(attacker).
Почему не сработала логика "HP clamp to 1.0"?
Логика value = (_actor.isMortal()) ? 0 : 1; срабатывает только если value <= 0 до вызова setHp(value).
В случае с 0.40, это значение больше 0, поэтому условие value <= 0 не выполняется, и HP не "зажимается" до 1.0, а устанавливается как 0.40. Это и приводит к вызову doDie.
Решение: Синхронизация метода reduceHp
Чтобы избежать такого состояния гонки, вам нужно убедиться, что только один поток может изменять HP существа в любой момент времени. Этого можно достичь, сделав метод reduceHp синхронизированным.
Объяснение synchronized:
Когда метод reduceHp помечен как synchronized, это означает, что только один поток может выполнять этот метод для данного объекта (this - т.е. для конкретного экземпляра CreatureStatus или Creature) в любой момент времени.
Если несколько источников урона (несколько потоков) пытаются одновременно вызвать reduceHp для одной и той же "Вероники", только один из них сможет войти в метод, а остальные будут ждать в очереди, пока первый не закончит.
Это гарантирует, что _hp всегда будет актуальным и не будет никаких "устаревших" значений, которые могли бы привести к неправильным расчетам и обходу логики "бессмертия".
После этого изменения, когда HP админа достигнет значения 1.0 (или любого другого значения, которое приведет к value <= 0 после вычитания урона), логика Actor is IMMORTAL (isMortal=false), HP clamped to 1.0. будет всегда корректно срабатывать, и HP не опустится ниже 1.0, предотвращая вызов doDie.
Итог: Ваш баг вызван не тем, что логика бессмертия не работает, а тем, что несколько источников урона могут одновременно пытаться изменить HP, вызывая состояние гонки. Синхронизация метода reduceHp должна это исправить.
Судя по логам, происходит следующее:
У Вероники было 409.40 HP.
На нее почти одновременно или очень быстро друг за другом действуют два источника урона.
Первый вызов reduceHp (урон 162.0):
Получает Initial HP: 409.40.
Вычисляет новое HP: 409.40 - 162.0 = 247.40.
Устанавливает HP в 247.40.
Второй вызов reduceHp (урон 247.0) - происходит параллельно или очень быстро:
Здесь кроется проблема: Лог Initial HP: 409.4000000000001, Damage value: 247.0 показывает, что этот второй вызов, возможно, начал работу с "устаревшим" значением HP (409.40), которое еще не было обновлено глобально первым вызовом reduceHp (или же произошла другая рассинхронизация).
Однако, лог Applying damage to HP. Initial _hp: 247.4000000000001, value to subtract: 247.0 показывает, что внутри этого второго вызова, когда дошло дело до применения урона, переменная _hp уже отражала результат первого вызова.
Это приводит к тому, что 247.40 - 247.0 = 0.40.
Поскольку 0.40 меньше 0.5, срабатывает условие if (_hp < 0.5), и вызывается _actor.doDie(attacker).
Почему не сработала логика "HP clamp to 1.0"?
Логика value = (_actor.isMortal()) ? 0 : 1; срабатывает только если value <= 0 до вызова setHp(value).
В случае с 0.40, это значение больше 0, поэтому условие value <= 0 не выполняется, и HP не "зажимается" до 1.0, а устанавливается как 0.40. Это и приводит к вызову doDie.
Решение: Синхронизация метода reduceHp
Чтобы избежать такого состояния гонки, вам нужно убедиться, что только один поток может изменять HP существа в любой момент времени. Этого можно достичь, сделав метод reduceHp синхронизированным.
Объяснение synchronized:
Когда метод reduceHp помечен как synchronized, это означает, что только один поток может выполнять этот метод для данного объекта (this - т.е. для конкретного экземпляра CreatureStatus или Creature) в любой момент времени.
Если несколько источников урона (несколько потоков) пытаются одновременно вызвать reduceHp для одной и той же "Вероники", только один из них сможет войти в метод, а остальные будут ждать в очереди, пока первый не закончит.
Это гарантирует, что _hp всегда будет актуальным и не будет никаких "устаревших" значений, которые могли бы привести к неправильным расчетам и обходу логики "бессмертия".
После этого изменения, когда HP админа достигнет значения 1.0 (или любого другого значения, которое приведет к value <= 0 после вычитания урона), логика Actor is IMMORTAL (isMortal=false), HP clamped to 1.0. будет всегда корректно срабатывать, и HP не опустится ниже 1.0, предотвращая вызов doDie.
Итог: Ваш баг вызван не тем, что логика бессмертия не работает, а тем, что несколько источников урона могут одновременно пытаться изменить HP, вызывая состояние гонки. Синхронизация метода reduceHp должна это исправить.