Hello there,
I’m currently working on a Node.js server emulator for Lineage 2 Chronicle 1: Harbingers of War.
I’ve encountered an issue with aligning the character’s coordinates between the server and the client. When you command a movement in the game, there’s a seamless transition with an animation. However, the server manages movement based on a timer, resulting in less fluid motion.
For illustration, I took a Java-based build l2j-lisvus. There are many builds, but they all fork from the l2jserver project https://l2jserver.com/, inheriting various features, including character movement.
In l2j-lisvus and all l2jserver assemblies, character movement on the server proceeds through a timer with uniform increments.
The problem arises when an action, such as striking an NPC, is needed after reaching a destination.
How character movement on the server works.
The foundation is the basic characteristics of the character. Running speed is 126.
This schema represents coordinate incrementation every 1000ms by 126 units. Based on the schema above, an example code snippet for character actions upon reaching the destination:
// No coordinate increment. Simply calculating when the character will reach the final coordinates.
const distance = 1500;
const playerSpeed = 126;
const ticks = distance / playerSpeed; // 11.90
const time = ticks * 1000; // 11900ms
setTimeout(() => {
// character action post-run
}, time);
The green zone indicates where the foot should have landed without discrepancies.
Character speed increase over time.
126 is the base speed. As the character progresses, movement speed increases, leading to larger discrepancies. However, before devising a formula, it’s essential to confirm the theory that walking speed affects the variance.
Characteristic data is transmitted from the server to the client.
A packet UserInfo.java line 83.
writeD(player.runSpeed);
writeD(player.walkSpeed);
Baseline values:
runSpeed: 126
walkSpeed: 88
Setting walkSpeed to 126. If the walking speed equals the running speed, discrepancies should vanish.
The character is synchronized and begins the attack timely. Now, it’s crucial to understand how walking speed influences discrepancies between the client and server.
So, how far does a character get before starting to run?
It’s essential to capture the moment when walking transitions to running. To do this, we’ll send client data where the walking speed is higher than the running speed. This discrepancy will reveal the transition and allow for calculating the distance covered while walking.
runSpeed: 10
walkSpeed: 600
At a stride of 600, the character manages to cover 250 before starting to run.
600 / 250 = 2.4
700 / 291 = 2.4
800 / 333 = 2.4
From this, it’s concluded that the character covers a distance 2.4 times less than its walking speed before beginning to run.
Thus, at a walking speed of 88, the character will cover 36 units.
88 / 2.4 = 36
Solution
A formula for calculating time:
distance_covered_at_start = walking_speed / 2.4
(((distance_between_npc_and_player – distance_covered_at_start) / running_speed) * 1000ms) + time_walked
For example, with a distance of 1500.
Out of which we covered 36 initially.
1500 – 36 = 1464 running distance.
Running speed 126 per second.
1464 / 126 = 11.61 (number of segments covered per second).
11.61 * 1000 = 11610ms of running.
Add the walking time to 11610
Walking speed 88 per second.
1000 / 88 = 11.36ms per 1 unit
36 units * 11.36ms = 408ms
11610 + 408 = 12018ms
12018ms is the precise duration from start to finish.
Comparing it to the old time of 11900ms. A difference of 118ms.
setTimeout(() => {
player.attack(npc);
}, 12018);
As showcased above, there’s no difference in foot positioning at various speeds, indicating the solution is effective.
Project link: lineage2js