Fix случайная смерть админа в режиме undying (acis 409)

Hitcher

Знаменитый
Местный
Сообщения
199
Розыгрыши
0
Репутация
1
Реакции
21
Баллы
1 280
Хроники
  1. Interlude
Исходники
Присутствуют
Сборка
acis 409
Делюсь самым минимальным фиксом за всю историю L2j)
***Специальная благодарность к разработчикам ИИ!)

Баг: случайная смерть админа в режиме 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 ---

Найдите метод 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)

Проблема: Состояние гонки (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 должна это исправить.
 

upd: фикс не работает) выясняю дальше))) админ стал умирать реже но проблема все еще есть.
 
Весьма опрометчиво ставить синхронизацию на этот метод. Вы можете совершенно спокойно поймать дедлок.
 
После переименования метода и синхронизации - Вы ничего не исправите)
Делюсь самым минимальным фиксом за всю историю L2j)
***Специальная благодарность к разработчикам ИИ!)

Баг: случайная смерть админа в режиме 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 ---

Найдите метод 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)

Проблема: Состояние гонки (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 должна это исправить.
 
Хз даже для чего этот режим, но почему бы просто не сделать проверку на статус админа, и запретить ему умирать в этом состоянии?
 
Сори за оверпостинг!) по другому не умею. Fix 0.2 проверил на парике из сотни мобов, и даже не сдох!=)
Изменения затронули два метода в двух классах:
Код:
Creature.java

    public boolean doDie(Creature killer)
    {
        // killing is only possible one time
        // ИЗМЕНЕНИЕ: Блок synchronized гарантирует, что только один поток
        // может выполнять критические операции смерти для этого существа одновременно.
        synchronized (this)
        {
           if (isDead())
           {
               return false;
           }

           // ИЗМЕНЕНИЕ: Это критически важная проверка.
           // Если актор не смертен (например, в режиме //undying),
           // то фактические шаги смерти (обнуление HP, установка флага isDead)
           // не должны выполняться. Это служит последним барьером безопасности.
           if (isMortal() == false)
           {
               // Возвращаем false, так как фактической смерти не произошло,
               // несмотря на попытку вызова doDie. Это важно для логики, которая
               // может зависеть от успешности вызова doDie.
               return false;
           }

           // now reset currentHp to zero
           getStatus().setHp(0);

           setIsDead(true);
        }

        // Abort attack, cast and move.
        abortAll(true);

        // Stop Regeneration task, and removes all current effects
        getStatus().stopHpMpRegeneration();
        stopAllEffectsExceptThoseThatLastThroughDeath();

        calculateRewards(killer);

        // Send the Server->Client packet StatusUpdate with current HP and MP to all other Player to inform
        getStatus().broadcastStatusUpdate();

        // Notify Creature AI
        getAI().notifyEvent(AiEventType.DEAD, null, null);

        return true; // Возвращаем true, так как смерть произошла (для смертных акторов)
    }



PlayerStatus.java

    // ИЗМЕНЕНИЕ: Добавление ключевого слова 'synchronized' к объявлению метода.
    // Причина и для чего: Это предотвращает состояния гонки, когда несколько источников урона
    // одновременно пытаются изменить HP существа, гарантируя,
    // что только один поток может выполнять этот метод для данного актора в любой момент времени.
    public final synchronized void reduceHp(double value, Creature attacker, boolean awake, boolean isDOT, boolean isHPConsumption, boolean ignoreCP)
    {
        if (_actor.isDead())
        {
            return;
        }

        // invul handling
        if (_actor.isInvul())
        {
            // other chars can't damage
            if (attacker != _actor)
            {
                return;
            }

            // only DOT and HP consumption allowed for damage self
            if (!isDOT && !isHPConsumption)
            {
                return;
            }
        }

        if (!isHPConsumption)
        {
            _actor.stopEffects(EffectType.SLEEP);
            _actor.stopEffects(EffectType.IMMOBILE_UNTIL_ATTACKED);

            // When taking a hit, stand up - except if under shop mode.
            if (_actor.isSitting() && !_actor.isInStoreMode())
                _actor.standUp();

            if (!isDOT && _actor.isStunned() && Rnd.get(10) == 0)
            {
                _actor.stopEffects(EffectType.STUN);

                // Refresh abnormal effects.
                _actor.updateAbnormalEffect();
            }
        }

        if (attacker != null && attacker != _actor)
        {
            final Player attackerPlayer = attacker.getActingPlayer();
            if (attackerPlayer != null && !attackerPlayer.getAccessLevel().canGiveDamage())
            {
                return;
            }

            if (_actor.isInDuel())
            {
                final DuelState playerState = _actor.getDuelState();
                if (playerState == DuelState.DEAD || playerState == DuelState.WINNER)
                {
                    return;
                }

                // Cancel duel if player got hit by another player that is not part of the duel or if player isn't in duel state.
                if (attackerPlayer == null || attackerPlayer.getDuelId() != _actor.getDuelId() || playerState != DuelState.DUELLING)
                {
                    _actor.setDuelState(DuelState.INTERRUPTED);
                }
            }

            int fullValue = (int) value;
            int tDmg = 0;

            // Check and calculate transfered damage, if any.
            final Summon summon = _actor.getSummon();
            if (summon instanceof Servitor && summon.isIn3DRadius(_actor, 900))
            {
                tDmg = (int) (value * calcStat(Stats.TRANSFER_DAMAGE_PERCENT, 0, null, null) / 100.);

                // Only transfer dmg up to current HP, it should not be killed
                tDmg = Math.min((int) summon.getStatus().getHp() - 1, tDmg);
                if (tDmg > 0)
                {
                    summon.reduceCurrentHp(tDmg, attacker, null);
                    value -= tDmg;
                    fullValue = (int) value; // reduce the announced value here as player will get a message about summon damage
                }
            }

            if (!ignoreCP && attacker instanceof Playable)
            {
                if (_cp >= value)
                {
                    setCp(_cp - value); // Set Cp to diff of Cp vs value
                    value = 0; // No need to subtract anything from Hp
                }
                else
                {
                    value -= _cp; // Get diff from value vs Cp; will apply diff to Hp
                    setCp(0, false); // Set Cp to 0
                }
            }

            if (fullValue > 0 && !isDOT)
            {
                _actor.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.S1_GAVE_YOU_S2_DMG).addCharName(attacker).addNumber(fullValue));

                if (tDmg > 0 && attackerPlayer != null)
                    attackerPlayer.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.GIVEN_S1_DAMAGE_TO_YOUR_TARGET_AND_S2_DAMAGE_TO_SERVITOR).addNumber(fullValue).addNumber(tDmg));
            }
        }

        if (value > 0)
        {
            // ИЗМЕНЕНИЕ: Используем новую переменную newHpValue для вычисления.
            // Причина и для чего: Это делает код более читаемым и предотвращает случайные изменения
            // входящего параметра 'value' до того, как он будет полностью обработан.
            double newHpValue = _hp - value;

            // ИЗМЕНЕНИЕ: Добавлена новая логика для ограничения HP бессмертных акторов.
            // Причина и для чего: Если актор не смертен (isMortal() == false) И вычисленное HP
            // опускается ниже 1.0 (но выше 0), то принудительно устанавливаем HP в 1.0.
            // Это гарантирует, что бессмертные существа всегда будут иметь минимум 1.0 HP
            // и не "умрут" из-за того, что их HP оказалось между 0 и 1.0.
            if (!_actor.isMortal() && newHpValue < 1.0)
            {
                newHpValue = 1.0;
            }
            
            // ИЗМЕНЕНИЕ: Пересмотренная логика обработки HP, когда оно опускается до 0 или ниже.
            // Причина и для чего: Этот блок теперь срабатывает после проверки бессмертия.
            // Он обрабатывает случаи, когда HP действительно становится 0 или меньше (для смертных)
            // или специфичную логику дуэлей. Для бессмертных акторов, HP уже будет 1.0,
            // поэтому это условие 'newHpValue <= 0' не будет для них истинным.
            if (newHpValue <= 0)
            {
                if (_actor.isInDuel())
                {
                    if (_actor.getDuelState() == DuelState.DUELLING)
                    {
                        _actor.disableAllSkills();
                        stopHpMpRegeneration();
                        DuelManager.getInstance().onPlayerDefeat(_actor);
                    }
                    newHpValue = 1; // В дуэли HP всегда 1 после поражения
                }
                else if (_actor.isMortal())
                {
                    newHpValue = 0;
                }
            }
            setHp(newHpValue); // Устанавливаем новое HP с учетом всех корректировок
        }

        // Handle die process if value is too low.
        // ИЗМЕНЕНИЕ: Теперь, благодаря новой логике ограничения HP, для бессмертных акторов
        // _hp никогда не будет меньше 0.5 (если оно не должно быть 0 для смертных),
        // что предотвращает ошибочный вызов doDie() для бессмертных.
        if (_hp < 0.5)
        {
            if (_actor.isInOlympiadMode())
            {
                // Abort attack, cast and move.
                _actor.abortAll(false);

                stopHpMpRegeneration();
                _actor.setIsDead(true); // Установка флага смерти

                final Summon summon = _actor.getSummon();
                if (summon != null)
                    summon.getAI().tryToIdle();

                return; // Выход из метода
            }

            _actor.doDie(attacker); // Вызов метода смерти (теперь защищен проверкой isMortal() внутри doDie)

            final QuestState qs = _actor.getQuestList().getQuestState("Tutorial");
            if (qs != null)
                qs.getQuest().notifyEvent("CE30", null, _actor);
        }
    }
Подскажите по синхронизации? лучше убрать?
 
Посмотреть вложение 88030

Это кандидаты на дедлоки я правильно понимаю?
Кандидатом на дедлок станет произвольный поток, который попробует вызвать этот метод, будучи самим вызванным из каких-то методов по стаку уходящих в этот метод. А внутри этого метода вызывается достаточно много всего, чтобы такая ситуация произошла с большой вероятностью.
 
ТС, почему просто не проверять в том же doDie флаг/свойство undying? или даже в isDead() дополнить проверку что толкьо если этого флага нет, то только тогда isDead может вернуть true.
без всяких синхронизаций/локов вобщем обойтись
 
Очень приятно, что настолько крутые ребята помогли админу не умереть - буквально спасли от смерти!) Благодарю вас!
Я обязательно испробую проверку на статус админа и еще вариант с doDie флаг/свойство undying. Все протестирую и отпишусь с результатами.
 
Что если дополнить проверку isAcis и не юзать ее на нормальных проектах. Она написана типа «умными» людьми, но в нормальных условиях обосрется со сборкой скории 2007го года
 
Что если дополнить проверку isAcis и не юзать ее на нормальных проектах. Она написана типа «умными» людьми, но в нормальных условиях обосрется со сборкой скории 2007го года
Божественный Свет — это бесконечная энергия Творца, изливающаяся в миры через сосуды. Сосуды не выдержали этого Света и разбились, породив хаос и рассеяв искры Божественности в материальном мире. Миссия человека — собирать эти искры через добрые дела, осознанность и духовную работу, восстанавливая утраченное единство с Божественным.
 
Хз даже для чего этот режим, но почему бы просто не сделать проверку на статус админа, и запретить ему умирать в этом состоянии?
Еcли я правильно понял вашу логику. То для абсолютного бессмертия админа уже есть метод Invul - полная неуязвимость. Но undying режим value = (_actor.isMortal()) ? 0 : 1; Предполагает что урон наносится, но у админа должно оставаться всегда 1 hp. Хотелось бы разобраться глубже в корне этой проблемы, по какой причине возникают ситуации когда hp пробивается и становится <1 (0.40) и вызывается doDie.

-----------------------------------------------------------------------------------------------

ТС, почему просто не проверять в том же doDie флаг/свойство undying? или даже в isDead() дополнить проверку что толкьо если этого флага нет, то только тогда isDead может вернуть true.
без всяких синхронизаций/локов вобщем обойтись
Определенно самый меткий и хирургически точный фикс заслужено остается за Gaikotsu.
Нужно смотреть на общую картину шире, и иногда задачу решать с ее конца а не с начала! Что и доказал Gaikotsu)
Считаю такой подход самым эффективным решением проблемы:
Java:
Creature.java

    public boolean doDie(Creature killer)
    {
        // Если персонаж бессмертен, смерть невозможна
        if (!isMortal())
            return false;

-----------------------------------------------------------------------------------------------
Весьма опрометчиво ставить синхронизацию на этот метод. Вы можете совершенно спокойно поймать дедлок.
Мой вывод: Не стоит слепо доверять решениям ии. Это может повлечь лавину проблем как предсказал Aristo по излишнему synchronized.
 
Еcли я правильно понял вашу логику. То для абсолютного бессмертия админа уже есть метод Invul - полная неуязвимость. Но undying режим value = (_actor.isMortal()) ? 0 : 1; Предполагает что урон наносится, но у админа должно оставаться всегда 1 hp. Хотелось бы разобраться глубже в корне этой проблемы, по какой причине возникают ситуации когда hp пробивается и становится <1 (0.40) и вызывается doDie.

-----------------------------------------------------------------------------------------------


Определенно самый меткий и хирургически точный фикс заслужено остается за Gaikotsu.
Нужно смотреть на общую картину шире, и иногда задачу решать с ее конца а не с начала! Что и доказал Gaikotsu)
Считаю такой подход самым эффективным решением проблемы:
Java:
Creature.java

    public boolean doDie(Creature killer)
    {
        // Если персонаж бессмертен, смерть невозможна
        if (!isMortal())
            return false;

-----------------------------------------------------------------------------------------------

Мой вывод: Не стоит слепо доверять решениям ии. Это может повлечь лавину проблем как предсказал Aristo по излишнему synchronized.
Не, я имел введу то что гайка написал.
 
Не, я имел введу то что гайка написал.
мне кажется, Hitcher тут выступает строго посредником между mmo-dev и ChatGPT. С большой долей вероятности он даже установку реакций согласовывает с ней.
 
мне кажется, Hitcher тут выступает строго посредником между mmo-dev и ChatGPT. С большой долей вероятности он даже установку реакций согласовывает с ней.
А за что мортал отвечает, и почему просто не возвращать труе, если !андаинг? Я не кодер, мне просто интересно, как более логично сделать.
 
мне кажется, Hitcher тут выступает строго посредником между mmo-dev и ChatGPT. С большой долей вероятности он даже установку реакций согласовывает с ней.
я и есть ChatGpt)
 
А за что мортал отвечает, и почему просто не возвращать труе, если !андаинг? Я не кодер, мне просто интересно, как более логично сделать.
Ну я хз зачем там чет менять, если там это по дефолту есть.
1752357946452.webp
и переключается оно командой админской
1752358000938.webp
+ сразу в начале метода есть как минимум проверка бессмертия,
1752358185231.webp
то вероятно админ дохнет от каких-то селфов или дотов, а судя по тому, что флаг смерти меняется произвольно с кучи мест, проблема может быть в принципе не в этом методе.
1752358356407.webp
Сорян, не ковырял глубоко. Скачал, бегло глянул.
 
Ну как я выше и говорил - можно просто в doDie или в reduceHp на этот самый isMortal проверять к примеру.
Единственный минус будет только в том что админ вобще не сможет помереть в этом режиме, даже если сам захочет суциднуться зачем-то.

З.Ы. у меня похожее тоже есть в onReduceCurrentHp базового Creature, в основном юзаемое для нпс/мобов, которые должны получать урон, но при этом не должны умирать. Ну и в частности и для ГМов в режиме бессмертия юзается. Если хп упало ниже 1, то проверяется иммунитет к смерти (или то что это игрок на олли) и если условие сработало то хп выставляется больше 1 (чтобы в других местах проверки не возвратили статус "умер") и дальше по коду не пускает, т.е. doDie не вызывает.
Java:
        if (getCurrentHp() < 1.)
        {
            if (isDeathImmune() || (isPlayer() && isInOlympiadMode()))
            {
                setCurrentHp(1.5);
                return;
            }

            _deathByReflect = damageType == DamageType.REFLECT;
            doDie(attacker);
        }
З.З.Ы. может возникнуть вопрос - почему я для тех же нпс/мобов обычную неуязвимость не заюзаю для подобных ситуаций. ну для реалий интерлюда то может бы и можно было, но только вот позже появился тип урона, который игнорирует обычную неуязвимость и все равно наносит урон в любом случае, так назваемый real-урон.
 
Последнее редактирование:
Назад
Сверху