Skip to content

Commit f0c7447

Browse files
committed
Calculating distance only in 1 place
1 parent e4fa01f commit f0c7447

File tree

3 files changed

+92
-95
lines changed

3 files changed

+92
-95
lines changed

game/src/movement.rs

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
11
use std::time::Instant;
22
use tokio::task::JoinHandle;
33

4+
/// Calculate Euclidean distance between two 3D points (x1,y1,z1) -> (x2,y2,z2)
5+
/// Returns None if coordinate subtraction overflows
6+
pub fn calculate_distance(x1: i32, y1: i32, z1: i32, x2: i32, y2: i32, z2: i32) -> Option<f64> {
7+
let dx = x2.checked_sub(x1)?;
8+
let dy = y2.checked_sub(y1)?;
9+
let dz = z2.checked_sub(z1)?;
10+
11+
let dx = f64::from(dx);
12+
let dy = f64::from(dy);
13+
let dz = f64::from(dz);
14+
15+
Some((dx * dx + dy * dy + dz * dz).sqrt())
16+
}
17+
418
/// Represents the current movement state of a player
519
#[derive(Debug)]
620
pub struct MovementState {
@@ -49,16 +63,22 @@ impl MovementState {
4963
}
5064

5165
/// Calculate the total distance to travel
52-
pub fn calculate_distance(&self) -> f64 {
53-
let dx = f64::from(self.dest_x) - f64::from(self.source_x);
54-
let dy = f64::from(self.dest_y) - f64::from(self.source_y);
55-
let dz = f64::from(self.dest_z) - f64::from(self.source_z);
56-
(dx * dx + dy * dy + dz * dz).sqrt()
66+
pub fn total_distance(&self) -> Option<f64> {
67+
calculate_distance(
68+
self.source_x,
69+
self.source_y,
70+
self.source_z,
71+
self.dest_x,
72+
self.dest_y,
73+
self.dest_z,
74+
)
5775
}
5876

5977
/// Calculate how long the entire journey should take (in seconds)
6078
pub fn calculate_travel_duration(&self) -> f64 {
61-
let distance = self.calculate_distance();
79+
let Some(distance) = self.total_distance() else {
80+
return 0.0;
81+
};
6282
if self.speed > 0.0 {
6383
distance / self.speed
6484
} else {
@@ -78,9 +98,12 @@ impl MovementState {
7898

7999
let progress = (elapsed / duration).min(1.0);
80100

81-
let current_x = self.source_x + ((f64::from(self.dest_x) - f64::from(self.source_x)) * progress) as i32;
82-
let current_y = self.source_y + ((f64::from(self.dest_y) - f64::from(self.source_y)) * progress) as i32;
83-
let current_z = self.source_z + ((f64::from(self.dest_z) - f64::from(self.source_z)) * progress) as i32;
101+
let current_x =
102+
self.source_x + ((f64::from(self.dest_x) - f64::from(self.source_x)) * progress) as i32;
103+
let current_y =
104+
self.source_y + ((f64::from(self.dest_y) - f64::from(self.source_y)) * progress) as i32;
105+
let current_z =
106+
self.source_z + ((f64::from(self.dest_z) - f64::from(self.source_z)) * progress) as i32;
84107

85108
(current_x, current_y, current_z)
86109
}
@@ -113,23 +136,25 @@ mod tests {
113136
#[test]
114137
fn test_calculate_distance() {
115138
let state = MovementState::new(0, 0, 0, 300, 400, 0, 100);
116-
let distance = state.calculate_distance();
117-
assert_eq!(distance, 500.0); // 3-4-5 triangle
139+
let distance = state.total_distance().unwrap();
140+
//float values can give 0.00000001 error, so we check for a range of 0.001
141+
assert!((distance - 500.0).abs() < 0.001); // 3-4-5 triangle
118142
}
119143

120144
#[test]
121145
fn test_calculate_travel_duration() {
122146
let state = MovementState::new(0, 0, 0, 500, 0, 0, 100);
123147
let duration = state.calculate_travel_duration();
124-
assert_eq!(duration, 5.0); // 500 units at 100 units/sec = 5 seconds
148+
//float values can give 0.00000001 error, so we check for a range of 0.001
149+
assert!((duration - 5.0).abs() < 0.1); // 500 units at 100 units/sec = 5 seconds
125150
}
126151

127152
#[test]
128153
fn test_calculate_current_position_at_start() {
129154
let state = MovementState::new(0, 0, 0, 1000, 0, 0, 100);
130155
let (x, y, z) = state.calculate_current_position();
131156
// Should be at or very near start position
132-
assert!(x >= 0 && x < 50); // Allow small movement
157+
assert!((0..10).contains(&x)); // Allow small movement
133158
assert_eq!(y, 0);
134159
assert_eq!(z, 0);
135160
}
Lines changed: 23 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,9 @@
1-
use anyhow::bail;
21
use bytes::BytesMut;
32
use kameo::message::{Context, Message};
43
use l2_core::shared_packets::common::ReadablePacket;
54
use l2_core::shared_packets::read::ReadablePacketBuffer;
6-
use tracing::{info, instrument, warn};
7-
8-
/// Helper function to calculate distance between two 3D points
9-
/// Returns None if coordinate subtraction overflows
10-
fn calculate_distance(x1: i32, y1: i32, z1: i32, x2: i32, y2: i32, z2: i32) -> Option<f64> {
11-
let dx = x2.checked_sub(x1)?;
12-
let dy = y2.checked_sub(y1)?;
13-
let dz = z2.checked_sub(z1)?;
14-
15-
let dx = f64::from(dx);
16-
let dy = f64::from(dy);
17-
let dz = f64::from(dz);
18-
19-
Some((dx * dx + dy * dy + dz * dz).sqrt())
20-
}
5+
use tracing::{instrument, warn};
6+
use crate::movement::calculate_distance;
217

228
use crate::pl_client::PlayerClient;
239

@@ -56,52 +42,41 @@ impl Message<RequestMoveToLocation> for PlayerClient {
5642
msg: RequestMoveToLocation,
5743
ctx: &mut Context<Self, Self::Reply>,
5844
) -> anyhow::Result<()> {
59-
info!("Received MoveToLocation packet {:?}", msg);
60-
6145
//TODO check with geodata if the location is valid.
62-
63-
// Get current position for distance validation
46+
47+
// Get the current position for distance validation
6448
let player = self.try_get_selected_char()?;
6549
let (current_x, current_y, current_z) = (player.get_x(), player.get_y(), player.get_z());
66-
50+
6751
// Calculate distance
68-
let distance = match calculate_distance(current_x, current_y, current_z, msg.x_to, msg.y_to, msg.z_to) {
69-
Some(d) => d,
70-
None => {
71-
warn!(
72-
"Player {} attempted movement causing coordinate overflow. Pos: ({},{},{}) -> Dest: ({},{},{})",
73-
player.char_model.name,
74-
current_x, current_y, current_z,
75-
msg.x_to, msg.y_to, msg.z_to
76-
);
77-
bail!("Movement coordinates cause overflow");
78-
}
52+
let Some(distance) = calculate_distance(
53+
current_x, current_y, current_z, msg.x_to, msg.y_to, msg.z_to,
54+
) else {
55+
warn!(
56+
"Player {} attempted movement causing coordinate overflow. Pos: ({},{},{}) -> Dest: ({},{},{})",
57+
player.char_model.name,
58+
current_x,
59+
current_y,
60+
current_z,
61+
msg.x_to,
62+
msg.y_to,
63+
msg.z_to
64+
);
65+
return Ok(());
7966
};
80-
67+
8168
// Check against max distance from config
8269
let cfg = self.controller.get_cfg();
8370
if cfg.max_movement_distance > 0 && distance > f64::from(cfg.max_movement_distance) {
8471
warn!(
8572
"Player {} attempted to move excessive distance: {:.2} (max: {})",
86-
player.char_model.name,
87-
distance,
88-
cfg.max_movement_distance
73+
player.char_model.name, distance, cfg.max_movement_distance
8974
);
9075
return Ok(());
9176
}
92-
93-
// Start or restart movement
94-
let (source_x, source_y, source_z) = self.start_movement(msg.x_to, msg.y_to, msg.z_to, ctx.actor_ref().clone())?;
95-
96-
// Send initial movement packet immediately (optional, but good for responsiveness if task has delay)
97-
// Note: The task sends update immediately, but we might want to send the "start" packet here?
98-
// Actually, the previous logic removed the explicit broadcast.
99-
// But wait, the task sends `CharMoveToLocation`.
100-
// If I remove the explicit broadcast here, the task will send it.
101-
// So I just need to call start_movement.
77+
78+
self.start_movement(msg.x_to, msg.y_to, msg.z_to, ctx.actor_ref().clone())?;
10279

10380
Ok(())
10481
}
10582
}
106-
107-

game/src/pl_client.rs

Lines changed: 31 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
use crate::controller::GameController;
22
use crate::cp_factory::build_client_packet;
33
use crate::movement::MovementState;
4+
use crate::packets::to_client::CharMoveToLocation;
45
use anyhow::{anyhow, bail};
56
use bytes::BytesMut;
67
use entities::entities::{character, user};
78
use entities::DBPool;
89
use kameo::actor::{ActorId, ActorRef, Spawn, WeakActorRef};
910
use kameo::error::{ActorStopReason, PanicError};
10-
use crate::packets::to_client::CharMoveToLocation;
1111
use kameo::message::{Context, Message};
1212
use kameo::Actor;
1313
use l2_core::crypt::game::GameClientEncryption;
@@ -327,15 +327,16 @@ impl PlayerClient {
327327
dest_z: i32,
328328
actor_ref: ActorRef<PlayerClient>,
329329
) -> anyhow::Result<(i32, i32, i32)> {
330-
// Cancel existing movement if any and get current position
331-
let (current_x, current_y, current_z) = if let Some(mut existing_movement) = self.movement_state.take() {
332-
existing_movement.cancel_task();
333-
existing_movement.calculate_current_position()
334-
} else {
335-
// Use player's stored position
336-
let player = self.try_get_selected_char()?;
337-
(player.get_x(), player.get_y(), player.get_z())
338-
};
330+
// Cancel existing movement if any and get the current position
331+
let (current_x, current_y, current_z) =
332+
if let Some(mut existing_movement) = self.movement_state.take() {
333+
existing_movement.cancel_task();
334+
existing_movement.calculate_current_position()
335+
} else {
336+
// Use a player's stored position
337+
let player = self.try_get_selected_char()?;
338+
(player.get_x(), player.get_y(), player.get_z())
339+
};
339340

340341
// Get player speed
341342
let player = self.try_get_selected_char()?;
@@ -345,53 +346,50 @@ impl PlayerClient {
345346
player.get_walk_speed()
346347
};
347348

348-
// Create new movement state
349+
// Create a new movement state
349350
let mut movement = MovementState::new(
350-
current_x,
351-
current_y,
352-
current_z,
353-
dest_x,
354-
dest_y,
355-
dest_z,
356-
speed,
351+
current_x, current_y, current_z, dest_x, dest_y, dest_z, speed,
357352
);
358353

359354
// Spawn periodic broadcast task
360355
let controller = self.controller.clone();
361-
356+
362357
let task_handle = tokio::spawn(async move {
363358
let mut interval = tokio::time::interval(std::time::Duration::from_millis(300));
364-
359+
365360
loop {
366361
// Check movement state BEFORE waiting
367-
let result = actor_ref
368-
.ask(GetMovementPosition)
369-
.await;
362+
let result = actor_ref.ask(GetMovementPosition).await;
370363

371364
if let Ok(Some((_x, _y, _z, has_arrived))) = result {
372365
if has_arrived {
373366
// Arrived at destination, stop broadcasting
374-
info!("Player arrived at destination ({}, {}, {})", dest_x, dest_y, dest_z);
367+
info!(
368+
"Player arrived at destination ({}, {}, {})",
369+
dest_x, dest_y, dest_z
370+
);
375371
break;
376372
}
377373

378374
// Broadcast current position
379-
if let Ok(player) = actor_ref.ask(crate::pl_client::GetCharInfo).await {
380-
if let Ok(packet) = CharMoveToLocation::new(&player, dest_x, dest_y, dest_z) {
381-
controller.broadcast_packet(packet);
382-
}
375+
if let Ok(player) = actor_ref.ask(GetCharInfo).await
376+
&& let Ok(packet) = CharMoveToLocation::new(&player, dest_x, dest_y, dest_z)
377+
{
378+
controller.broadcast_packet(packet);
379+
} else {
380+
error!("Error while broadcasting movement packet");
383381
}
384382
} else {
385-
// Error or no movement state, stop task
383+
// Error or no movement state, stop a task
386384
break;
387385
}
388386

389-
// Wait for next tick
387+
// Wait for the next tick
390388
interval.tick().await;
391389
}
392390
});
393391

394-
// Store the task handle in movement state and set it in client
392+
// Store the task handle in a movement state and set it in a client
395393
movement.task_handle = Some(task_handle);
396394
self.movement_state = Some(movement);
397395

@@ -409,7 +407,6 @@ impl PlayerClient {
409407
}
410408
}
411409

412-
413410
impl Actor for PlayerClient {
414411
type Args = (
415412
Self,
@@ -569,12 +566,12 @@ impl Message<GetMovementPosition> for PlayerClient {
569566
if let Some(movement) = self.movement_state.as_ref() {
570567
let (x, y, z) = movement.calculate_current_position();
571568
let has_arrived = movement.has_arrived();
572-
569+
573570
// Update player's actual position
574571
if let Ok(player) = self.try_get_selected_char_mut() {
575572
let _ = player.set_location(x, y, z);
576573
}
577-
574+
578575
Ok(Some((x, y, z, has_arrived)))
579576
} else {
580577
Ok(None)

0 commit comments

Comments
 (0)