aboutsummaryrefslogtreecommitdiff
path: root/azalea-client/src/client.rs
diff options
context:
space:
mode:
authormat <27899617+mat-1@users.noreply.github.com>2023-05-03 20:57:27 -0500
committerGitHub <noreply@github.com>2023-05-03 20:57:27 -0500
commit634cb8d72c6608512aedba19e5cd669104bc35ea (patch)
treef8e76ce9eb43403d29cc0cbcf9a4f51522419dc2 /azalea-client/src/client.rs
parent1fb4418f2c9cbd004c64c2f23d2d0352ee12c0e5 (diff)
downloadazalea-drasl-634cb8d72c6608512aedba19e5cd669104bc35ea.tar.xz
Inventory (#48)
* start adding azalea-inventory * design more of how inventories are defined * start working on az-inv-macros * inventory macro works * start adding inventory codegen * update some deps * add inventory codegen * manually write inventory menus * put the inventories in Client * start on containersetcontent * inventory menu should hopefully work * checks in containersetcontent * format a comment * move some variant matches * inventory.rs * inventory stuff * more inventory stuff * inventory/container tracking works * start adding interact function * sequence number * start adding HitResultComponent * implement traverse_blocks * start adding clip * add clip function * update_hit_result_component * start trying to fix * fix * make some stuff simpler * clippy * lever * chest * container handle * fix ambiguity * fix some doc tests * move some container stuff from az-client to azalea * clicking container * start implementing simulate_click * keep working on simulate click * implement more of simulate_click this is really boring * inventory fixes * start implementing shift clicking * fix panic in azalea-chat i hope * shift clicking implemented * more inventory stuff * fix items not showing in containers sometimes * fix test * fix all warnings * remove a println --------- Co-authored-by: mat <git@matdoes.dev>
Diffstat (limited to 'azalea-client/src/client.rs')
-rw-r--r--azalea-client/src/client.rs127
1 files changed, 114 insertions, 13 deletions
diff --git a/azalea-client/src/client.rs b/azalea-client/src/client.rs
index 47cc7235..7a4285e6 100644
--- a/azalea-client/src/client.rs
+++ b/azalea-client/src/client.rs
@@ -2,11 +2,13 @@ use crate::{
chat::ChatPlugin,
disconnect::{DisconnectEvent, DisconnectPlugin},
events::{Event, EventPlugin, LocalPlayerEvents},
+ interact::{CurrentSequenceNumber, InteractPlugin},
+ inventory::{InventoryComponent, InventoryPlugin},
local_player::{
death_event, handle_send_packet_event, update_in_loaded_chunk, GameProfileComponent,
LocalPlayer, PhysicsState, SendPacketEvent,
},
- movement::PlayerMovePlugin,
+ movement::{LastSentLookDirection, PlayerMovePlugin},
packet_handling::{self, PacketHandlerPlugin, PacketReceiver},
player::retroactively_add_game_profile_component,
task_pool::TaskPoolPlugin,
@@ -15,11 +17,13 @@ use crate::{
use azalea_auth::{game_profile::GameProfile, sessionserver::ClientSessionServerError};
use azalea_chat::FormattedText;
+use azalea_core::Vec3;
use azalea_physics::{PhysicsPlugin, PhysicsSet};
use azalea_protocol::{
connect::{Connection, ConnectionError},
packets::{
game::{
+ clientbound_player_abilities_packet::ClientboundPlayerAbilitiesPacket,
serverbound_client_information_packet::ServerboundClientInformationPacket,
ClientboundGamePacket, ServerboundGamePacket,
},
@@ -37,16 +41,17 @@ use azalea_protocol::{
resolver, ServerAddress,
};
use azalea_world::{
- entity::{EntityPlugin, EntityUpdateSet, Local, WorldName},
+ entity::{EntityPlugin, EntityUpdateSet, Local, Position, WorldName},
Instance, InstanceContainer, PartialInstance,
};
-use bevy_app::{App, CoreSchedule, Plugin, PluginGroup, PluginGroupBuilder};
+use bevy_app::{App, CoreSchedule, IntoSystemAppConfig, Plugin, PluginGroup, PluginGroupBuilder};
use bevy_ecs::{
bundle::Bundle,
component::Component,
entity::Entity,
schedule::IntoSystemConfig,
schedule::{LogLevel, ScheduleBuildSettings, ScheduleLabel},
+ system::{ResMut, Resource},
world::World,
};
use bevy_log::LogPlugin;
@@ -56,7 +61,10 @@ use log::{debug, error};
use parking_lot::{Mutex, RwLock};
use std::{collections::HashMap, fmt::Debug, io, net::SocketAddr, sync::Arc, time::Duration};
use thiserror::Error;
-use tokio::{sync::mpsc, time};
+use tokio::{
+ sync::{broadcast, mpsc},
+ time,
+};
use uuid::Uuid;
/// `Client` has the things that a user interacting with the library will want.
@@ -93,11 +101,50 @@ pub struct Client {
}
/// A component that contains some of the "settings" for this client that are
-/// sent to the server, such as render distance.
+/// 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,
+ }
+ }
+}
+
/// A component that contains a map of player UUIDs to their information in the
-/// tab list
+/// tab list.
+///
+/// ```
+/// # 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, Clone, Debug, Deref, DerefMut, Default)]
pub struct TabList(HashMap<Uuid, PlayerInfo>);
@@ -246,8 +293,12 @@ impl Client {
game_profile: GameProfileComponent(game_profile),
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(),
_local: Local,
});
@@ -421,6 +472,11 @@ impl Client {
self.query::<&T>(&mut self.ecs.lock()).clone()
}
+ /// Get a component from this client, or `None` if it doesn't exist.
+ pub fn get_component<T: Component + Clone>(&self) -> Option<T> {
+ self.query::<Option<&T>>(&mut self.ecs.lock()).cloned()
+ }
+
/// Get a reference to our (potentially shared) world.
///
/// This gets the [`Instance`] from our world container. If it's a normal
@@ -430,8 +486,8 @@ impl Client {
pub fn world(&self) -> Arc<RwLock<Instance>> {
let world_name = self.component::<WorldName>();
let ecs = self.ecs.lock();
- let world_container = ecs.resource::<InstanceContainer>();
- world_container.get(&world_name).unwrap()
+ let instance_container = ecs.resource::<InstanceContainer>();
+ instance_container.get(&world_name).unwrap()
}
/// Returns whether we have a received the login packet yet.
@@ -478,6 +534,15 @@ impl Client {
}
}
+impl Client {
+ /// Get the position of this client.
+ ///
+ /// This is a shortcut for `Vec3::from(&bot.component::<Position>())`.
+ pub fn position(&self) -> Vec3 {
+ Vec3::from(&self.component::<Position>())
+ }
+}
+
/// 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 [`Local`].
#[derive(Bundle)]
@@ -487,8 +552,12 @@ pub struct JoinedClientBundle {
pub game_profile: GameProfileComponent,
pub physics_state: PhysicsState,
pub local_player_events: LocalPlayerEvents,
+ pub inventory: InventoryComponent,
pub client_information: ClientInformation,
pub tab_list: TabList,
+ pub current_sequence_number: CurrentSequenceNumber,
+ pub last_sent_direction: LastSentLookDirection,
+ pub abilities: PlayerAbilities,
pub _local: Local,
}
@@ -498,11 +567,7 @@ impl Plugin for AzaleaPlugin {
// Minecraft ticks happen every 50ms
app.insert_resource(FixedTime::new(Duration::from_millis(50)));
- app.add_system(
- update_in_loaded_chunk
- .after(PhysicsSet)
- .after(handle_send_packet_event),
- );
+ app.add_system(update_in_loaded_chunk.after(PhysicsSet));
// fire the Death event when the player dies.
app.add_system(death_event);
@@ -599,6 +664,39 @@ pub async fn tick_run_schedule_loop(run_schedule_sender: mpsc::UnboundedSender<(
}
}
+/// A resource that contains a [`broadcast::Sender`] that will be sent every
+/// Minecraft tick.
+///
+/// This is useful for running code every schedule from async user code.
+///
+/// ```
+/// use azalea_client::TickBroadcast;
+/// # async fn example(client: azalea_client::Client) {
+/// let mut receiver = {
+/// let ecs = client.ecs.lock();
+/// let tick_broadcast = ecs.resource::<TickBroadcast>();
+/// tick_broadcast.subscribe()
+/// };
+/// while receiver.recv().await.is_ok() {
+/// // do something
+/// }
+/// # }
+/// ```
+#[derive(Resource, Deref)]
+pub struct TickBroadcast(broadcast::Sender<()>);
+
+fn send_tick_broadcast(tick_broadcast: ResMut<TickBroadcast>) {
+ let _ = tick_broadcast.0.send(());
+}
+/// A plugin that makes the [`RanScheduleBroadcast`] resource available.
+pub struct TickBroadcastPlugin;
+impl Plugin for TickBroadcastPlugin {
+ fn build(&self, app: &mut App) {
+ app.insert_resource(TickBroadcast(broadcast::channel(1).0))
+ .add_system(send_tick_broadcast.in_schedule(CoreSchedule::FixedUpdate));
+ }
+}
+
/// This plugin group will add all the default plugins necessary for Azalea to
/// work.
pub struct DefaultPlugins;
@@ -614,8 +712,11 @@ impl PluginGroup for DefaultPlugins {
.add(PhysicsPlugin)
.add(EventPlugin)
.add(TaskPoolPlugin::default())
+ .add(InventoryPlugin)
.add(ChatPlugin)
.add(DisconnectPlugin)
.add(PlayerMovePlugin)
+ .add(InteractPlugin)
+ .add(TickBroadcastPlugin)
}
}