Skip to content

Commit b8078fc

Browse files
committed
feat(player): track position and rotation
1 parent 8368f9d commit b8078fc

File tree

4 files changed

+144
-36
lines changed

4 files changed

+144
-36
lines changed

src/net/entity.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright (c) 2024 Andrew Brower.
3+
* This file is part of Crawlspace.
4+
*
5+
* Crawlspace is free software: you can redistribute it and/or
6+
* modify it under the terms of the GNU Affero General Public
7+
* License as published by the Free Software Foundation, either
8+
* version 3 of the License, or (at your option) any later version.
9+
*
10+
* Crawlspace is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Affero General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Affero General Public
16+
* License along with Crawlspace. If not, see
17+
* <https://www.gnu.org/licenses/>.
18+
*/
19+
20+
#[derive(Default, Debug)]
21+
pub struct Entity {
22+
pub x: f64,
23+
pub y: f64,
24+
pub z: f64,
25+
pub yaw: f32,
26+
pub pitch: f32,
27+
}
28+
29+
impl Entity {
30+
pub fn reposition(&mut self, x: f64, y: f64, z: f64) {
31+
self.x = x;
32+
self.y = y;
33+
self.z = z;
34+
}
35+
36+
pub fn rotate(&mut self, yaw: f32, pitch: f32) {
37+
self.yaw = yaw;
38+
self.pitch = pitch;
39+
}
40+
}

src/net/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use tokio::net::TcpListener;
2424
pub mod cache;
2525
pub mod player;
2626

27+
mod entity;
2728
mod io;
2829

2930
use crate::CrawlState;

src/net/player.rs

Lines changed: 79 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
use std::{sync::Arc, time::Duration};
2121

22-
use color_eyre::eyre::Result;
22+
use color_eyre::eyre::{bail, Result};
2323
use rand::Rng;
2424
use serde_json::json;
2525
use thiserror::Error;
@@ -50,7 +50,7 @@ use crate::{
5050
#[cfg(feature = "encryption")]
5151
use crate::protocol::{datatypes::Bytes, packets::login::PluginRequestC};
5252

53-
use super::io::NetIo;
53+
use super::{entity::Entity, io::NetIo};
5454

5555
#[derive(Debug)]
5656
pub struct Player {
@@ -62,14 +62,16 @@ pub struct Player {
6262
packet_state: RwLock<PacketState>,
6363

6464
uuid: RwLock<Option<Uuid>>,
65-
tp_state: Mutex<TeleportState>,
65+
tp_state: RwLock<TeleportState>,
6666

6767
last_keepalive: RwLock<Instant>,
68+
69+
entity: RwLock<Entity>,
6870
}
6971

70-
#[derive(Debug)]
72+
#[derive(Debug, PartialEq)]
7173
enum TeleportState {
72-
Pending(i32),
74+
Pending(i32, Instant),
7375
Clear,
7476
}
7577

@@ -92,10 +94,12 @@ impl SharedPlayer {
9294
crawlstate,
9395
packet_state: RwLock::new(PacketState::Handshaking),
9496

95-
tp_state: Mutex::new(TeleportState::Clear),
9697
uuid: RwLock::new(None),
98+
tp_state: RwLock::new(TeleportState::Clear),
9799

98100
last_keepalive: RwLock::new(Instant::now()),
101+
102+
entity: RwLock::new(Entity::default()),
99103
}))
100104
}
101105

@@ -339,7 +343,6 @@ impl SharedPlayer {
339343
// thread but realistically who knows burhhhh
340344
state.player_send.send(self.clone()).await?;
341345

342-
state.shutdown_token.cancelled().await;
343346
Ok(())
344347
}
345348

@@ -392,17 +395,6 @@ impl SharedPlayer {
392395
Ok(())
393396
}
394397

395-
async fn confirm_teleport(&self, id: i32) -> Result<(), TeleportError> {
396-
let tp_state = self.0.tp_state.lock().await;
397-
match *tp_state {
398-
TeleportState::Clear => Err(TeleportError::Unexpected),
399-
TeleportState::Pending(expected) => match id == expected {
400-
true => Ok(()),
401-
false => Err(TeleportError::WrongId(expected, id)),
402-
},
403-
}
404-
}
405-
406398
pub async fn uuid(&self) -> Uuid {
407399
let uuid = self.0.uuid.read().await;
408400
uuid.expect("uuid() called on uninitialized player - only call this after login!")
@@ -416,20 +408,30 @@ impl SharedPlayer {
416408
yaw: f32,
417409
pitch: f32,
418410
) -> Result<()> {
411+
{
412+
let tp_state = self.0.tp_state.read().await;
413+
if *tp_state != TeleportState::Clear {
414+
bail!("Player {} already has a teleport pending", self.0.id);
415+
};
416+
}
417+
419418
let mut io = self.0.io.lock().await;
420419

421420
let tp = SynchronisePositionC::new(x, y, z, yaw, pitch);
422421
{
423-
let mut tp_state = self.0.tp_state.lock().await;
422+
let mut tp_state = self.0.tp_state.write().await;
424423
// player will be given 5 (FIVE) SECONDS TO ACK!!!!!
425-
*tp_state = TeleportState::Pending(tp.id);
424+
*tp_state = TeleportState::Pending(tp.id, Instant::now());
426425
}
427426
io.tx(&tp).await?;
428427

429428
let tp_ack = io.rx::<ConfirmTeleportS>().await?;
430429

431430
match tokio::time::timeout(Duration::from_secs(5), self.confirm_teleport(tp_ack.id)).await {
432-
Ok(Ok(())) => (),
431+
Ok(Ok(())) => {
432+
let mut tp_state = self.0.tp_state.write().await;
433+
*tp_state = TeleportState::Clear;
434+
}
433435
Ok(Err(why)) => {
434436
warn!("Spawning player {} failed: {why}", self.0.id);
435437
Err(why)?;
@@ -442,23 +444,66 @@ impl SharedPlayer {
442444
Ok(())
443445
}
444446

447+
async fn confirm_teleport(&self, id: i32) -> Result<(), TeleportError> {
448+
let tp_state = self.0.tp_state.read().await;
449+
match *tp_state {
450+
TeleportState::Clear => Err(TeleportError::Unexpected),
451+
TeleportState::Pending(expected, _) if id == expected => Ok(()),
452+
TeleportState::Pending(expected, _) => Err(TeleportError::WrongId(expected, id)),
453+
}
454+
}
455+
456+
pub async fn check_teleports(
457+
&self,
458+
ack: Option<ConfirmTeleportS>,
459+
) -> Result<(), TeleportError> {
460+
let tp_state = self.0.tp_state.read().await;
461+
462+
match *tp_state {
463+
TeleportState::Pending(pending_id, sent_at) => {
464+
if Instant::now() - sent_at > Duration::from_secs(5) {
465+
return Err(TeleportError::TimedOut);
466+
}
467+
468+
match ack {
469+
Some(ack) if ack.id == pending_id => {
470+
drop(tp_state);
471+
let mut tp_state = self.0.tp_state.write().await;
472+
*tp_state = TeleportState::Clear;
473+
Ok(())
474+
}
475+
Some(ack) => Err(TeleportError::WrongId(ack.id, pending_id)),
476+
None => Err(TeleportError::Pending(pending_id)),
477+
}
478+
}
479+
TeleportState::Clear => match ack {
480+
None => Ok(()),
481+
Some(_) => Err(TeleportError::Unexpected),
482+
},
483+
}
484+
}
485+
445486
async fn handle_frames(&self, frames: Vec<Frame>) -> Result<()> {
446487
for frame in frames {
447488
match frame.id {
448489
SetPlayerPositionS::ID => {
449490
let packet: SetPlayerPositionS = frame.decode()?;
450-
debug!(
451-
"Player {} moved to {}, {}, {}",
452-
self.0.id, packet.x, packet.feet_y, packet.z
453-
);
491+
492+
let mut entity = self.0.entity.write().await;
493+
entity.reposition(packet.x, packet.feet_y, packet.z);
454494
}
455495

456496
SetPlayerPositionAndRotationS::ID => {
457497
let packet: SetPlayerPositionAndRotationS = frame.decode()?;
458-
debug!(
459-
"Player {} moved to {}, {}, {} rotated {} {}",
460-
self.0.id, packet.x, packet.feet_y, packet.z, packet.pitch, packet.yaw
461-
);
498+
499+
let mut entity = self.0.entity.write().await;
500+
entity.reposition(packet.x, packet.feet_y, packet.z);
501+
entity.rotate(packet.yaw, packet.pitch);
502+
}
503+
504+
ConfirmTeleportS::ID => {
505+
let packet: ConfirmTeleportS = frame.decode()?;
506+
self.check_teleports(Some(packet)).await?;
462507
}
463508

464509
id => {
@@ -475,9 +520,13 @@ impl SharedPlayer {
475520
}
476521

477522
#[derive(Debug, Error)]
478-
enum TeleportError {
523+
pub enum TeleportError {
479524
#[error("Client was not expecting a teleport acknowledgement")]
480525
Unexpected,
481526
#[error("Teleport ack has wrong ID (expected {0}, got {1})")]
482527
WrongId(i32, i32),
528+
#[error("Teleport timed out")]
529+
TimedOut,
530+
#[error("Waiting for teleport acknowledgement for id {0}")]
531+
Pending(i32),
483532
}

src/server/mod.rs

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,15 @@
1919

2020
pub mod ticker;
2121

22-
use std::sync::Arc;
22+
use std::{collections::HashMap, sync::Arc};
2323

2424
use color_eyre::eyre::Result;
2525

2626
use crate::{
27-
net::{cache::WorldCache, player::SharedPlayer},
27+
net::{
28+
cache::WorldCache,
29+
player::{SharedPlayer, TeleportError},
30+
},
2831
CrawlState,
2932
};
3033

@@ -35,7 +38,7 @@ pub struct Server {
3538
pub ticker: Ticker,
3639

3740
world_cache: Arc<WorldCache>,
38-
players: Vec<SharedPlayer>,
41+
players: HashMap<u16, SharedPlayer>,
3942

4043
crawlstate: CrawlState,
4144
}
@@ -46,7 +49,7 @@ impl Server {
4649
Server {
4750
ticker: Ticker::new(tick_rate),
4851
world_cache: Arc::new(world_cache),
49-
players: Vec::new(),
52+
players: HashMap::new(),
5053
crawlstate: state,
5154
}
5255
}
@@ -56,13 +59,28 @@ impl Server {
5659
let mut player_recv = state.player_recv.lock().await;
5760

5861
while let Ok(p) = player_recv.try_recv() {
59-
self.players.push(p.clone());
62+
self.players.insert(p.0.id, p.clone());
6063
tokio::spawn(Self::send_world_to(p.clone(), self.world_cache.clone()));
6164
}
6265

63-
for player in &self.players {
66+
let mut invalid_players: Vec<u16> = Vec::new();
67+
68+
for (id, player) in &self.players {
6469
let _ = player.keepalive().await;
6570
let _ = player.handle_all_packets().await;
71+
72+
match player.check_teleports(None).await {
73+
Err(TeleportError::TimedOut) | Err(TeleportError::WrongId(..)) => {
74+
warn!("Player {} teleport failed, removing", player.0.id);
75+
invalid_players.push(*id);
76+
}
77+
_ => (),
78+
}
79+
}
80+
81+
for id in invalid_players {
82+
// TODO: kick player properly
83+
self.players.remove(&id);
6684
}
6785
}
6886

0 commit comments

Comments
 (0)