diff options
| author | mat <github@matdoes.dev> | 2022-06-25 16:23:40 -0500 |
|---|---|---|
| committer | mat <github@matdoes.dev> | 2022-06-25 16:23:40 -0500 |
| commit | 460bdfb8bbaf0969df6451a00fc3de214728aec0 (patch) | |
| tree | bc88105b401bcfc27922e70227a89a8f1ac0259a | |
| parent | 77980f0018eca3a192994021b76ad5d05bff88ea (diff) | |
| download | azalea-drasl-460bdfb8bbaf0969df6451a00fc3de214728aec0.tar.xz | |
merge ClientState and Client
| -rwxr-xr-x | Cargo.lock | 16 | ||||
| -rwxr-xr-x | azalea-client/Cargo.toml | 1 | ||||
| -rw-r--r-- | azalea-client/src/account.rs | 9 | ||||
| -rw-r--r-- | azalea-client/src/client.rs | 184 | ||||
| -rw-r--r-- | azalea-client/src/movement.rs | 19 | ||||
| -rw-r--r-- | azalea-client/src/player.rs | 5 | ||||
| -rw-r--r-- | bot/src/main.rs | 14 |
7 files changed, 100 insertions, 148 deletions
@@ -120,7 +120,6 @@ dependencies = [ "azalea-entity", "azalea-protocol", "azalea-world", - "owning_ref", "tokio", "uuid", ] @@ -921,15 +920,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] -name = "owning_ref" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce" -dependencies = [ - "stable_deref_trait", -] - -[[package]] name = "packet-macros" version = "0.1.0" dependencies = [ @@ -1278,12 +1268,6 @@ dependencies = [ ] [[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] name = "syn" version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/azalea-client/Cargo.toml b/azalea-client/Cargo.toml index b39d6a49..dc340812 100755 --- a/azalea-client/Cargo.toml +++ b/azalea-client/Cargo.toml @@ -12,6 +12,5 @@ azalea-crypto = {path = "../azalea-crypto"} azalea-entity = {path = "../azalea-entity"} azalea-protocol = {path = "../azalea-protocol"} azalea-world = {path = "../azalea-world"} -owning_ref = "0.4.1" tokio = {version = "1.19.2", features = ["sync"]} uuid = "1.1.2" diff --git a/azalea-client/src/account.rs b/azalea-client/src/account.rs index 56f4918a..32a46406 100644 --- a/azalea-client/src/account.rs +++ b/azalea-client/src/account.rs @@ -1,7 +1,8 @@ //! Connect to Minecraft servers. -use crate::Client; +use crate::{Client, Event}; use azalea_protocol::ServerAddress; +use tokio::sync::mpsc::UnboundedReceiver; /// Something that can join Minecraft servers. pub struct Account { @@ -14,7 +15,11 @@ impl Account { } } - pub async fn join(&self, address: &ServerAddress) -> Result<Client, String> { + /// Joins the Minecraft server on the given address using this account. + pub async fn join( + &self, + address: &ServerAddress, + ) -> Result<(Client, UnboundedReceiver<Event>), String> { Client::join(self, address).await } } diff --git a/azalea-client/src/client.rs b/azalea-client/src/client.rs index 2bc73551..11b098ca 100644 --- a/azalea-client/src/client.rs +++ b/azalea-client/src/client.rs @@ -24,22 +24,15 @@ use azalea_protocol::{ resolver, ServerAddress, }; use azalea_world::Dimension; -use owning_ref::OwningRef; use std::{ fmt::Debug, sync::{Arc, Mutex}, }; use tokio::{ sync::mpsc::{self, UnboundedReceiver, UnboundedSender}, - time::{self, MissedTickBehavior}, + time::{self}, }; -#[derive(Default)] -pub struct ClientState { - pub player: Player, - pub world: Option<Dimension>, -} - #[derive(Debug, Clone)] pub enum Event { Login, @@ -64,11 +57,12 @@ pub enum ChatPacket { // } /// A player that you can control that is currently in a Minecraft server. +#[derive(Clone)] pub struct Client { - event_receiver: UnboundedReceiver<Event>, game_profile: GameProfile, pub conn: Arc<tokio::sync::Mutex<GameConnection>>, - pub state: Arc<Mutex<ClientState>>, + pub player: Arc<Mutex<Player>>, + pub dimension: Arc<Mutex<Option<Dimension>>>, // game_loop } @@ -80,7 +74,10 @@ struct HandleError(String); impl Client { /// Connect to a Minecraft server with an account. - pub async fn join(account: &Account, address: &ServerAddress) -> Result<Self, String> { + pub async fn join( + account: &Account, + address: &ServerAddress, + ) -> Result<(Self, UnboundedReceiver<Event>), String> { let resolved_address = resolver::resolve_address(address).await?; let mut conn = HandshakeConnection::new(&resolved_address).await?; @@ -159,51 +156,39 @@ impl Client { // we got the GameConnection, so the server is now connected :) let client = Client { game_profile: game_profile.clone(), - event_receiver: rx, conn: conn.clone(), - state: Arc::new(Mutex::new(ClientState::default())), + player: Arc::new(Mutex::new(Player::default())), + dimension: Arc::new(Mutex::new(None)), }; // just start up the game loop and we're ready! - let game_loop_state = client.state.clone(); + let game_loop_state = client.clone(); // 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 // you might be able to just drop the lock or put it in its own scope to fix - tokio::spawn(Self::protocol_loop( - conn.clone(), - tx.clone(), - game_loop_state.clone(), - game_profile.clone(), - )); - tokio::spawn(Self::game_tick_loop(conn, tx, game_loop_state)); - - Ok(client) + tokio::spawn(Self::protocol_loop(client.clone(), tx.clone())); + tokio::spawn(Self::game_tick_loop(client.clone(), tx.clone())); + + Ok((client, rx)) } - async fn protocol_loop( - conn: Arc<tokio::sync::Mutex<GameConnection>>, - tx: UnboundedSender<Event>, - state: Arc<Mutex<ClientState>>, - game_profile: GameProfile, - ) { + async fn protocol_loop(client: Client, tx: UnboundedSender<Event>) { loop { - let r = conn.lock().await.read().await; + let r = client.conn.lock().await.read().await; match r { - Ok(packet) => { - match Self::handle(&packet, &tx, &state, &conn, &game_profile).await { - Ok(_) => {} - Err(e) => { - println!("Error handling packet: {:?}", e); - if IGNORE_ERRORS { - continue; - } else { - panic!("Error handling packet: {:?}", e); - } + Ok(packet) => match Self::handle(&packet, &client, &tx).await { + Ok(_) => {} + Err(e) => { + println!("Error handling packet: {:?}", e); + if IGNORE_ERRORS { + continue; + } else { + panic!("Error handling packet: {:?}", e); } } - } + }, Err(e) => { if IGNORE_ERRORS { println!("Error: {:?}", e); @@ -220,18 +205,14 @@ impl Client { async fn handle( packet: &GamePacket, + client: &Client, tx: &UnboundedSender<Event>, - state: &Arc<Mutex<ClientState>>, - conn: &Arc<tokio::sync::Mutex<GameConnection>>, - game_profile: &GameProfile, ) -> Result<(), HandleError> { match packet { GamePacket::ClientboundLoginPacket(p) => { println!("Got login packet {:?}", p); { - let mut state_lock = state.lock()?; - // // write p into login.txt // std::io::Write::write_all( // &mut std::fs::File::create("login.txt").unwrap(), @@ -292,23 +273,28 @@ impl Client { .as_int() .expect("min_y tag is not an int"); + let mut dimension_lock = client.dimension.lock().unwrap(); // the 16 here is our render distance // i'll make this an actual setting later - state_lock.world = Some(Dimension::new(16, height, min_y)); + *dimension_lock = Some(Dimension::new(16, height, min_y)); - let entity = Entity::new(p.player_id, game_profile.uuid, EntityPos::default()); - state_lock - .world + let entity = + Entity::new(p.player_id, client.game_profile.uuid, EntityPos::default()); + dimension_lock .as_mut() .expect( "Dimension doesn't exist! We should've gotten a login packet by now.", ) .add_entity(entity); - state_lock.player.set_entity_id(p.player_id); + let mut player_lock = client.player.lock().unwrap(); + + player_lock.set_entity_id(p.player_id); } - conn.lock() + client + .conn + .lock() .await .write( ServerboundCustomPayloadPacket { @@ -360,12 +346,17 @@ impl Client { println!("Got player position packet {:?}", p); let (new_pos, y_rot, x_rot) = { - let mut state_lock = state.lock()?; - let player_entity_id = state_lock.player.entity_id; - let world = state_lock.world.as_mut().unwrap(); - let player_entity = world + let player_lock = client.player.lock().unwrap(); + let player_entity_id = player_lock.entity_id; + drop(player_lock); + + let mut dimension_lock = client.dimension.lock().unwrap(); + let dimension = dimension_lock.as_mut().unwrap(); + + let player_entity = dimension .mut_entity_by_id(player_entity_id) .expect("Player entity doesn't exist"); + let delta_movement = &player_entity.delta; let is_x_relative = p.relative_arguments.x; @@ -416,14 +407,14 @@ impl Client { y: new_pos_y, z: new_pos_z, }; - world + dimension .move_entity(player_entity_id, new_pos) .expect("The player entity should always exist"); (new_pos, y_rot, x_rot) }; - let mut conn_lock = conn.lock().await; + let mut conn_lock = client.conn.lock().await; conn_lock .write(ServerboundAcceptTeleportationPacket { id: p.id }.get()) .await; @@ -447,9 +438,9 @@ impl Client { } GamePacket::ClientboundSetChunkCacheCenterPacket(p) => { println!("Got chunk cache center packet {:?}", p); - state + client + .dimension .lock()? - .world .as_mut() .unwrap() .update_view_center(&ChunkPos::new(p.x, p.z)); @@ -459,9 +450,9 @@ impl Client { let pos = ChunkPos::new(p.x, p.z); // let chunk = Chunk::read_with_world_height(&mut p.chunk_data); // println("chunk {:?}") - state + client + .dimension .lock()? - .world .as_mut() .expect("Dimension doesn't exist! We should've gotten a login packet by now.") .replace_with_packet_data(&pos, &mut p.chunk_data.data.as_slice()) @@ -473,9 +464,9 @@ impl Client { GamePacket::ClientboundAddEntityPacket(p) => { println!("Got add entity packet {:?}", p); let entity = Entity::from(p); - state + client + .dimension .lock()? - .world .as_mut() .expect("Dimension doesn't exist! We should've gotten a login packet by now.") .add_entity(entity); @@ -495,9 +486,9 @@ impl Client { GamePacket::ClientboundAddPlayerPacket(p) => { println!("Got add player packet {:?}", p); let entity = Entity::from(p); - state + client + .dimension .lock()? - .world .as_mut() .expect("Dimension doesn't exist! We should've gotten a login packet by now.") .add_entity(entity); @@ -521,10 +512,10 @@ impl Client { println!("Got set experience packet {:?}", p); } GamePacket::ClientboundTeleportEntityPacket(p) => { - let mut state_lock = state.lock()?; - let world = state_lock.world.as_mut().unwrap(); + let mut dimension_lock = client.dimension.lock()?; + let dimension = dimension_lock.as_mut().unwrap(); - world.move_entity( + dimension.move_entity( p.id, EntityPos { x: p.x, @@ -540,23 +531,25 @@ impl Client { // println!("Got rotate head packet {:?}", p); } GamePacket::ClientboundMoveEntityPosPacket(p) => { - let mut state_lock = state.lock()?; - let world = state_lock.world.as_mut().unwrap(); + let mut dimension_lock = client.dimension.lock()?; + let dimension = dimension_lock.as_mut().unwrap(); - world.move_entity_with_delta(p.entity_id, &p.delta)?; + dimension.move_entity_with_delta(p.entity_id, &p.delta)?; } GamePacket::ClientboundMoveEntityPosRotPacket(p) => { - let mut state_lock = state.lock()?; - let world = state_lock.world.as_mut().unwrap(); + let mut dimension_lock = client.dimension.lock()?; + let dimension = dimension_lock.as_mut().unwrap(); - world.move_entity_with_delta(p.entity_id, &p.delta)?; + dimension.move_entity_with_delta(p.entity_id, &p.delta)?; } GamePacket::ClientboundMoveEntityRotPacket(p) => { println!("Got move entity rot packet {:?}", p); } GamePacket::ClientboundKeepAlivePacket(p) => { println!("Got keep alive packet {:?}", p); - conn.lock() + client + .conn + .lock() .await .write(ServerboundKeepAlivePacket { id: p.id }.get()) .await; @@ -611,55 +604,24 @@ impl Client { Ok(()) } - pub async fn next(&mut self) -> Option<Event> { - self.event_receiver.recv().await - } - /// Runs game_tick every 50 milliseconds. - async fn game_tick_loop( - conn: Arc<tokio::sync::Mutex<GameConnection>>, - tx: UnboundedSender<Event>, - state: Arc<Mutex<ClientState>>, - ) { + async fn game_tick_loop(client: Client, tx: UnboundedSender<Event>) { 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; - Self::game_tick(&conn, &tx, &state).await; + Self::game_tick(&client, &tx).await; } } /// Runs every 50 milliseconds. - async fn game_tick( - conn: &Arc<tokio::sync::Mutex<GameConnection>>, - tx: &UnboundedSender<Event>, - state: &Arc<Mutex<ClientState>>, - ) { - if state.lock().unwrap().world.is_none() { + async fn game_tick(client: &Client, tx: &UnboundedSender<Event>) { + if client.dimension.lock().unwrap().is_none() { return; } tx.send(Event::GameTick).unwrap(); } - - /// Gets the `Dimension` the client is in. - /// - /// This is basically a shortcut for `client.state.lock().unwrap().world.as_ref().unwrap()`. - /// If the client hasn't received a login packet yet, this will panic. - pub fn world(&self) -> OwningRef<std::sync::MutexGuard<ClientState>, Dimension> { - let state_lock: std::sync::MutexGuard<ClientState> = self.state.lock().unwrap(); - let state_lock_ref = OwningRef::new(state_lock); - state_lock_ref.map(|state| state.world.as_ref().expect("Dimension doesn't exist!")) - } - - /// Gets the `Player` struct for our player. - /// - /// This is basically a shortcut for `client.state.lock().unwrap().player`. - pub fn player(&self) -> OwningRef<std::sync::MutexGuard<ClientState>, Player> { - let state_lock: std::sync::MutexGuard<ClientState> = self.state.lock().unwrap(); - let state_lock_ref = OwningRef::new(state_lock); - state_lock_ref.map(|state| &state.player) - } } impl<T> From<std::sync::PoisonError<T>> for HandleError { diff --git a/azalea-client/src/movement.rs b/azalea-client/src/movement.rs index f74d48df..7ab4ddc2 100644 --- a/azalea-client/src/movement.rs +++ b/azalea-client/src/movement.rs @@ -5,24 +5,21 @@ use azalea_protocol::packets::game::serverbound_move_player_packet_pos_rot::Serv impl Client { /// Set the client's position to the given coordinates. pub async fn move_to(&mut self, new_pos: EntityPos) -> Result<(), String> { - println!("obtaining lock on state"); - let mut state_lock = self.state.lock().unwrap(); - println!("obtained lock on state"); + let mut dimension_lock = self.dimension.lock().unwrap(); + let dimension = dimension_lock.as_mut().unwrap(); - let world = state_lock.world.as_ref().unwrap(); + let mut player_lock = self.player.lock().unwrap(); - let player = &state_lock.player; - let player_id = if let Some(player) = player.entity(world) { - player.id + let player_id = if let Some(player_lock) = player_lock.entity(dimension) { + player_lock.id } else { return Err("Player entity not found".to_string()); }; - let world = state_lock.world.as_mut().unwrap(); - world.move_entity(player_id, new_pos)?; - drop(state_lock); + dimension.move_entity(player_id, new_pos)?; + drop(dimension_lock); + drop(player_lock); - println!("obtaining lock on conn"); self.conn .lock() .await diff --git a/azalea-client/src/player.rs b/azalea-client/src/player.rs index 7501eede..6c093517 100644 --- a/azalea-client/src/player.rs +++ b/azalea-client/src/player.rs @@ -2,6 +2,11 @@ use azalea_entity::Entity; use azalea_world::Dimension; use uuid::Uuid; +/// Something that has a dimension associated to it. Usually, this is a `Client`. +pub trait DimensionHaver { + fn dimension(&self) -> &Dimension; +} + #[derive(Default, Debug)] pub struct Player { /// The player's uuid. diff --git a/bot/src/main.rs b/bot/src/main.rs index 2976920b..825025d4 100644 --- a/bot/src/main.rs +++ b/bot/src/main.rs @@ -6,17 +6,17 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> { println!("Hello, world!"); // let address = "95.111.249.143:10000"; - let address = "localhost:65399"; + let address = "localhost:56150"; // let response = azalea_client::ping::ping_server(&address.try_into().unwrap()) // .await // .unwrap(); // println!("{}", response.description.to_ansi(None)); let account = Account::offline("bot"); - let mut client = account.join(&address.try_into().unwrap()).await.unwrap(); + let (mut client, mut rx) = account.join(&address.try_into().unwrap()).await.unwrap(); println!("connected"); - while let Some(e) = &client.next().await { + while let Some(e) = &rx.recv().await { match e { // TODO: have a "loaded" or "ready" event that fires when all chunks are loaded Event::Login => {} @@ -40,11 +40,11 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> { // } Event::Chat(msg) => { let new_pos = { - let state_lock = client.state.lock().unwrap(); - let world = state_lock.world.as_ref().unwrap(); - let player = &state_lock.player; + let dimension_lock = client.dimension.lock().unwrap(); + let dimension = dimension_lock.as_ref().unwrap(); + let player = client.player.lock().unwrap(); let entity = player - .entity(&world) + .entity(&dimension) .expect("Player entity is not in world"); entity.pos().add_y(0.5) }; |
