Валидация playable-персонажей

TheMads

Знаменитый
Участник
Сообщения
118
Розыгрыши
0
Репутация
5
Реакции
15
Баллы
1 270
Добрый вечер, решил переписать flytype-скилы (Rush Impact, Shadow Step, Warp и тд) и на последнем этапе столкнулся с проблемой некорректной обработки движения после использования Charge-скилов.

Баг актуален для всех сборок на базе l2p (Так же актуально для l2gw) даже если не трогать изначальный код.

В чем суть:
Если после окончания каста (после срабатывания clearCastVars() и finishFly()) начать отбегать то подключается nextAction.MOVE (обрабатывается в PlayableAI). По окончанию бега если персонаж на стороне клиента не добегает до точки то его возвращает в последние координаты после каста (в координаты, что находится в переменной _flyLoc и возвращается функцией getFlyLoc()).

Соответственно бежать можно бесконечно и по итогу персонаж все равно окажется на "левых" координатах. При этом после окончания бега сработает пакет validatePosition и переместит/портанет персонажа в зависимости от diff между клиентом и сервером.

Данного бага не было на лайве лостворлда (в шарных сурсах он остался)

Подробней как это проявляется:

Собственно вопрос - кто-то фиксил этот момент? Такое чувство что он зарыт где-то в мув-листе
 
Сегодня проверю на l2gw. L2GW основан на лыже вроде, нет?
 
Сегодня проверю на l2gw. L2GW основан на лыже вроде, нет?
На сколько я помню на старой версии CCP, от лыжи там мало чего есть. Даже если и на лыже то механика передвижений почти полностью совпадает с фениксо-подобными. А основа для геодвижка бралась (судя по копирайтам в GeoEngine) та же что и на большинстве l2p-подобных исходниках.

При этом есть некоторые зависимости этого бага от скорости бега и самой локации.
Если использовать скил без бафа - все отлично. Если бафнуть фул - баг проявляется. Но даже убрав из бафа вику он практически пропадает.
Боюсь предположить что будет если на берса прокинуть Throwling Dagger или получить штраф по весу.

Если в getFlyLocation (В l2gw getRushLoc или getRushLocation) поставить локацию не ближайшую к таргету а сам таргет, т.е:
loc = target.getLoc(); то раш работает оффлайк за исключением конечной точки куда он подлетает (т.е встает прямо в цель, что не приемлемо как по мне), но если во время раша таргет умирает то баг проявляется снова (опять же не понятна зависимость мертвого таргета и юза скила, идет аброад каста, или что-то еще?.

По идее нужно сделать валидацию более мягкой без резких дерганий персонажа.
На ПТСке если на фуле персонаж в случае MoveNext не долетает до таргета и начинает идти к точке то он останавливается на пол пути к указателю и указатель пропадает без анимации. Тоесть клиент-серверная локация не совпадает для самого игрока. После любого движения персонаж начинает ускоренно передвигаться к нужной точке если она близка к текущей, конечно.

P.S: На сколько помню как раз на лыже все работает в этом плане приемлемо

Забыл указать - на большинстве сборок используются две формулы для FlyToBack и соотв. Front что в корне не верно, советую выделить исключительно по ФлайТайпу ЧАРДЖ-скилы и использовать для них функцию applyOffset по типу:

applyOffset(target.getLoc(), target.getColRadius()) или просто 45 вместо радиуса коллизии.

Функция как раз просчитывает необходимую ближайшую локацию, в противном случае если у вас в раш-скиле стоит isFlyToBack - конечная локация будет спина, в противном - лицо, т.е не будут обрабатываться сайды таргера.
На сколько помню на l2gw это сделано так, как надо.
 
Последнее редактирование модератором:
У себя на лыже проверил... Такой фигни нету)
 
TheMads, такой баг и на овероподобных.

Нет.
Вообще везде надо переписывать мувинг.

Т.е предположение верно и в массив координат при передвижении передаются "левые" точки?
Ок, попробую переписать функции связанные с передвижением
 
У себя на лыже проверил... Такой фигни нету)
Т.е предположение верно и в массив координат при передвижении передаются "левые" точки?
Ок, попробую переписать функции связанные с передвижением
Пока решения нет. Для интересующихся - копать следует в таск передвижения.
Конкретней - строчки с просчетом дистанции по типу
Код:
donedist += (now - _startMoveTime) * _previousSpeed / 1000.;

Левый порт проявляется на условии done >= 1.
Как правило при передвижении в любом случае done выходит за пределы единицы доходя до примерно 1.3.
Код:
                if(done >= 1)
                {
                    moveNext(false);
                    return;
                }

Скорее всего проблема зарыта где-то тут.
Странность в том, что сам массив moveList не содержит "левых координат"
Код:
                int index = (int) (moveList.size() * done);
                if(index >= moveList.size())
                    index = moveList.size() - 1;
                if(index < 0)
                    index = 0;
 
Соответственно бежать можно бесконечно и по итогу персонаж все равно окажется на "левых" координатах. При этом после окончания бега сработает пакет validatePosition и переместит/портанет персонажа в зависимости от diff между клиентом и сервером.

Попробуйте точку полета выставлять в lastServerPosition
что-то вроде такого.
Код:
    public void setLocFly(Location loc)
    {
        if(loc == null)
        {
            return;
        }
        setXYZ(loc.x, loc.y, loc.z);
        if(isPlayer())
        {
            getPlayer().setLastServerPosition(loc);
        }
    }
 
Попробуйте точку полета выставлять в lastServerPosition
что-то вроде такого.
Код:
    public void setLocFly(Location loc)
    {
        if(loc == null)
        {
            return;
        }
        setXYZ(loc.x, loc.y, loc.z);
        if(isPlayer())
        {
            getPlayer().setLastServerPosition(loc);
        }
    }
Пробовал, дело не в этом.
 
Обратите внимание, что данный пользователь заблокирован! Не совершайте с ним никаких сделок! Перейдите в его профиль, чтобы узнать причину блокировки.
Добрый вечер, решил переписать flytype-скилы (Rush Impact, Shadow Step, Warp и тд) и на последнем этапе столкнулся с проблемой некорректной обработки движения после использования Charge-скилов.

Баг актуален для всех сборок на базе l2p (Так же актуально для l2gw) даже если не трогать изначальный код.

В чем суть:
Если после окончания каста (после срабатывания clearCastVars() и finishFly()) начать отбегать то подключается nextAction.MOVE (обрабатывается в PlayableAI). По окончанию бега если персонаж на стороне клиента не добегает до точки то его возвращает в последние координаты после каста (в координаты, что находится в переменной _flyLoc и возвращается функцией getFlyLoc()).

Соответственно бежать можно бесконечно и по итогу персонаж все равно окажется на "левых" координатах. При этом после окончания бега сработает пакет validatePosition и переместит/портанет персонажа в зависимости от diff между клиентом и сервером.

Данного бага не было на лайве лостворлда (в шарных сурсах он остался)

Подробней как это проявляется:

Собственно вопрос - кто-то фиксил этот момент? Такое чувство что он зарыт где-то в мув-листе
А что глянуть декомпил L2Server, религия не позволяет ?
И вот это
int index = (int) (moveList.size() * done);
if(index >= moveList.size())
index = moveList.size() - 1;
if(index < 0)
index = 0;
Похоже на Костыль/заглушку, от того что автор не вкурсе как оно должно обрабатываться на самом деле.


И вообще хорошо былоб предоставить дебаг от сервера, а то так без информации, пальцем в небо !
 
Странно, на оффе примерно такой же отброс есть. Только там практически сразу телепортит назад.
На офе отброса нет. На любой птс сборке персонаж просто останавливается на пол пути не доходя до курсора, т.е со стороны сервера он дошел, а со стороны клиента нет, т.е там есть допуск рассинхрона.

А что глянуть декомпил L2Server, религия не позволяет ?
И вот это

Похоже на Костыль/заглушку, от того что автор не вкурсе как оно должно обрабатываться на самом деле.


И вообще хорошо былоб предоставить дебаг от сервера, а то так без информации, пальцем в небо !

Да, это похоже на заглушку, на что я и указал, т.к этот код пальцем в небо. (Код не мой)
И он присутствует на большинстве сборок основанных на l2p, ребы, оверах и тд.

На лыже мувинг написан более менее адекватно, но адаптировать его немного проблемно из-за разницы в классах.
В дебаге передвижения никаких разительных изменений локации не происходит, вот координаты передвижения при данном баге:

(первоначальный Loc - точка конца раша получаемая через getFlyLoc, XYZ - передвижение, окончательный Loc - вызывается через stopMove())

Более подробный с виду дебаг. Извиняюсь за качество картинки, писал встроенным в клиент рекодером.
Можно увидеть как влияет на валидацию после чардж скила наличие бафа на минус скорость бега (вика и уд)
Так же в чате отображаются локации передвижения и вывод переменной done.

 
Последнее редактирование модератором:
Обратите внимание, что данный пользователь заблокирован! Не совершайте с ним никаких сделок! Перейдите в его профиль, чтобы узнать причину блокировки.
Я имел ввиду более подробный дебаг( в эклипске), нужно пройтись по каждому классу который отвечает за передвижение во время каста, так с этими XYZ мало что поймешь.
И вообще правильнее будет смотреть в L2Server.exe, и разбирать что там указанно, а так можно вечно гадать не видя всего кода - мало кто сможет помочь, либо скинь полностью весь код который отвечает за все.
clearCastVars() и finishFly()) начать отбегать то подключается nextAction.MOVE
Каждую часть вот этого, желательно увидеть полностью в коде, и в эклипсе !
 
Я имел ввиду более подробный дебаг( в эклипске), нужно пройтись по каждому классу который отвечает за передвижение во время каста, так с этими XYZ мало что поймешь.
И вообще правильнее будет смотреть в L2Server.exe, и разбирать что там указанно, а так можно вечно гадать не видя всего кода - мало кто сможет помочь, либо скинь полностью весь код который отвечает за все.

Каждую часть вот этого, желательно увидеть полностью в коде, и в эклипсе !

Полный алгоритм по рашу:
doCast - начало каста:
Код:
        _flyLoc = null; // Обнуляем переменную _flyLoc
        switch(skill.getFlyType())
        {
            case CHARGE:
                _flyLoc = getFlyLocation(target, skill); // Получаем координату к которой рашим
                if(_flyLoc != null) {                       
                        setPrevLoc(getLoc()); // Устанавливаем изначальную позицию (нужно позднее для SkillType.TARGET_TUNNEL)
                        broadcastPacket(new FlyToLocation(this, _flyLoc, skill.getFlyType())); // Броадкаст пакета полета
                }
                else
                {
                    sendPacket(SystemMsg.CANNOT_SEE_TARGET);
                    return;
                }
            default:
                break;
        }

getFlyLocation - получение координаты (лишнее удалено тк не связано):
Код:
        if(skill.getFlyType() == FlyType.CHARGE){
            // Получаем ближайшую точку к цели
            loc = applyOffset(target.getLoc(), (int) target.getColRadius()+40);
            // loc.correctGeoZ; Нет смысла корректировать координату Z тк это делает applyOffset
              
            // Коррекировки если скилл используется в полете
            if(isFlying())
            {
                if(isPlayer() && ((Player) this).isInFlyingTransform() && (loc.z <= 0 || loc.z >= 6000))
                    return null;
                if(GeoEngine.moveCheckInAir(getX(), getY(), getZ(), loc.x, loc.y, loc.z, 45, getGeoIndex()) == null)
                    return null;
            }
          
            // Проверка на возможность перемещения
            if(!GeoEngine.canMoveToCoord(getX(), getY(), getZ(), loc.x, loc.y, loc.z, getGeoIndex()))
            {
                loc = target.getLoc(); // Если не получается встать рядом с объектом, пробуем встать прямо в него
                if(!GeoEngine.canMoveToCoord(getX(), getY(), getZ(), loc.x, loc.y, loc.z, getGeoIndex()))
                    return null;
            }
          
            return loc;
        }

onMagicUseTimer - вызывается из таска использования скила, функции выполняемые в конце каста
Код:
        switch(skill.getFlyType())
        {
            case CHARGE:
                    setLocOnFly(_flyLoc); // Перемещаем персонажа к позиции полученной из getFlyLocation
                    // stopMove(false, true); Тестовый стоп мув для предотвращения бага с использованием валидации, не помогает
                    finishFly(false); // Вызов finishFly
                break;
        }
      
        setPrevLoc(null); // Обнуляем _prevLocation что бы избежать багов в SkillType.TARGET_TUNNEL

finishFly - вызывается при отмене каста или успешном окончании. Перенесен с ЛВ
Код:
    private void finishFly(boolean aborted)
    {
        Skill skill = _castingSkill; // Получаем кастуемый скилл
        Location flyLoc = _flyLoc; // Сохраняем локацию
        _flyLoc = null; // Обнуляем на всякий случай (По факту так же обнуляется при начале каста)
        if(flyLoc != null)
        {
            // TODO: DS: уточнить пакет при обрыве каста
            if (aborted && skill != null && skill.getFlyType() != FlyType.CHARGE)
                broadcastPacket(new FlyToLocation(this, getLoc(), FlyType.NONE));
            else
                if(skill.getFlyType() != FlyType.CHARGE)
                    setLocOnFly(flyLoc); // Используется для альтернативного юза по типу Wrap, Blink, Shadow Step и тд тк локация для них не выставляется в onMagicUseTimer
        }
    }

Непосредственно setLocOnFly(Location loc)
Код:
   public void setLocOnFly(Location loc)
    {
        /* Тестовая установка Клиент-серверных координат, никак не влияет
        if(isPlayer()) {
            getPlayer().setLastClientPosition(loc);
            getPlayer().setLastServerPosition(loc);
        }*/    
        setXYZ(loc.x, loc.y, loc.z);
    }


Кстати говоря в функции setXYZ есть мувлок:
Код:
    public void setXYZ(int x, int y, int z, boolean MoveTask)
    {
        if(!MoveTask)
            stopMove();

        moveLock.lock();
        try
        {
            super.setXYZ(x, y, z);
        }
        finally
        {
            moveLock.unlock();
        }

        updateZones(); // Zones Listener
    }

Я имел ввиду более подробный дебаг( в эклипске), нужно пройтись по каждому классу который отвечает за передвижение во время каста, так с этими XYZ мало что поймешь.
И вообще правильнее будет смотреть в L2Server.exe, и разбирать что там указанно, а так можно вечно гадать не видя всего кода - мало кто сможет помочь, либо скинь полностью весь код который отвечает за все.

Каждую часть вот этого, желательно увидеть полностью в коде, и в эклипсе !

Во время передвижения движение не отрабатывается как таковое. Сам полет бродкастится пакетом FlyToLocation.

При передвижении таски движения обрываются проверкой isCastingNow() в isMovementDisabled().
Код:
    public boolean isMovementDisabled()
    {
        return isBlocked() || isRooted() || isImmobilized() || isAlikeDead() || isStunned() || isSleeping() || isParalyzed() || isAttackingNow() || (isCastingNow() && !canMoveOnCurCast()) || isFrozen();
    }
 
Последнее редактирование модератором:
Обратите внимание, что данный пользователь заблокирован! Не совершайте с ним никаких сделок! Перейдите в его профиль, чтобы узнать причину блокировки.
Ну вот теперь немного понятнее
loc = applyOffset(target.getLoc(), (int) target.getColRadius()+40);
Зачем +40, Colradius всегда равен 0 ?
loc = target.getLoc();
if(!GeoEngine.canMoveToCoord(getX(), getY(), getZ(), loc.x, loc.y, loc.z, getGeoIndex()))
return null;
Вот тут лучше поставить брекпойнт и output, чтобы точно видеть что он делает.
Но выглядит оно не правильно, 3 раза просчитывается Loc , еще не понятно что делает canMoveToCoord(он двигает самого персонажа ? )
И что из себя представляет setXYZ ? Отправляется ли там пакет ?

Ну и суда
if (aborted && skill != null && skill.getFlyType() != FlyType.CHARGE)
broadcastPacket(new FlyToLocation(this, getLoc(), FlyType.NONE));
else
if(skill.getFlyType() != FlyType.CHARGE)
setLocOnFly(flyLoc);
Желательно поставить breakpoint с Output,
ибо не понятно что за значение, имеет getLoc(), до того как был задействован метод FinishFly, и зачем вызывать setXYZ() еще раз(при завершении), если предыдущее движение(до FinishFly) должно завершится .

P.S.
На лицо вся суть java Разработок, один написал не правильно и все за ним скопировали, а работает оно как надо или нет, особо не волнует!
Советую все-таки глянуть декомпил L2server, выдумывать велосипед не всегда того стоит.
 
Ну вот теперь немного понятнее

Зачем +40, Colradius всегда равен 0 ?

Вот тут лучше поставить брекпойнт и output, чтобы точно видеть что он делает.
Но выглядит оно не правильно, 3 раза просчитывается Loc , еще не понятно что делает canMoveToCoord(он двигает самого персонажа ? )
И что из себя представляет setXYZ ? Отправляется ли там пакет ?

Ну и суда

Желательно поставить breakpoint с Output,
ибо не понятно что за значение, имеет getLoc(), до того как был задействован метод FinishFly, и зачем вызывать setXYZ() еще раз(при завершении), если предыдущее движение(до FinishFly) должно завершится .

P.S.
На лицо вся суть java Разработок, один написал не правильно и все за ним скопировали, а работает оно как надо или нет, особо не волнует!
Советую все-таки глянуть декомпил L2server, выдумывать велосипед не всегда того стоит.

ColRadius может быть грубо говоря до 8 до 20 если нпц большой, иногда этого мало и мы влетает в лицо, 40 оптимальное значение что бы получить точку не в мобе и при этом оставаться на дистанции атаки

canMoveToCoord - определяет может ли персонаж добежать до точки (В функцию передается соотв координата начала и конца). Возращается булево значение.
Впринципе выглядит годно т.к там идет просчет изначальной координаты, потом идет пересчет координаты если персонаж в флай-трансформе и соотв идет проверка на возможность передвижения.

finishFly я поставил просто для понимания. Если посмотреть внимательней он не задействуется для раш скилов из за условия skill.getFlyType() != FlyType.CHARGE

SetXYZ - соотв. перемещает персонажа в нужную локацию. Если в функцию передается булева переменная move как true то идет бродкаст пакет MoveToLocation

Единственное что я бы проработал в алгоритме - лишние обнуления переменной _flyLoc.

P.S: Занимался декомпилом только AI.obj, есть ли в шаре пригодный для разбора декомпил серверной части птс того же финала?
 
Обратите внимание, что данный пользователь заблокирован! Не совершайте с ним никаких сделок! Перейдите в его профиль, чтобы узнать причину блокировки.
ColRadius может быть грубо говоря до 8 до 20 если нпц большой, иногда этого мало и мы влетает в лицо, 40 оптимальное значение что бы получить точку не в мобе и при этом оставаться на дистанции атаки

canMoveToCoord - определяет может ли персонаж добежать до точки (В функцию передается соотв координата начала и конца). Возращается булево значение.
Впринципе выглядит годно т.к там идет просчет изначальной координаты, потом идет пересчет координаты если персонаж в флай-трансформе и соотв идет проверка на возможность передвижения.

finishFly я поставил просто для понимания. Если посмотреть внимательней он не задействуется для раш скилов из за условия skill.getFlyType() != FlyType.CHARGE

SetXYZ - соотв. перемещает персонажа в нужную локацию. Если в функцию передается булева переменная move как true то идет бродкаст пакет MoveToLocation

Единственное что я бы проработал в алгоритме - лишние обнуления переменной _flyLoc.

P.S: Занимался декомпилом только AI.obj, есть ли в шаре пригодный для разбора декомпил серверной части птс того же финала?
А зачем в начале темы ты пишешь что используешь Finish fly
Если после окончания каста (после срабатывания clearCastVars() и finishFly())
Если по сути этот метод не как не обрабатывает скилы с багом ?

Тут весь глюк в неверных расчетах или точнее заглушках,
вот зачем присваивать loc = applyOffset(target.getLoc(), (int) target.getColRadius()+40); с левым радиусом, от 8-20+40 ?
а потом снова присваивать Loc от цели , если она "чем то закрыта" , получается что присваивание Offset не как не влияет .
if(!GeoEngine.canMoveToCoord(getX(), getY(), getZ(), loc.x, loc.y, loc.z, getGeoIndex()))
{
loc = target.getLoc(); // Если не получается встать рядом с объектом, пробуем встать прямо в него
if(!GeoEngine.canMoveToCoord(getX(), getY(), getZ(), loc.x, loc.y, loc.z, getGeoIndex()))
return null;
}
как по мне это та самая проблема, и она возможно глубжке( в самом canMoveToCoord) .

P.S.
Вот честно зачем делать отдельный метод SetLocOnFly , если все что он делает это перемещает по XYZ .И я бы глянул что из себя представляет этот XYZ у тебя!
Да и пихать в getFlyLocation проверки которые в большенстве дублируют те что в обычном getLocation, на мой взгляд не стоит .
И как вот это понимать ?
if(isFlying())
{
if(isPlayer() && ((Player) this).isInFlyingTransform() && (loc.z <= 0 || loc.z >= 6000))
return null;
if(GeoEngine.moveCheckInAir(getX(), getY(), getZ(), loc.x, loc.y, loc.z, 45, getGeoIndex()) == null)
return null;
}
Если перс в ФлайТрансформе/виверне, то getFlyLocation всегда будет возвращать Null, смысл с метода который возвращает null ?
 
Назад
Сверху Снизу