Привет.

Я разрабатываю эмулятор сервера для Lineage 2 Chronicle 1: Harbingers of war на Node.js.

Столкнулся с проблемой синхронизации скорости персонажа на сервере с клиентом. Когда в игре вы нажимаете мышкой в то место, куда хотите перейти то происходит плавный переход с анимацией движения. На сервере в этот момент тоже происходит движение по таймеру, но не такое плавное.

C(client) – двигается плавно из одной точки в другую. S(server) – делает прирост координат по таймеру.
C(client) – двигается плавно из одной точки в другую. S(server) – делает прирост координат по таймеру.

Для примера я взял сборку написанную на java l2j-lisvus Сборок много. Но все они являются fork’ами проекта l2jserver https://l2jserver.com/И многое наследуется. В том числе и передвижение персонажа.

В l2j-lisvus, как и во всех сборках l2jserver перемещение персонажа на сервере идет при помощи таймера с приростом одинаковых значений.

Проблема проявляется, когда нам надо сделать какое-то действие после того, как персонаж добежал до пункта назначения. Например, нанести удар по NPC.

На коротких расстояниях проблема незаметна. Нога наступает точно в монету.
На коротких расстояниях проблема незаметна. Нога наступает точно в монету.
На длинных расстояниях действие атака начинается раньше, чем персонаж добегает до цели.
На длинных расстояниях действие атака начинается раньше, чем персонаж добегает до цели.
А если выкрутить скорость на максимум (900) то проблема расхождения очевидна. Это связанно с тем, что помимо скорости бега есть скорость ходьбы.
А если выкрутить скорость на максимум (900) то проблема расхождения очевидна. Это связанно с тем, что помимо скорости бега есть скорость ходьбы.

Как вообще работает передвижение персонажа на сервере.

За основу взяты базовые характеристики персонажа. Скорость бега 126.

126 — это количество внутренних unit’ов за секунду.
126 — это количество внутренних unit’ов за секунду.

На данной схеме идет прирост координат персонажа каждые 1000мс на 126 unit’ов. Исходя из схемы выше пример кода для действий персонажем после достижения пункта назначения:

// Прироста координат нет. Просто считаем когда персонаж дойдет до конечных координат.
const distance = 1500;
const playerSpeed = 126;
const ticks = distance / playerSpeed; // 11.90
const time = ticks * 1000; // 11900mc

setTimeout(() => {
    // действие персонажа после бега
}, time);
На коротких расстояниях.
На коротких расстояниях.
На длинных расстояниях.
На длинных расстояниях.
Расхождения на коротких расстояниях.
Расхождения на коротких расстояниях.
Расхождения на длинных расстояниях.
Расхождения на длинных расстояниях.

Зеленой зоной показана точка куда должна ступить нога персонажа если бы не было расхождений.

Рост скорости при развитии персонажа.

126 — это базовая скорость. И по мере развития персонажа будет расти и скорость передвижения. А значит расхождение будет больше. Но перед тем, как создать формулу надо подтвердить теорию, что скорость ходьбы влияет на расхождение.

Данные о характеристиках персонажа передаются от сервера к клиенту.

Пакет UserInfo.js 83 строчка.

writeD(player.runSpeed);
writeD(player.walkSpeed);

Базовые значения:

runSpeed: 126

walkSpeed: 88

Выставляю значения walkSpeed: 126. Если скорость ходьбы будет равна скорости бега, то расхождения должны пропасть.

Нога персонажа достигает правильной конечной точки.
Нога персонажа достигает правильной конечной точки.

Персонаж синхронизирован и начинает атаку вовремя. Теперь надо понять, как скорость ходьбы влияет на расхождения между клиентом и сервером.

Сколько же персонаж успевает пройти перед тем, как начинает бежать?

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

runSpeed: 10

walkSpeed: 600

Ходьба быстрее бега.
Ходьба быстрее бега.

При скорости шага в 600 персонаж успевает пройти 250, прежде чем начинает бежать.

600 / 250 = 2.4

700 / 291 = 2.4

800 / 333 = 2.4

Из этого вывод, что персонаж перед тем, как начать бежать успевает пройти расстояние в 2.4 раза меньше, чем его скорость ходьбы.

Значит при скорости ходьбы 88 персонаж пройдет 36 unit’ов.

88 / 2.4 = 36

Первое деление — это начало движения (ходьба) а следующие деления — это бег.
Первое деление — это начало движения (ходьба) а следующие деления — это бег.

Решение

Формула для расчета времени:

сколько_прошли_на_старте = скорость_ходьбы / 2.4

(((дистанция_между_нпц_и_игроком - сколько_прошли_на_старте) / скорость_бега) * 1000мс) + время_которое_прошли

Для примера дистанция 1500.

Из них мы 36 прошли.

1500 - 36 = 1464 расстояние для бега.

Скорость бега 126 в секунду.

1464 / 126 = 11.61 (количество отрезков, которое мы пройдем за секунду).

11.61 * 1000 = 11610мс бега.

к 11610 надо прибавить время ходьбы

Скорость ходьбы 88 в секунду.

1000 / 88 = 11.36мс за 1 unit

36 unit * 11.36мс = 408мс

11610 + 408 = 12018мс

12018мс является точным временем от начала старта и до конца.

Сравниваем со старым временем 11900мс. Разница в 118мс.

setTimeout(() => {
    player.attack(npc);
}, 12018);
Скорость бега 126.
Скорость бега 126.
Скорость бега 900.
Скорость бега 900.
Положение ноги при скорости 126.
Положение ноги при скорости 126.
Положение ноги при скорости 900.
Положение ноги при скорости 900.

Как видно выше разница положения ног при разных скоростях отсутствует, а значит решение работает.

Ссылка на проект: lineage2js

Комментарии (5)


  1. Horocek
    16.05.2024 07:37

    Прикольно


  1. Billander
    16.05.2024 07:37
    +2

    Прикольно что кто то ещё занимается разработкой в л2)) Такая проблема на многих серверах, не только с бегом, раньше думал что это сетевая проблема.


    1. space2pacman Автор
      16.05.2024 07:37

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

      Можно было понять разработчиков, если бы это редко встречалось. Но это сопровождает игрока с момента старта и всю игру.

      Но если ping большой то расхождения будут и на официальных PTS серверах :)


      1. T-helper
        16.05.2024 07:37
        +1

        Как меня в свое время этот баг подбешивал, я думал что мне кажется что он не добегает....


  1. Jurgon
    16.05.2024 07:37
    +1

    Может пригодится)

    https://github.com/master-toma/l2datapack/tree/c1_public