From cf7b2423d0752b90e0d5e0805a69ecf8c181b570 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Thu, 5 Jan 2023 20:08:04 +0000 Subject: [PATCH] start fixing stuff --- azalea-client/src/client.rs | 31 +- azalea-client/src/local_player.rs | 4 +- azalea-client/src/movement.rs | 130 +-- azalea-client/src/packet_handling.rs | 1111 +++++++++++++------------- 4 files changed, 647 insertions(+), 629 deletions(-) diff --git a/azalea-client/src/client.rs b/azalea-client/src/client.rs index a7dd7b0f..a54627d9 100644 --- a/azalea-client/src/client.rs +++ b/azalea-client/src/client.rs @@ -1,7 +1,7 @@ pub use crate::chat::ChatPacket; use crate::{ local_player::{send_tick_event, update_in_loaded_chunk, LocalPlayer}, - movement::send_position, + movement::{local_player_ai_step, send_position}, packet_handling, plugins::PluginStates, Account, PlayerInfo, @@ -200,7 +200,7 @@ impl Client { // An event that causes the schedule to run. This is only used internally. let (run_schedule_sender, run_schedule_receiver) = mpsc::unbounded_channel(); - let ecs_lock = start_ecs(run_schedule_receiver).await; + let ecs_lock = start_ecs(run_schedule_receiver, run_schedule_sender.clone()).await; let mut ecs = ecs_lock.lock(); ecs.init_resource::(); @@ -227,7 +227,7 @@ impl Client { // start receiving packets let packet_receiver = packet_handling::PacketReceiver { packets: Arc::new(Mutex::new(Vec::new())), - run_schedule_sender, + run_schedule_sender: run_schedule_sender.clone(), }; let read_packets_task = tokio::spawn(packet_receiver.clone().read_task(read_conn)); @@ -471,6 +471,7 @@ impl Client { #[doc(hidden)] pub async fn start_ecs( run_schedule_receiver: mpsc::UnboundedReceiver<()>, + run_schedule_sender: mpsc::UnboundedSender<()>, ) -> Arc> { // if you get an error right here that means you're doing something with locks // wrong read the error to see where the issue is @@ -484,8 +485,7 @@ pub async fn start_ecs( SystemSet::new() .with_system(send_position) .with_system(update_in_loaded_chunk) - .with_system(send_position) - .with_system(LocalPlayer::ai_step) + .with_system(local_player_ai_step) .with_system(send_tick_event), ) .add_system(packet_handling::handle_packets.label("handle_packets")) @@ -496,15 +496,12 @@ pub async fn start_ecs( // app let ecs = Arc::new(Mutex::new(app.world)); - let mut game_tick_interval = time::interval(time::Duration::from_millis(50)); - // TODO: Minecraft bursts up to 10 ticks and then skips, we should too - game_tick_interval.set_missed_tick_behavior(time::MissedTickBehavior::Burst); - tokio::spawn(run_schedule_loop( ecs.clone(), app.schedule, run_schedule_receiver, )); + tokio::spawn(tick_run_schedule_loop(run_schedule_sender)); ecs } @@ -520,3 +517,19 @@ async fn run_schedule_loop( schedule.run(&mut ecs.lock()); } } + +/// Send an event to run the schedule every 50 milliseconds. It will stop when +/// the receiver is dropped. +pub async fn tick_run_schedule_loop(run_schedule_sender: mpsc::UnboundedSender<()>) { + let mut game_tick_interval = time::interval(time::Duration::from_millis(50)); + // TODO: Minecraft bursts up to 10 ticks and then skips, we should too + game_tick_interval.set_missed_tick_behavior(time::MissedTickBehavior::Burst); + + loop { + game_tick_interval.tick().await; + if run_schedule_sender.send(()).is_err() { + // the sender is closed so end the task + return; + } + } +} diff --git a/azalea-client/src/local_player.rs b/azalea-client/src/local_player.rs index b1af23a2..cc1c201e 100644 --- a/azalea-client/src/local_player.rs +++ b/azalea-client/src/local_player.rs @@ -95,7 +95,9 @@ impl LocalPlayer { /// Spawn a task to write a packet directly to the server. pub fn write_packet(&mut self, packet: ServerboundGamePacket) { - self.packet_writer.send(packet); + self.packet_writer + .send(packet) + .expect("write_packet shouldn't be able to be called if the connection is closed"); } } diff --git a/azalea-client/src/movement.rs b/azalea-client/src/movement.rs index 8cc8cd77..9289827c 100644 --- a/azalea-client/src/movement.rs +++ b/azalea-client/src/movement.rs @@ -192,71 +192,6 @@ impl LocalPlayer { } } - /// Makes the bot do one physics tick. Note that this is already handled - /// automatically by the client. - pub fn ai_step( - mut query: Query< - ( - Entity, - &mut LocalPlayer, - &mut entity::Physics, - &mut entity::Position, - &mut entity::metadata::Sprinting, - &mut entity::Attributes, - ), - &LocalPlayerInLoadedChunk, - >, - ) { - for ( - ecs_entity_id, - mut local_player, - mut physics, - mut position, - mut sprinting, - mut attributes, - ) in query.iter_mut() - { - let physics_state = &mut local_player.physics_state; - - Self::tick_controls(None, physics_state); - - // server ai step - physics.xxa = physics_state.left_impulse; - physics.zza = physics_state.forward_impulse; - - // TODO: food data and abilities - // let has_enough_food_to_sprint = self.food_data().food_level || - // self.abilities().may_fly; - let has_enough_food_to_sprint = true; - - // TODO: double tapping w to sprint i think - - let trying_to_sprint = physics_state.trying_to_sprint; - - if !**sprinting - && ( - // !self.is_in_water() - // || self.is_underwater() && - Self::has_enough_impulse_to_start_sprinting(physics_state) - && has_enough_food_to_sprint - // && !self.using_item() - // && !self.has_effect(MobEffects.BLINDNESS) - && trying_to_sprint - ) - { - Self::set_sprinting(true, &mut sprinting, &mut attributes); - } - - azalea_physics::ai_step( - &local_player.world.read(), - &mut physics, - &mut position, - &sprinting, - &attributes, - ) - } - } - /// Update the impulse from self.move_direction. The multipler is used for /// sneaking. pub(crate) fn tick_controls(multiplier: Option, physics_state: &mut PhysicsState) { @@ -371,6 +306,71 @@ impl LocalPlayer { } } +/// Makes the bot do one physics tick. Note that this is already handled +/// automatically by the client. +pub fn local_player_ai_step( + mut query: Query< + ( + Entity, + &mut LocalPlayer, + &mut entity::Physics, + &mut entity::Position, + &mut entity::metadata::Sprinting, + &mut entity::Attributes, + ), + &LocalPlayerInLoadedChunk, + >, +) { + for ( + ecs_entity_id, + mut local_player, + mut physics, + mut position, + mut sprinting, + mut attributes, + ) in query.iter_mut() + { + let physics_state = &mut local_player.physics_state; + + LocalPlayer::tick_controls(None, physics_state); + + // server ai step + physics.xxa = physics_state.left_impulse; + physics.zza = physics_state.forward_impulse; + + // TODO: food data and abilities + // let has_enough_food_to_sprint = self.food_data().food_level || + // self.abilities().may_fly; + let has_enough_food_to_sprint = true; + + // TODO: double tapping w to sprint i think + + let trying_to_sprint = physics_state.trying_to_sprint; + + if !**sprinting + && ( + // !self.is_in_water() + // || self.is_underwater() && + LocalPlayer::has_enough_impulse_to_start_sprinting(physics_state) + && has_enough_food_to_sprint + // && !self.using_item() + // && !self.has_effect(MobEffects.BLINDNESS) + && trying_to_sprint + ) + { + LocalPlayer::set_sprinting(true, &mut sprinting, &mut attributes); + } + + azalea_physics::ai_step( + &local_player.world.read(), + &mut physics, + &mut position, + &sprinting, + &attributes, + ) + } +} + #[derive(Clone, Copy, Debug, Default)] pub enum WalkDirection { #[default] diff --git a/azalea-client/src/packet_handling.rs b/azalea-client/src/packet_handling.rs index 3e1facb4..5561a30f 100644 --- a/azalea-client/src/packet_handling.rs +++ b/azalea-client/src/packet_handling.rs @@ -5,6 +5,7 @@ use azalea_protocol::{ packets::game::{ClientboundGamePacket, ServerboundGamePacket}, }; use bevy_ecs::{component::Component, prelude::Entity, query::Changed, system::Query}; +use log::{error, debug}; use parking_lot::Mutex; use tokio::sync::mpsc; @@ -16,10 +17,12 @@ pub struct PacketReceiver { } pub fn handle_packets( - query: Query<(Entity, &PacketReceiver), Changed>, + ecs: &mut bevy_ecs::world::World, // ecs: &mut bevy_ecs::world::World, ) { - for (entity, packet_events) in &query { + let mut query = ecs.query_filtered::<(Entity, &PacketReceiver), Changed>(); + + for (entity, packet_events) in query.iter_mut(ecs) { for packet in packet_events.packets.lock().iter() { handle_packet(entity, packet); } @@ -27,576 +30,572 @@ pub fn handle_packets( } pub fn handle_packet(entity: Entity, packet: &ClientboundGamePacket) { - // match packet { - // ClientboundGamePacket::Login(p) => { - // debug!("Got login packet"); + match packet { + ClientboundGamePacket::Login(p) => { + debug!("Got login packet"); - // { - // // // write p into login.txt - // // std::io::Write::write_all( - // // &mut std::fs::File::create("login.txt").unwrap(), - // // format!("{:#?}", p).as_bytes(), - // // ) - // // .unwrap(); + { + // // write p into login.txt + // std::io::Write::write_all( + // &mut std::fs::File::create("login.txt").unwrap(), + // format!("{:#?}", p).as_bytes(), + // ) + // .unwrap(); - // // TODO: have registry_holder be a struct because this sucks - // rn // best way would be to add serde support to - // azalea-nbt + // TODO: have registry_holder be a struct because this sucks rn + // best way would be to add serde support to azalea-nbt - // let registry_holder = p - // .registry_holder - // .as_compound() - // .expect("Registry holder is not a compound") - // .get("") - // .expect("No \"\" tag") - // .as_compound() - // .expect("\"\" tag is not a compound"); - // let dimension_types = registry_holder - // .get("minecraft:dimension_type") - // .expect("No dimension_type tag") - // .as_compound() - // .expect("dimension_type is not a compound") - // .get("value") - // .expect("No dimension_type value") - // .as_list() - // .expect("dimension_type value is not a list"); - // let dimension_type = dimension_types - // .iter() - // .find(|t| { - // t.as_compound() - // .expect("dimension_type value is not a compound") - // .get("name") - // .expect("No name tag") - // .as_string() - // .expect("name is not a string") - // == p.dimension_type.to_string() - // }) - // .unwrap_or_else(|| panic!("No dimension_type with name - // {}", p.dimension_type)) .as_compound() - // .unwrap() - // .get("element") - // .expect("No element tag") - // .as_compound() - // .expect("element is not a compound"); - // let height = (*dimension_type - // .get("height") - // .expect("No height tag") - // .as_int() - // .expect("height tag is not an int")) - // .try_into() - // .expect("height is not a u32"); - // let min_y = *dimension_type - // .get("min_y") - // .expect("No min_y tag") - // .as_int() - // .expect("min_y tag is not an int"); + let registry_holder = p + .registry_holder + .as_compound() + .expect("Registry holder is not a compound") + .get("") + .expect("No \"\" tag") + .as_compound() + .expect("\"\" tag is not a compound"); + let dimension_types = registry_holder + .get("minecraft:dimension_type") + .expect("No dimension_type tag") + .as_compound() + .expect("dimension_type is not a compound") + .get("value") + .expect("No dimension_type value") + .as_list() + .expect("dimension_type value is not a list"); + let dimension_type = dimension_types + .iter() + .find(|t| { + t.as_compound() + .expect("dimension_type value is not a compound") + .get("name") + .expect("No name tag") + .as_string() + .expect("name is not a string") + == p.dimension_type.to_string() + }) + .unwrap_or_else(|| panic!("No dimension_type with name {}", p.dimension_type)) .as_compound() + .unwrap() + .get("element") + .expect("No element tag") + .as_compound() + .expect("element is not a compound"); + let height = (*dimension_type + .get("height") + .expect("No height tag") + .as_int() + .expect("height tag is not an int")) + .try_into() + .expect("height is not a u32"); + let min_y = *dimension_type + .get("min_y") + .expect("No min_y tag") + .as_int() + .expect("min_y tag is not an int"); - // let world_name = p.dimension.clone(); + let world_name = p.dimension.clone(); - // local_player.world_name = Some(world_name.clone()); - // // add this world to the world_container (or don't if it's - // already there) let weak_world = - // world_container.insert(world_name, height, min_y); // set - // the loaded_world to an empty world // (when we add chunks - // or entities those will be in the world_container) let mut - // world_lock = local_player.world.write(); *world_lock = - // PartialWorld::new( - // local_player.client_information.view_distance.into(), - // weak_world, Some(EntityId(p.player_id)), - // ); + local_player.world_name = Some(world_name.clone()); + // add this world to the world_container (or don't if it's already there) + let weak_world = world_container.insert(world_name, height, min_y); + // set the loaded_world to an empty world + // (when we add chunks or entities those will be in the world_container) + let mut world_lock = local_player.world.write(); *world_lock = PartialWorld::new( + local_player.client_information.view_distance.into(), + weak_world, Some(EntityId(p.player_id)), + ); - // let player_bundle = entity::PlayerBundle { - // entity: entity::EntityBundle::new( - // local_player.profile.uuid, - // Vec3::default(), - // azalea_registry::EntityKind::Player, - // ), - // metadata: PlayerMetadataBundle::default(), - // }; - // // let entity = EntityData::new( - // // client.profile.uuid, - // // Vec3::default(), - // // EntityMetadata::Player(metadata::Player::default()), - // // ); - // // the first argument makes it so other entities don't update - // this entity in // a shared world - // world_lock.add_entity(EntityId(p.player_id), player_bundle); + let player_bundle = entity::PlayerBundle { + entity: entity::EntityBundle::new( + local_player.profile.uuid, + Vec3::default(), + azalea_registry::EntityKind::Player, + ), + metadata: PlayerMetadataBundle::default(), + }; + // let entity = EntityData::new( + // client.profile.uuid, + // Vec3::default(), + // EntityMetadata::Player(metadata::Player::default()), + // ); + // the first argument makes it so other entities don't update this entity in a shared world + world_lock.add_entity(EntityId(p.player_id), player_bundle); - // *client.entity_id.write() = EntityId(p.player_id); - // } + *client.entity_id.write() = EntityId(p.player_id); + } - // // send the client information that we have set - // let client_information_packet: ClientInformation = - // client.local_player().client_information.clone(); - // log::debug!( - // "Sending client information because login: {:?}", - // client_information_packet - // ); - // client.write_packet(client_information_packet.get()); + // send the client information that we have set + let client_information_packet: ClientInformation = + client.local_player().client_information.clone(); + log::debug!( + "Sending client information because login: {:?}", + client_information_packet + ); + client.write_packet(client_information_packet.get()); - // // brand - // client - // .write_packet( - // ServerboundCustomPayloadPacket { - // identifier: ResourceLocation::new("brand").unwrap(), - // // they don't have to know :) - // data: "vanilla".into(), - // } - // .get(), - // ) - // .await?; + // brand + client + .write_packet( + ServerboundCustomPayloadPacket { + identifier: ResourceLocation::new("brand").unwrap(), + // they don't have to know :) + data: "vanilla".into(), + } + .get(), + ) + .await?; - // tx.send(Event::Login).await?; - // } - // ClientboundGamePacket::SetChunkCacheRadius(p) => { - // debug!("Got set chunk cache radius packet {:?}", p); - // } - // ClientboundGamePacket::CustomPayload(p) => { - // debug!("Got custom payload packet {:?}", p); - // } - // ClientboundGamePacket::ChangeDifficulty(p) => { - // debug!("Got difficulty packet {:?}", p); - // } - // ClientboundGamePacket::Commands(_p) => { - // debug!("Got declare commands packet"); - // } - // ClientboundGamePacket::PlayerAbilities(p) => { - // debug!("Got player abilities packet {:?}", p); - // } - // ClientboundGamePacket::SetCarriedItem(p) => { - // debug!("Got set carried item packet {:?}", p); - // } - // ClientboundGamePacket::UpdateTags(_p) => { - // debug!("Got update tags packet"); - // } - // ClientboundGamePacket::Disconnect(p) => { - // debug!("Got disconnect packet {:?}", p); - // client.disconnect().await?; - // } - // ClientboundGamePacket::UpdateRecipes(_p) => { - // debug!("Got update recipes packet"); - // } - // ClientboundGamePacket::EntityEvent(_p) => { - // // debug!("Got entity event packet {:?}", p); - // } - // ClientboundGamePacket::Recipe(_p) => { - // debug!("Got recipe packet"); - // } - // ClientboundGamePacket::PlayerPosition(p) => { - // // TODO: reply with teleport confirm - // debug!("Got player position packet {:?}", p); + tx.send(Event::Login).await?; + } + ClientboundGamePacket::SetChunkCacheRadius(p) => { + debug!("Got set chunk cache radius packet {:?}", p); + } + ClientboundGamePacket::CustomPayload(p) => { + debug!("Got custom payload packet {:?}", p); + } + ClientboundGamePacket::ChangeDifficulty(p) => { + debug!("Got difficulty packet {:?}", p); + } + ClientboundGamePacket::Commands(_p) => { + debug!("Got declare commands packet"); + } + ClientboundGamePacket::PlayerAbilities(p) => { + debug!("Got player abilities packet {:?}", p); + } + ClientboundGamePacket::SetCarriedItem(p) => { + debug!("Got set carried item packet {:?}", p); + } + ClientboundGamePacket::UpdateTags(_p) => { + debug!("Got update tags packet"); + } + ClientboundGamePacket::Disconnect(p) => { + debug!("Got disconnect packet {:?}", p); + client.disconnect().await?; + } + ClientboundGamePacket::UpdateRecipes(_p) => { + debug!("Got update recipes packet"); + } + ClientboundGamePacket::EntityEvent(_p) => { + // debug!("Got entity event packet {:?}", p); + } + ClientboundGamePacket::Recipe(_p) => { + debug!("Got recipe packet"); + } + ClientboundGamePacket::PlayerPosition(p) => { + // TODO: reply with teleport confirm + debug!("Got player position packet {:?}", p); - // let (new_pos, y_rot, x_rot) = { - // let player_entity_id = *client.entity(); - // let world = client.world(); - // // let mut player_entity = - // world.entity_mut(player_entity_id).unwrap(); let (mut - // physics, position) = client.query::<(&mut - // entity::Physics, &mut entity::Position)>(); + let (new_pos, y_rot, x_rot) = { + let player_entity_id = *client.entity(); + let world = client.world(); + // let mut player_entity = + world.entity_mut(player_entity_id).unwrap(); let (mut + physics, position) = client.query::<(&mut + entity::Physics, &mut entity::Position)>(); - // let delta_movement = physics.delta; + let delta_movement = physics.delta; - // let is_x_relative = p.relative_arguments.x; - // let is_y_relative = p.relative_arguments.y; - // let is_z_relative = p.relative_arguments.z; + let is_x_relative = p.relative_arguments.x; + let is_y_relative = p.relative_arguments.y; + let is_z_relative = p.relative_arguments.z; - // let (delta_x, new_pos_x) = if is_x_relative { - // physics.last_pos.x += p.x; - // (delta_movement.x, position.x + p.x) - // } else { - // physics.last_pos.x = p.x; - // (0.0, p.x) - // }; - // let (delta_y, new_pos_y) = if is_y_relative { - // physics.last_pos.y += p.y; - // (delta_movement.y, position.y + p.y) - // } else { - // physics.last_pos.y = p.y; - // (0.0, p.y) - // }; - // let (delta_z, new_pos_z) = if is_z_relative { - // physics.last_pos.z += p.z; - // (delta_movement.z, position.z + p.z) - // } else { - // physics.last_pos.z = p.z; - // (0.0, p.z) - // }; + let (delta_x, new_pos_x) = if is_x_relative { + physics.last_pos.x += p.x; + (delta_movement.x, position.x + p.x) + } else { + physics.last_pos.x = p.x; + (0.0, p.x) + }; + let (delta_y, new_pos_y) = if is_y_relative { + physics.last_pos.y += p.y; + (delta_movement.y, position.y + p.y) + } else { + physics.last_pos.y = p.y; + (0.0, p.y) + }; + let (delta_z, new_pos_z) = if is_z_relative { + physics.last_pos.z += p.z; + (delta_movement.z, position.z + p.z) + } else { + physics.last_pos.z = p.z; + (0.0, p.z) + }; - // let mut y_rot = p.y_rot; - // let mut x_rot = p.x_rot; - // if p.relative_arguments.x_rot { - // x_rot += physics.x_rot; - // } - // if p.relative_arguments.y_rot { - // y_rot += physics.y_rot; - // } + let mut y_rot = p.y_rot; + let mut x_rot = p.x_rot; + if p.relative_arguments.x_rot { + x_rot += physics.x_rot; + } + if p.relative_arguments.y_rot { + y_rot += physics.y_rot; + } - // physics.delta = Vec3 { - // x: delta_x, - // y: delta_y, - // z: delta_z, - // }; - // entity::set_rotation(physics.into_inner(), y_rot, x_rot); - // // TODO: minecraft sets "xo", "yo", and "zo" here but idk - // what that means // so investigate that ig - // let new_pos = Vec3 { - // x: new_pos_x, - // y: new_pos_y, - // z: new_pos_z, - // }; - // world - // .set_entity_pos( - // player_entity_id, - // new_pos, - // position.into_inner(), - // physics.into_inner(), - // ) - // .expect("The player entity should always exist"); + physics.delta = Vec3 { + x: delta_x, + y: delta_y, + z: delta_z, + }; + entity::set_rotation(physics.into_inner(), y_rot, x_rot); + // TODO: minecraft sets "xo", "yo", and "zo" here but idk + what that means // so investigate that ig + let new_pos = Vec3 { + x: new_pos_x, + y: new_pos_y, + z: new_pos_z, + }; + world + .set_entity_pos( + player_entity_id, + new_pos, + position.into_inner(), + physics.into_inner(), + ) + .expect("The player entity should always exist"); - // (new_pos, y_rot, x_rot) - // }; + (new_pos, y_rot, x_rot) + }; - // client - // .write_packet(ServerboundAcceptTeleportationPacket { id: p.id - // }.get()) .await?; - // client - // .write_packet( - // ServerboundMovePlayerPosRotPacket { - // x: new_pos.x, - // y: new_pos.y, - // z: new_pos.z, - // y_rot, - // x_rot, - // // this is always false - // on_ground: false, - // } - // .get(), - // ) - // .await?; - // } - // ClientboundGamePacket::PlayerInfoUpdate(p) => { - // debug!("Got player info packet {:?}", p); - // let mut events = Vec::new(); - // { - // let mut players_lock = client.players.write(); - // for updated_info in &p.entries { - // // add the new player maybe - // if p.actions.add_player { - // let player_info = PlayerInfo { - // profile: updated_info.profile.clone(), - // uuid: updated_info.profile.uuid, - // gamemode: updated_info.game_mode, - // latency: updated_info.latency, - // display_name: updated_info.display_name.clone(), - // }; - // players_lock.insert(updated_info.profile.uuid, - // player_info.clone()); - // events.push(Event::AddPlayer(player_info)); } else if - // let Some(info) = players_lock.get_mut(&updated_info.profile.uuid) { - // // `else if` because the block for add_player above - // // already sets all the fields - // if p.actions.update_game_mode { - // info.gamemode = updated_info.game_mode; - // } - // if p.actions.update_latency { - // info.latency = updated_info.latency; - // } - // if p.actions.update_display_name { - // info.display_name = - // updated_info.display_name.clone(); } - // events.push(Event::UpdatePlayer(info.clone())); - // } else { - // warn!( - // "Ignoring PlayerInfoUpdate for unknown player - // {}", updated_info.profile.uuid - // ); - // } - // } - // } - // for event in events { - // tx.send(event).await?; - // } - // } - // ClientboundGamePacket::PlayerInfoRemove(p) => { - // let mut events = Vec::new(); - // { - // let mut players_lock = client.players.write(); - // for uuid in &p.profile_ids { - // if let Some(info) = players_lock.remove(uuid) { - // events.push(Event::RemovePlayer(info)); - // } - // } - // } - // for event in events { - // tx.send(event).await?; - // } - // } - // ClientboundGamePacket::SetChunkCacheCenter(p) => { - // debug!("Got chunk cache center packet {:?}", p); - // client - // .world - // .write() - // .update_view_center(&ChunkPos::new(p.x, p.z)); - // } - // ClientboundGamePacket::LevelChunkWithLight(p) => { - // // debug!("Got chunk with light packet {} {}", p.x, p.z); - // let pos = ChunkPos::new(p.x, p.z); + client + .write_packet(ServerboundAcceptTeleportationPacket { id: p.id + }.get()) .await?; + client + .write_packet( + ServerboundMovePlayerPosRotPacket { + x: new_pos.x, + y: new_pos.y, + z: new_pos.z, + y_rot, + x_rot, + // this is always false + on_ground: false, + } + .get(), + ) + .await?; + } + ClientboundGamePacket::PlayerInfoUpdate(p) => { + debug!("Got player info packet {:?}", p); + let mut events = Vec::new(); + { + let mut players_lock = client.players.write(); + for updated_info in &p.entries { + // add the new player maybe + if p.actions.add_player { + let player_info = PlayerInfo { + profile: updated_info.profile.clone(), + uuid: updated_info.profile.uuid, + gamemode: updated_info.game_mode, + latency: updated_info.latency, + display_name: updated_info.display_name.clone(), + }; + players_lock.insert(updated_info.profile.uuid, + player_info.clone()); + events.push(Event::AddPlayer(player_info)); } else if + let Some(info) = players_lock.get_mut(&updated_info.profile.uuid) { + // `else if` because the block for add_player above + // already sets all the fields + if p.actions.update_game_mode { + info.gamemode = updated_info.game_mode; + } + if p.actions.update_latency { + info.latency = updated_info.latency; + } + if p.actions.update_display_name { + info.display_name = + updated_info.display_name.clone(); } + events.push(Event::UpdatePlayer(info.clone())); + } else { + warn!( + "Ignoring PlayerInfoUpdate for unknown player + {}", updated_info.profile.uuid + ); + } + } + } + for event in events { + tx.send(event).await?; + } + } + ClientboundGamePacket::PlayerInfoRemove(p) => { + let mut events = Vec::new(); + { + let mut players_lock = client.players.write(); + for uuid in &p.profile_ids { + if let Some(info) = players_lock.remove(uuid) { + events.push(Event::RemovePlayer(info)); + } + } + } + for event in events { + tx.send(event).await?; + } + } + ClientboundGamePacket::SetChunkCacheCenter(p) => { + debug!("Got chunk cache center packet {:?}", p); + client + .world + .write() + .update_view_center(&ChunkPos::new(p.x, p.z)); + } + ClientboundGamePacket::LevelChunkWithLight(p) => { + // debug!("Got chunk with light packet {} {}", p.x, p.z); + let pos = ChunkPos::new(p.x, p.z); - // // OPTIMIZATION: if we already know about the chunk from the - // // shared world (and not ourselves), then we don't need to - // // parse it again. This is only used when we have a shared - // // world, since we check that the chunk isn't currently owned - // // by this client. - // let shared_has_chunk = - // client.world.read().get_chunk(&pos).is_some(); let - // this_client_has_chunk = - // client.world.read().chunks.limited_get(&pos).is_some(); if - // shared_has_chunk && !this_client_has_chunk { trace!( - // "Skipping parsing chunk {:?} because we already know - // about it", pos - // ); - // return Ok(()); - // } + // OPTIMIZATION: if we already know about the chunk from the + // shared world (and not ourselves), then we don't need to + // parse it again. This is only used when we have a shared + // world, since we check that the chunk isn't currently owned + // by this client. + let shared_has_chunk = + client.world.read().get_chunk(&pos).is_some(); let + this_client_has_chunk = + client.world.read().chunks.limited_get(&pos).is_some(); if + shared_has_chunk && !this_client_has_chunk { trace!( + "Skipping parsing chunk {:?} because we already know + about it", pos + ); + return Ok(()); + } - // // let chunk = Chunk::read_with_world_height(&mut p.chunk_data); - // // debug("chunk {:?}") - // if let Err(e) = client - // .world - // .write() - // .replace_with_packet_data(&pos, &mut - // Cursor::new(&p.chunk_data.data)) { - // error!("Couldn't set chunk data: {}", e); - // } - // } - // ClientboundGamePacket::LightUpdate(_p) => { - // // debug!("Got light update packet {:?}", p); - // } - // ClientboundGamePacket::AddEntity(p) => { - // debug!("Got add entity packet {:?}", p); - // let bundle = p.as_entity_bundle(); - // let mut world = client.world.write(); - // world.add_entity(EntityId(p.id), bundle); - // // the bundle doesn't include the default entity metadata so we - // add that // separately - // let mut entities = world.entity_infos.shared.write(); - // let mut entity = - // entities.ecs_entity_mut(EntityId(p.id)).unwrap(); - // p.apply_metadata(&mut entity); - // } - // ClientboundGamePacket::SetEntityData(p) => { - // debug!("Got set entity data packet {:?}", p); - // let world = client.world.write(); - // let mut entities = world.entity_infos.shared.write(); - // let entity = entities.ecs_entity_mut(EntityId(p.id)); - // if let Some(mut entity) = entity { - // entity::metadata::apply_metadata(&mut entity, - // p.packed_items.0.clone()); } else { - // // warn!("Server sent an entity data packet for an - // // entity id ({}) that we don't - // // know about", p.id); - // } - // } - // ClientboundGamePacket::UpdateAttributes(_p) => { - // // debug!("Got update attributes packet {:?}", p); - // } - // ClientboundGamePacket::SetEntityMotion(_p) => { - // // debug!("Got entity velocity packet {:?}", p); - // } - // ClientboundGamePacket::SetEntityLink(p) => { - // debug!("Got set entity link packet {:?}", p); - // } - // ClientboundGamePacket::AddPlayer(p) => { - // debug!("Got add player packet {:?}", p); - // let bundle = p.as_player_bundle(); - // let mut world = client.world.write(); - // world.add_entity(EntityId(p.id), bundle); - // // the default metadata was already included in the bundle - // // for us - // } - // ClientboundGamePacket::InitializeBorder(p) => { - // debug!("Got initialize border packet {:?}", p); - // } - // ClientboundGamePacket::SetTime(p) => { - // debug!("Got set time packet {:?}", p); - // } - // ClientboundGamePacket::SetDefaultSpawnPosition(p) => { - // debug!("Got set default spawn position packet {:?}", p); - // } - // ClientboundGamePacket::ContainerSetContent(p) => { - // debug!("Got container set content packet {:?}", p); - // } - // ClientboundGamePacket::SetHealth(p) => { - // debug!("Got set health packet {:?}", p); - // if p.health == 0.0 { - // // we can't define a variable here with client.dead.lock() - // // because of https://github.com/rust-lang/rust/issues/57478 - // if !*client.dead.lock() { - // *client.dead.lock() = true; - // tx.send(Event::Death(None)).await?; - // } - // } - // } - // ClientboundGamePacket::SetExperience(p) => { - // debug!("Got set experience packet {:?}", p); - // } - // ClientboundGamePacket::TeleportEntity(p) => { - // let mut world = client.world.write(); - // let (pos, physics) = self.query::<(&entity::Position, - // &entity::Physics)>(); let _ = world.set_entity_pos( - // EntityId(p.id), - // Vec3 { - // x: p.x, - // y: p.y, - // z: p.z, - // }, - // pos, - // physics, - // ); - // } - // ClientboundGamePacket::UpdateAdvancements(p) => { - // debug!("Got update advancements packet {:?}", p); - // } - // ClientboundGamePacket::RotateHead(_p) => { - // // debug!("Got rotate head packet {:?}", p); - // } - // ClientboundGamePacket::MoveEntityPos(p) => { - // let mut world = client.world.write(); - // let _ = world.move_entity_with_delta(EntityId(p.entity_id), - // &p.delta); } - // ClientboundGamePacket::MoveEntityPosRot(p) => { - // let mut world = client.world.write(); - // let _ = world.move_entity_with_delta(EntityId(p.entity_id), - // &p.delta); } - // ClientboundGamePacket::MoveEntityRot(_p) => { - // // debug!("Got move entity rot packet {:?}", p); - // } - // ClientboundGamePacket::KeepAlive(p) => { - // debug!("Got keep alive packet {:?}", p); - // client - // .write_packet(ServerboundKeepAlivePacket { id: p.id }.get()) - // .await?; - // } - // ClientboundGamePacket::RemoveEntities(p) => { - // debug!("Got remove entities packet {:?}", p); - // } - // ClientboundGamePacket::PlayerChat(p) => { - // debug!("Got player chat packet {:?}", p); - // tx.send(Event::Chat(ChatPacket::Player(Arc::new(p.clone())))) - // .await?; - // } - // ClientboundGamePacket::SystemChat(p) => { - // debug!("Got system chat packet {:?}", p); - // tx.send(Event::Chat(ChatPacket::System(Arc::new(p.clone())))) - // .await?; - // } - // ClientboundGamePacket::Sound(_p) => { - // // debug!("Got sound packet {:?}", p); - // } - // ClientboundGamePacket::LevelEvent(p) => { - // debug!("Got level event packet {:?}", p); - // } - // ClientboundGamePacket::BlockUpdate(p) => { - // debug!("Got block update packet {:?}", p); - // let mut world = client.world.write(); - // world.set_block_state(&p.pos, p.block_state); - // } - // ClientboundGamePacket::Animate(p) => { - // debug!("Got animate packet {:?}", p); - // } - // ClientboundGamePacket::SectionBlocksUpdate(p) => { - // debug!("Got section blocks update packet {:?}", p); - // let mut world = client.world.write(); - // for state in &p.states { - // world.set_block_state(&(p.section_pos + state.pos.clone()), - // state.state); } - // } - // ClientboundGamePacket::GameEvent(p) => { - // debug!("Got game event packet {:?}", p); - // } - // ClientboundGamePacket::LevelParticles(p) => { - // debug!("Got level particles packet {:?}", p); - // } - // ClientboundGamePacket::ServerData(p) => { - // debug!("Got server data packet {:?}", p); - // } - // ClientboundGamePacket::SetEquipment(p) => { - // debug!("Got set equipment packet {:?}", p); - // } - // ClientboundGamePacket::UpdateMobEffect(p) => { - // debug!("Got update mob effect packet {:?}", p); - // } - // ClientboundGamePacket::AddExperienceOrb(_) => {} - // ClientboundGamePacket::AwardStats(_) => {} - // ClientboundGamePacket::BlockChangedAck(_) => {} - // ClientboundGamePacket::BlockDestruction(_) => {} - // ClientboundGamePacket::BlockEntityData(_) => {} - // ClientboundGamePacket::BlockEvent(_) => {} - // ClientboundGamePacket::BossEvent(_) => {} - // ClientboundGamePacket::CommandSuggestions(_) => {} - // ClientboundGamePacket::ContainerSetData(_) => {} - // ClientboundGamePacket::ContainerSetSlot(_) => {} - // ClientboundGamePacket::Cooldown(_) => {} - // ClientboundGamePacket::CustomChatCompletions(_) => {} - // ClientboundGamePacket::DeleteChat(_) => {} - // ClientboundGamePacket::Explode(_) => {} - // ClientboundGamePacket::ForgetLevelChunk(_) => {} - // ClientboundGamePacket::HorseScreenOpen(_) => {} - // ClientboundGamePacket::MapItemData(_) => {} - // ClientboundGamePacket::MerchantOffers(_) => {} - // ClientboundGamePacket::MoveVehicle(_) => {} - // ClientboundGamePacket::OpenBook(_) => {} - // ClientboundGamePacket::OpenScreen(_) => {} - // ClientboundGamePacket::OpenSignEditor(_) => {} - // ClientboundGamePacket::Ping(_) => {} - // ClientboundGamePacket::PlaceGhostRecipe(_) => {} - // ClientboundGamePacket::PlayerCombatEnd(_) => {} - // ClientboundGamePacket::PlayerCombatEnter(_) => {} - // ClientboundGamePacket::PlayerCombatKill(p) => { - // debug!("Got player kill packet {:?}", p); - // if client.entity() == EntityId(p.player_id) { - // // we can't define a variable here with client.dead.lock() - // // because of https://github.com/rust-lang/rust/issues/57478 - // if !*client.dead.lock() { - // *client.dead.lock() = true; - // tx.send(Event::Death(Some(Arc::new(p.clone())))).await?; - // } - // } - // } - // ClientboundGamePacket::PlayerLookAt(_) => {} - // ClientboundGamePacket::RemoveMobEffect(_) => {} - // ClientboundGamePacket::ResourcePack(_) => {} - // ClientboundGamePacket::Respawn(p) => { - // debug!("Got respawn packet {:?}", p); - // // Sets clients dead state to false. - // let mut dead_lock = client.dead.lock(); - // *dead_lock = false; - // } - // ClientboundGamePacket::SelectAdvancementsTab(_) => {} - // ClientboundGamePacket::SetActionBarText(_) => {} - // ClientboundGamePacket::SetBorderCenter(_) => {} - // ClientboundGamePacket::SetBorderLerpSize(_) => {} - // ClientboundGamePacket::SetBorderSize(_) => {} - // ClientboundGamePacket::SetBorderWarningDelay(_) => {} - // ClientboundGamePacket::SetBorderWarningDistance(_) => {} - // ClientboundGamePacket::SetCamera(_) => {} - // ClientboundGamePacket::SetDisplayObjective(_) => {} - // ClientboundGamePacket::SetObjective(_) => {} - // ClientboundGamePacket::SetPassengers(_) => {} - // ClientboundGamePacket::SetPlayerTeam(_) => {} - // ClientboundGamePacket::SetScore(_) => {} - // ClientboundGamePacket::SetSimulationDistance(_) => {} - // ClientboundGamePacket::SetSubtitleText(_) => {} - // ClientboundGamePacket::SetTitleText(_) => {} - // ClientboundGamePacket::SetTitlesAnimation(_) => {} - // ClientboundGamePacket::SoundEntity(_) => {} - // ClientboundGamePacket::StopSound(_) => {} - // ClientboundGamePacket::TabList(_) => {} - // ClientboundGamePacket::TagQuery(_) => {} - // ClientboundGamePacket::TakeItemEntity(_) => {} - // ClientboundGamePacket::DisguisedChat(_) => {} - // ClientboundGamePacket::UpdateEnabledFeatures(_) => {} - // ClientboundGamePacket::ContainerClose(_) => {} - // } + // let chunk = Chunk::read_with_world_height(&mut p.chunk_data); + // debug("chunk {:?}") + if let Err(e) = client + .world + .write() + .replace_with_packet_data(&pos, &mut + Cursor::new(&p.chunk_data.data)) { + error!("Couldn't set chunk data: {}", e); + } + } + ClientboundGamePacket::LightUpdate(_p) => { + // debug!("Got light update packet {:?}", p); + } + ClientboundGamePacket::AddEntity(p) => { + debug!("Got add entity packet {:?}", p); + let bundle = p.as_entity_bundle(); + let mut world = client.world.write(); + world.add_entity(EntityId(p.id), bundle); + // the bundle doesn't include the default entity metadata so we + add that // separately + let mut entities = world.entity_infos.shared.write(); + let mut entity = + entities.ecs_entity_mut(EntityId(p.id)).unwrap(); + p.apply_metadata(&mut entity); + } + ClientboundGamePacket::SetEntityData(p) => { + debug!("Got set entity data packet {:?}", p); + let world = client.world.write(); + let mut entities = world.entity_infos.shared.write(); + let entity = entities.ecs_entity_mut(EntityId(p.id)); + if let Some(mut entity) = entity { + entity::metadata::apply_metadata(&mut entity, + p.packed_items.0.clone()); } else { + // warn!("Server sent an entity data packet for an + // entity id ({}) that we don't + // know about", p.id); + } + } + ClientboundGamePacket::UpdateAttributes(_p) => { + // debug!("Got update attributes packet {:?}", p); + } + ClientboundGamePacket::SetEntityMotion(_p) => { + // debug!("Got entity velocity packet {:?}", p); + } + ClientboundGamePacket::SetEntityLink(p) => { + debug!("Got set entity link packet {:?}", p); + } + ClientboundGamePacket::AddPlayer(p) => { + debug!("Got add player packet {:?}", p); + let bundle = p.as_player_bundle(); + let mut world = client.world.write(); + world.add_entity(EntityId(p.id), bundle); + // the default metadata was already included in the bundle + // for us + } + ClientboundGamePacket::InitializeBorder(p) => { + debug!("Got initialize border packet {:?}", p); + } + ClientboundGamePacket::SetTime(p) => { + debug!("Got set time packet {:?}", p); + } + ClientboundGamePacket::SetDefaultSpawnPosition(p) => { + debug!("Got set default spawn position packet {:?}", p); + } + ClientboundGamePacket::ContainerSetContent(p) => { + debug!("Got container set content packet {:?}", p); + } + ClientboundGamePacket::SetHealth(p) => { + debug!("Got set health packet {:?}", p); + if p.health == 0.0 { + // we can't define a variable here with client.dead.lock() + // because of https://github.com/rust-lang/rust/issues/57478 + if !*client.dead.lock() { + *client.dead.lock() = true; + tx.send(Event::Death(None)).await?; + } + } + } + ClientboundGamePacket::SetExperience(p) => { + debug!("Got set experience packet {:?}", p); + } + ClientboundGamePacket::TeleportEntity(p) => { + let mut world = client.world.write(); + let (pos, physics) = self.query::<(&entity::Position, + &entity::Physics)>(); let _ = world.set_entity_pos( + EntityId(p.id), + Vec3 { + x: p.x, + y: p.y, + z: p.z, + }, + pos, + physics, + ); + } + ClientboundGamePacket::UpdateAdvancements(p) => { + debug!("Got update advancements packet {:?}", p); + } + ClientboundGamePacket::RotateHead(_p) => { + // debug!("Got rotate head packet {:?}", p); + } + ClientboundGamePacket::MoveEntityPos(p) => { + let mut world = client.world.write(); + let _ = world.move_entity_with_delta(EntityId(p.entity_id), + &p.delta); } + ClientboundGamePacket::MoveEntityPosRot(p) => { + let mut world = client.world.write(); + let _ = world.move_entity_with_delta(EntityId(p.entity_id), + &p.delta); } + ClientboundGamePacket::MoveEntityRot(_p) => { + // debug!("Got move entity rot packet {:?}", p); + } + ClientboundGamePacket::KeepAlive(p) => { + debug!("Got keep alive packet {:?}", p); + client + .write_packet(ServerboundKeepAlivePacket { id: p.id }.get()) + .await?; + } + ClientboundGamePacket::RemoveEntities(p) => { + debug!("Got remove entities packet {:?}", p); + } + ClientboundGamePacket::PlayerChat(p) => { + debug!("Got player chat packet {:?}", p); + tx.send(Event::Chat(ChatPacket::Player(Arc::new(p.clone())))) + .await?; + } + ClientboundGamePacket::SystemChat(p) => { + debug!("Got system chat packet {:?}", p); + tx.send(Event::Chat(ChatPacket::System(Arc::new(p.clone())))) + .await?; + } + ClientboundGamePacket::Sound(_p) => { + // debug!("Got sound packet {:?}", p); + } + ClientboundGamePacket::LevelEvent(p) => { + debug!("Got level event packet {:?}", p); + } + ClientboundGamePacket::BlockUpdate(p) => { + debug!("Got block update packet {:?}", p); + let mut world = client.world.write(); + world.set_block_state(&p.pos, p.block_state); + } + ClientboundGamePacket::Animate(p) => { + debug!("Got animate packet {:?}", p); + } + ClientboundGamePacket::SectionBlocksUpdate(p) => { + debug!("Got section blocks update packet {:?}", p); + let mut world = client.world.write(); + for state in &p.states { + world.set_block_state(&(p.section_pos + state.pos.clone()), + state.state); } + } + ClientboundGamePacket::GameEvent(p) => { + debug!("Got game event packet {:?}", p); + } + ClientboundGamePacket::LevelParticles(p) => { + debug!("Got level particles packet {:?}", p); + } + ClientboundGamePacket::ServerData(p) => { + debug!("Got server data packet {:?}", p); + } + ClientboundGamePacket::SetEquipment(p) => { + debug!("Got set equipment packet {:?}", p); + } + ClientboundGamePacket::UpdateMobEffect(p) => { + debug!("Got update mob effect packet {:?}", p); + } + ClientboundGamePacket::AddExperienceOrb(_) => {} + ClientboundGamePacket::AwardStats(_) => {} + ClientboundGamePacket::BlockChangedAck(_) => {} + ClientboundGamePacket::BlockDestruction(_) => {} + ClientboundGamePacket::BlockEntityData(_) => {} + ClientboundGamePacket::BlockEvent(_) => {} + ClientboundGamePacket::BossEvent(_) => {} + ClientboundGamePacket::CommandSuggestions(_) => {} + ClientboundGamePacket::ContainerSetData(_) => {} + ClientboundGamePacket::ContainerSetSlot(_) => {} + ClientboundGamePacket::Cooldown(_) => {} + ClientboundGamePacket::CustomChatCompletions(_) => {} + ClientboundGamePacket::DeleteChat(_) => {} + ClientboundGamePacket::Explode(_) => {} + ClientboundGamePacket::ForgetLevelChunk(_) => {} + ClientboundGamePacket::HorseScreenOpen(_) => {} + ClientboundGamePacket::MapItemData(_) => {} + ClientboundGamePacket::MerchantOffers(_) => {} + ClientboundGamePacket::MoveVehicle(_) => {} + ClientboundGamePacket::OpenBook(_) => {} + ClientboundGamePacket::OpenScreen(_) => {} + ClientboundGamePacket::OpenSignEditor(_) => {} + ClientboundGamePacket::Ping(_) => {} + ClientboundGamePacket::PlaceGhostRecipe(_) => {} + ClientboundGamePacket::PlayerCombatEnd(_) => {} + ClientboundGamePacket::PlayerCombatEnter(_) => {} + ClientboundGamePacket::PlayerCombatKill(p) => { + debug!("Got player kill packet {:?}", p); + let entity_id = + if client.entity() == EntityId(p.player_id) { + // we can't define a variable here with client.dead.lock() + // because of https://github.com/rust-lang/rust/issues/57478 + if !*client.dead.lock() { + *client.dead.lock() = true; + tx.send(Event::Death(Some(Arc::new(p.clone())))).await?; + } + } + } + ClientboundGamePacket::PlayerLookAt(_) => {} + ClientboundGamePacket::RemoveMobEffect(_) => {} + ClientboundGamePacket::ResourcePack(_) => {} + ClientboundGamePacket::Respawn(p) => { + debug!("Got respawn packet {:?}", p); + // Sets clients dead state to false. + let mut dead_lock = client.dead.lock(); + *dead_lock = false; + } + ClientboundGamePacket::SelectAdvancementsTab(_) => {} + ClientboundGamePacket::SetActionBarText(_) => {} + ClientboundGamePacket::SetBorderCenter(_) => {} + ClientboundGamePacket::SetBorderLerpSize(_) => {} + ClientboundGamePacket::SetBorderSize(_) => {} + ClientboundGamePacket::SetBorderWarningDelay(_) => {} + ClientboundGamePacket::SetBorderWarningDistance(_) => {} + ClientboundGamePacket::SetCamera(_) => {} + ClientboundGamePacket::SetDisplayObjective(_) => {} + ClientboundGamePacket::SetObjective(_) => {} + ClientboundGamePacket::SetPassengers(_) => {} + ClientboundGamePacket::SetPlayerTeam(_) => {} + ClientboundGamePacket::SetScore(_) => {} + ClientboundGamePacket::SetSimulationDistance(_) => {} + ClientboundGamePacket::SetSubtitleText(_) => {} + ClientboundGamePacket::SetTitleText(_) => {} + ClientboundGamePacket::SetTitlesAnimation(_) => {} + ClientboundGamePacket::SoundEntity(_) => {} + ClientboundGamePacket::StopSound(_) => {} + ClientboundGamePacket::TabList(_) => {} + ClientboundGamePacket::TagQuery(_) => {} + ClientboundGamePacket::TakeItemEntity(_) => {} + ClientboundGamePacket::DisguisedChat(_) => {} + ClientboundGamePacket::UpdateEnabledFeatures(_) => {} + ClientboundGamePacket::ContainerClose(_) => {} + } } /// A system that clears all packets in the clientbound packet events. @@ -613,7 +612,7 @@ impl PacketReceiver { while let Ok(packet) = read_conn.read().await { self.packets.lock().push(packet); // tell the client to run all the systems - self.run_schedule_sender.send(()); + self.run_schedule_sender.send(()).unwrap(); } } @@ -626,7 +625,11 @@ impl PacketReceiver { mut write_receiver: mpsc::UnboundedReceiver, ) { while let Some(packet) = write_receiver.recv().await { - write_conn.write(packet); + if let Err(err) = write_conn.write(packet).await { + error!("Disconnecting because we couldn't write a packet: {err}."); + break; + }; } + // receiver is automatically closed when it's dropped } }