Валидация 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 это сделано так, как надо.
 
Последнее редактирование модератором:
У себя на лыже проверил... Такой фигни нету)
 

Т.е предположение верно и в массив координат при передвижении передаются "левые" точки?
Ок, попробую переписать функции связанные с передвижением
 
У себя на лыже проверил... Такой фигни нету)
Пока решения нет. Для интересующихся - копать следует в таск передвижения.
Конкретней - строчки с просчетом дистанции по типу
Код:
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;
 
Интересно, где же разгадка.
 

Попробуйте точку полета выставлять в lastServerPosition
что-то вроде такого.
Код:
    public void setLocFly(Location loc)
    {
        if(loc == null)
        {
            return;
        }
        setXYZ(loc.x, loc.y, loc.z);
        if(isPlayer())
        {
            getPlayer().setLastServerPosition(loc);
        }
    }
 
Пробовал, дело не в этом.
 
Обратите внимание, что данный пользователь заблокирован! Не совершайте с ним никаких сделок! Перейдите в его профиль, чтобы узнать причину блокировки.
А что глянуть декомпил L2Server, религия не позволяет ?
И вот это
int index = (int) (moveList.size() * done);
if(index >= moveList.size())
index = moveList.size() - 1;
if(index < 0)
index = 0;
Похоже на Костыль/заглушку, от того что автор не вкурсе как оно должно обрабатываться на самом деле.


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


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

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

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

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

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

Полный алгоритм по рашу:
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
    }


Во время передвижения движение не отрабатывается как таковое. Сам полет бродкастится пакетом 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 ? Отправляется ли там пакет ?

Ну и суда
Желательно поставить 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, есть ли в шаре пригодный для разбора декомпил серверной части птс того же финала?
 
Обратите внимание, что данный пользователь заблокирован! Не совершайте с ним никаких сделок! Перейдите в его профиль, чтобы узнать причину блокировки.
А зачем в начале темы ты пишешь что используешь Finish fly
Если после окончания каста (после срабатывания clearCastVars() и finishFly())
Если по сути этот метод не как не обрабатывает скилы с багом ?

Тут весь глюк в неверных расчетах или точнее заглушках,
вот зачем присваивать loc = applyOffset(target.getLoc(), (int) target.getColRadius()+40); с левым радиусом, от 8-20+40 ?
а потом снова присваивать Loc от цели , если она "чем то закрыта" , получается что присваивание Offset не как не влияет .
как по мне это та самая проблема, и она возможно глубжке( в самом canMoveToCoord) .

P.S.
Вот честно зачем делать отдельный метод SetLocOnFly , если все что он делает это перемещает по XYZ .И я бы глянул что из себя представляет этот XYZ у тебя!
Да и пихать в getFlyLocation проверки которые в большенстве дублируют те что в обычном getLocation, на мой взгляд не стоит .
И как вот это понимать ?
Если перс в ФлайТрансформе/виверне, то getFlyLocation всегда будет возвращать Null, смысл с метода который возвращает null ?