diff options
| author | mat <27899617+mat-1@users.noreply.github.com> | 2023-09-21 11:16:29 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-09-21 11:16:29 -0500 |
| commit | 7b3e2e4bf793466a351510c7fbbd08234e93bb0e (patch) | |
| tree | 7177a919de9982d9e3c7f36a76d2025696f465b6 /azalea-client/src/client.rs | |
| parent | 83cce236145cdab1872a472a70943b669a880965 (diff) | |
| download | azalea-drasl-7b3e2e4bf793466a351510c7fbbd08234e93bb0e.tar.xz | |
1.20.2 (#99)
* add configuration state
* start updating to 23w31a
* implement a bit more of 23w31a
* chunk batching
* start adding configuration state
* ioasfhjgsd
* almost works
* configuration state mostly implemented
* handle other packets in configuration state and fix keepalive
* cleanup, fix warnings
* 23w32a
* fix some doctests
* 23w33a
* 23w35a
* 1.20.2-pre2
* fix system conflicts
* 1.20.2-pre4
* make tests compile
* tests pass
* 1.20.2-rc2
* 1.20.2
* Revert "1.20.2"
This reverts commit dd152fd265332ead333c919e585ded6d609d7468.
* didn't mean to commit that code
---------
Co-authored-by: mat <git@matdoes.dev>
Diffstat (limited to 'azalea-client/src/client.rs')
| -rw-r--r-- | azalea-client/src/client.rs | 269 |
1 files changed, 121 insertions, 148 deletions
diff --git a/azalea-client/src/client.rs b/azalea-client/src/client.rs index 8424bf39..cd191e0f 100644 --- a/azalea-client/src/client.rs +++ b/azalea-client/src/client.rs @@ -1,26 +1,29 @@ use crate::{ attack::{self, AttackPlugin}, chat::ChatPlugin, + chunk_batching::{ChunkBatchInfo, ChunkBatchingPlugin}, disconnect::{DisconnectEvent, DisconnectPlugin}, events::{Event, EventPlugin, LocalPlayerEvents}, interact::{CurrentSequenceNumber, InteractPlugin}, inventory::{InventoryComponent, InventoryPlugin}, local_player::{ - death_event, handle_send_packet_event, GameProfileComponent, Hunger, LocalPlayer, - SendPacketEvent, + death_event, handle_send_packet_event, GameProfileComponent, Hunger, InstanceHolder, + PermissionLevel, PlayerAbilities, SendPacketEvent, TabList, }, mining::{self, MinePlugin}, movement::{LastSentLookDirection, PhysicsState, PlayerMovePlugin}, - packet_handling::{self, PacketHandlerPlugin, PacketReceiver}, + packet_handling::PacketHandlerPlugin, player::retroactively_add_game_profile_component, + raw_connection::RawConnection, respawn::RespawnPlugin, task_pool::TaskPoolPlugin, - Account, PlayerInfo, + Account, PlayerInfo, ReceivedRegistries, }; use azalea_auth::{game_profile::GameProfile, sessionserver::ClientSessionServerError}; +use azalea_buf::McBufWritable; use azalea_chat::FormattedText; -use azalea_core::Vec3; +use azalea_core::{ResourceLocation, Vec3}; use azalea_entity::{ indexing::{EntityIdIndex, Loaded}, metadata::Health, @@ -30,19 +33,21 @@ use azalea_physics::PhysicsPlugin; use azalea_protocol::{ connect::{Connection, ConnectionError}, packets::{ - game::{ - clientbound_player_abilities_packet::ClientboundPlayerAbilitiesPacket, - serverbound_client_information_packet::ServerboundClientInformationPacket, - ClientboundGamePacket, ServerboundGamePacket, + configuration::{ + serverbound_client_information_packet::ClientInformation, + ClientboundConfigurationPacket, ServerboundConfigurationPacket, }, - handshake::{ + game::ServerboundGamePacket, + handshaking::{ client_intention_packet::ClientIntentionPacket, ClientboundHandshakePacket, ServerboundHandshakePacket, }, login::{ - serverbound_custom_query_packet::ServerboundCustomQueryPacket, + serverbound_custom_query_answer_packet::ServerboundCustomQueryAnswerPacket, serverbound_hello_packet::ServerboundHelloPacket, - serverbound_key_packet::ServerboundKeyPacket, ClientboundLoginPacket, + serverbound_key_packet::ServerboundKeyPacket, + serverbound_login_acknowledged_packet::ServerboundLoginAcknowledgedPacket, + ClientboundLoginPacket, }, ConnectionProtocol, PROTOCOL_VERSION, }, @@ -59,10 +64,12 @@ use bevy_ecs::{ world::World, }; use bevy_time::{prelude::FixedTime, TimePlugin}; -use derive_more::{Deref, DerefMut}; +use derive_more::Deref; use log::{debug, error}; use parking_lot::{Mutex, RwLock}; -use std::{collections::HashMap, fmt::Debug, io, net::SocketAddr, sync::Arc, time::Duration}; +use std::{ + collections::HashMap, fmt::Debug, io, net::SocketAddr, ops::Deref, sync::Arc, time::Duration, +}; use thiserror::Error; use tokio::{ sync::{broadcast, mpsc}, @@ -71,7 +78,6 @@ use tokio::{ use uuid::Uuid; /// `Client` has the things that a user interacting with the library will want. -/// Things that a player in the world will want to know are in [`LocalPlayer`]. /// /// To make a new client, use either [`azalea::ClientBuilder`] or /// [`Client::join`]. @@ -105,62 +111,6 @@ pub struct Client { pub run_schedule_sender: mpsc::UnboundedSender<()>, } -/// A component that contains some of the "settings" for this client that are -/// sent to the server, such as render distance. This is only present on local -/// players. -pub type ClientInformation = ServerboundClientInformationPacket; - -/// A component that contains the abilities the player has, like flying -/// or instantly breaking blocks. This is only present on local players. -#[derive(Clone, Debug, Component, Default)] -pub struct PlayerAbilities { - pub invulnerable: bool, - pub flying: bool, - pub can_fly: bool, - /// Whether the player can instantly break blocks and can duplicate blocks - /// in their inventory. - pub instant_break: bool, - - pub flying_speed: f32, - /// Used for the fov - pub walking_speed: f32, -} -impl From<ClientboundPlayerAbilitiesPacket> for PlayerAbilities { - fn from(packet: ClientboundPlayerAbilitiesPacket) -> Self { - Self { - invulnerable: packet.flags.invulnerable, - flying: packet.flags.flying, - can_fly: packet.flags.can_fly, - instant_break: packet.flags.instant_break, - flying_speed: packet.flying_speed, - walking_speed: packet.walking_speed, - } - } -} - -/// Level must be 0..=4 -#[derive(Component, Clone, Default, Deref, DerefMut)] -pub struct PermissionLevel(pub u8); - -/// A component and resource that contains a map of player UUIDs to their -/// information in the tab list. -/// -/// This is a component on local players in case you want to get the tab list -/// that a certain client is seeing, and it's also a resource in case you know -/// that the server gives the same tab list to every player. -/// -/// ``` -/// # use azalea_client::TabList; -/// # fn example(client: &azalea_client::Client) { -/// let tab_list = client.component::<TabList>(); -/// println!("Online players:"); -/// for (uuid, player_info) in tab_list.iter() { -/// println!("- {} ({}ms)", player_info.profile.name, player_info.latency); -/// } -/// # } -#[derive(Component, Resource, Clone, Debug, Deref, DerefMut, Default)] -pub struct TabList(HashMap<Uuid, PlayerInfo>); - /// An error that happened while joining the server. #[derive(Error, Debug)] pub enum JoinError { @@ -261,71 +211,55 @@ impl Client { let entity = ecs_lock.lock().spawn(account.to_owned()).id(); let conn = Connection::new(resolved_address).await?; - let (conn, game_profile) = Self::handshake(conn, account, address).await?; + let (mut conn, game_profile) = Self::handshake(conn, account, address).await?; + + { + // quickly send the brand here + let mut brand_data = Vec::new(); + // they don't have to know :) + "vanilla".write_into(&mut brand_data).unwrap(); + conn.write( + azalea_protocol::packets::configuration::serverbound_custom_payload_packet::ServerboundCustomPayloadPacket { + identifier: ResourceLocation::new("brand"), + data: brand_data.into(), + } + .get(), + ).await?; + } + let (read_conn, write_conn) = conn.into_split(); + let (read_conn, write_conn) = (read_conn.raw, write_conn.raw); // we did the handshake, so now we're connected to the server let (tx, rx) = mpsc::unbounded_channel(); - let (packet_writer_sender, packet_writer_receiver) = mpsc::unbounded_channel(); - - // start receiving packets - let packet_receiver = packet_handling::PacketReceiver { - packets: Arc::new(Mutex::new(Vec::new())), - run_schedule_sender: run_schedule_sender.clone(), - }; - - let read_packets_task = tokio::spawn(packet_receiver.clone().read_task(read_conn)); - let write_packets_task = tokio::spawn( - packet_receiver - .clone() - .write_task(write_conn, packet_writer_receiver), - ); - - let local_player = crate::local_player::LocalPlayer::new( - entity, - packet_writer_sender, - // default to an empty world, it'll be set correctly later when we - // get the login packet - Arc::new(RwLock::new(Instance::default())), - read_packets_task, - write_packets_task, - ); - - ecs_lock - .lock() - .entity_mut(entity) - .insert(JoinedClientBundle { - local_player, - packet_receiver, - game_profile: GameProfileComponent(game_profile.clone()), - physics_state: PhysicsState::default(), - local_player_events: LocalPlayerEvents(tx), - inventory: InventoryComponent::default(), - client_information: ClientInformation::default(), - tab_list: TabList::default(), - current_sequence_number: CurrentSequenceNumber::default(), - last_sent_direction: LastSentLookDirection::default(), - abilities: PlayerAbilities::default(), - permission_level: PermissionLevel::default(), - hunger: Hunger::default(), - - entity_id_index: EntityIdIndex::default(), - - mining: mining::MineBundle::default(), - attack: attack::AttackBundle::default(), - - _local: LocalEntity, - _loaded: Loaded, - }); + let mut ecs = ecs_lock.lock(); + // we got the ConfigurationConnection, so the client is now connected :) let client = Client::new( - game_profile, + game_profile.clone(), entity, ecs_lock.clone(), run_schedule_sender.clone(), ); + + ecs.entity_mut(entity).insert(( + // these stay when we switch to the game state + LocalPlayerBundle { + raw_connection: RawConnection::new( + run_schedule_sender, + ConnectionProtocol::Configuration, + read_conn, + write_conn, + ), + received_registries: ReceivedRegistries::default(), + local_player_events: LocalPlayerEvents(tx), + game_profile: GameProfileComponent(game_profile), + }, + InConfigurationState, + )); + Ok((client, rx)) } @@ -340,7 +274,7 @@ impl Client { address: &ServerAddress, ) -> Result< ( - Connection<ClientboundGamePacket, ServerboundGamePacket>, + Connection<ClientboundConfigurationPacket, ServerboundConfigurationPacket>, GameProfile, ), JoinError, @@ -362,7 +296,9 @@ impl Client { conn.write( ServerboundHelloPacket { name: account.username.clone(), - profile_id: account.uuid, + // TODO: pretty sure this should generate an offline-mode uuid instead of just + // Uuid::default() + profile_id: account.uuid.unwrap_or_default(), } .get(), ) @@ -428,8 +364,13 @@ impl Client { conn.set_compression_threshold(p.compression_threshold); } ClientboundLoginPacket::GameProfile(p) => { - debug!("Got profile {:?}", p.game_profile); - break (conn.game(), p.game_profile); + debug!( + "Got profile {:?}. handshake is finished and we're now switching to the configuration state", + p.game_profile + ); + conn.write(ServerboundLoginAcknowledgedPacket {}.get()) + .await?; + break (conn.configuration(), p.game_profile); } ClientboundLoginPacket::LoginDisconnect(p) => { debug!("Got disconnect {:?}", p); @@ -438,7 +379,7 @@ impl Client { ClientboundLoginPacket::CustomQuery(p) => { debug!("Got custom query {:?}", p); conn.write( - ServerboundCustomQueryPacket { + ServerboundCustomQueryAnswerPacket { transaction_id: p.transaction_id, data: None, } @@ -453,9 +394,12 @@ impl Client { } /// Write a packet directly to the server. - pub fn write_packet(&self, packet: ServerboundGamePacket) { - self.local_player_mut(&mut self.ecs.lock()) - .write_packet(packet); + pub fn write_packet( + &self, + packet: ServerboundGamePacket, + ) -> Result<(), crate::raw_connection::WritePacketError> { + self.raw_connection_mut(&mut self.ecs.lock()) + .write_packet(packet) } /// Disconnect this client from the server by ending all tasks. @@ -468,14 +412,24 @@ impl Client { }); } - pub fn local_player<'a>(&'a self, ecs: &'a mut World) -> &'a LocalPlayer { - self.query::<&LocalPlayer>(ecs) + pub fn local_player<'a>(&'a self, ecs: &'a mut World) -> &'a InstanceHolder { + self.query::<&InstanceHolder>(ecs) } pub fn local_player_mut<'a>( &'a self, ecs: &'a mut World, - ) -> bevy_ecs::world::Mut<'a, LocalPlayer> { - self.query::<&mut LocalPlayer>(ecs) + ) -> bevy_ecs::world::Mut<'a, InstanceHolder> { + self.query::<&mut InstanceHolder>(ecs) + } + + pub fn raw_connection<'a>(&'a self, ecs: &'a mut World) -> &'a RawConnection { + self.query::<&RawConnection>(ecs) + } + pub fn raw_connection_mut<'a>( + &'a self, + ecs: &'a mut World, + ) -> bevy_ecs::world::Mut<'a, RawConnection> { + self.query::<&mut RawConnection>(ecs) } /// Get a component from this client. This will clone the component and @@ -538,8 +492,8 @@ impl Client { /// ``` pub async fn set_client_information( &self, - client_information: ServerboundClientInformationPacket, - ) -> Result<(), std::io::Error> { + client_information: ClientInformation, + ) -> Result<(), crate::raw_connection::WritePacketError> { { let mut ecs = self.ecs.lock(); let mut client_information_mut = self.query::<&mut ClientInformation>(&mut ecs); @@ -551,7 +505,7 @@ impl Client { "Sending client information (already logged in): {:?}", client_information ); - self.write_packet(client_information.get()); + self.write_packet(azalea_protocol::packets::game::serverbound_client_information_packet::ServerboundClientInformationPacket { information: client_information.clone() }.get())?; } Ok(()) @@ -606,21 +560,33 @@ impl Client { /// Get a map of player UUIDs to their information in the tab list. /// - /// This is a shortcut for `bot.component::<TabList>().0`. + /// This is a shortcut for `*bot.component::<TabList>()`. pub fn tab_list(&self) -> HashMap<Uuid, PlayerInfo> { - self.component::<TabList>().0 + self.component::<TabList>().deref().clone() } } -/// A bundle for the components that are present on a local player that received -/// a login packet. If you want to filter for this, just use [`LocalEntity`]. +/// The bundle of components that's shared when we're either in the +/// `configuration` or `game` state. +/// +/// For the components that are only present in the `game` state, see +/// [`JoinedClientBundle`] and for the ones in the `configuration` state, see +/// [`ConfigurationClientBundle`]. #[derive(Bundle)] -pub struct JoinedClientBundle { - pub local_player: LocalPlayer, - pub packet_receiver: PacketReceiver, +pub struct LocalPlayerBundle { + pub raw_connection: RawConnection, + pub received_registries: ReceivedRegistries, + pub local_player_events: LocalPlayerEvents, pub game_profile: GameProfileComponent, +} + +/// A bundle for the components that are present on a local player that is +/// currently in the `game` protocol state. If you want to filter for this, just +/// use [`LocalEntity`]. +#[derive(Bundle)] +pub struct JoinedClientBundle { + pub instance_holder: InstanceHolder, pub physics_state: PhysicsState, - pub local_player_events: LocalPlayerEvents, pub inventory: InventoryComponent, pub client_information: ClientInformation, pub tab_list: TabList, @@ -628,6 +594,7 @@ pub struct JoinedClientBundle { pub last_sent_direction: LastSentLookDirection, pub abilities: PlayerAbilities, pub permission_level: PermissionLevel, + pub chunk_batch_info: ChunkBatchInfo, pub hunger: Hunger, pub entity_id_index: EntityIdIndex, @@ -635,10 +602,15 @@ pub struct JoinedClientBundle { pub mining: mining::MineBundle, pub attack: attack::AttackBundle, - pub _local: LocalEntity, + pub _local_entity: LocalEntity, pub _loaded: Loaded, } +/// A marker component for local players that are currently in the +/// `configuration` state. +#[derive(Component)] +pub struct InConfigurationState; + pub struct AzaleaPlugin; impl Plugin for AzaleaPlugin { fn build(&self, app: &mut App) { @@ -790,6 +762,7 @@ impl PluginGroup for DefaultPlugins { .add(RespawnPlugin) .add(MinePlugin) .add(AttackPlugin) + .add(ChunkBatchingPlugin) .add(TickBroadcastPlugin); #[cfg(feature = "log")] { |
