aboutsummaryrefslogtreecommitdiff
path: root/azalea-client/src
diff options
context:
space:
mode:
authormat <git@matdoes.dev>2023-05-26 15:18:04 -0500
committermat <git@matdoes.dev>2023-05-26 15:18:04 -0500
commit6188230009b49f96b755ade32a28b932e7810196 (patch)
treef7b6bc8e25dfda27f3162f9e6bd53721fb3a86cc /azalea-client/src
parent9bdace4aab064257dccb39fab4d47fde6dd9a062 (diff)
downloadazalea-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-xazalea-client/src/account.rs40
-rw-r--r--azalea-client/src/interact.rs70
-rw-r--r--azalea-client/src/inventory.rs40
-rw-r--r--azalea-client/src/lib.rs1
-rw-r--r--azalea-client/src/mining.rs35
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() {
+ //
+ }
+}