From c8fee9b23c76d1d69cadd632c012593431a01207 Mon Sep 17 00:00:00 2001 From: Snowiiii Date: Mon, 9 Sep 2024 21:12:49 +0200 Subject: [PATCH 1/3] Try to solve Pumpkin's locking problem --- pumpkin-inventory/src/player.rs | 6 +- pumpkin-protocol/src/packet_encoder.rs | 1 - pumpkin/src/client/client_packet.rs | 60 +++--- pumpkin/src/client/container.rs | 63 +++--- pumpkin/src/client/mod.rs | 138 +++++++------ pumpkin/src/client/player_packet.rs | 274 ++++++++++++------------- pumpkin/src/commands/arg_player.rs | 2 +- pumpkin/src/commands/cmd_echest.rs | 5 +- pumpkin/src/commands/cmd_gamemode.rs | 10 +- pumpkin/src/commands/mod.rs | 4 +- pumpkin/src/entity/mod.rs | 41 ++-- pumpkin/src/entity/player.rs | 169 +++++++-------- pumpkin/src/main.rs | 36 ++-- pumpkin/src/proxy/velocity.rs | 6 +- pumpkin/src/server/mod.rs | 25 +-- pumpkin/src/world/mod.rs | 128 ++++++------ pumpkin/src/world/player_chunker.rs | 37 ++-- 17 files changed, 508 insertions(+), 497 deletions(-) diff --git a/pumpkin-inventory/src/player.rs b/pumpkin-inventory/src/player.rs index d8e127b78..999c9a9e1 100644 --- a/pumpkin-inventory/src/player.rs +++ b/pumpkin-inventory/src/player.rs @@ -1,3 +1,5 @@ +use std::sync::atomic::AtomicU32; + use crate::container_click::MouseClick; use crate::{handle_item_change, Container, InventoryError, WindowType}; use pumpkin_world::item::ItemStack; @@ -11,7 +13,7 @@ pub struct PlayerInventory { offhand: Option, // current selected slot in hotbar selected: usize, - pub state_id: u32, + pub state_id: AtomicU32, // Notchian server wraps this value at 100, we can just keep it as a u8 that automatically wraps pub total_opened_containers: u8, } @@ -32,7 +34,7 @@ impl PlayerInventory { offhand: None, // TODO: What when player spawns in with an different index ? selected: 0, - state_id: 0, + state_id: AtomicU32::new(0), total_opened_containers: 2, } } diff --git a/pumpkin-protocol/src/packet_encoder.rs b/pumpkin-protocol/src/packet_encoder.rs index 36b9c3697..1c545804b 100644 --- a/pumpkin-protocol/src/packet_encoder.rs +++ b/pumpkin-protocol/src/packet_encoder.rs @@ -85,7 +85,6 @@ impl PacketEncoder { let mut front = &mut self.buf[start_len..]; - #[allow(clippy::needless_borrows_for_generic_args)] VarInt(packet_len as i32) .encode(&mut front) .map_err(|_| PacketError::EncodeLength)?; diff --git a/pumpkin/src/client/client_packet.rs b/pumpkin/src/client/client_packet.rs index 38c734b40..b81b40790 100644 --- a/pumpkin/src/client/client_packet.rs +++ b/pumpkin/src/client/client_packet.rs @@ -37,12 +37,16 @@ use super::{ /// NEVER TRUST THE CLIENT. HANDLE EVERY ERROR, UNWRAP/EXPECT /// TODO: REMOVE ALL UNWRAPS impl Client { - pub fn handle_handshake(&mut self, _server: &Arc, handshake: SHandShake) { + pub fn handle_handshake(&self, _server: &Arc, handshake: SHandShake) { dbg!("handshake"); - self.protocol_version = handshake.protocol_version.0; - self.connection_state = handshake.next_state; - if self.connection_state != ConnectionState::Status { - let protocol = self.protocol_version; + let version = handshake.protocol_version.0; + self.protocol_version + .store(version, std::sync::atomic::Ordering::Relaxed); + let mut connection_state = self.connection_state.lock().unwrap(); + + *connection_state = handshake.next_state; + if *connection_state != ConnectionState::Status { + let protocol = version; match protocol.cmp(&(CURRENT_MC_PROTOCOL as i32)) { std::cmp::Ordering::Less => { self.kick(&format!("Client outdated ({protocol}), Server uses Minecraft {CURRENT_MC_VERSION}, Protocol {CURRENT_MC_PROTOCOL}")); @@ -55,11 +59,11 @@ impl Client { } } - pub fn handle_status_request(&mut self, server: &Arc, _status_request: SStatusRequest) { + pub fn handle_status_request(&self, server: &Arc, _status_request: SStatusRequest) { self.send_packet(&CStatusResponse::new(&server.status_response_json)); } - pub fn handle_ping_request(&mut self, _server: &Arc, ping_request: SStatusPingRequest) { + pub fn handle_ping_request(&self, _server: &Arc, ping_request: SStatusPingRequest) { dbg!("ping"); self.send_packet(&CPingResponse::new(ping_request.payload)); self.close(); @@ -72,7 +76,7 @@ impl Client { .all(|c| c > 32_u8 as char && c < 127_u8 as char) } - pub fn handle_login_start(&mut self, server: &Arc, login_start: SLoginStart) { + pub fn handle_login_start(&self, server: &Arc, login_start: SLoginStart) { log::debug!("login start, State {:?}", self.connection_state); if !Self::is_valid_player_name(&login_start.name) { @@ -81,7 +85,8 @@ impl Client { } // default game profile, when no online mode // TODO: make offline uuid - self.gameprofile = Some(GameProfile { + let mut gameprofile = self.gameprofile.lock().unwrap(); + *gameprofile = Some(GameProfile { id: login_start.uuid, name: login_start.name, properties: vec![], @@ -108,7 +113,7 @@ impl Client { } pub async fn handle_encryption_response( - &mut self, + &self, server: &Arc, encryption_response: SEncryptionResponse, ) { @@ -120,15 +125,17 @@ impl Client { self.enable_encryption(&shared_secret) .unwrap_or_else(|e| self.kick(&e.to_string())); + let mut gameprofile = self.gameprofile.lock().unwrap(); + if BASIC_CONFIG.online_mode { let hash = Sha1::new() .chain_update(&shared_secret) .chain_update(&server.public_key_der) .finalize(); let hash = auth_digest(&hash); - let ip = self.address.ip(); + let ip = self.address.lock().unwrap().ip(); match authentication::authenticate( - &self.gameprofile.as_ref().unwrap().name, + &gameprofile.as_ref().unwrap().name, &hash, &ip, server, @@ -159,12 +166,12 @@ impl Client { } } } - self.gameprofile = Some(p); + *gameprofile = Some(p); } Err(e) => self.kick(&e.to_string()), } } - for ele in self.gameprofile.as_ref().unwrap().properties.clone() { + for ele in gameprofile.as_ref().unwrap().properties.clone() { // todo, use this unpack_textures(ele, &ADVANCED_CONFIG.authentication.textures); } @@ -177,7 +184,7 @@ impl Client { self.set_compression(Some((threshold, level))); } - if let Some(profile) = self.gameprofile.as_ref().cloned() { + if let Some(profile) = gameprofile.as_ref().cloned() { let packet = CLoginSuccess::new(&profile.id, &profile.name, &profile.properties, false); self.send_packet(&packet); } else { @@ -186,18 +193,18 @@ impl Client { } pub fn handle_plugin_response( - &mut self, + &self, _server: &Arc, _plugin_response: SLoginPluginResponse, ) { } pub fn handle_login_acknowledged( - &mut self, + &self, server: &Arc, _login_acknowledged: SLoginAcknowledged, ) { - self.connection_state = ConnectionState::Config; + *self.connection_state.lock().unwrap() = ConnectionState::Config; server.send_brand(self); let resource_config = &ADVANCED_CONFIG.resource_pack; @@ -228,12 +235,12 @@ impl Client { dbg!("login achnowlaged"); } pub fn handle_client_information_config( - &mut self, + &self, _server: &Arc, client_information: SClientInformationConfig, ) { dbg!("got client settings"); - self.config = Some(PlayerConfig { + *self.config.lock().unwrap() = Some(PlayerConfig { locale: client_information.locale, view_distance: client_information.view_distance, chat_mode: ChatMode::from_i32(client_information.chat_mode.into()).unwrap(), @@ -245,19 +252,19 @@ impl Client { }); } - pub fn handle_plugin_message(&mut self, _server: &Arc, plugin_message: SPluginMessage) { + pub fn handle_plugin_message(&self, _server: &Arc, plugin_message: SPluginMessage) { if plugin_message.channel.starts_with("minecraft:brand") || plugin_message.channel.starts_with("MC|Brand") { dbg!("got a client brand"); match String::from_utf8(plugin_message.data) { - Ok(brand) => self.brand = Some(brand), + Ok(brand) => *self.brand.lock().unwrap() = Some(brand), Err(e) => self.kick(&e.to_string()), } } } - pub fn handle_known_packs(&mut self, server: &Arc, _config_acknowledged: SKnownPacks) { + pub fn handle_known_packs(&self, server: &Arc, _config_acknowledged: SKnownPacks) { for registry in &server.cached_registry { self.send_packet(&CRegistryData::new( ®istry.registry_id, @@ -271,12 +278,13 @@ impl Client { } pub async fn handle_config_acknowledged( - &mut self, + &self, _server: &Arc, _config_acknowledged: SAcknowledgeFinishConfig, ) { dbg!("config acknowledged"); - self.connection_state = ConnectionState::Play; - self.make_player = true; + *self.connection_state.lock().unwrap() = ConnectionState::Play; + self.make_player + .store(true, std::sync::atomic::Ordering::Relaxed); } } diff --git a/pumpkin/src/client/container.rs b/pumpkin/src/client/container.rs index 68b4391b9..c11ccb381 100644 --- a/pumpkin/src/client/container.rs +++ b/pumpkin/src/client/container.rs @@ -19,7 +19,7 @@ use pumpkin_world::item::ItemStack; use std::sync::{Arc, Mutex}; impl Player { - pub fn open_container(&mut self, server: &Arc, minecraft_menu_id: &str) { + pub fn open_container(&self, server: &Arc, minecraft_menu_id: &str) { self.inventory.state_id = 0; let total_opened_containers = self.inventory.total_opened_containers; let container = self.get_open_container(server); @@ -49,9 +49,9 @@ impl Player { self.set_container_content(container.as_deref_mut()); } - pub fn set_container_content(&mut self, container: Option<&mut Box>) { + pub fn set_container_content(&self, container: Option<&mut Box>) { let total_opened_containers = self.inventory.total_opened_containers; - let container = OptionallyCombinedContainer::new(&mut self.inventory, container); + let container = OptionallyCombinedContainer::new(&mut self.inventory.lock().unwrap(), container); let slots = container .all_slots_ref() @@ -77,7 +77,7 @@ impl Player { } /// The official Minecraft client is weird, and will always just close *any* window that is opened when this gets sent - pub fn close_container(&mut self) { + pub fn close_container(&self) { self.inventory.total_opened_containers += 1; self.client.send_packet(&CCloseContainer::new( self.inventory.total_opened_containers, @@ -97,7 +97,7 @@ impl Player { } pub async fn handle_click_container( - &mut self, + &self, server: &Arc, packet: SClickContainer, ) -> Result<(), InventoryError> { @@ -178,7 +178,7 @@ impl Player { self.send_whole_container_change(server).await?; } else if let container_click::Slot::Normal(slot_index) = click.slot { let combined_container = OptionallyCombinedContainer::new( - &mut self.inventory, + &mut self.inventory.lock().unwrap().lock().unwrap().lock().unwrap(), Some(&mut opened_container), ); if let Some(slot) = combined_container.get_slot_excluding_inventory(slot_index) { @@ -193,27 +193,30 @@ impl Player { } fn mouse_click( - &mut self, + &self, opened_container: Option<&mut Box>, mouse_click: MouseClick, slot: container_click::Slot, ) -> Result<(), InventoryError> { - let mut container = OptionallyCombinedContainer::new(&mut self.inventory, opened_container); - + let mut inventory = self.inventory.lock().unwrap(); + let mut container = + OptionallyCombinedContainer::new(&mut inventory, opened_container); + match slot { container_click::Slot::Normal(slot) => { - container.handle_item_change(&mut self.carried_item, slot, mouse_click) + container.handle_item_change(&mut self.carried_item.lock().unwrap(), slot, mouse_click) } container_click::Slot::OutsideInventory => Ok(()), } } fn shift_mouse_click( - &mut self, + &self, opened_container: Option<&mut Box>, slot: container_click::Slot, ) -> Result<(), InventoryError> { - let mut container = OptionallyCombinedContainer::new(&mut self.inventory, opened_container); + let mut inventory = self.inventory.lock().unwrap(); + let mut container = OptionallyCombinedContainer::new(&mut inventory, opened_container); match slot { container_click::Slot::Normal(slot) => { @@ -265,7 +268,7 @@ impl Player { KeyClick::Offhand => 45, }; let mut changing_item_slot = self.inventory.get_slot(changing_slot as usize)?.to_owned(); - let mut container = OptionallyCombinedContainer::new(&mut self.inventory, opened_container); + let mut container = OptionallyCombinedContainer::new(&mut self.inventory.lock().unwrap(), opened_container); container.handle_item_change(&mut changing_item_slot, slot, MouseClick::Left)?; *self.inventory.get_slot(changing_slot as usize)? = changing_item_slot; @@ -280,7 +283,7 @@ impl Player { if self.gamemode != GameMode::Creative { return Err(InventoryError::PermissionError); } - let mut container = OptionallyCombinedContainer::new(&mut self.inventory, opened_container); + let mut container = OptionallyCombinedContainer::new(&mut self.inventory.lock().unwrap(), opened_container); if let Some(Some(item)) = container.all_slots().get_mut(slot) { self.carried_item = Some(item.to_owned()) } @@ -292,7 +295,7 @@ impl Player { opened_container: Option<&mut Box>, slot: usize, ) -> Result<(), InventoryError> { - let mut container = OptionallyCombinedContainer::new(&mut self.inventory, opened_container); + let mut container = OptionallyCombinedContainer::new(&mut self.inventory.lock().unwrap(), opened_container); let mut slots = container.all_slots(); let Some(item) = slots.get_mut(slot) else { @@ -345,7 +348,7 @@ impl Player { MouseDragState::AddSlot(slot) => drag_handler.add_slot(container_id, player_id, slot), MouseDragState::End => { let mut container = - OptionallyCombinedContainer::new(&mut self.inventory, opened_container); + OptionallyCombinedContainer::new(&mut self.inventory.lock().unwrap(), opened_container); drag_handler.apply_drag( &mut self.carried_item, &mut container, @@ -356,10 +359,7 @@ impl Player { } } - async fn get_current_players_in_container( - &mut self, - server: &Server, - ) -> Vec>> { + async fn get_current_players_in_container(&self, server: &Server) -> Vec> { let player_ids = { let open_containers = server .open_containers @@ -378,13 +378,18 @@ impl Player { // TODO: Figure out better way to get only the players from player_ids // Also refactor out a better method to get individual advanced state ids - let world = self.entity.world.lock().await; - let players = world + let players = self + .entity + .lock() + .unwrap() + .world .current_players + .lock() + .unwrap() .iter() .filter_map(|(token, player)| { if *token != player_token { - let entity_id = player.lock().unwrap().entity_id(); + let entity_id = player.entity_id(); if player_ids.contains(&entity_id) { Some(player.clone()) } else { @@ -405,13 +410,16 @@ impl Player { slot: Slot, ) -> Result<(), InventoryError> { for player in self.get_current_players_in_container(server).await { - let mut player = player.lock().unwrap(); let total_opened_containers = player.inventory.total_opened_containers; - player.inventory.state_id += 1; + // Returns previous value + let i = player + .inventory + .state_id + .fetch_add(1, std::sync::atomic::Ordering::Relaxed); let packet = CSetContainerSlot::new( total_opened_containers as i8, - player.inventory.state_id as i32, + (i + 1) as i32, slot_index, &slot, ); @@ -420,11 +428,10 @@ impl Player { Ok(()) } - async fn send_whole_container_change(&mut self, server: &Server) -> Result<(), InventoryError> { + async fn send_whole_container_change(&self, server: &Server) -> Result<(), InventoryError> { let players = self.get_current_players_in_container(server).await; for player in players { - let mut player = player.lock().unwrap(); let container = player.get_open_container(server); let mut container = container.as_ref().map(|v| v.lock().unwrap()); player.set_container_content(container.as_deref_mut()); diff --git a/pumpkin/src/client/mod.rs b/pumpkin/src/client/mod.rs index 7a7d9ba05..53ca10189 100644 --- a/pumpkin/src/client/mod.rs +++ b/pumpkin/src/client/mod.rs @@ -1,7 +1,10 @@ use std::{ io::{self, Write}, net::SocketAddr, - sync::Arc, + sync::{ + atomic::{AtomicBool, AtomicI32}, + Arc, Mutex, + }, }; use crate::{ @@ -31,7 +34,7 @@ use thiserror::Error; pub mod authentication; mod client_packet; -mod container; +// mod container; pub mod player_packet; #[derive(Clone)] @@ -62,95 +65,104 @@ impl Default for PlayerConfig { } pub struct Client { - pub gameprofile: Option, + pub gameprofile: Mutex>, - pub config: Option, - pub brand: Option, + pub config: Mutex>, + pub brand: Mutex>, - pub protocol_version: i32, - pub connection_state: ConnectionState, - pub encryption: bool, - pub closed: bool, + pub protocol_version: AtomicI32, + pub connection_state: Mutex, + pub encryption: AtomicBool, + pub closed: AtomicBool, pub token: Token, - pub connection: TcpStream, - pub address: SocketAddr, - enc: PacketEncoder, - dec: PacketDecoder, - pub client_packets_queue: Vec, + pub connection: Arc>, + pub address: Mutex, + enc: Arc>, + dec: Arc>, + pub client_packets_queue: Arc>>, - pub make_player: bool, + pub make_player: AtomicBool, } impl Client { pub fn new(token: Token, connection: TcpStream, address: SocketAddr) -> Self { Self { - protocol_version: 0, - gameprofile: None, - config: None, - brand: None, + protocol_version: AtomicI32::new(0), + gameprofile: Mutex::new(None), + config: Mutex::new(None), + brand: Mutex::new(None), token, - address, - connection_state: ConnectionState::HandShake, - connection, - enc: PacketEncoder::default(), - dec: PacketDecoder::default(), - encryption: true, - closed: false, - client_packets_queue: Vec::new(), - make_player: false, + address: Mutex::new(address), + connection_state: Mutex::new(ConnectionState::HandShake), + connection: Arc::new(Mutex::new(connection)), + enc: Arc::new(Mutex::new(PacketEncoder::default())), + dec: Arc::new(Mutex::new(PacketDecoder::default())), + encryption: AtomicBool::new(false), + closed: AtomicBool::new(false), + client_packets_queue: Arc::new(Mutex::new(Vec::new())), + make_player: AtomicBool::new(false), } } /// adds a Incoming packet to the queue - pub fn add_packet(&mut self, packet: RawPacket) { - self.client_packets_queue.push(packet); + pub fn add_packet(&self, packet: RawPacket) { + let mut client_packets_queue = self.client_packets_queue.lock().unwrap(); + client_packets_queue.push(packet); } /// enables encryption pub fn enable_encryption( - &mut self, + &self, shared_secret: &[u8], // decrypted ) -> Result<(), EncryptionError> { - self.encryption = true; + self.encryption + .store(true, std::sync::atomic::Ordering::Relaxed); let crypt_key: [u8; 16] = shared_secret .try_into() .map_err(|_| EncryptionError::SharedWrongLength)?; - self.dec.enable_encryption(&crypt_key); - self.enc.enable_encryption(&crypt_key); + self.dec.lock().unwrap().enable_encryption(&crypt_key); + self.enc.lock().unwrap().enable_encryption(&crypt_key); Ok(()) } // Compression threshold, Compression level - pub fn set_compression(&mut self, compression: Option<(u32, u32)>) { - self.dec.set_compression(compression.map(|v| v.0)); - self.enc.set_compression(compression); + pub fn set_compression(&self, compression: Option<(u32, u32)>) { + self.dec + .lock() + .unwrap() + .set_compression(compression.map(|v| v.0)); + self.enc.lock().unwrap().set_compression(compression); } /// Send a Clientbound Packet to the Client - pub fn send_packet(&mut self, packet: &P) { + pub fn send_packet(&self, packet: &P) { // assert!(!self.closed); - - self.enc - .append_packet(packet) + let mut enc = self.enc.lock().unwrap(); + enc.append_packet(packet) .unwrap_or_else(|e| self.kick(&e.to_string())); self.connection - .write_all(&self.enc.take()) + .lock() + .unwrap() + .write_all(&enc.take()) .map_err(|_| PacketError::ConnectionWrite) .unwrap_or_else(|e| self.kick(&e.to_string())); } - pub fn try_send_packet(&mut self, packet: &P) -> Result<(), PacketError> { + pub fn try_send_packet(&self, packet: &P) -> Result<(), PacketError> { // assert!(!self.closed); - self.enc.append_packet(packet)?; + let mut enc = self.enc.lock().unwrap(); + enc.append_packet(packet)?; self.connection - .write_all(&self.enc.take()) + .lock() + .unwrap() + .write_all(&enc.take()) .map_err(|_| PacketError::ConnectionWrite)?; Ok(()) } - pub async fn process_packets(&mut self, server: &Arc) { - while let Some(mut packet) = self.client_packets_queue.pop() { + pub async fn process_packets(&self, server: &Arc) { + while let Some(mut packet) = self.client_packets_queue.lock().unwrap().pop() { match self.handle_packet(server, &mut packet).await { Ok(_) => {} Err(e) => { @@ -164,13 +176,13 @@ impl Client { /// Handles an incoming decoded not Play state Packet pub async fn handle_packet( - &mut self, + &self, server: &Arc, packet: &mut RawPacket, ) -> Result<(), DeserializerError> { // TODO: handle each packet's Error instead of calling .unwrap() let bytebuf = &mut packet.bytebuf; - match self.connection_state { + match *self.connection_state.lock().unwrap() { pumpkin_protocol::ConnectionState::HandShake => match packet.id.0 { SHandShake::PACKET_ID => { self.handle_handshake(server, SHandShake::read(bytebuf)?); @@ -268,15 +280,16 @@ impl Client { } } - // Reads the connection until our buffer of len 4096 is full, then decode - /// Close connection when an error occurs - pub async fn poll(&mut self, event: &Event) { + /// Reads the connection until our buffer of len 4096 is full, then decode + /// Close connection when an error occurs or when the Client closed the connection + pub async fn poll(&self, event: &Event) { if event.is_readable() { let mut received_data = vec![0; 4096]; let mut bytes_read = 0; - // We can (maybe) read from the connection. loop { - match self.connection.read(&mut received_data[bytes_read..]) { + let connection = self.connection.clone(); + let mut connection = connection.lock().unwrap(); + match connection.read(&mut received_data[bytes_read..]) { Ok(0) => { // Reading 0 bytes means the other side has closed the // connection or is done writing, then so are we. @@ -297,9 +310,9 @@ impl Client { } if bytes_read != 0 { - self.dec.reserve(4096); - self.dec.queue_slice(&received_data[..bytes_read]); - match self.dec.decode() { + let mut dec = self.dec.lock().unwrap(); + dec.queue_slice(&received_data[..bytes_read]); + match dec.decode() { Ok(packet) => { if let Some(packet) = packet { self.add_packet(packet); @@ -307,15 +320,15 @@ impl Client { } Err(err) => self.kick(&err.to_string()), } - self.dec.clear(); + dec.clear(); } } } /// Kicks the Client with a reason depending on the connection state - pub fn kick(&mut self, reason: &str) { + pub fn kick(&self, reason: &str) { dbg!(reason); - match self.connection_state { + match *self.connection_state.lock().unwrap() { ConnectionState::Login => { self.try_send_packet(&CLoginDisconnect::new( &serde_json::to_string_pretty(&reason).unwrap_or("".into()), @@ -339,8 +352,9 @@ impl Client { } /// You should prefer to use `kick` when you can - pub fn close(&mut self) { - self.closed = true; + pub fn close(&self) { + self.closed + .store(true, std::sync::atomic::Ordering::Relaxed); } } diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/client/player_packet.rs index ac40d57b1..04c7788f6 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/client/player_packet.rs @@ -42,16 +42,20 @@ fn modulus(a: f32, b: f32) -> f32 { /// NEVER TRUST THE CLIENT. HANDLE EVERY ERROR, UNWRAP/EXPECT ARE FORBIDDEN impl Player { pub fn handle_confirm_teleport( - &mut self, + &self, _server: &Arc, confirm_teleport: SConfirmTeleport, ) { - if let Some((id, position)) = self.awaiting_teleport.as_ref() { + let mut awaiting_teleport = self.awaiting_teleport.lock().unwrap(); + if let Some((id, position)) = awaiting_teleport.as_ref() { if id == &confirm_teleport.teleport_id { // we should set the pos now to that we requested in the teleport packet, Is may fixed issues when the client sended position packets while being teleported - self.entity.set_pos(position.x, position.y, position.z); + self.entity + .lock() + .unwrap() + .set_pos(position.x, position.y, position.z); - self.awaiting_teleport = None; + *awaiting_teleport = None; } else { self.kick(TextComponent::text("Wrong teleport id")) } @@ -70,13 +74,14 @@ impl Player { pos.clamp(-2.0E7, 2.0E7) } - pub async fn handle_position(&mut self, _server: &Arc, position: SPlayerPosition) { + pub async fn handle_position(&self, _server: &Arc, position: SPlayerPosition) { if position.x.is_nan() || position.feet_y.is_nan() || position.z.is_nan() { self.kick(TextComponent::text("Invalid movement")); return; } - let entity = &mut self.entity; - self.last_position = entity.pos; + let mut entity = self.entity.lock().unwrap(); + let mut last_position = self.last_position.lock().unwrap(); + *last_position = entity.pos; entity.set_pos( Self::clamp_horizontal(position.x), Self::clamp_vertical(position.feet_y), @@ -86,9 +91,8 @@ impl Player { let on_ground = entity.on_ground; let entity_id = entity.entity_id; let (x, y, z) = entity.pos.into(); - let (lastx, lasty, lastz) = self.last_position.into(); - let world = self.entity.world.clone(); - let world = world.lock().await; + let (lastx, lasty, lastz) = (last_position.x, last_position.y, last_position.z); + let world = entity.world.clone(); // let delta = Vector3::new(x - lastx, y - lasty, z - lastz); // let velocity = self.velocity; @@ -103,7 +107,7 @@ impl Player { // return; // } // send new position to all other players - world.broadcast_packet( + world.broadcast_packet_expect( &[self.client.token], &CUpdateEntityPos::new( entity_id.into(), @@ -117,7 +121,7 @@ impl Player { } pub async fn handle_position_rotation( - &mut self, + &self, _server: &Arc, position_rotation: SPlayerPositionRotation, ) { @@ -132,9 +136,10 @@ impl Player { self.kick(TextComponent::text("Invalid rotation")); return; } - let entity = &mut self.entity; + let mut entity = self.entity.lock().unwrap(); - self.last_position = entity.pos; + let mut last_position = self.last_position.lock().unwrap(); + *last_position = entity.pos; entity.set_pos( Self::clamp_horizontal(position_rotation.x), Self::clamp_vertical(position_rotation.feet_y), @@ -147,12 +152,12 @@ impl Player { let on_ground = entity.on_ground; let entity_id = entity.entity_id; let (x, y, z) = entity.pos.into(); - let (lastx, lasty, lastz) = self.last_position.into(); + let (lastx, lasty, lastz) = (last_position.x, last_position.y, last_position.z); let yaw = modulus(entity.yaw * 256.0 / 360.0, 256.0); let pitch = modulus(entity.pitch * 256.0 / 360.0, 256.0); // let head_yaw = (entity.head_yaw * 256.0 / 360.0).floor(); - let world = self.entity.world.clone(); - let world = world.lock().await; + let entity = self.entity.lock().unwrap(); + let world = &entity.world; // let delta = Vector3::new(x - lastx, y - lasty, z - lastz); // let velocity = self.velocity; @@ -168,7 +173,7 @@ impl Player { // } // send new position to all other players - world.broadcast_packet( + world.broadcast_packet_expect( &[self.client.token], &CUpdateEntityPosRot::new( entity_id.into(), @@ -180,20 +185,20 @@ impl Player { on_ground, ), ); - world.broadcast_packet( + world.broadcast_packet_expect( &[self.client.token], &CHeadRot::new(entity_id.into(), yaw as u8), ); - player_chunker::update_position(&world, self).await; + player_chunker::update_position(world, self).await; } - pub async fn handle_rotation(&mut self, _server: &Arc, rotation: SPlayerRotation) { + pub async fn handle_rotation(&self, _server: &Arc, rotation: SPlayerRotation) { if !rotation.yaw.is_finite() || !rotation.pitch.is_finite() { self.kick(TextComponent::text("Invalid rotation")); return; } - let entity = &mut self.entity; + let mut entity = self.entity.lock().unwrap(); entity.on_ground = rotation.ground; entity.yaw = wrap_degrees(rotation.yaw) % 360.0; entity.pitch = wrap_degrees(rotation.pitch).clamp(-90.0, 90.0) % 360.0; @@ -204,61 +209,58 @@ impl Player { let pitch = modulus(entity.pitch * 256.0 / 360.0, 256.0); // let head_yaw = modulus(entity.head_yaw * 256.0 / 360.0, 256.0); - let world = self.entity.world.lock().await; + let world = &entity.world; let packet = CUpdateEntityRot::new(entity_id.into(), yaw as u8, pitch as u8, on_ground); - // self.client.send_packet(&packet); - world.broadcast_packet(&[self.client.token], &packet); + world.broadcast_packet_expect(&[self.client.token], &packet); let packet = CHeadRot::new(entity_id.into(), yaw as u8); - // self.client.send_packet(&packet); - world.broadcast_packet(&[self.client.token], &packet); + world.broadcast_packet_expect(&[self.client.token], &packet); } - pub fn handle_chat_command(&mut self, server: &Arc, command: SChatCommand) { + pub fn handle_chat_command(&self, server: &Arc, command: SChatCommand) { let dispatcher = server.command_dispatcher.clone(); dispatcher.handle_command(&mut CommandSender::Player(self), server, &command.command); } - pub fn handle_player_ground(&mut self, _server: &Arc, ground: SSetPlayerGround) { - self.entity.on_ground = ground.on_ground; + pub fn handle_player_ground(&self, _server: &Arc, ground: SSetPlayerGround) { + self.entity.lock().unwrap().on_ground = ground.on_ground; } - pub async fn handle_player_command(&mut self, _server: &Arc, command: SPlayerCommand) { - if command.entity_id != self.entity.entity_id.into() { + pub async fn handle_player_command(&self, _server: &Arc, command: SPlayerCommand) { + if command.entity_id != self.entity_id().into() { return; } if let Some(action) = Action::from_i32(command.action.0) { + let mut entity = self.entity.lock().unwrap(); match action { pumpkin_protocol::server::play::Action::StartSneaking => { - if !self.entity.sneaking { - self.entity.set_sneaking(&mut self.client, true).await + if !entity.sneaking { + entity.set_sneaking(true).await } } pumpkin_protocol::server::play::Action::StopSneaking => { - if self.entity.sneaking { - self.entity.set_sneaking(&mut self.client, false).await + if entity.sneaking { + entity.set_sneaking(false).await } } pumpkin_protocol::server::play::Action::LeaveBed => todo!(), pumpkin_protocol::server::play::Action::StartSprinting => { - if !self.entity.sprinting { - self.entity.set_sprinting(&mut self.client, true).await + if !entity.sprinting { + entity.set_sprinting(true).await } } pumpkin_protocol::server::play::Action::StopSprinting => { - if self.entity.sprinting { - self.entity.set_sprinting(&mut self.client, false).await + if entity.sprinting { + entity.set_sprinting(false).await } } pumpkin_protocol::server::play::Action::StartHorseJump => todo!(), pumpkin_protocol::server::play::Action::StopHorseJump => todo!(), pumpkin_protocol::server::play::Action::OpenVehicleInventory => todo!(), pumpkin_protocol::server::play::Action::StartFlyingElytra => { - let fall_flying = self.entity.check_fall_flying(); - if self.entity.fall_flying != fall_flying { - self.entity - .set_fall_flying(&mut self.client, fall_flying) - .await; + let fall_flying = entity.check_fall_flying(); + if entity.fall_flying != fall_flying { + entity.set_fall_flying(fall_flying).await; } } // TODO } @@ -267,7 +269,7 @@ impl Player { } } - pub async fn handle_swing_arm(&mut self, _server: &Arc, swing_arm: SSwingArm) { + pub async fn handle_swing_arm(&self, _server: &Arc, swing_arm: SSwingArm) { match Hand::from_i32(swing_arm.hand.0) { Some(hand) => { let animation = match hand { @@ -275,8 +277,9 @@ impl Player { Hand::Off => Animation::SwingOffhand, }; let id = self.entity_id(); - let world = self.entity.world.lock().await; - world.broadcast_packet( + let entity = self.entity.lock().unwrap(); + let world = &entity.world; + world.broadcast_packet_expect( &[self.client.token], &CEntityAnimation::new(id.into(), animation as u8), ) @@ -287,7 +290,7 @@ impl Player { }; } - pub async fn handle_chat_message(&mut self, _server: &Arc, chat_message: SChatMessage) { + pub async fn handle_chat_message(&self, _server: &Arc, chat_message: SChatMessage) { dbg!("got message"); let message = chat_message.message; @@ -299,24 +302,22 @@ impl Player { // TODO: filter message & validation let gameprofile = &self.gameprofile; - let world = self.entity.world.lock().await; - world.broadcast_packet( - &[self.client.token], - &CPlayerChatMessage::new( - pumpkin_protocol::uuid::UUID(gameprofile.id), - 1.into(), - chat_message.signature.as_deref(), - &message, - chat_message.timestamp, - chat_message.salt, - &[], - Some(TextComponent::text(&message)), - FilterType::PassThrough, - 1.into(), - TextComponent::text(&gameprofile.name.clone()), - None, - ), - ) + let entity = self.entity.lock().unwrap(); + let world = &entity.world; + world.broadcast_packet_all(&CPlayerChatMessage::new( + pumpkin_protocol::uuid::UUID(gameprofile.id), + 1.into(), + chat_message.signature.as_deref(), + &message, + chat_message.timestamp, + chat_message.salt, + &[], + Some(TextComponent::text(&message)), + FilterType::PassThrough, + 1.into(), + TextComponent::text(&gameprofile.name.clone()), + None, + )) /* server.broadcast_packet( self, @@ -330,7 +331,7 @@ impl Player { } pub fn handle_client_information_play( - &mut self, + &self, _server: &Arc, client_information: SClientInformationPlay, ) { @@ -338,7 +339,7 @@ impl Player { Hand::from_i32(client_information.main_hand.into()), ChatMode::from_i32(client_information.chat_mode.into()), ) { - self.config = PlayerConfig { + *self.config.lock().unwrap() = PlayerConfig { locale: client_information.locale, view_distance: client_information.view_distance, chat_mode, @@ -353,10 +354,11 @@ impl Player { } } - pub async fn handle_interact(&mut self, _: &Arc, interact: SInteract) { + pub async fn handle_interact(&self, _: &Arc, interact: SInteract) { let sneaking = interact.sneaking; - if self.entity.sneaking != sneaking { - self.entity.set_sneaking(&mut self.client, sneaking).await; + let mut entity = self.entity.lock().unwrap(); + if entity.sneaking != sneaking { + entity.set_sneaking(sneaking).await; } match ActionType::from_i32(interact.typ.0) { Some(action) => match action { @@ -365,19 +367,20 @@ impl Player { // TODO: do validation and stuff let config = &ADVANCED_CONFIG.pvp; if config.enabled { - let world = self.entity.world.clone(); - let world = world.lock().await; + let world = entity.world.clone(); let attacked_player = world.get_by_entityid(self, entity_id.0 as EntityId); - if let Some(mut player) = attacked_player { - let token = player.client.token; - let velo = player.entity.velocity; - if config.protect_creative && player.gamemode == GameMode::Creative { + if let Some(player) = attacked_player { + let mut victem_entity = player.entity.lock().unwrap(); + let velo = victem_entity.velocity; + if config.protect_creative + && *player.gamemode.lock().unwrap() == GameMode::Creative + { return; } if config.knockback { - let yaw = self.entity.yaw; + let yaw = entity.yaw; let strength = 1.0; - player.entity.knockback( + victem_entity.knockback( strength * 0.5, (yaw * (PI / 180.0)).sin() as f64, -(yaw * (PI / 180.0)).cos() as f64, @@ -388,21 +391,15 @@ impl Player { velo.y as f32, velo.z as f32, ); - self.entity.velocity = self.entity.velocity.multiply(0.6, 1.0, 0.6); + entity.velocity = entity.velocity.multiply(0.6, 1.0, 0.6); - player.entity.velocity = velo; + victem_entity.velocity = velo; player.client.send_packet(packet); } if config.hurt_animation { - // TODO - // thats how we prevent borrow errors :c - let packet = &CHurtAnimation::new(&entity_id, self.entity.yaw); - self.client.send_packet(packet); - player.client.send_packet(packet); - world.broadcast_packet( - &[self.client.token, token], - &CHurtAnimation::new(&entity_id, 10.0), - ) + world.broadcast_packet_all(&CHurtAnimation::new( + &entity_id, entity.yaw, + )) } if config.swing {} } else { @@ -420,11 +417,7 @@ impl Player { None => self.kick(TextComponent::text("Invalid action type")), } } - pub async fn handle_player_action( - &mut self, - _server: &Arc, - player_action: SPlayerAction, - ) { + pub async fn handle_player_action(&self, _server: &Arc, player_action: SPlayerAction) { match Status::from_i32(player_action.status.0) { Some(status) => match status { Status::StartedDigging => { @@ -434,20 +427,15 @@ impl Player { } // TODO: do validation // TODO: Config - if self.gamemode == GameMode::Creative { + if *self.gamemode.lock().unwrap() == GameMode::Creative { let location = player_action.location; // Block break & block break sound // TODO: currently this is always dirt replace it - let world = self.entity.world.lock().await; - world.broadcast_packet( - &[self.client.token], - &CWorldEvent::new(2001, &location, 11, false), - ); + let entity = self.entity.lock().unwrap(); + let world = &entity.world; + world.broadcast_packet_all(&CWorldEvent::new(2001, &location, 11, false)); // AIR - world.broadcast_packet( - &[self.client.token], - &CBlockUpdate::new(&location, 0.into()), - ); + world.broadcast_packet_all(&CBlockUpdate::new(&location, 0.into())); } } Status::CancelledDigging => { @@ -455,7 +443,8 @@ impl Player { // TODO: maybe log? return; } - self.current_block_destroy_stage = 0; + self.current_block_destroy_stage + .store(0, std::sync::atomic::Ordering::Relaxed); } Status::FinishedDigging => { // TODO: do validation @@ -466,16 +455,11 @@ impl Player { } // Block break & block break sound // TODO: currently this is always dirt replace it - let world = self.entity.world.lock().await; - world.broadcast_packet( - &[self.client.token], - &CWorldEvent::new(2001, &location, 11, false), - ); + let entity = self.entity.lock().unwrap(); + let world = &entity.world; + world.broadcast_packet_all(&CWorldEvent::new(2001, &location, 11, false)); // AIR - world.broadcast_packet( - &[self.client.token], - &CBlockUpdate::new(&location, 0.into()), - ); + world.broadcast_packet_all(&CBlockUpdate::new(&location, 0.into())); // TODO: Send this every tick self.client .send_packet(&CAcknowledgeBlockChange::new(player_action.sequence)); @@ -497,12 +481,12 @@ impl Player { } } - pub fn handle_play_ping_request(&mut self, _server: &Arc, request: SPlayPingRequest) { + pub fn handle_play_ping_request(&self, _server: &Arc, request: SPlayPingRequest) { self.client .send_packet(&CPingResponse::new(request.payload)); } - pub async fn handle_use_item_on(&mut self, _server: &Arc, use_item_on: SUseItemOn) { + pub async fn handle_use_item_on(&self, _server: &Arc, use_item_on: SUseItemOn) { let location = use_item_on.location; if !self.can_interact_with_block_at(&location, 1.0) { @@ -511,25 +495,23 @@ impl Player { } if let Some(face) = BlockFace::from_i32(use_item_on.face.0) { - if let Some(item) = self.inventory.held_item() { + if let Some(item) = self.inventory.lock().unwrap().held_item() { let minecraft_id = global_registry::find_minecraft_id( global_registry::ITEM_REGISTRY, item.item_id, ) .expect("All item ids are in the global registry"); if let Ok(block_state_id) = BlockId::new(minecraft_id, None) { - let world = self.entity.world.lock().await; - world.broadcast_packet( - &[self.client.token], - &CBlockUpdate::new(&location, block_state_id.get_id_mojang_repr().into()), - ); - world.broadcast_packet( - &[self.client.token], - &CBlockUpdate::new( - &WorldPosition(location.0 + face.to_offset()), - block_state_id.get_id_mojang_repr().into(), - ), - ); + let entity = self.entity.lock().unwrap(); + let world = &entity.world; + world.broadcast_packet_all(&CBlockUpdate::new( + &location, + block_state_id.get_id_mojang_repr().into(), + )); + world.broadcast_packet_all(&CBlockUpdate::new( + &WorldPosition(location.0 + face.to_offset()), + block_state_id.get_id_mojang_repr().into(), + )); } } self.client @@ -539,38 +521,46 @@ impl Player { } } - pub fn handle_use_item(&mut self, _server: &Arc, _use_item: SUseItem) { + pub fn handle_use_item(&self, _server: &Arc, _use_item: SUseItem) { // TODO: handle packet correctly log::error!("An item was used(SUseItem), but the packet is not implemented yet"); } - pub fn handle_set_held_item(&mut self, _server: &Arc, held: SSetHeldItem) { + pub fn handle_set_held_item(&self, _server: &Arc, held: SSetHeldItem) { let slot = held.slot; if !(0..=8).contains(&slot) { self.kick(TextComponent::text("Invalid held slot")) } - self.inventory.set_selected(slot as usize); + self.inventory.lock().unwrap().set_selected(slot as usize); } pub fn handle_set_creative_slot( - &mut self, + &self, _server: &Arc, packet: SSetCreativeSlot, ) -> Result<(), InventoryError> { - if self.gamemode != GameMode::Creative { + if *self.gamemode.lock().unwrap() != GameMode::Creative { return Err(InventoryError::PermissionError); } - self.inventory - .set_slot(packet.slot as usize, packet.clicked_item.to_item(), false) + self.inventory.lock().unwrap().set_slot( + packet.slot as usize, + packet.clicked_item.to_item(), + false, + ) } // TODO: // This function will in the future be used to keep track of if the client is in a valid state. // But this is not possible yet - pub fn handle_close_container(&mut self, server: &Arc, packet: SCloseContainer) { + pub fn handle_close_container(&self, server: &Arc, packet: SCloseContainer) { // window_id 0 represents both 9x1 Generic AND inventory here - self.inventory.state_id = 0; - if let Some(id) = self.open_container { + self.inventory + .lock() + .unwrap() + .state_id + .store(0, std::sync::atomic::Ordering::Relaxed); + let mut open_container = self.open_container.lock().unwrap(); + if let Some(id) = *open_container { let mut open_containers = server .open_containers .write() @@ -578,7 +568,7 @@ impl Player { if let Some(container) = open_containers.get_mut(&id) { container.remove_player(self.entity_id()) } - self.open_container = None; + *open_container = None; } let Some(_window_type) = WindowType::from_u8(packet.window_id) else { self.kick(TextComponent::text("Invalid window ID")); diff --git a/pumpkin/src/commands/arg_player.rs b/pumpkin/src/commands/arg_player.rs index e3506b4cb..98a71fde7 100644 --- a/pumpkin/src/commands/arg_player.rs +++ b/pumpkin/src/commands/arg_player.rs @@ -31,7 +31,7 @@ pub fn parse_arg_player<'a>( src: &'a mut CommandSender, arg_name: &str, consumed_args: &ConsumedArgs, -) -> Result<&'a mut crate::entity::player::Player, InvalidTreeError> { +) -> Result<&'a crate::entity::player::Player, InvalidTreeError> { let s = consumed_args .get(arg_name) .ok_or(InvalidConsumptionError(None))? diff --git a/pumpkin/src/commands/cmd_echest.rs b/pumpkin/src/commands/cmd_echest.rs index c942d4c31..fbf7bdd69 100644 --- a/pumpkin/src/commands/cmd_echest.rs +++ b/pumpkin/src/commands/cmd_echest.rs @@ -11,7 +11,7 @@ pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> { CommandTree::new(NAMES, DESCRIPTION).execute(&|sender, server, _| { if let Some(player) = sender.as_mut_player() { let entity_id = player.entity_id(); - player.open_container = Some(0); + *player.open_container.lock().unwrap() = Some(0); { let mut open_containers = server .open_containers @@ -27,7 +27,8 @@ pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> { } } } - player.open_container(server, "minecraft:generic_9x3"); + // TODO + // player.open_container(server, "minecraft:generic_9x3"); } Ok(()) diff --git a/pumpkin/src/commands/cmd_gamemode.rs b/pumpkin/src/commands/cmd_gamemode.rs index d7b25ecf5..92600d395 100644 --- a/pumpkin/src/commands/cmd_gamemode.rs +++ b/pumpkin/src/commands/cmd_gamemode.rs @@ -65,15 +65,14 @@ pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> { let gamemode = parse_arg_gamemode(args)?; return if let Player(target) = sender { - if target.gamemode == gamemode { + if *target.gamemode.lock().unwrap() == gamemode { target.send_system_message(TextComponent::text(&format!( "You already in {:?} gamemode", gamemode ))); } else { // TODO - #[expect(clippy::let_underscore_future)] - let _ = target.set_gamemode(gamemode); + target.set_gamemode(gamemode); target.send_system_message(TextComponent::text(&format!( "Game mode was set to {:?}", gamemode @@ -90,15 +89,14 @@ pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> { let gamemode = parse_arg_gamemode(args)?; let target = parse_arg_player(sender, ARG_TARGET, args)?; - if target.gamemode == gamemode { + if *target.gamemode.lock().unwrap() == gamemode { target.send_system_message(TextComponent::text(&format!( "You already in {:?} gamemode", gamemode ))); } else { // TODO - #[expect(clippy::let_underscore_future)] - let _ = target.set_gamemode(gamemode); + target.set_gamemode(gamemode); target.send_system_message(TextComponent::text(&format!( "Game mode was set to {:?}", gamemode diff --git a/pumpkin/src/commands/mod.rs b/pumpkin/src/commands/mod.rs index 3b3350b0d..17393346e 100644 --- a/pumpkin/src/commands/mod.rs +++ b/pumpkin/src/commands/mod.rs @@ -21,7 +21,7 @@ mod tree_format; pub enum CommandSender<'a> { Rcon(&'a mut Vec), Console, - Player(&'a mut Player), + Player(&'a Player), } impl<'a> CommandSender<'a> { @@ -49,7 +49,7 @@ impl<'a> CommandSender<'a> { CommandSender::Rcon(_) => true, } } - pub fn as_mut_player(&mut self) -> Option<&mut Player> { + pub fn as_mut_player(&mut self) -> Option<&Player> { match self { CommandSender::Player(player) => Some(player), CommandSender::Console => None, diff --git a/pumpkin/src/entity/mod.rs b/pumpkin/src/entity/mod.rs index 986df49c0..a8f03821d 100644 --- a/pumpkin/src/entity/mod.rs +++ b/pumpkin/src/entity/mod.rs @@ -9,14 +9,14 @@ use pumpkin_protocol::{ VarInt, }; -use crate::{client::Client, world::World}; +use crate::world::World; pub mod player; pub struct Entity { pub entity_id: EntityId, pub entity_type: EntityType, - pub world: Arc>, + pub world: Arc, pub pos: Vector3, pub block_pos: WorldPosition, @@ -38,11 +38,10 @@ pub struct Entity { pub pose: EntityPose, } -// TODO: Remove client: &mut Client, world: Arc> bs impl Entity { pub fn new( entity_id: EntityId, - world: Arc>, + world: Arc, entity_type: EntityType, standing_eye_height: f32, ) -> Self { @@ -87,6 +86,10 @@ impl Entity { } } + pub async fn remove(&mut self) { + self.world.remove_entity(self); + } + pub fn knockback(&mut self, strength: f64, x: f64, z: f64) { // This has some vanilla magic let mut x = x; @@ -109,11 +112,10 @@ impl Entity { ); } - pub async fn set_sneaking(&mut self, client: &mut Client, sneaking: bool) { + pub async fn set_sneaking(&mut self, sneaking: bool) { assert!(self.sneaking != sneaking); self.sneaking = sneaking; - self.set_flag(client, Self::SNEAKING_FLAG_INDEX, sneaking) - .await; + self.set_flag(Self::SNEAKING_FLAG_INDEX, sneaking).await; // if sneaking { // self.set_pose(EntityPose::Crouching).await; // } else { @@ -121,21 +123,20 @@ impl Entity { // } } - pub async fn set_sprinting(&mut self, client: &mut Client, sprinting: bool) { + pub async fn set_sprinting(&mut self, sprinting: bool) { assert!(self.sprinting != sprinting); self.sprinting = sprinting; - self.set_flag(client, Self::SPRINTING_FLAG_INDEX, sprinting) - .await; + self.set_flag(Self::SPRINTING_FLAG_INDEX, sprinting).await; } pub fn check_fall_flying(&self) -> bool { !self.on_ground } - pub async fn set_fall_flying(&mut self, client: &mut Client, fall_flying: bool) { + pub async fn set_fall_flying(&mut self, fall_flying: bool) { assert!(self.fall_flying != fall_flying); self.fall_flying = fall_flying; - self.set_flag(client, Self::FALL_FLYING_FLAG_INDEX, fall_flying) + self.set_flag(Self::FALL_FLYING_FLAG_INDEX, fall_flying) .await; } @@ -146,7 +147,7 @@ impl Entity { pub const INVISIBLE_FLAG_INDEX: u32 = 5; pub const GLOWING_FLAG_INDEX: u32 = 6; pub const FALL_FLYING_FLAG_INDEX: u32 = 7; - async fn set_flag(&mut self, client: &mut Client, index: u32, value: bool) { + async fn set_flag(&mut self, index: u32, value: bool) { let mut b = 0i8; if value { b |= 1 << index; @@ -154,24 +155,16 @@ impl Entity { b &= !(1 << index); } let packet = CSetEntityMetadata::new(self.entity_id.into(), Metadata::new(0, 0.into(), b)); - client.send_packet(&packet); - self.world - .lock() - .await - .broadcast_packet(&[client.token], &packet); + self.world.broadcast_packet_all(&packet); } - pub async fn set_pose(&mut self, client: &mut Client, pose: EntityPose) { + pub async fn set_pose(&mut self, pose: EntityPose) { self.pose = pose; let pose = self.pose as i32; let packet = CSetEntityMetadata::::new( self.entity_id.into(), Metadata::new(6, 20.into(), (pose).into()), ); - client.send_packet(&packet); - self.world - .lock() - .await - .broadcast_packet(&[client.token], &packet) + self.world.broadcast_packet_all(&packet) } } diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 774d308ec..a46116d15 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -1,4 +1,7 @@ -use std::sync::Arc; +use std::sync::{ + atomic::{AtomicI32, AtomicU8}, + Arc, Mutex, + }; use num_derive::FromPrimitive; use num_traits::ToPrimitive; @@ -58,43 +61,38 @@ impl Default for PlayerAbilities { } pub struct Player { - pub entity: Entity, + pub entity: Mutex, pub gameprofile: GameProfile, pub client: Client, - pub config: PlayerConfig, + pub config: Mutex, /// Current gamemode - pub gamemode: GameMode, + pub gamemode: Mutex, // TODO: prbly should put this into an Living Entitiy or something - pub health: f32, - pub food: i32, - pub food_saturation: f32, - pub inventory: PlayerInventory, - pub open_container: Option, - pub carried_item: Option, + pub health: Mutex, + pub food: AtomicI32, + pub food_saturation: Mutex, + pub inventory: Mutex, + pub open_container: Mutex>, + pub carried_item: Mutex>, /// send `send_abilties_update` when changed pub abilities: PlayerAbilities, - pub last_position: Vector3, + pub last_position: Mutex>, // TODO: This is currently unused, We have to calculate the block breaking speed our own and then break the block our own if its done - pub current_block_destroy_stage: u8, + pub current_block_destroy_stage: AtomicU8, - pub teleport_id_count: i32, + pub teleport_id_count: AtomicI32, // Current awaiting teleport id and location, None if did not teleport - pub awaiting_teleport: Option<(VarInt, Vector3)>, + pub awaiting_teleport: Mutex)>>, - pub watched_section: Vector3, + pub watched_section: Mutex>, } impl Player { - pub fn new( - client: Client, - world: Arc>, - entity_id: EntityId, - gamemode: GameMode, - ) -> Self { - let gameprofile = match client.gameprofile.clone() { + pub fn new(client: Client, world: Arc, entity_id: EntityId, gamemode: GameMode) -> Self { + let gameprofile = match client.gameprofile.lock().unwrap().clone() { Some(profile) => profile, None => { log::error!("No gameprofile?. Impossible"); @@ -106,37 +104,36 @@ impl Player { } } }; - let config = client.config.clone().unwrap_or_default(); + let config = client.config.lock().unwrap().clone().unwrap_or_default(); Self { - entity: Entity::new(entity_id, world, EntityType::Player, 1.62), - config, + entity: Mutex::new(Entity::new(entity_id, world, EntityType::Player, 1.62)), + config: Mutex::new(config), gameprofile, client, - awaiting_teleport: None, + awaiting_teleport: Mutex::new(None), // TODO: Load this from previous instance - health: 20.0, - food: 20, - food_saturation: 20.0, - current_block_destroy_stage: 0, - inventory: PlayerInventory::new(), - open_container: None, - carried_item: None, - teleport_id_count: 0, + health: Mutex::new(20.0), + food: AtomicI32::new(20), + food_saturation: Mutex::new(20.0), + current_block_destroy_stage: AtomicU8::new(0), + inventory: Mutex::new(PlayerInventory::new()), + open_container: Mutex::new(None), + carried_item: Mutex::new(None), + teleport_id_count: AtomicI32::new(0), abilities: PlayerAbilities::default(), - gamemode, - watched_section: Vector3::new(0, 0, 0), - last_position: Vector3::new(0.0, 0.0, 0.0), + gamemode: Mutex::new(gamemode), + watched_section: Mutex::new(Vector3::new(0, 0, 0)), + last_position: Mutex::new(Vector3::new(0.0, 0.0, 0.0)), } } - // TODO: Put this into entity /// Removes the Player out of the current World - pub async fn remove(&mut self) { - self.entity.world.lock().await.remove_player(self); + pub async fn remove(&self) { + self.entity.lock().unwrap().world.remove_player(self); } pub fn entity_id(&self) -> EntityId { - self.entity.entity_id + self.entity.lock().unwrap().entity_id } pub fn send_abilties_update(&mut self) { @@ -162,17 +159,22 @@ impl Player { )); } - pub fn teleport(&mut self, x: f64, y: f64, z: f64, yaw: f32, pitch: f32) { + pub fn teleport(&self, x: f64, y: f64, z: f64, yaw: f32, pitch: f32) { // this is the ultra special magic code used to create the teleport id - self.teleport_id_count += 1; - if self.teleport_id_count == i32::MAX { - self.teleport_id_count = 0; + // This returns the old value + let i = self + .teleport_id_count + .fetch_add(1, std::sync::atomic::Ordering::Relaxed); + if i + 2 == i32::MAX { + self.teleport_id_count + .store(0, std::sync::atomic::Ordering::Relaxed); } - let entity = &mut self.entity; + let teleport_id = i + 1; + let mut entity = self.entity.lock().unwrap(); entity.set_pos(x, y, z); entity.yaw = yaw; entity.pitch = pitch; - self.awaiting_teleport = Some((self.teleport_id_count.into(), Vector3::new(x, y, z))); + *self.awaiting_teleport.lock().unwrap() = Some((teleport_id.into(), Vector3::new(x, y, z))); self.client.send_packet(&CSyncPlayerPosition::new( x, y, @@ -180,12 +182,12 @@ impl Player { yaw, pitch, 0, - self.teleport_id_count.into(), + teleport_id.into(), )); } pub fn block_interaction_range(&self) -> f64 { - if self.gamemode == GameMode::Creative { + if *self.gamemode.lock().unwrap() == GameMode::Creative { 5.0 } else { 4.5 @@ -195,17 +197,21 @@ impl Player { pub fn can_interact_with_block_at(&self, pos: &WorldPosition, additional_range: f64) -> bool { let d = self.block_interaction_range() + additional_range; let box_pos = BoundingBox::from_block(pos); + let entity = self.entity.lock().unwrap(); box_pos.squared_magnitude(Vector3 { - x: self.entity.pos.x, - y: self.entity.pos.y + self.entity.standing_eye_height as f64, - z: self.entity.pos.z, + x: entity.pos.x, + y: entity.pos.y + entity.standing_eye_height as f64, + z: entity.pos.z, }) < d * d } /// Kicks the Client with a reason depending on the connection state - pub fn kick(&mut self, reason: TextComponent) { - assert!(self.client.connection_state == ConnectionState::Play); - assert!(!self.client.closed); + pub fn kick(&self, reason: TextComponent) { + assert!(*self.client.connection_state.lock().unwrap() == ConnectionState::Play); + assert!(!self + .client + .closed + .load(std::sync::atomic::Ordering::Relaxed)); self.client .try_send_packet(&CPlayDisconnect::new(&reason)) @@ -218,53 +224,47 @@ impl Player { self.client.close() } - pub fn update_health(&mut self, health: f32, food: i32, food_saturation: f32) { - self.health = health; - self.food = food; - self.food_saturation = food_saturation; + pub fn update_health(&self, health: f32, food: i32, food_saturation: f32) { + *self.health.lock().unwrap() = health; + self.food.store(food, std::sync::atomic::Ordering::Relaxed); + *self.food_saturation.lock().unwrap() = food_saturation; } - pub async fn set_gamemode(&mut self, gamemode: GameMode) { + pub fn set_gamemode(&self, gamemode: GameMode) { // We could send the same gamemode without problems. But why waste bandwidth ? + let mut current_gamemode = self.gamemode.lock().unwrap(); assert!( - self.gamemode != gamemode, + *current_gamemode != gamemode, "Setting the same gamemode as already is" ); - self.gamemode = gamemode; + *current_gamemode = gamemode; // So a little story time. I actually made an abitlties_from_gamemode function. I looked at vanilla and they always send the abilties from the gamemode. But the funny thing actually is. That the client // does actually use the same method and set the abilties when receiving the CGameEvent gamemode packet. Just Mojang nonsense - - // TODO: fix this ugly mess :c, It gives me a liftime error when saving packet as a var - self.client.send_packet(&CPlayerInfoUpdate::new( - 0x04, - &[pumpkin_protocol::client::play::Player { - uuid: self.gameprofile.id, - actions: vec![PlayerAction::UpdateGameMode((self.gamemode as i32).into())], - }], - )); - self.entity.world.lock().await.broadcast_packet( - &[self.client.token], - &CPlayerInfoUpdate::new( + self.entity + .lock() + .unwrap() + .world + .broadcast_packet_all(&CPlayerInfoUpdate::new( 0x04, &[pumpkin_protocol::client::play::Player { uuid: self.gameprofile.id, - actions: vec![PlayerAction::UpdateGameMode((self.gamemode as i32).into())], + actions: vec![PlayerAction::UpdateGameMode((gamemode as i32).into())], }], - ), - ); + )); self.client .send_packet(&CGameEvent::new(3, gamemode.to_f32().unwrap())); } - pub fn send_system_message(&mut self, text: TextComponent) { + pub fn send_system_message(&self, text: TextComponent) { self.client .send_packet(&CSystemChatMessage::new(text, false)); } } impl Player { - pub async fn process_packets(&mut self, server: &Arc) { - while let Some(mut packet) = self.client.client_packets_queue.pop() { + pub async fn process_packets(&self, server: &Arc) { + let mut packets = self.client.client_packets_queue.lock().unwrap(); + while let Some(mut packet) = packets.pop() { match self.handle_play_packet(server, &mut packet).await { Ok(_) => {} Err(e) => { @@ -277,7 +277,7 @@ impl Player { } pub async fn handle_play_packet( - &mut self, + &self, server: &Arc, packet: &mut RawPacket, ) -> Result<(), DeserializerError> { @@ -362,9 +362,10 @@ impl Player { Ok(()) } SClickContainer::PACKET_ID => { - self.handle_click_container(server, SClickContainer::read(bytebuf)?) - .await - .unwrap(); + // TODO + // self.handle_click_container(server, SClickContainer::read(bytebuf)?) + // .await + // .unwrap(); Ok(()) } SCloseContainer::PACKET_ID => { diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs index 4be8e54c6..f90f5b041 100644 --- a/pumpkin/src/main.rs +++ b/pumpkin/src/main.rs @@ -7,7 +7,7 @@ use mio::net::TcpListener; use mio::{Events, Interest, Poll, Token}; use std::collections::HashMap; -use std::io::{self}; +use std::io::{self, Read}; use client::{interrupted, Client}; use server::Server; @@ -24,7 +24,7 @@ pub mod util; pub mod world; fn main() -> io::Result<()> { - use std::sync::{Arc, Mutex}; + use std::sync::Arc; use entity::player::Player; use pumpkin_config::{ADVANCED_CONFIG, BASIC_CONFIG}; @@ -79,7 +79,7 @@ fn main() -> io::Result<()> { let rcon = ADVANCED_CONFIG.rcon.clone(); let mut clients: HashMap = HashMap::new(); - let mut players: HashMap>> = HashMap::new(); + let mut players: HashMap> = HashMap::new(); let server = Arc::new(Server::new()); log::info!("Started Server took {}ms", time.elapsed().as_millis()); @@ -159,17 +159,19 @@ fn main() -> io::Result<()> { token => { // Poll Players if let Some(player) = players.get_mut(&token) { - let mut player = player.lock().unwrap(); player.client.poll(event).await; - if !player.client.closed { + let closed = player + .client + .closed + .load(std::sync::atomic::Ordering::Relaxed); + if !closed { player.process_packets(&server).await; } - if player.client.closed { - drop(player); + if closed { if let Some(player) = players.remove(&token) { - let mut player = player.lock().unwrap(); player.remove().await; - poll.registry().deregister(&mut player.client.connection)?; + let connection = &mut player.client.connection.lock().unwrap(); + poll.registry().deregister(connection.by_ref())?; } } }; @@ -178,23 +180,29 @@ fn main() -> io::Result<()> { // Maybe received an event for a TCP connection. let (done, make_player) = if let Some(client) = clients.get_mut(&token) { client.poll(event).await; - if !client.closed { + let closed = client.closed.load(std::sync::atomic::Ordering::Relaxed); + if !closed { client.process_packets(&server).await; } - (client.closed, client.make_player) + ( + closed, + client + .make_player + .load(std::sync::atomic::Ordering::Relaxed), + ) } else { // Sporadic events happen, we can safely ignore them. (false, false) }; if done || make_player { - if let Some(mut client) = clients.remove(&token) { + if let Some(client) = clients.remove(&token) { if done { - poll.registry().deregister(&mut client.connection)?; + let connection = &mut client.connection.lock().unwrap(); + poll.registry().deregister(connection.by_ref())?; } else if make_player { let token = client.token; let (player, world) = server.add_player(token, client).await; players.insert(token, player.clone()); - let mut world = world.lock().await; world.spawn_player(&BASIC_CONFIG, player).await; } } diff --git a/pumpkin/src/proxy/velocity.rs b/pumpkin/src/proxy/velocity.rs index b23e17082..869f470f2 100644 --- a/pumpkin/src/proxy/velocity.rs +++ b/pumpkin/src/proxy/velocity.rs @@ -15,7 +15,7 @@ type HmacSha256 = Hmac; const MAX_SUPPORTED_FORWARDING_VERSION: i32 = 4; const PLAYER_INFO_CHANNEL: &str = "velocity:player_info"; -pub fn velocity_login(client: &mut Client) { +pub fn velocity_login(client: &Client) { let velocity_message_id: i32 = 0; let mut buf = BytesMut::new(); @@ -36,7 +36,7 @@ pub fn check_integrity(data: (&[u8], &[u8]), secret: String) -> bool { } pub fn receive_plugin_response( - client: &mut Client, + client: &Client, config: VelocityConfig, response: SLoginPluginResponse, ) { @@ -62,7 +62,7 @@ pub fn receive_plugin_response( } // TODO: no unwrap let addr: SocketAddr = buf.get_string().unwrap().parse().unwrap(); - client.address = addr; + *client.address.lock().unwrap() = addr; todo!() } else { client.kick("This server requires you to connect with Velocity.") diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index d9aaf61a3..986842d60 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -45,7 +45,7 @@ pub struct Server { pub command_dispatcher: Arc>, - pub worlds: Vec>>, + pub worlds: Vec>, pub status_response: StatusResponse, // We cache the json response here so we don't parse it every time someone makes a Status request. // Keep in mind that we must parse this again, when the StatusResponse changes which usally happen when a player joins or leaves @@ -109,7 +109,7 @@ impl Server { drag_handler: DragHandler::new(), // 0 is invalid entity_id: 2.into(), - worlds: vec![Arc::new(tokio::sync::Mutex::new(world))], + worlds: vec![Arc::new(world)], public_key, cached_server_brand, private_key, @@ -121,11 +121,7 @@ impl Server { } } - pub async fn add_player( - &self, - token: Token, - client: Client, - ) -> (Arc>, Arc>) { + pub async fn add_player(&self, token: Token, client: Client) -> (Arc, Arc) { let entity_id = self.new_entity_id(); let gamemode = match BASIC_CONFIG.default_gamemode { GameMode::Undefined => GameMode::Survival, @@ -135,13 +131,8 @@ impl Server { // TODO: select default from config let world = self.worlds[0].clone(); - let player = Arc::new(Mutex::new(Player::new( - client, - world.clone(), - entity_id, - gamemode, - ))); - world.lock().await.add_player(token, player.clone()); + let player = Arc::new(Player::new(client, world.clone(), entity_id, gamemode)); + world.add_player(token, player.clone()); (player, world) } @@ -161,12 +152,12 @@ impl Server { } /// Sends a Packet to all Players in all worlds - pub fn broadcast_packet_all

(&self, except: &[Token], packet: &P) + pub fn broadcast_packet_all

(&self, packet: &P) where P: ClientPacket, { for world in &self.worlds { - world.blocking_lock().broadcast_packet(except, packet) + world.broadcast_packet_all(packet) } } @@ -183,7 +174,7 @@ impl Server { buf } - pub fn send_brand(&self, client: &mut Client) { + pub fn send_brand(&self, client: &Client) { // send server brand client.send_packet(&CPluginMessage::new( "minecraft:brand", diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 4c4f514c2..672e37136 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -1,7 +1,6 @@ use std::{ collections::HashMap, - ops::DerefMut, - sync::{Arc, Mutex, MutexGuard}, + sync::{Arc, Mutex}, }; pub mod player_chunker; @@ -22,11 +21,14 @@ use pumpkin_protocol::{ use pumpkin_world::level::Level; use tokio::sync::mpsc; -use crate::{client::Client, entity::player::Player}; +use crate::{ + client::Client, + entity::{player::Player, Entity}, +}; pub struct World { pub level: Arc>, - pub current_players: HashMap>>, + pub current_players: Arc>>>, // entities, players... } @@ -34,35 +36,36 @@ impl World { pub fn load(level: Level) -> Self { Self { level: Arc::new(Mutex::new(level)), - current_players: HashMap::new(), + current_players: Arc::new(Mutex::new(HashMap::new())), } } - /// Sends a Packet to all Players, Expect some players. Because we can't lock them twice - pub fn broadcast_packet

(&self, except: &[Token], packet: &P) + /// Sends a Packet to all Players in the World + pub fn broadcast_packet_all

(&self, packet: &P) where P: ClientPacket, { - for (_, player) in self - .current_players - .iter() - .filter(|c| !except.contains(c.0)) - { - let mut player = player.lock().unwrap(); + let current_players = self.current_players.lock().unwrap(); + for (_, player) in current_players.iter() { player.client.send_packet(packet); } } - pub async fn spawn_player( - &mut self, - base_config: &BasicConfiguration, - player: Arc>, - ) { - let mut player = player.lock().unwrap(); - let player = player.deref_mut(); + /// Sends a Packet to all Players in the World, Expect the Players given the the expect parameter + pub fn broadcast_packet_expect

(&self, except: &[Token], packet: &P) + where + P: ClientPacket, + { + let current_players = self.current_players.lock().unwrap(); + for (_, player) in current_players.iter().filter(|c| !except.contains(c.0)) { + player.client.send_packet(packet); + } + } + + pub async fn spawn_player(&self, base_config: &BasicConfiguration, player: Arc) { // This code follows the vanilla packet order let entity_id = player.entity_id(); - let gamemode = player.gamemode; + let gamemode = player.gamemode.lock().unwrap(); log::debug!("spawning player, entity id {}", entity_id); // login packet for our new player @@ -104,7 +107,7 @@ impl World { let gameprofile = &player.gameprofile; // first send info update to our new player, So he can see his Skin // also send his info to everyone else - player.client.send_packet(&CPlayerInfoUpdate::new( + self.broadcast_packet_all(&CPlayerInfoUpdate::new( 0x01 | 0x08, &[pumpkin_protocol::client::play::Player { uuid: gameprofile.id, @@ -117,31 +120,16 @@ impl World { ], }], )); - self.broadcast_packet( - &[player.client.token], - &CPlayerInfoUpdate::new( - 0x01 | 0x08, - &[pumpkin_protocol::client::play::Player { - uuid: gameprofile.id, - actions: vec![ - PlayerAction::AddPlayer { - name: gameprofile.name.clone(), - properties: gameprofile.properties.clone(), - }, - PlayerAction::UpdateListed(true), - ], - }], - ), - ); // here we send all the infos of already joined players let mut entries = Vec::new(); for (_, playerr) in self .current_players + .lock() + .unwrap() .iter() .filter(|(c, _)| **c != player.client.token) { - let playerr = playerr.as_ref().lock().unwrap(); let gameprofile = &playerr.gameprofile; entries.push(pumpkin_protocol::client::play::Player { uuid: gameprofile.id, @@ -161,7 +149,7 @@ impl World { let gameprofile = &player.gameprofile; // spawn player for every client - self.broadcast_packet( + self.broadcast_packet_expect( &[player.client.token], // TODO: add velo &CSpawnEntity::new( @@ -182,9 +170,14 @@ impl World { ); // spawn players for our client let token = player.client.token; - for (_, existing_player) in self.current_players.iter().filter(|c| c.0 != &token) { - let existing_player = existing_player.as_ref().lock().unwrap(); - let entity = &existing_player.entity; + for (_, existing_player) in self + .current_players + .lock() + .unwrap() + .iter() + .filter(|c| c.0 != &token) + { + let entity = existing_player.entity.lock().unwrap(); let gameprofile = &existing_player.gameprofile; player.client.send_packet(&CSpawnEntity::new( existing_player.entity_id().into(), @@ -204,33 +197,27 @@ impl World { } // entity meta data // set skin parts - if let Some(config) = player.client.config.as_ref() { + if let Some(config) = player.client.config.lock().unwrap().as_ref() { let packet = CSetEntityMetadata::new( entity_id.into(), Metadata::new(17, VarInt(0), config.skin_parts), ); - player.client.send_packet(&packet); - self.broadcast_packet(&[player.client.token], &packet) + self.broadcast_packet_all(&packet) } // Start waiting for level chunks player.client.send_packet(&CGameEvent::new(13, 0.0)); // Spawn in inital chunks - player_chunker::player_join(self, player).await; + player_chunker::player_join(self, player.clone()).await; } - async fn spawn_world_chunks( - &self, - client: &mut Client, - chunks: Vec>, - distance: i32, - ) { + async fn spawn_world_chunks(&self, client: &Client, chunks: Vec>, distance: i32) { let inst = std::time::Instant::now(); let (sender, mut chunk_receiver) = mpsc::channel(distance as usize); let level = self.level.clone(); - let closed = client.closed; + let closed = client.closed.load(std::sync::atomic::Ordering::Relaxed); let chunks = Arc::new(chunks); tokio::task::spawn_blocking(move || { level.lock().unwrap().fetch_chunks(&chunks, sender, closed) @@ -255,42 +242,47 @@ impl World { len / (1024 * 1024) ); } - if !client.closed { + if !client.closed.load(std::sync::atomic::Ordering::Relaxed) { client.send_packet(&CChunkData(&chunk_data)); } } dbg!("DONE CHUNKS", inst.elapsed()); } - /// TODO: This definitly should be in world - pub fn get_by_entityid(&self, from: &Player, id: EntityId) -> Option> { + pub fn get_by_entityid(&self, from: &Player, id: EntityId) -> Option> { for (_, player) in self .current_players + .lock() + .unwrap() .iter() .filter(|c| c.0 != &from.client.token) { - let player = player.lock().unwrap(); if player.entity_id() == id { - return Some(player); + return Some(player.clone()); } } None } - pub fn add_player(&mut self, token: Token, player: Arc>) { - self.current_players.insert(token, player); + pub fn add_player(&self, token: Token, player: Arc) { + self.current_players.lock().unwrap().insert(token, player); } - pub fn remove_player(&mut self, player: &Player) { - self.current_players.remove(&player.client.token).unwrap(); - // despawn the player - // todo: put this into the entitiy struct - let id = player.entity_id(); + pub fn remove_player(&self, player: &Player) { + self.current_players + .lock() + .unwrap() + .remove(&player.client.token) + .unwrap(); let uuid = player.gameprofile.id; - self.broadcast_packet( + self.broadcast_packet_expect( &[player.client.token], &CRemovePlayerInfo::new(1.into(), &[UUID(uuid)]), ); - self.broadcast_packet(&[player.client.token], &CRemoveEntities::new(&[id.into()])) + self.remove_entity(&player.entity.lock().unwrap()); + } + + pub fn remove_entity(&self, entity: &Entity) { + self.broadcast_packet_all(&CRemoveEntities::new(&[entity.entity_id.into()])) } } diff --git a/pumpkin/src/world/player_chunker.rs b/pumpkin/src/world/player_chunker.rs index a060dfbd0..6c91e3508 100644 --- a/pumpkin/src/world/player_chunker.rs +++ b/pumpkin/src/world/player_chunker.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use pumpkin_config::BASIC_CONFIG; use pumpkin_core::math::{ get_section_cord, position::WorldPosition, vector2::Vector2, vector3::Vector3, @@ -12,22 +14,26 @@ use super::World; fn get_view_distance(player: &Player) -> i8 { player .config + .lock() + .unwrap() .view_distance .clamp(2, BASIC_CONFIG.view_distance as i8) } -pub async fn player_join(world: &World, player: &mut Player) { - let new_watched = chunk_section_from_pos(&player.entity.block_pos); - player.watched_section = new_watched; - let chunk_pos = player.entity.chunk_pos; +pub async fn player_join(world: &World, player: Arc) { + let entity = player.entity.lock().unwrap(); + let new_watched = chunk_section_from_pos(&entity.block_pos); + let mut watched_section = player.watched_section.lock().unwrap(); + *watched_section = new_watched; + let chunk_pos = entity.chunk_pos; player.client.send_packet(&CCenterChunk { chunk_x: chunk_pos.x.into(), chunk_z: chunk_pos.z.into(), }); - let view_distance = get_view_distance(player) as i32; + let view_distance = get_view_distance(&player) as i32; dbg!(view_distance); let old_cylindrical = Cylindrical::new( - Vector2::new(player.watched_section.x, player.watched_section.z), + Vector2::new(watched_section.x, watched_section.z), view_distance, ); let new_cylindrical = Cylindrical::new(Vector2::new(chunk_pos.x, chunk_pos.z), view_distance); @@ -47,16 +53,17 @@ pub async fn player_join(world: &World, player: &mut Player) { ); if !loading_chunks.is_empty() { world - .spawn_world_chunks(&mut player.client, loading_chunks, view_distance) + .spawn_world_chunks(&player.client, loading_chunks, view_distance) .await; } } -pub async fn update_position(world: &World, player: &mut Player) { - let current_watched = player.watched_section; - let new_watched = chunk_section_from_pos(&player.entity.block_pos); - if current_watched != new_watched { - let chunk_pos = player.entity.chunk_pos; +pub async fn update_position(world: &World, player: &Player) { + let mut current_watched = player.watched_section.lock().unwrap(); + let entity = player.entity.lock().unwrap(); + let new_watched = chunk_section_from_pos(&entity.block_pos); + if *current_watched != new_watched { + let chunk_pos = entity.chunk_pos; player.client.send_packet(&CCenterChunk { chunk_x: chunk_pos.x.into(), chunk_z: chunk_pos.z.into(), @@ -64,12 +71,12 @@ pub async fn update_position(world: &World, player: &mut Player) { let view_distance = get_view_distance(player) as i32; let old_cylindrical = Cylindrical::new( - Vector2::new(player.watched_section.x, player.watched_section.z), + Vector2::new(current_watched.x, current_watched.z), view_distance, ); let new_cylindrical = Cylindrical::new(Vector2::new(chunk_pos.x, chunk_pos.z), view_distance); - player.watched_section = new_watched; + *current_watched = new_watched; let mut loading_chunks = Vec::new(); Cylindrical::for_each_changed_chunk( old_cylindrical, @@ -86,7 +93,7 @@ pub async fn update_position(world: &World, player: &mut Player) { ); if !loading_chunks.is_empty() { world - .spawn_world_chunks(&mut player.client, loading_chunks, view_distance) + .spawn_world_chunks(&player.client, loading_chunks, view_distance) .await; } } From 58831cd0c939f7a5a57ca8d415f5506db6fd685c Mon Sep 17 00:00:00 2001 From: Snowiiii Date: Mon, 9 Sep 2024 21:14:22 +0200 Subject: [PATCH 2/3] Fix: Format --- pumpkin/src/client/client_packet.rs | 2 +- pumpkin/src/entity/player.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pumpkin/src/client/client_packet.rs b/pumpkin/src/client/client_packet.rs index b81b40790..7fc6d11d1 100644 --- a/pumpkin/src/client/client_packet.rs +++ b/pumpkin/src/client/client_packet.rs @@ -43,7 +43,7 @@ impl Client { self.protocol_version .store(version, std::sync::atomic::Ordering::Relaxed); let mut connection_state = self.connection_state.lock().unwrap(); - + *connection_state = handshake.next_state; if *connection_state != ConnectionState::Status { let protocol = version; diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index a46116d15..ed2e78b59 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -1,7 +1,7 @@ use std::sync::{ - atomic::{AtomicI32, AtomicU8}, - Arc, Mutex, - }; + atomic::{AtomicI32, AtomicU8}, + Arc, Mutex, +}; use num_derive::FromPrimitive; use num_traits::ToPrimitive; From e5bfbce2ce484e0914e7b072bf3d219d9fa3c252 Mon Sep 17 00:00:00 2001 From: Snowiiii Date: Mon, 9 Sep 2024 23:22:55 +0200 Subject: [PATCH 3/3] Fix dead locks --- pumpkin-protocol/src/lib.rs | 2 +- pumpkin/src/client/mod.rs | 5 +- pumpkin/src/client/player_packet.rs | 134 +++++++++++++++------------- pumpkin/src/entity/mod.rs | 128 ++++++++++++++------------ pumpkin/src/entity/player.rs | 24 +++-- pumpkin/src/main.rs | 4 + pumpkin/src/world/mod.rs | 17 ++-- pumpkin/src/world/player_chunker.rs | 17 ++-- 8 files changed, 181 insertions(+), 150 deletions(-) diff --git a/pumpkin-protocol/src/lib.rs b/pumpkin-protocol/src/lib.rs index 66caee7e9..dfabd2e99 100644 --- a/pumpkin-protocol/src/lib.rs +++ b/pumpkin-protocol/src/lib.rs @@ -151,7 +151,7 @@ pub enum PacketError { MalformedLength, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum ConnectionState { HandShake, Status, diff --git a/pumpkin/src/client/mod.rs b/pumpkin/src/client/mod.rs index 53ca10189..19a89fc1f 100644 --- a/pumpkin/src/client/mod.rs +++ b/pumpkin/src/client/mod.rs @@ -182,7 +182,10 @@ impl Client { ) -> Result<(), DeserializerError> { // TODO: handle each packet's Error instead of calling .unwrap() let bytebuf = &mut packet.bytebuf; - match *self.connection_state.lock().unwrap() { + let locked_state = self.connection_state.lock().unwrap(); + let state = locked_state.clone(); + drop(locked_state); + match state { pumpkin_protocol::ConnectionState::HandShake => match packet.id.0 { SHandShake::PACKET_ID => { self.handle_handshake(server, SHandShake::read(bytebuf)?); diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/client/player_packet.rs index 04c7788f6..d2c2a2716 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/client/player_packet.rs @@ -50,10 +50,7 @@ impl Player { if let Some((id, position)) = awaiting_teleport.as_ref() { if id == &confirm_teleport.teleport_id { // we should set the pos now to that we requested in the teleport packet, Is may fixed issues when the client sended position packets while being teleported - self.entity - .lock() - .unwrap() - .set_pos(position.x, position.y, position.z); + self.entity.set_pos(position.x, position.y, position.z); *awaiting_teleport = None; } else { @@ -79,18 +76,20 @@ impl Player { self.kick(TextComponent::text("Invalid movement")); return; } - let mut entity = self.entity.lock().unwrap(); - let mut last_position = self.last_position.lock().unwrap(); - *last_position = entity.pos; + let entity = &self.entity; entity.set_pos( Self::clamp_horizontal(position.x), Self::clamp_vertical(position.feet_y), Self::clamp_horizontal(position.z), ); - entity.on_ground = position.ground; - let on_ground = entity.on_ground; + let mut last_position = self.last_position.lock().unwrap(); + let pos = entity.pos.lock().unwrap(); + *last_position = *pos; + entity + .on_ground + .store(position.ground, std::sync::atomic::Ordering::Relaxed); let entity_id = entity.entity_id; - let (x, y, z) = entity.pos.into(); + let (x, y, z) = (*pos).into(); let (lastx, lasty, lastz) = (last_position.x, last_position.y, last_position.z); let world = entity.world.clone(); @@ -114,10 +113,10 @@ impl Player { (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, + position.ground, ), ); - player_chunker::update_position(&world, self).await; + player_chunker::update_position(entity, self).await; } pub async fn handle_position_rotation( @@ -136,27 +135,31 @@ impl Player { self.kick(TextComponent::text("Invalid rotation")); return; } - let mut entity = self.entity.lock().unwrap(); + let entity = &self.entity; - let mut last_position = self.last_position.lock().unwrap(); - *last_position = entity.pos; entity.set_pos( Self::clamp_horizontal(position_rotation.x), Self::clamp_vertical(position_rotation.feet_y), Self::clamp_horizontal(position_rotation.z), ); - entity.on_ground = position_rotation.ground; - entity.yaw = wrap_degrees(position_rotation.yaw) % 360.0; - entity.pitch = wrap_degrees(position_rotation.pitch).clamp(-90.0, 90.0) % 360.0; + let mut last_position = self.last_position.lock().unwrap(); + let pos = entity.pos.lock().unwrap(); + *last_position = *pos; + entity.on_ground.store( + position_rotation.ground, + std::sync::atomic::Ordering::Relaxed, + ); + entity.set_rotation( + wrap_degrees(position_rotation.yaw) % 360.0, + wrap_degrees(position_rotation.pitch).clamp(-90.0, 90.0) % 360.0, + ); - let on_ground = entity.on_ground; let entity_id = entity.entity_id; - let (x, y, z) = entity.pos.into(); + let (x, y, z) = (*pos).into(); let (lastx, lasty, lastz) = (last_position.x, last_position.y, last_position.z); - let yaw = modulus(entity.yaw * 256.0 / 360.0, 256.0); - let pitch = modulus(entity.pitch * 256.0 / 360.0, 256.0); + let yaw = modulus(*entity.yaw.lock().unwrap() * 256.0 / 360.0, 256.0); + let pitch = modulus(*entity.pitch.lock().unwrap() * 256.0 / 360.0, 256.0); // let head_yaw = (entity.head_yaw * 256.0 / 360.0).floor(); - let entity = self.entity.lock().unwrap(); let world = &entity.world; // let delta = Vector3::new(x - lastx, y - lasty, z - lastz); @@ -182,15 +185,14 @@ impl Player { (z * 4096.0 - lastz * 4096.0) as i16, yaw as u8, pitch as u8, - on_ground, + position_rotation.ground, ), ); world.broadcast_packet_expect( &[self.client.token], &CHeadRot::new(entity_id.into(), yaw as u8), ); - - player_chunker::update_position(world, self).await; + player_chunker::update_position(entity, self).await; } pub async fn handle_rotation(&self, _server: &Arc, rotation: SPlayerRotation) { @@ -198,19 +200,23 @@ impl Player { self.kick(TextComponent::text("Invalid rotation")); return; } - let mut entity = self.entity.lock().unwrap(); - entity.on_ground = rotation.ground; - entity.yaw = wrap_degrees(rotation.yaw) % 360.0; - entity.pitch = wrap_degrees(rotation.pitch).clamp(-90.0, 90.0) % 360.0; + let entity = &self.entity; + entity + .on_ground + .store(rotation.ground, std::sync::atomic::Ordering::Relaxed); + entity.set_rotation( + wrap_degrees(rotation.yaw) % 360.0, + wrap_degrees(rotation.pitch).clamp(-90.0, 90.0) % 360.0, + ); // send new position to all other players - let on_ground = entity.on_ground; let entity_id = entity.entity_id; - let yaw = modulus(entity.yaw * 256.0 / 360.0, 256.0); - let pitch = modulus(entity.pitch * 256.0 / 360.0, 256.0); + let yaw = modulus(*entity.yaw.lock().unwrap() * 256.0 / 360.0, 256.0); + let pitch = modulus(*entity.pitch.lock().unwrap() * 256.0 / 360.0, 256.0); // let head_yaw = modulus(entity.head_yaw * 256.0 / 360.0, 256.0); let world = &entity.world; - let packet = CUpdateEntityRot::new(entity_id.into(), yaw as u8, pitch as u8, on_ground); + let packet = + CUpdateEntityRot::new(entity_id.into(), yaw as u8, pitch as u8, rotation.ground); world.broadcast_packet_expect(&[self.client.token], &packet); let packet = CHeadRot::new(entity_id.into(), yaw as u8); world.broadcast_packet_expect(&[self.client.token], &packet); @@ -222,7 +228,9 @@ impl Player { } pub fn handle_player_ground(&self, _server: &Arc, ground: SSetPlayerGround) { - self.entity.lock().unwrap().on_ground = ground.on_ground; + self.entity + .on_ground + .store(ground.on_ground, std::sync::atomic::Ordering::Relaxed); } pub async fn handle_player_command(&self, _server: &Arc, command: SPlayerCommand) { @@ -231,26 +239,26 @@ impl Player { } if let Some(action) = Action::from_i32(command.action.0) { - let mut entity = self.entity.lock().unwrap(); + let entity = &self.entity; match action { pumpkin_protocol::server::play::Action::StartSneaking => { - if !entity.sneaking { + if !entity.sneaking.load(std::sync::atomic::Ordering::Relaxed) { entity.set_sneaking(true).await } } pumpkin_protocol::server::play::Action::StopSneaking => { - if entity.sneaking { + if entity.sneaking.load(std::sync::atomic::Ordering::Relaxed) { entity.set_sneaking(false).await } } pumpkin_protocol::server::play::Action::LeaveBed => todo!(), pumpkin_protocol::server::play::Action::StartSprinting => { - if !entity.sprinting { + if !entity.sprinting.load(std::sync::atomic::Ordering::Relaxed) { entity.set_sprinting(true).await } } pumpkin_protocol::server::play::Action::StopSprinting => { - if entity.sprinting { + if entity.sprinting.load(std::sync::atomic::Ordering::Relaxed) { entity.set_sprinting(false).await } } @@ -259,7 +267,11 @@ impl Player { pumpkin_protocol::server::play::Action::OpenVehicleInventory => todo!(), pumpkin_protocol::server::play::Action::StartFlyingElytra => { let fall_flying = entity.check_fall_flying(); - if entity.fall_flying != fall_flying { + if entity + .fall_flying + .load(std::sync::atomic::Ordering::Relaxed) + != fall_flying + { entity.set_fall_flying(fall_flying).await; } } // TODO @@ -277,8 +289,7 @@ impl Player { Hand::Off => Animation::SwingOffhand, }; let id = self.entity_id(); - let entity = self.entity.lock().unwrap(); - let world = &entity.world; + let world = &self.entity.world; world.broadcast_packet_expect( &[self.client.token], &CEntityAnimation::new(id.into(), animation as u8), @@ -302,7 +313,7 @@ impl Player { // TODO: filter message & validation let gameprofile = &self.gameprofile; - let entity = self.entity.lock().unwrap(); + let entity = &self.entity; let world = &entity.world; world.broadcast_packet_all(&CPlayerChatMessage::new( pumpkin_protocol::uuid::UUID(gameprofile.id), @@ -356,8 +367,8 @@ impl Player { pub async fn handle_interact(&self, _: &Arc, interact: SInteract) { let sneaking = interact.sneaking; - let mut entity = self.entity.lock().unwrap(); - if entity.sneaking != sneaking { + let entity = &self.entity; + if entity.sneaking.load(std::sync::atomic::Ordering::Relaxed) != sneaking { entity.set_sneaking(sneaking).await; } match ActionType::from_i32(interact.typ.0) { @@ -370,35 +381,38 @@ impl Player { let world = entity.world.clone(); let attacked_player = world.get_by_entityid(self, entity_id.0 as EntityId); if let Some(player) = attacked_player { - let mut victem_entity = player.entity.lock().unwrap(); - let velo = victem_entity.velocity; + let victem_entity = &player.entity; if config.protect_creative && *player.gamemode.lock().unwrap() == GameMode::Creative { return; } if config.knockback { - let yaw = entity.yaw; + let yaw = entity.yaw.lock().unwrap(); let strength = 1.0; + let mut victem_velocity = victem_entity.velocity.lock().unwrap(); + let saved_velo = *victem_velocity; victem_entity.knockback( strength * 0.5, - (yaw * (PI / 180.0)).sin() as f64, - -(yaw * (PI / 180.0)).cos() as f64, + (*yaw * (PI / 180.0)).sin() as f64, + -(*yaw * (PI / 180.0)).cos() as f64, ); let packet = &CEntityVelocity::new( &entity_id, - velo.x as f32, - velo.y as f32, - velo.z as f32, + victem_velocity.x as f32, + victem_velocity.y as f32, + victem_velocity.z as f32, ); - entity.velocity = entity.velocity.multiply(0.6, 1.0, 0.6); + let mut velocity = entity.velocity.lock().unwrap(); + *velocity = velocity.multiply(0.6, 1.0, 0.6); - victem_entity.velocity = velo; + *victem_velocity = saved_velo; player.client.send_packet(packet); } if config.hurt_animation { world.broadcast_packet_all(&CHurtAnimation::new( - &entity_id, entity.yaw, + &entity_id, + *entity.yaw.lock().unwrap(), )) } if config.swing {} @@ -431,7 +445,7 @@ impl Player { let location = player_action.location; // Block break & block break sound // TODO: currently this is always dirt replace it - let entity = self.entity.lock().unwrap(); + let entity = &self.entity; let world = &entity.world; world.broadcast_packet_all(&CWorldEvent::new(2001, &location, 11, false)); // AIR @@ -455,7 +469,7 @@ impl Player { } // Block break & block break sound // TODO: currently this is always dirt replace it - let entity = self.entity.lock().unwrap(); + let entity = &self.entity; let world = &entity.world; world.broadcast_packet_all(&CWorldEvent::new(2001, &location, 11, false)); // AIR @@ -502,7 +516,7 @@ impl Player { ) .expect("All item ids are in the global registry"); if let Ok(block_state_id) = BlockId::new(minecraft_id, None) { - let entity = self.entity.lock().unwrap(); + let entity = &self.entity; let world = &entity.world; world.broadcast_packet_all(&CBlockUpdate::new( &location, diff --git a/pumpkin/src/entity/mod.rs b/pumpkin/src/entity/mod.rs index a8f03821d..132dfedf0 100644 --- a/pumpkin/src/entity/mod.rs +++ b/pumpkin/src/entity/mod.rs @@ -1,4 +1,4 @@ -use std::sync::Arc; +use std::sync::{atomic::AtomicBool, Arc, Mutex}; use pumpkin_core::math::{ get_section_cord, position::WorldPosition, vector2::Vector2, vector3::Vector3, @@ -18,24 +18,24 @@ pub struct Entity { pub entity_type: EntityType, pub world: Arc, - pub pos: Vector3, - pub block_pos: WorldPosition, - pub chunk_pos: Vector2, + pub pos: Mutex>, + pub block_pos: Mutex, + pub chunk_pos: Mutex>, - pub sneaking: bool, - pub sprinting: bool, - pub fall_flying: bool, - pub velocity: Vector3, + pub sneaking: AtomicBool, + pub sprinting: AtomicBool, + pub fall_flying: AtomicBool, + pub velocity: Mutex>, // Should be not trusted - pub on_ground: bool, + pub on_ground: AtomicBool, - pub yaw: f32, - pub head_yaw: f32, - pub pitch: f32, + pub yaw: Mutex, + pub head_yaw: Mutex, + pub pitch: Mutex, // TODO: Change this in diffrent poses pub standing_eye_height: f32, - pub pose: EntityPose, + pub pose: Mutex, } impl Entity { @@ -48,49 +48,58 @@ impl Entity { Self { entity_id, entity_type, - on_ground: false, - pos: Vector3::new(0.0, 0.0, 0.0), - block_pos: WorldPosition(Vector3::new(0, 0, 0)), - chunk_pos: Vector2::new(0, 0), - sneaking: false, + on_ground: AtomicBool::new(false), + pos: Mutex::new(Vector3::new(0.0, 0.0, 0.0)), + block_pos: Mutex::new(WorldPosition(Vector3::new(0, 0, 0))), + chunk_pos: Mutex::new(Vector2::new(0, 0)), + sneaking: AtomicBool::new(false), world, - sprinting: false, - fall_flying: false, - yaw: 0.0, - head_yaw: 0.0, - pitch: 0.0, - velocity: Vector3::new(0.0, 0.0, 0.0), + sprinting: AtomicBool::new(false), + fall_flying: AtomicBool::new(false), + yaw: Mutex::new(0.0), + head_yaw: Mutex::new(0.0), + pitch: Mutex::new(0.0), + velocity: Mutex::new(Vector3::new(0.0, 0.0, 0.0)), standing_eye_height, - pose: EntityPose::Standing, + pose: Mutex::new(EntityPose::Standing), } } - pub fn set_pos(&mut self, x: f64, y: f64, z: f64) { - if self.pos.x != x || self.pos.y != y || self.pos.z != z { - self.pos = Vector3::new(x, y, z); + pub fn set_pos(&self, x: f64, y: f64, z: f64) { + let mut pos = self.pos.lock().unwrap(); + if pos.x != x || pos.y != y || pos.z != z { + *pos = Vector3::new(x, y, z); let i = x.floor() as i32; let j = y.floor() as i32; let k = z.floor() as i32; - let block_pos = self.block_pos.0; - if i != block_pos.x || j != block_pos.y || k != block_pos.z { - self.block_pos = WorldPosition(Vector3::new(i, j, k)); + let mut block_pos = self.block_pos.lock().unwrap(); + let block_pos_vec = block_pos.0; + if i != block_pos_vec.x || j != block_pos_vec.y || k != block_pos_vec.z { + *block_pos = WorldPosition(Vector3::new(i, j, k)); - if get_section_cord(i) != self.chunk_pos.x - || get_section_cord(k) != self.chunk_pos.z - { - self.chunk_pos = - Vector2::new(get_section_cord(block_pos.x), get_section_cord(block_pos.z)); + let mut chunk_pos = self.chunk_pos.lock().unwrap(); + if get_section_cord(i) != chunk_pos.x || get_section_cord(k) != chunk_pos.z { + *chunk_pos = Vector2::new( + get_section_cord(block_pos_vec.x), + get_section_cord(block_pos_vec.z), + ); } } } } + pub fn set_rotation(&self, yaw: f32, pitch: f32) { + // TODO + *self.yaw.lock().unwrap() = yaw; + *self.pitch.lock().unwrap() = pitch + } + pub async fn remove(&mut self) { self.world.remove_entity(self); } - pub fn knockback(&mut self, strength: f64, x: f64, z: f64) { + pub fn knockback(&self, strength: f64, x: f64, z: f64) { // This has some vanilla magic let mut x = x; let mut z = z; @@ -100,21 +109,22 @@ impl Entity { } let var8 = Vector3::new(x, 0.0, z).normalize() * strength; - let var7 = self.velocity; - self.velocity = Vector3::new( - var7.x / 2.0 - var8.x, - if self.on_ground { - (var7.y / 2.0 + strength).min(0.4) + let mut velocity = self.velocity.lock().unwrap(); + *velocity = Vector3::new( + velocity.x / 2.0 - var8.x, + if self.on_ground.load(std::sync::atomic::Ordering::Relaxed) { + (velocity.y / 2.0 + strength).min(0.4) } else { - var7.y + velocity.y }, - var7.z / 2.0 - var8.z, + velocity.z / 2.0 - var8.z, ); } - pub async fn set_sneaking(&mut self, sneaking: bool) { - assert!(self.sneaking != sneaking); - self.sneaking = sneaking; + pub async fn set_sneaking(&self, sneaking: bool) { + assert!(self.sneaking.load(std::sync::atomic::Ordering::Relaxed) != sneaking); + self.sneaking + .store(sneaking, std::sync::atomic::Ordering::Relaxed); self.set_flag(Self::SNEAKING_FLAG_INDEX, sneaking).await; // if sneaking { // self.set_pose(EntityPose::Crouching).await; @@ -123,19 +133,21 @@ impl Entity { // } } - pub async fn set_sprinting(&mut self, sprinting: bool) { - assert!(self.sprinting != sprinting); - self.sprinting = sprinting; + pub async fn set_sprinting(&self, sprinting: bool) { + assert!(self.sprinting.load(std::sync::atomic::Ordering::Relaxed) != sprinting); + self.sprinting + .store(sprinting, std::sync::atomic::Ordering::Relaxed); self.set_flag(Self::SPRINTING_FLAG_INDEX, sprinting).await; } pub fn check_fall_flying(&self) -> bool { - !self.on_ground + !self.on_ground.load(std::sync::atomic::Ordering::Relaxed) } - pub async fn set_fall_flying(&mut self, fall_flying: bool) { - assert!(self.fall_flying != fall_flying); - self.fall_flying = fall_flying; + pub async fn set_fall_flying(&self, fall_flying: bool) { + assert!(self.fall_flying.load(std::sync::atomic::Ordering::Relaxed) != fall_flying); + self.fall_flying + .store(fall_flying, std::sync::atomic::Ordering::Relaxed); self.set_flag(Self::FALL_FLYING_FLAG_INDEX, fall_flying) .await; } @@ -147,7 +159,7 @@ impl Entity { pub const INVISIBLE_FLAG_INDEX: u32 = 5; pub const GLOWING_FLAG_INDEX: u32 = 6; pub const FALL_FLYING_FLAG_INDEX: u32 = 7; - async fn set_flag(&mut self, index: u32, value: bool) { + async fn set_flag(&self, index: u32, value: bool) { let mut b = 0i8; if value { b |= 1 << index; @@ -158,9 +170,9 @@ impl Entity { self.world.broadcast_packet_all(&packet); } - pub async fn set_pose(&mut self, pose: EntityPose) { - self.pose = pose; - let pose = self.pose as i32; + pub async fn set_pose(&self, pose: EntityPose) { + *self.pose.lock().unwrap() = pose; + let pose = pose as i32; let packet = CSetEntityMetadata::::new( self.entity_id.into(), Metadata::new(6, 20.into(), (pose).into()), diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index ed2e78b59..67ed05e8f 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -61,7 +61,7 @@ impl Default for PlayerAbilities { } pub struct Player { - pub entity: Mutex, + pub entity: Entity, pub gameprofile: GameProfile, pub client: Client, @@ -106,7 +106,7 @@ impl Player { }; let config = client.config.lock().unwrap().clone().unwrap_or_default(); Self { - entity: Mutex::new(Entity::new(entity_id, world, EntityType::Player, 1.62)), + entity: Entity::new(entity_id, world, EntityType::Player, 1.62), config: Mutex::new(config), gameprofile, client, @@ -129,11 +129,11 @@ impl Player { /// Removes the Player out of the current World pub async fn remove(&self) { - self.entity.lock().unwrap().world.remove_player(self); + self.entity.world.remove_player(self); } pub fn entity_id(&self) -> EntityId { - self.entity.lock().unwrap().entity_id + self.entity.entity_id } pub fn send_abilties_update(&mut self) { @@ -170,10 +170,9 @@ impl Player { .store(0, std::sync::atomic::Ordering::Relaxed); } let teleport_id = i + 1; - let mut entity = self.entity.lock().unwrap(); + let entity = &self.entity; entity.set_pos(x, y, z); - entity.yaw = yaw; - entity.pitch = pitch; + entity.set_rotation(yaw, pitch); *self.awaiting_teleport.lock().unwrap() = Some((teleport_id.into(), Vector3::new(x, y, z))); self.client.send_packet(&CSyncPlayerPosition::new( x, @@ -197,11 +196,12 @@ impl Player { pub fn can_interact_with_block_at(&self, pos: &WorldPosition, additional_range: f64) -> bool { let d = self.block_interaction_range() + additional_range; let box_pos = BoundingBox::from_block(pos); - let entity = self.entity.lock().unwrap(); + let entity_pos = self.entity.pos.lock().unwrap(); + let standing_eye_height = self.entity.standing_eye_height; box_pos.squared_magnitude(Vector3 { - x: entity.pos.x, - y: entity.pos.y + entity.standing_eye_height as f64, - z: entity.pos.z, + x: entity_pos.x, + y: entity_pos.y + standing_eye_height as f64, + z: entity_pos.z, }) < d * d } @@ -241,8 +241,6 @@ impl Player { // So a little story time. I actually made an abitlties_from_gamemode function. I looked at vanilla and they always send the abilties from the gamemode. But the funny thing actually is. That the client // does actually use the same method and set the abilties when receiving the CGameEvent gamemode packet. Just Mojang nonsense self.entity - .lock() - .unwrap() .world .broadcast_packet_all(&CPlayerInfoUpdate::new( 0x04, diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs index f90f5b041..8a00ebbb4 100644 --- a/pumpkin/src/main.rs +++ b/pumpkin/src/main.rs @@ -169,8 +169,12 @@ fn main() -> io::Result<()> { } if closed { if let Some(player) = players.remove(&token) { + dbg!("a"); player.remove().await; + dbg!("b"); let connection = &mut player.client.connection.lock().unwrap(); + dbg!("c"); + poll.registry().deregister(connection.by_ref())?; } } diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 672e37136..253fa27b4 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -177,18 +177,19 @@ impl World { .iter() .filter(|c| c.0 != &token) { - let entity = existing_player.entity.lock().unwrap(); + let entity = &existing_player.entity; + let pos = entity.pos.lock().unwrap(); let gameprofile = &existing_player.gameprofile; player.client.send_packet(&CSpawnEntity::new( existing_player.entity_id().into(), UUID(gameprofile.id), (EntityType::Player as i32).into(), - entity.pos.x, - entity.pos.y, - entity.pos.z, - entity.yaw, - entity.pitch, - entity.head_yaw, + pos.x, + pos.y, + pos.z, + *entity.yaw.lock().unwrap(), + *entity.pitch.lock().unwrap(), + *entity.head_yaw.lock().unwrap(), 0.into(), 0.0, 0.0, @@ -279,7 +280,7 @@ impl World { &[player.client.token], &CRemovePlayerInfo::new(1.into(), &[UUID(uuid)]), ); - self.remove_entity(&player.entity.lock().unwrap()); + self.remove_entity(&player.entity); } pub fn remove_entity(&self, entity: &Entity) { diff --git a/pumpkin/src/world/player_chunker.rs b/pumpkin/src/world/player_chunker.rs index 6c91e3508..0a8464883 100644 --- a/pumpkin/src/world/player_chunker.rs +++ b/pumpkin/src/world/player_chunker.rs @@ -7,7 +7,7 @@ use pumpkin_core::math::{ use pumpkin_protocol::client::play::{CCenterChunk, CUnloadChunk}; use pumpkin_world::cylindrical_chunk_iterator::Cylindrical; -use crate::entity::player::Player; +use crate::entity::{player::Player, Entity}; use super::World; @@ -21,11 +21,10 @@ fn get_view_distance(player: &Player) -> i8 { } pub async fn player_join(world: &World, player: Arc) { - let entity = player.entity.lock().unwrap(); - let new_watched = chunk_section_from_pos(&entity.block_pos); + let new_watched = chunk_section_from_pos(&player.entity.block_pos.lock().unwrap()); let mut watched_section = player.watched_section.lock().unwrap(); *watched_section = new_watched; - let chunk_pos = entity.chunk_pos; + let chunk_pos = player.entity.chunk_pos.lock().unwrap(); player.client.send_packet(&CCenterChunk { chunk_x: chunk_pos.x.into(), chunk_z: chunk_pos.z.into(), @@ -58,12 +57,11 @@ pub async fn player_join(world: &World, player: Arc) { } } -pub async fn update_position(world: &World, player: &Player) { +pub async fn update_position(entity: &Entity, player: &Player) { let mut current_watched = player.watched_section.lock().unwrap(); - let entity = player.entity.lock().unwrap(); - let new_watched = chunk_section_from_pos(&entity.block_pos); + let new_watched = chunk_section_from_pos(&entity.block_pos.lock().unwrap()); if *current_watched != new_watched { - let chunk_pos = entity.chunk_pos; + let chunk_pos = entity.chunk_pos.lock().unwrap(); player.client.send_packet(&CCenterChunk { chunk_x: chunk_pos.x.into(), chunk_z: chunk_pos.z.into(), @@ -92,7 +90,8 @@ pub async fn update_position(world: &World, player: &Player) { false, ); if !loading_chunks.is_empty() { - world + entity + .world .spawn_world_chunks(&player.client, loading_chunks, view_distance) .await; }