diff options
| author | mat <git@matdoes.dev> | 2023-05-26 15:18:04 -0500 |
|---|---|---|
| committer | mat <git@matdoes.dev> | 2023-05-26 15:18:04 -0500 |
| commit | 6188230009b49f96b755ade32a28b932e7810196 (patch) | |
| tree | f7b6bc8e25dfda27f3162f9e6bd53721fb3a86cc /azalea-client/src | |
| parent | 9bdace4aab064257dccb39fab4d47fde6dd9a062 (diff) | |
| download | azalea-drasl-6188230009b49f96b755ade32a28b932e7810196.tar.xz | |
add stuff related to chat signing
and also some stuff related to digging because i forgot to do a different branch lol
Diffstat (limited to 'azalea-client/src')
| -rwxr-xr-x | azalea-client/src/account.rs | 40 | ||||
| -rw-r--r-- | azalea-client/src/interact.rs | 70 | ||||
| -rw-r--r-- | azalea-client/src/inventory.rs | 40 | ||||
| -rw-r--r-- | azalea-client/src/lib.rs | 1 | ||||
| -rw-r--r-- | azalea-client/src/mining.rs | 35 |
5 files changed, 162 insertions, 24 deletions
diff --git a/azalea-client/src/account.rs b/azalea-client/src/account.rs index dba2d0f1..12a16493 100755 --- a/azalea-client/src/account.rs +++ b/azalea-client/src/account.rs @@ -3,7 +3,9 @@ use std::sync::Arc; use crate::get_mc_dir; +use azalea_auth::certs::{Certificates, FetchCertificatesError}; use parking_lot::Mutex; +use thiserror::Error; use uuid::Uuid; /// Something that can join Minecraft servers. @@ -38,17 +40,22 @@ pub struct Account { pub uuid: Option<Uuid>, /// The parameters (i.e. email) that were passed for creating this - /// [`Account`]. This is used to for automatic reauthentication when we get + /// [`Account`]. This is used for automatic reauthentication when we get /// "Invalid Session" errors. If you don't need that feature (like in /// offline mode), then you can set this to `AuthOpts::default()`. pub account_opts: AccountOpts, + + /// The certificates used for chat signing. + /// + /// This is set when you call [`Self::request_certs`], but you only + /// need to if the servers you're joining require it. + pub certs: Option<Certificates>, } /// The parameters that were passed for creating the associated [`Account`]. #[derive(Clone, Debug)] pub enum AccountOpts { Offline { username: String }, - // this is an enum so legacy Mojang auth can be added in the future Microsoft { email: String }, } @@ -64,6 +71,7 @@ impl Account { account_opts: AccountOpts::Offline { username: username.to_string(), }, + certs: None, } } @@ -93,6 +101,8 @@ impl Account { account_opts: AccountOpts::Microsoft { email: email.to_string(), }, + // we don't do chat signing by default unless the user asks for it + certs: None, }) } @@ -122,3 +132,29 @@ impl Account { } } } + +#[derive(Error, Debug)] +pub enum RequestCertError { + #[error("Failed to fetch certificates")] + FetchCertificates(#[from] FetchCertificatesError), + #[error("You can't request certificates for an offline account")] + NoAccessToken, +} + +impl Account { + /// Request the certificates used for chat signing and set it in + /// [`Self::certs`]. + pub async fn request_certs(&mut self) -> Result<(), RequestCertError> { + let certs = azalea_auth::certs::fetch_certificates( + &self + .access_token + .as_ref() + .ok_or(RequestCertError::NoAccessToken)? + .lock(), + ) + .await?; + self.certs = Some(certs); + + Ok(()) + } +} diff --git a/azalea-client/src/interact.rs b/azalea-client/src/interact.rs index fa05aa49..afb55bbf 100644 --- a/azalea-client/src/interact.rs +++ b/azalea-client/src/interact.rs @@ -1,4 +1,7 @@ +use azalea_block::BlockState; use azalea_core::{BlockHitResult, BlockPos, Direction, GameMode, Vec3}; +use azalea_inventory::{ItemSlot, ItemSlotData}; +use azalea_nbt::NbtList; use azalea_physics::clip::{BlockShapeType, ClipContext, FluidPickType}; use azalea_protocol::packets::game::{ serverbound_interact_packet::InteractionHand, @@ -20,6 +23,8 @@ use derive_more::{Deref, DerefMut}; use log::warn; use crate::{ + client::PlayerAbilities, + inventory::InventoryComponent, local_player::{handle_send_packet_event, LocalGameMode}, Client, LocalPlayer, }; @@ -193,3 +198,68 @@ pub fn pick( }, ) } + +/// Whether we can't interact with the block, based on your gamemode. If +/// this is false, then we can interact with the block. +/// +/// Passing the inventory, block position, and instance is necessary for the +/// adventure mode check. +pub fn check_is_interaction_restricted( + instance: &Instance, + block_pos: &BlockPos, + game_mode: &GameMode, + inventory: &InventoryComponent, +) -> bool { + match game_mode { + GameMode::Adventure => { + // vanilla checks for abilities.mayBuild here but servers have no + // way of modifying that + + let held_item = inventory.held_item(); + if let ItemSlot::Present(item) = &held_item { + let block = instance.chunks.get_block_state(block_pos); + let Some(block) = block else { + // block isn't loaded so just say that it is restricted + return true; + }; + check_block_can_be_broken_by_item_in_adventure_mode(item, &block) + } else { + true + } + } + GameMode::Spectator => true, + _ => false, + } +} + +/// Check if the item has the `CanDestroy` tag for the block. +pub fn check_block_can_be_broken_by_item_in_adventure_mode( + item: &ItemSlotData, + block: &BlockState, +) -> bool { + // minecraft caches the last checked block but that's kind of an unnecessary + // optimization and makes the code too complicated + + let Some(can_destroy) = item + .nbt + .as_compound() + .and_then(|nbt| nbt.get("tag").and_then(|nbt| nbt.as_compound())) + .and_then(|nbt| nbt.get("CanDestroy").and_then(|nbt| nbt.as_list())) else { + // no CanDestroy tag + return false; + }; + + let NbtList::String(can_destroy) = can_destroy else { + // CanDestroy tag must be a list of strings + return false; + }; + + return false; + + // for block_predicate in can_destroy { + // // TODO + // // defined in BlockPredicateArgument.java + // } + + // true +} diff --git a/azalea-client/src/inventory.rs b/azalea-client/src/inventory.rs index bce629b0..f8c2b2a4 100644 --- a/azalea-client/src/inventory.rs +++ b/azalea-client/src/inventory.rs @@ -78,6 +78,9 @@ pub struct InventoryComponent { pub container_menu: Option<azalea_inventory::Menu>, /// The item that is currently held by the cursor. `Slot::Empty` if nothing /// is currently being held. + /// + /// This is different from [`Self::hotbar_selected_index`], which is the + /// item that's selected in the hotbar. pub carried: ItemSlot, /// An identifier used by the server to track client inventory desyncs. This /// is sent on every container click, and it's only ever updated when the @@ -89,12 +92,13 @@ pub struct InventoryComponent { /// A set of the indexes of the slots that have been right clicked in /// this "quick craft". pub quick_craft_slots: HashSet<u16>, - // minecraft also has these fields, but i don't - // think they're necessary?: - // private final NonNullList<ItemStack> - // remoteSlots; - // private final IntList remoteDataSlots; - // private ItemStack remoteCarried; + + /// The index of the item in the hotbar that's currently being held by the + /// player. This MUST be in the range 0..9 (not including 9). + /// + /// In a vanilla client this is changed by pressing the number keys or using + /// the scroll wheel. + pub selected_hotbar_slot: u8, } impl InventoryComponent { /// Returns a reference to the currently active menu. If a container is open @@ -272,7 +276,6 @@ impl InventoryComponent { }; *menu.slot_mut(slot_index as usize).unwrap() = ItemSlot::Present(new_carried); - // } } } } else { @@ -493,6 +496,13 @@ impl InventoryComponent { self.quick_craft_status = QuickCraftStatusKind::Start; self.quick_craft_slots.clear(); } + + /// Get the item in the player's hotbar that is currently being held. + pub fn held_item(&self) -> ItemSlot { + let inventory = &self.inventory_menu; + let hotbar_items = &inventory.slots()[inventory.hotbar_slots_range()]; + hotbar_items[self.selected_hotbar_slot as usize].clone() + } } fn can_item_quick_replace( @@ -521,21 +531,6 @@ fn can_item_quick_replace( count <= item.kind.max_stack_size() as u16 } -// public static void getQuickCraftSlotCount(Set<Slot> quickCraftSlots, int -// quickCraftType, ItemStack itemStack, int var3) { -// switch (quickCraftType) { -// case 0: -// itemStack.setCount(Mth.floor((float) itemStack.getCount() / (float) -// quickCraftSlots.size())); break; -// case 1: -// itemStack.setCount(1); -// break; -// case 2: -// itemStack.setCount(itemStack.getItem().getMaxStackSize()); -// } - -// itemStack.grow(var3); -// } fn get_quick_craft_slot_count( quick_craft_slots: &HashSet<u16>, quick_craft_kind: &QuickCraftKind, @@ -561,6 +556,7 @@ impl Default for InventoryComponent { quick_craft_status: QuickCraftStatusKind::Start, quick_craft_kind: QuickCraftKind::Middle, quick_craft_slots: HashSet::new(), + selected_hotbar_slot: 0, } } } diff --git a/azalea-client/src/lib.rs b/azalea-client/src/lib.rs index 10a50e92..c47c5e29 100644 --- a/azalea-client/src/lib.rs +++ b/azalea-client/src/lib.rs @@ -21,6 +21,7 @@ mod get_mc_dir; pub mod interact; pub mod inventory; mod local_player; +mod mining; mod movement; pub mod packet_handling; pub mod ping; diff --git a/azalea-client/src/mining.rs b/azalea-client/src/mining.rs new file mode 100644 index 00000000..5af9a20b --- /dev/null +++ b/azalea-client/src/mining.rs @@ -0,0 +1,35 @@ +use azalea_core::BlockPos; +use bevy_app::{App, Plugin}; +use bevy_ecs::prelude::*; + +use crate::Client; + +/// A plugin that allows clients to break blocks in the world. +pub struct MinePlugin; +impl Plugin for MinePlugin { + fn build(&self, app: &mut App) { + app.add_event::<StartMiningBlockEvent>() + .add_system(handle_start_mining_block_event); + } +} + +impl Client { + /// Start mining a block. + pub fn start_mining_block(&self, position: BlockPos) { + self.ecs.lock().send_event(StartMiningBlockEvent { + entity: self.entity, + position, + }); + } +} + +pub struct StartMiningBlockEvent { + pub entity: Entity, + pub position: BlockPos, +} + +fn handle_start_mining_block_event(mut events: EventReader<StartMiningBlockEvent>) { + for event in events.iter() { + // + } +} |
