From 42bc84b23ccecc2b45ce51b048d841653151a8ee Mon Sep 17 00:00:00 2001 From: Snowiiii Date: Sun, 4 Aug 2024 20:46:09 +0200 Subject: [PATCH] Add Serverbound Movement Packets - SPlayerPosition - SPlayerPositionRotation - SPlayerRotation --- pumpkin-protocol/src/client/play/mod.rs | 6 ++- pumpkin-protocol/src/packet_decoder.rs | 1 - pumpkin-protocol/src/server/play/mod.rs | 62 +++++++++++++++++++++++++ pumpkin/src/client/client_packet.rs | 2 - pumpkin/src/client/mod.rs | 27 +++++++++-- pumpkin/src/client/player_packet.rs | 54 ++++++++++++++++++++- pumpkin/src/entity/player.rs | 14 ++++++ pumpkin/src/main.rs | 19 ++++++-- pumpkin/src/server.rs | 46 +++++++++++++++--- 9 files changed, 209 insertions(+), 22 deletions(-) diff --git a/pumpkin-protocol/src/client/play/mod.rs b/pumpkin-protocol/src/client/play/mod.rs index f78aa1ca9..d36b56f10 100644 --- a/pumpkin-protocol/src/client/play/mod.rs +++ b/pumpkin-protocol/src/client/play/mod.rs @@ -35,7 +35,9 @@ pub enum PlayerAction { }, InitializeChat(u8), UpdateGameMode(u8), - UpdateListed(u8), + UpdateListed { + listed: bool, + }, UpdateLatency(u8), UpdateDisplayName(u8), } @@ -68,7 +70,7 @@ impl<'a> ClientPacket for CPlayerInfoUpdate<'a> { } PlayerAction::InitializeChat(_) => todo!(), PlayerAction::UpdateGameMode(_) => todo!(), - PlayerAction::UpdateListed(_) => todo!(), + PlayerAction::UpdateListed { listed } => p.put_bool(*listed), PlayerAction::UpdateLatency(_) => todo!(), PlayerAction::UpdateDisplayName(_) => todo!(), } diff --git a/pumpkin-protocol/src/packet_decoder.rs b/pumpkin-protocol/src/packet_decoder.rs index 17afaab3f..5d59ab81b 100644 --- a/pumpkin-protocol/src/packet_decoder.rs +++ b/pumpkin-protocol/src/packet_decoder.rs @@ -49,7 +49,6 @@ impl PacketDecoder { .0; data.advance(data.len() - r.len()); - dbg!(packet_id); Ok(Some(RawPacket { len: packet_len, id: packet_id, diff --git a/pumpkin-protocol/src/server/play/mod.rs b/pumpkin-protocol/src/server/play/mod.rs index e4c0a0a7a..f17b2ad22 100644 --- a/pumpkin-protocol/src/server/play/mod.rs +++ b/pumpkin-protocol/src/server/play/mod.rs @@ -13,3 +13,65 @@ impl ServerPacket for SConfirmTeleport { } } } + +pub struct SPlayerPosition { + pub x: f64, + pub feet_y: f64, + pub z: f64, + pub ground: bool, +} + +impl ServerPacket for SPlayerPosition { + const PACKET_ID: VarInt = 0x1A; + + fn read(bytebuf: &mut crate::bytebuf::ByteBuffer) -> Self { + Self { + x: bytebuf.get_f64(), + feet_y: bytebuf.get_f64(), + z: bytebuf.get_f64(), + ground: bytebuf.get_bool(), + } + } +} + +pub struct SPlayerPositionRotation { + pub x: f64, + pub feet_y: f64, + pub z: f64, + pub yaw: f32, + pub pitch: f32, + pub ground: bool, +} + +impl ServerPacket for SPlayerPositionRotation { + const PACKET_ID: VarInt = 0x1B; + + fn read(bytebuf: &mut crate::bytebuf::ByteBuffer) -> Self { + Self { + x: bytebuf.get_f64(), + feet_y: bytebuf.get_f64(), + z: bytebuf.get_f64(), + yaw: bytebuf.get_f32(), + pitch: bytebuf.get_f32(), + ground: bytebuf.get_bool(), + } + } +} + +pub struct SPlayerRotation { + pub yaw: f32, + pub pitch: f32, + pub ground: bool, +} + +impl ServerPacket for SPlayerRotation { + const PACKET_ID: VarInt = 0x1C; + + fn read(bytebuf: &mut crate::bytebuf::ByteBuffer) -> Self { + Self { + yaw: bytebuf.get_f32(), + pitch: bytebuf.get_f32(), + ground: bytebuf.get_bool(), + } + } +} diff --git a/pumpkin/src/client/client_packet.rs b/pumpkin/src/client/client_packet.rs index 386c3e338..86eb9829c 100644 --- a/pumpkin/src/client/client_packet.rs +++ b/pumpkin/src/client/client_packet.rs @@ -72,8 +72,6 @@ impl ClientPacketProcessor for Client { } fn handle_status_request(&mut self, server: &mut Server, _status_request: SStatusRequest) { - dbg!("sending status"); - self.send_packet(CStatusResponse::new(&server.status_response_json)) .unwrap_or_else(|e| self.kick(&e.to_string())); } diff --git a/pumpkin/src/client/mod.rs b/pumpkin/src/client/mod.rs index dca85f2b9..a639c83d8 100644 --- a/pumpkin/src/client/mod.rs +++ b/pumpkin/src/client/mod.rs @@ -25,7 +25,7 @@ use pumpkin_protocol::{ config::{SAcknowledgeFinishConfig, SClientInformation, SKnownPacks, SPluginMessage}, handshake::SHandShake, login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart}, - play::SConfirmTeleport, + play::{SConfirmTeleport, SPlayerPosition, SPlayerPositionRotation, SPlayerRotation}, status::{SPingRequest, SStatusRequest}, }, text::Text, @@ -109,10 +109,13 @@ impl Client { Ok(()) } + pub fn is_player(&self) -> bool { + self.player.is_some() + } + /// Im many cases we want to kick the Client when an Packet Error occours, But especially in the Client state we will not try to kick when not important packets /// e.g Postion, Rotation... has not been send pub fn send_packet(&mut self, packet: P) -> Result<(), PacketError> { - dbg!("sending packet"); self.enc.append_packet(packet)?; self.connection .write_all(&self.enc.take()) @@ -121,10 +124,16 @@ impl Client { } pub fn teleport(&mut self, x: f64, y: f64, z: f64, yaw: f32, pitch: f32) { - assert!(self.player.is_some()); + assert!(self.is_player()); // todo let id = 0; - self.player.as_mut().unwrap().awaiting_teleport = Some(0); + let player = self.player.as_mut().unwrap(); + player.x = x; + player.y = y; + player.z = z; + player.yaw = yaw; + player.pitch = pitch; + player.awaiting_teleport = Some(id); self.send_packet(CSyncPlayerPostion::new(x, y, z, yaw, pitch, 0, id)) .unwrap_or_else(|e| self.kick(&e.to_string())); } @@ -140,7 +149,6 @@ impl Client { /// Handles an incoming decoded Packet pub fn handle_packet(&mut self, server: &mut Server, packet: &mut RawPacket) { - dbg!("Handling packet"); let bytebuf = &mut packet.bytebuf; match self.connection_state { pumpkin_protocol::ConnectionState::HandShake => match packet.id { @@ -216,6 +224,15 @@ impl Client { SConfirmTeleport::PACKET_ID => { self.handle_confirm_teleport(server, SConfirmTeleport::read(bytebuf)) } + SPlayerPosition::PACKET_ID => { + self.handle_position(server, SPlayerPosition::read(bytebuf)) + } + SPlayerPositionRotation::PACKET_ID => { + self.handle_position_rotation(server, SPlayerPositionRotation::read(bytebuf)) + } + SPlayerRotation::PACKET_ID => { + self.handle_rotation(server, SPlayerRotation::read(bytebuf)) + } _ => log::error!("Failed to handle player packet id {}", packet.id), } } diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/client/player_packet.rs index e8e624a39..4fa4da4bc 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/client/player_packet.rs @@ -1,4 +1,6 @@ -use pumpkin_protocol::server::play::SConfirmTeleport; +use pumpkin_protocol::server::play::{ + SConfirmTeleport, SPlayerPosition, SPlayerPositionRotation, SPlayerRotation, +}; use crate::server::Server; @@ -7,6 +9,15 @@ use super::Client; // implement player packets pub trait PlayerPacketProcessor { fn handle_confirm_teleport(&mut self, server: &mut Server, confirm_teleport: SConfirmTeleport); + + fn handle_position(&mut self, server: &mut Server, position: SPlayerPosition); + + fn handle_position_rotation( + &mut self, + server: &mut Server, + position_rotation: SPlayerPositionRotation, + ); + fn handle_rotation(&mut self, server: &mut Server, rotation: SPlayerRotation); } impl PlayerPacketProcessor for Client { @@ -26,4 +37,45 @@ impl PlayerPacketProcessor for Client { self.kick("Send Teleport confirm, but we did not teleport") } } + + 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; + } + + fn handle_position_rotation( + &mut self, + _server: &mut Server, + position_rotation: SPlayerPositionRotation, + ) { + if position_rotation.x.is_nan() + || position_rotation.feet_y.is_nan() + || position_rotation.z.is_nan() + { + self.kick("Invalid movement"); + } + if !position_rotation.yaw.is_finite() || !position_rotation.pitch.is_finite() { + 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; + } + + 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; + } } diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 3d6125de5..256df0e29 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -4,6 +4,14 @@ use super::{Entity, EntityId}; 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, // Current awaiting teleport id, None if did not teleport pub awaiting_teleport: Option, @@ -13,6 +21,12 @@ impl Player { pub fn new(entity: Entity) -> Self { Self { entity, + x: 0.0, + y: 0.0, + z: 0.0, + yaw: 0.0, + pitch: 0.0, + on_ground: false, awaiting_teleport: None, } } diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs index 1075c422e..e74b7d322 100644 --- a/pumpkin/src/main.rs +++ b/pumpkin/src/main.rs @@ -17,7 +17,7 @@ pub mod util; #[cfg(not(target_os = "wasi"))] fn main() -> io::Result<()> { - use std::{collections::HashMap, rc::Rc}; + use std::{cell::RefCell, collections::HashMap, rc::Rc}; use client::Client; use configuration::AdvancedConfiguration; @@ -51,7 +51,7 @@ fn main() -> io::Result<()> { // Unique token for each incoming connection. let mut unique_token = Token(SERVER.0 + 1); - let mut connections: HashMap = HashMap::new(); + let mut connections: HashMap>> = HashMap::new(); log::info!("You now can connect to the server"); @@ -94,13 +94,20 @@ fn main() -> io::Result<()> { token, Interest::READABLE.add(Interest::WRITABLE), )?; - - connections.insert(token, Client::new(Rc::new(token), connection, addr)); + let rc_token = Rc::new(token); + let client = Rc::new(RefCell::new(Client::new( + Rc::clone(&rc_token), + connection, + addr, + ))); + server.add_client(rc_token, Rc::clone(&client)); + connections.insert(token, client); }, token => { // 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, event); client.closed } else { @@ -108,7 +115,9 @@ fn main() -> io::Result<()> { false }; if done { - if let Some(mut client) = connections.remove(&token) { + if let Some(client) = connections.remove(&token) { + let mut client = client.borrow_mut(); + server.remove_client(&token); poll.registry().deregister(&mut client.connection)?; } } diff --git a/pumpkin/src/server.rs b/pumpkin/src/server.rs index 0424784f0..bb0019d05 100644 --- a/pumpkin/src/server.rs +++ b/pumpkin/src/server.rs @@ -1,16 +1,19 @@ use std::{ + cell::RefCell, + collections::HashMap, io::Cursor, + rc::Rc, sync::atomic::{AtomicI32, Ordering}, }; use base64::{engine::general_purpose, Engine}; -use mio::{event::Event, Poll}; +use mio::{event::Event, Poll, Token}; use pumpkin_protocol::{ client::{ config::CPluginMessage, play::{CChunkDataUpdateLight, CGameEvent, CLogin, CPlayerInfoUpdate, PlayerAction}, }, - BitSet, PacketError, Players, Sample, StatusResponse, VarInt, VarInt32, Version, + BitSet, ClientPacket, PacketError, Players, Sample, StatusResponse, VarInt, VarInt32, Version, }; use pumpkin_world::chunk::TestChunk; use rsa::{rand_core::OsRng, traits::PublicKeyParts, RsaPrivateKey, RsaPublicKey}; @@ -41,6 +44,8 @@ pub struct Server { /// Cache the Server brand buffer so we don't have to rebuild them every time a player joins pub cached_server_brand: Vec, + pub current_clients: HashMap, Rc>>, + // todo replace with HashMap entity_id: AtomicI32, // todo: place this into every world pub base_config: BasicConfiguration, @@ -83,6 +88,7 @@ impl Server { status_response, status_response_json, public_key_der, + current_clients: HashMap::new(), base_config: config.0, auth_client, advanced_config: config.1, @@ -95,8 +101,17 @@ impl Server { client.poll(self, event) } + pub fn add_client(&mut self, token: Rc, client: Rc>) { + self.current_clients.insert(token, client); + } + + pub fn remove_client(&mut self, token: &Token) { + self.current_clients.remove(token); + } + // 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(); let player = Player::new(Entity { entity_id }); @@ -108,16 +123,16 @@ impl Server { self.base_config.hardcore, vec!["minecraft:overworld".into()], self.base_config.max_players as VarInt, - 8, // view distance todo - 8, // sim view dinstance todo + self.base_config.view_distances as VarInt, // view distance todo + self.base_config.simulation_distance as VarInt, // sim view dinstance todo false, false, false, 0, "minecraft:overworld".into(), 0, // seed - GameMode::Spectator.to_byte() as u8, - GameMode::Spectator.to_byte(), + GameMode::Survival.to_byte() as u8, + GameMode::Survival.to_byte(), false, false, false, // deth loc @@ -130,6 +145,8 @@ impl Server { // teleport client.teleport(10.0, 500.0, 10.0, 10.0, 10.0); let gameprofile = client.gameprofile.as_ref().unwrap(); + // first send info update to our new player, So he can see his Skin + // TODO: send more actions, (chat. list, ping) client .send_packet(CPlayerInfoUpdate::new( 0x01, @@ -142,6 +159,7 @@ impl Server { }], )) .unwrap_or_else(|e| client.kick(&e.to_string())); + // TODO: Now we send this to all other client // Start waiting for level chunks client @@ -151,6 +169,22 @@ impl Server { // Server::spawn_test_chunk(client); } + /// Sends a Packet to all Players + fn broadcast_packet

(&mut self, packet: P) -> Result<(), PacketError> + where + P: ClientPacket, + P: Clone, + { + for client in self.current_clients.values() { + // Check if client is a player + if client.borrow().is_player() { + // we need to clone, Because we send a new packet to every client + client.borrow_mut().send_packet(packet.clone())?; + } + } + Ok(()) + } + // todo: do this in a world fn _spawn_test_chunk(client: &mut Client) { let test_chunk = TestChunk::new();