Привет.
Я разрабатываю эмулятор сервера для Lineage 2 Chronicle 1: Harbingers of war на Node.js.
Столкнулся с проблемой синхронизации координат персонажа на сервере с клиентом. Когда в игре вы нажимаете мышкой в то место, куда хотите перейти то происходит плавный переход с анимацией движения. На сервере в этот момент тоже происходит движение по таймеру, но не такое плавное.
Для примера я взял сборку написанную на java l2j-lisvus Сборок много. Но все они являются fork’ами проекта l2jserver https://l2jserver.com/И многое наследуется. В том числе и передвижение персонажа.
В l2j-lisvus, как и во всех сборках l2jserver перемещение персонажа на сервере идет при помощи таймера с приростом одинаковых значений.
Проблема проявляется, когда нам надо сделать какое-то действие после того, как персонаж добежал до пункта назначения. Например, нанести удар по NPC.
Как вообще работает передвижение персонажа на сервере.
За основу взяты базовые характеристики персонажа. Скорость бега 126.
На данной схеме идет прирост координат персонажа каждые 1000мс на 126 unit’ов. Исходя из схемы выше пример кода для действий персонажем после достижения пункта назначения:
// Прироста координат нет. Просто считаем когда персонаж дойдет до конечных координат.
const distance = 1500;
const playerSpeed = 126;
const ticks = distance / playerSpeed; // 11.90
const time = ticks * 1000; // 11900mc
setTimeout(() => {
// действие персонажа после бега
}, time);
Зеленой зоной показана точка куда должна ступить нога персонажа если бы не было расхождений.
Рост скорости при развитии персонажа.
126 — это базовая скорость. И по мере развития персонажа будет расти и скорость передвижения. А значит расхождение будет больше. Но перед тем, как создать формулу надо подтвердить теорию, что скорость ходьбы влияет на расхождение.
Данные о характеристиках персонажа передаются от сервера к клиенту.
Пакет UserInfo.java 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);
Как видно выше разница положения ног при разных скоростях отсутствует а значит решение работает.
Ссылка на проект: lineage2js