![Как я использовал Node.js для синхронизации координат персонажа на сервере Lineage 2 Как я использовал Node.js для синхронизации координат персонажа на сервере Lineage 2](https://habrastorage.org/getpro/habr/upload_files/287/089/e47/287089e4798564c060643cf328e587ce.jpg)
Привет.
Я разрабатываю эмулятор сервера для Lineage 2 Chronicle 1: Harbingers of war на Node.js.
Столкнулся с проблемой синхронизации координат персонажа на сервере с клиентом. Когда в игре вы нажимаете мышкой в то место, куда хотите перейти то происходит плавный переход с анимацией движения. На сервере в этот момент тоже происходит движение по таймеру, но не такое плавное.
![](https://habrastorage.org/getpro/habr/upload_files/313/dfc/0ed/313dfc0eda08ed71b62f33dac4fdf313.gif)
![C(client) – двигается плавно из одной точки в другую. S(server) – делает прирост координат по таймеру. C(client) – двигается плавно из одной точки в другую. S(server) – делает прирост координат по таймеру.](https://habrastorage.org/getpro/habr/upload_files/234/6e1/578/2346e1578c8f7b133533b9d6e8685bd9.gif)
Для примера я взял сборку написанную на java l2j-lisvus Сборок много. Но все они являются fork’ами проекта l2jserver https://l2jserver.com/И многое наследуется. В том числе и передвижение персонажа.
В l2j-lisvus, как и во всех сборках l2jserver перемещение персонажа на сервере идет при помощи таймера с приростом одинаковых значений.
Проблема проявляется, когда нам надо сделать какое-то действие после того, как персонаж добежал до пункта назначения. Например, нанести удар по NPC.
![На коротких расстояниях проблема незаметна. Нога наступает точно в монету. На коротких расстояниях проблема незаметна. Нога наступает точно в монету.](https://habrastorage.org/getpro/habr/upload_files/555/19e/601/55519e601d9ccb2465846badc08ffe75.gif)
![На длинных расстояниях действие атака начинается раньше, чем персонаж добегает до цели. На длинных расстояниях действие атака начинается раньше, чем персонаж добегает до цели.](https://habrastorage.org/getpro/habr/upload_files/d9d/6f7/e60/d9d6f7e603bd4e4da5dd57498840d2d0.gif)
![А если выкрутить скорость на максимум (900) то проблема расхождения очевидна. Это связанно с тем, что помимо скорости бега есть скорость ходьбы. А если выкрутить скорость на максимум (900) то проблема расхождения очевидна. Это связанно с тем, что помимо скорости бега есть скорость ходьбы.](https://habrastorage.org/getpro/habr/upload_files/669/33f/1a2/66933f1a25b34099b361157545bbb22e.gif)
Как вообще работает передвижение персонажа на сервере.
За основу взяты базовые характеристики персонажа. Скорость бега 126.
![126 — это количество внутренних unit’ов за секунду. 126 — это количество внутренних unit’ов за секунду.](https://habrastorage.org/getpro/habr/upload_files/5cc/54f/20e/5cc54f20ecc14417c059ca2874010abf.png)
На данной схеме идет прирост координат персонажа каждые 1000мс на 126 unit’ов. Исходя из схемы выше пример кода для действий персонажем после достижения пункта назначения:
// Прироста координат нет. Просто считаем когда персонаж дойдет до конечных координат.
const distance = 1500;
const playerSpeed = 126;
const ticks = distance / playerSpeed; // 11.90
const time = ticks * 1000; // 11900mc
setTimeout(() => {
// действие персонажа после бега
}, time);
![На коротких расстояниях. На коротких расстояниях.](https://habrastorage.org/getpro/habr/upload_files/121/bf8/77e/121bf877ef918ae6a5d3329330d4a063.gif)
![На длинных расстояниях. На длинных расстояниях.](https://habrastorage.org/getpro/habr/upload_files/46c/bac/0f7/46cbac0f761b92c2e7049433851d2f7a.gif)
![Расхождения на коротких расстояниях. Расхождения на коротких расстояниях.](https://habrastorage.org/getpro/habr/upload_files/bc9/c2e/11a/bc9c2e11a1e8f9235cd4fa8a09630f6f.png)
![Расхождения на длинных расстояниях. Расхождения на длинных расстояниях.](https://habrastorage.org/getpro/habr/upload_files/e0e/bed/6e4/e0ebed6e4705101f448303c2ca29dd04.png)
Зеленой зоной показана точка куда должна ступить нога персонажа если бы не было расхождений.
Рост скорости при развитии персонажа.
126 — это базовая скорость. И по мере развития персонажа будет расти и скорость передвижения. А значит расхождение будет больше. Но перед тем, как создать формулу надо подтвердить теорию, что скорость ходьбы влияет на расхождение.
Данные о характеристиках персонажа передаются от сервера к клиенту.
Пакет UserInfo.java 83 строчка.
writeD(player.runSpeed);
writeD(player.walkSpeed);
Базовые значения:
runSpeed: 126
walkSpeed: 88
Выставляю значения walkSpeed: 126. Если скорость ходьбы будет равна скорости бега, то расхождения должны пропасть.
![](https://habrastorage.org/getpro/habr/upload_files/5bb/8db/05f/5bb8db05f2e2e28d176dd1a0af6186d2.gif)
![Нога персонажа достигает правильной конечной точки. Нога персонажа достигает правильной конечной точки.](https://habrastorage.org/getpro/habr/upload_files/9de/610/775/9de610775e01ee6bd79b46a036be01a2.png)
Персонаж синхронизирован и начинает атаку вовремя. Теперь надо понять, как скорость ходьбы влияет на расхождения между клиентом и сервером.
Сколько же персонаж успевает пройти перед тем, как начинает бежать?
Надо поймать момент когда ходьба переходит в бег. Для этого передадим в клиент данные, где скорость ходьбы будет больше скорости бега. Из-за этой разницы будет виден переход и можно будет рассчитать пройденное расстояние при ходьбе.
runSpeed: 10
walkSpeed: 600
![Ходьба быстрее бега. Ходьба быстрее бега.](https://habrastorage.org/getpro/habr/upload_files/ff1/eae/bd1/ff1eaebd1265ea798ce11e6566f21ffd.gif)
При скорости шага в 600 персонаж успевает пройти 250, прежде чем начинает бежать.
600 / 250 = 2.4
700 / 291 = 2.4
800 / 333 = 2.4
Из этого вывод, что персонаж перед тем, как начать бежать успевает пройти расстояние в 2.4 раза меньше, чем его скорость ходьбы.
Значит при скорости ходьбы 88 персонаж пройдет 36 unit’ов.
88 / 2.4 = 36
![Первое деление — это начало движения (ходьба) а следующие деления — это бег. Первое деление — это начало движения (ходьба) а следующие деления — это бег.](https://habrastorage.org/getpro/habr/upload_files/a82/3c8/c43/a823c8c439c2eaedb320c8b490afba9d.png)
Решение
Формула для расчета времени:
сколько_прошли_на_старте = скорость_ходьбы / 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.](https://habrastorage.org/getpro/habr/upload_files/e25/dd5/0ab/e25dd50abb687888a06ee4028cd8f86d.gif)
![Скорость бега 900. Скорость бега 900.](https://habrastorage.org/getpro/habr/upload_files/07d/a7c/608/07da7c608037d54b57856bd16f818fbe.gif)
![Положение ноги при скорости 126. Положение ноги при скорости 126.](https://habrastorage.org/getpro/habr/upload_files/991/6b7/b9d/9916b7b9d99a45057773e74cf1cf4855.png)
![Положение ноги при скорости 900. Положение ноги при скорости 900.](https://habrastorage.org/getpro/habr/upload_files/344/024/296/3440242963bb8a2d1eb11eb6c8e4debd.png)
Как видно выше разница положения ног при разных скоростях отсутствует а значит решение работает.
Ссылка на проект: lineage2js