From eb1887396084045dc588282f3c46214781db182d Mon Sep 17 00:00:00 2001 From: Snowiiii Date: Tue, 6 Aug 2024 18:10:03 +0200 Subject: [PATCH] Add Player movement --- README.md | 2 +- pumpkin-entity/src/lib.rs | 18 ++++ .../src/client/play/c_head_rot.rs | 20 ++++ .../src/client/play/c_spawn_player.rs | 1 + .../client/play/c_update_entitiy_pos_rot.rs | 38 +++++++ .../src/client/play/c_update_entity_pos.rs | 32 ++++++ .../src/client/play/c_update_entity_rot.rs | 24 +++++ pumpkin-protocol/src/client/play/mod.rs | 8 ++ pumpkin/src/client/mod.rs | 14 ++- pumpkin/src/client/player_packet.rs | 98 +++++++++++++++---- pumpkin/src/entity/player.rs | 15 +-- pumpkin/src/main.rs | 11 +-- pumpkin/src/server.rs | 18 ++-- 13 files changed, 246 insertions(+), 53 deletions(-) create mode 100644 pumpkin-protocol/src/client/play/c_head_rot.rs create mode 100644 pumpkin-protocol/src/client/play/c_update_entitiy_pos_rot.rs create mode 100644 pumpkin-protocol/src/client/play/c_update_entity_pos.rs create mode 100644 pumpkin-protocol/src/client/play/c_update_entity_rot.rs diff --git a/README.md b/README.md index b7cd8ec2..9a58292c 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ Pumpkin is currently under heavy development. - [x] Player Skin - [x] Player Client brand - [x] Player Teleport - - [ ] Player Movement + - [x] Player Movement - [ ] Player Animation - [ ] Player Inventory - [ ] Player Attack diff --git a/pumpkin-entity/src/lib.rs b/pumpkin-entity/src/lib.rs index 743414bd..5efdb3e1 100644 --- a/pumpkin-entity/src/lib.rs +++ b/pumpkin-entity/src/lib.rs @@ -7,6 +7,15 @@ pub type EntityId = i32; pub struct Entity { pub entity_id: EntityId, pub entity_type: EntityType, + pub x: f64, + pub y: f64, + pub z: f64, + pub lastx: f64, + pub lasty: f64, + pub lastz: f64, + pub yaw: f32, + pub head_yaw: f32, + pub pitch: f32, } impl Entity { @@ -14,6 +23,15 @@ impl Entity { Self { entity_id, entity_type, + x: 0.0, + y: 0.0, + z: 0.0, + lastx: 0.0, + lasty: 0.0, + lastz: 0.0, + yaw: 0.0, + head_yaw: 0.0, + pitch: 0.0, } } } diff --git a/pumpkin-protocol/src/client/play/c_head_rot.rs b/pumpkin-protocol/src/client/play/c_head_rot.rs new file mode 100644 index 00000000..48f04cde --- /dev/null +++ b/pumpkin-protocol/src/client/play/c_head_rot.rs @@ -0,0 +1,20 @@ +use pumpkin_macros::packet; +use serde::Serialize; + +use crate::VarInt; + +#[derive(Serialize, Clone)] +#[packet(0x48)] +pub struct CHeadRot { + entity_id: VarInt, + head_yaw: u8, +} + +impl CHeadRot { + pub fn new(entity_id: VarInt, head_yaw: u8) -> Self { + Self { + entity_id, + head_yaw, + } + } +} diff --git a/pumpkin-protocol/src/client/play/c_spawn_player.rs b/pumpkin-protocol/src/client/play/c_spawn_player.rs index 17c7d43f..9bfa4a69 100644 --- a/pumpkin-protocol/src/client/play/c_spawn_player.rs +++ b/pumpkin-protocol/src/client/play/c_spawn_player.rs @@ -21,6 +21,7 @@ pub struct CSpawnEntity { } impl CSpawnEntity { + #[allow(clippy::too_many_arguments)] pub fn new( entity_id: VarInt, entity_uuid: uuid::Uuid, diff --git a/pumpkin-protocol/src/client/play/c_update_entitiy_pos_rot.rs b/pumpkin-protocol/src/client/play/c_update_entitiy_pos_rot.rs new file mode 100644 index 00000000..ba50ac52 --- /dev/null +++ b/pumpkin-protocol/src/client/play/c_update_entitiy_pos_rot.rs @@ -0,0 +1,38 @@ +use pumpkin_macros::packet; +use serde::Serialize; + +use crate::VarInt; + +#[derive(Serialize, Clone)] +#[packet(0x2F)] +pub struct CUpdateEntityPosRot { + entity_id: VarInt, + delta_x: i16, + delta_y: i16, + delta_z: i16, + yaw: u8, + pitch: u8, + on_ground: bool, +} + +impl CUpdateEntityPosRot { + pub fn new( + entity_id: VarInt, + delta_x: i16, + delta_y: i16, + delta_z: i16, + yaw: u8, + pitch: u8, + on_ground: bool, + ) -> Self { + Self { + entity_id, + delta_x, + delta_y, + delta_z, + yaw, + pitch, + on_ground, + } + } +} diff --git a/pumpkin-protocol/src/client/play/c_update_entity_pos.rs b/pumpkin-protocol/src/client/play/c_update_entity_pos.rs new file mode 100644 index 00000000..dc437eec --- /dev/null +++ b/pumpkin-protocol/src/client/play/c_update_entity_pos.rs @@ -0,0 +1,32 @@ +use pumpkin_macros::packet; +use serde::Serialize; + +use crate::VarInt; + +#[derive(Serialize, Clone)] +#[packet(0x2E)] +pub struct CUpdateEntityPos { + entity_id: VarInt, + delta_x: i16, + delta_y: i16, + delta_z: i16, + on_ground: bool, +} + +impl CUpdateEntityPos { + pub fn new( + entity_id: VarInt, + delta_x: i16, + delta_y: i16, + delta_z: i16, + on_ground: bool, + ) -> Self { + Self { + entity_id, + delta_x, + delta_y, + delta_z, + on_ground, + } + } +} diff --git a/pumpkin-protocol/src/client/play/c_update_entity_rot.rs b/pumpkin-protocol/src/client/play/c_update_entity_rot.rs new file mode 100644 index 00000000..4b374a71 --- /dev/null +++ b/pumpkin-protocol/src/client/play/c_update_entity_rot.rs @@ -0,0 +1,24 @@ +use pumpkin_macros::packet; +use serde::Serialize; + +use crate::VarInt; + +#[derive(Serialize, Clone)] +#[packet(0x30)] +pub struct CUpdateEntityRot { + entity_id: VarInt, + yaw: u8, + pitch: u8, + on_ground: bool, +} + +impl CUpdateEntityRot { + pub fn new(entity_id: VarInt, yaw: u8, pitch: u8, on_ground: bool) -> Self { + Self { + entity_id, + yaw, + pitch, + on_ground, + } + } +} diff --git a/pumpkin-protocol/src/client/play/mod.rs b/pumpkin-protocol/src/client/play/mod.rs index addc0590..e807663c 100644 --- a/pumpkin-protocol/src/client/play/mod.rs +++ b/pumpkin-protocol/src/client/play/mod.rs @@ -1,6 +1,7 @@ mod c_change_difficulty; mod c_chunk_data_update_light; mod c_game_event; +mod c_head_rot; mod c_login; mod c_play_disconnect; mod c_player_abilities; @@ -9,11 +10,15 @@ mod c_set_held_item; mod c_spawn_player; mod c_sync_player_position; mod c_system_chat_message; +mod c_update_entitiy_pos_rot; +mod c_update_entity_pos; +mod c_update_entity_rot; mod player_action; pub use c_change_difficulty::*; pub use c_chunk_data_update_light::*; pub use c_game_event::*; +pub use c_head_rot::*; pub use c_login::*; pub use c_play_disconnect::*; pub use c_player_abilities::*; @@ -22,4 +27,7 @@ pub use c_set_held_item::*; pub use c_spawn_player::*; pub use c_sync_player_position::*; pub use c_system_chat_message::*; +pub use c_update_entitiy_pos_rot::*; +pub use c_update_entity_pos::*; +pub use c_update_entity_rot::*; pub use player_action::*; diff --git a/pumpkin/src/client/mod.rs b/pumpkin/src/client/mod.rs index 69598fea..908f644b 100644 --- a/pumpkin/src/client/mod.rs +++ b/pumpkin/src/client/mod.rs @@ -138,11 +138,15 @@ impl Client { // todo let id = 0; let player = self.player.as_mut().unwrap(); - player.x = x; - player.y = y; - player.z = z; - player.yaw = yaw; - player.pitch = pitch; + let entity = &mut player.entity; + entity.x = x; + entity.y = y; + entity.z = z; + entity.lastx = x; + entity.lasty = y; + entity.lastz = z; + entity.yaw = yaw; + entity.pitch = pitch; player.awaiting_teleport = Some(id.into()); self.send_packet(CSyncPlayerPostion::new(x, y, z, yaw, pitch, 0, id.into())); } diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/client/player_packet.rs index a90fd6f8..7801580f 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/client/player_packet.rs @@ -1,6 +1,9 @@ -use pumpkin_protocol::server::play::{ - SChatCommand, SConfirmTeleport, SPlayerCommand, SPlayerPosition, SPlayerPositionRotation, - SPlayerRotation, +use pumpkin_protocol::{ + client::play::{CHeadRot, CUpdateEntityPos, CUpdateEntityPosRot, CUpdateEntityRot}, + server::play::{ + SChatCommand, SConfirmTeleport, SPlayerCommand, SPlayerPosition, SPlayerPositionRotation, + SPlayerRotation, + }, }; use crate::{ @@ -29,19 +32,42 @@ impl Client { } } - pub fn handle_position(&mut self, _server: &mut Server, position: SPlayerPosition) { + pub fn handle_position(&mut self, server: &mut Server, position: SPlayerPosition) { if position.x.is_nan() || position.feet_y.is_nan() || position.z.is_nan() { self.kick("Invalid movement"); } let player = self.player.as_mut().unwrap(); - player.x = position.x; - player.y = position.feet_y; - player.z = position.z; + let entity = &mut player.entity; + entity.lastx = entity.x; + entity.lasty = entity.y; + entity.lastz = entity.z; + entity.x = position.x; + entity.y = position.feet_y; + entity.z = position.z; + // todo: teleport when moving > 8 block + + // send new position to all other players + let on_ground = player.on_ground; + let entity_id = entity.entity_id; + let (x, lastx) = (entity.x, entity.lastx); + let (y, lasty) = (entity.y, entity.lasty); + let (z, lastz) = (entity.z, entity.lastz); + + server.broadcast_packet( + self, + CUpdateEntityPos::new( + entity_id.into(), + (x * 4096.0 - lastx * 4096.0) as i16, + (y * 4096.0 - lasty * 4096.0) as i16, + (z * 4096.0 - lastz * 4096.0) as i16, + on_ground, + ), + ); } pub fn handle_position_rotation( &mut self, - _server: &mut Server, + server: &mut Server, position_rotation: SPlayerPositionRotation, ) { if position_rotation.x.is_nan() @@ -54,23 +80,61 @@ impl Client { self.kick("Invalid rotation"); } let player = self.player.as_mut().unwrap(); - player.x = position_rotation.x; - player.y = position_rotation.feet_y; - player.z = position_rotation.z; - player.yaw = position_rotation.yaw; - player.pitch = position_rotation.pitch; + let entity = &mut player.entity; + + entity.x = position_rotation.x; + entity.y = position_rotation.feet_y; + entity.z = position_rotation.z; + entity.yaw = position_rotation.yaw % 360.0; + entity.pitch = position_rotation.pitch.clamp(-90.0, 90.0) % 360.0; + + // send new position to all other players + let on_ground = player.on_ground; + let entity_id = entity.entity_id; + let (x, lastx) = (entity.x, entity.lastx); + let (y, lasty) = (entity.y, entity.lasty); + let (z, lastz) = (entity.z, entity.lastz); + let yaw = (entity.yaw * 256.0 / 360.0).floor(); + let pitch = (entity.pitch * 256.0 / 360.0).floor(); + let head_yaw = (entity.head_yaw * 256.0 / 360.0).floor(); + + server.broadcast_packet( + self, + CUpdateEntityPosRot::new( + entity_id.into(), + (x * 4096.0 - lastx * 4096.0) as i16, + (y * 4096.0 - lasty * 4096.0) as i16, + (z * 4096.0 - lastz * 4096.0) as i16, + yaw as u8, + pitch as u8, + on_ground, + ), + ); + server.broadcast_packet(self, CHeadRot::new(entity_id.into(), head_yaw as u8)); } - pub fn handle_rotation(&mut self, _server: &mut Server, rotation: SPlayerRotation) { + pub fn handle_rotation(&mut self, server: &mut Server, rotation: SPlayerRotation) { if !rotation.yaw.is_finite() || !rotation.pitch.is_finite() { self.kick("Invalid rotation"); } let player = self.player.as_mut().unwrap(); - player.yaw = rotation.yaw; - player.pitch = rotation.pitch; + let entity = &mut player.entity; + entity.yaw = rotation.yaw % 360.0; + entity.pitch = rotation.pitch.clamp(-90.0, 90.0) % 360.0; + // send new position to all other players + let on_ground = player.on_ground; + let entity_id = entity.entity_id; + let yaw = entity.yaw; + let pitch = entity.pitch; + + server.broadcast_packet( + self, + CUpdateEntityRot::new(entity_id.into(), yaw as u8, pitch as u8, on_ground), + ); + server.broadcast_packet(self, CHeadRot::new(entity_id.into(), yaw as u8)); } - pub fn handle_chat_command(&mut self, server: &mut Server, command: SChatCommand) { + pub fn handle_chat_command(&mut self, _server: &mut Server, command: SChatCommand) { handle_command(&mut CommandSender::Player(self), command.command); } diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 8f66a2a2..23c69078 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -7,11 +7,6 @@ use serde::{Deserialize, Serialize}; pub struct Player { pub entity: Entity, - pub x: f64, - pub y: f64, - pub z: f64, - pub yaw: f32, - pub pitch: f32, // Client side value, Should be not trusted pub on_ground: bool, @@ -26,15 +21,7 @@ pub struct Player { impl Player { pub fn new(entity_id: EntityId) -> Self { Self { - entity: Entity { - entity_id, - entity_type: EntityType::Player, - }, - x: 0.0, - y: 0.0, - z: 0.0, - yaw: 0.0, - pitch: 0.0, + entity: Entity::new(entity_id, EntityType::Player), on_ground: false, awaiting_teleport: None, sneaking: false, diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs index 81136854..967e1362 100644 --- a/pumpkin/src/main.rs +++ b/pumpkin/src/main.rs @@ -9,7 +9,6 @@ use configuration::AdvancedConfiguration; use std::{ collections::HashMap, rc::Rc, - sync::{Arc, Mutex}, thread, }; @@ -65,10 +64,10 @@ fn main() -> io::Result<()> { let mut connections: HashMap>> = HashMap::new(); - let server = Arc::new(Mutex::new(Server::new(( + let mut server = Server::new(( basic_config, advanced_configuration, - )))); + )); log::info!("Started Server took {}ms", time.elapsed().as_millis()); log::info!("You now can connect to the server"); @@ -128,8 +127,6 @@ fn main() -> io::Result<()> { addr, ))); server - .lock() - .unwrap() .add_client(rc_token, Rc::clone(&client)); connections.insert(token, client); }, @@ -138,7 +135,7 @@ fn main() -> io::Result<()> { // Maybe received an event for a TCP connection. let done = if let Some(client) = connections.get_mut(&token) { let mut client = client.borrow_mut(); - client.poll(&mut server.lock().unwrap(), event); + client.poll(&mut server, event); client.closed } else { // Sporadic events happen, we can safely ignore them. @@ -147,7 +144,7 @@ fn main() -> io::Result<()> { if done { if let Some(client) = connections.remove(&token) { let mut client = client.borrow_mut(); - server.lock().unwrap().remove_client(&token); + server.remove_client(&token); poll.registry().deregister(&mut client.connection)?; } } diff --git a/pumpkin/src/server.rs b/pumpkin/src/server.rs index 88797d6f..c60fa6ee 100644 --- a/pumpkin/src/server.rs +++ b/pumpkin/src/server.rs @@ -18,8 +18,7 @@ use pumpkin_protocol::{ CSpawnEntity, PlayerAction, }, }, - BitSet, ClientPacket, Players, Sample, StatusResponse, VarInt, - Version, CURRENT_MC_PROTOCOL, + BitSet, ClientPacket, Players, Sample, StatusResponse, VarInt, Version, CURRENT_MC_PROTOCOL, }; use pumpkin_world::chunk::TestChunk; use rsa::{rand_core::OsRng, traits::PublicKeyParts, RsaPrivateKey, RsaPublicKey}; @@ -117,8 +116,8 @@ impl Server { // todo: do this in a world pub fn spawn_player(&mut self, client: &mut Client) { // This code follows the vanilla packet order - dbg!("spawning player"); let entity_id = self.new_entity_id(); + log::debug!("spawning player, entity id {}", entity_id); let player = Player::new(entity_id); client.player = Some(player); @@ -234,16 +233,17 @@ impl Server { for (_, existing_client) in self.current_clients.iter().filter(|c| c.0 != &token) { let existing_client = existing_client.borrow(); if let Some(player) = &existing_client.player { + let entity = &player.entity; let gameprofile = existing_client.gameprofile.as_ref().unwrap(); client.send_packet(CSpawnEntity::new( player.entity_id().into(), gameprofile.id, EntityType::Player.to_i32().unwrap().into(), - x, - y, - z, - 0, - 0, + entity.x, + entity.y, + entity.z, + entity.yaw as u8, + entity.pitch as u8, 0, 0.into(), 0, @@ -257,7 +257,7 @@ impl Server { } /// Sends a Packet to all Players - fn broadcast_packet

(&mut self, from: &Client, packet: P) + pub fn broadcast_packet

(&mut self, from: &Client, packet: P) where P: ClientPacket, P: Clone,