diff --git a/pumpkin-entity/src/lib.rs b/pumpkin-entity/src/lib.rs index 5efdb3e1e..db2b0ebad 100644 --- a/pumpkin-entity/src/lib.rs +++ b/pumpkin-entity/src/lib.rs @@ -16,10 +16,12 @@ pub struct Entity { pub yaw: f32, pub head_yaw: f32, pub pitch: f32, + // TODO: Change this in diffrent poses + pub standing_eye_height: f32, } impl Entity { - pub fn new(entity_id: EntityId, entity_type: EntityType) -> Self { + pub fn new(entity_id: EntityId, entity_type: EntityType, standing_eye_height: f32) -> Self { Self { entity_id, entity_type, @@ -32,6 +34,7 @@ impl Entity { yaw: 0.0, head_yaw: 0.0, pitch: 0.0, + standing_eye_height, } } } diff --git a/pumpkin-protocol/src/client/play/c_block_update.rs b/pumpkin-protocol/src/client/play/c_block_update.rs index c6e9aae82..d2cd09dd2 100644 --- a/pumpkin-protocol/src/client/play/c_block_update.rs +++ b/pumpkin-protocol/src/client/play/c_block_update.rs @@ -5,13 +5,13 @@ use crate::{position::WorldPosition, VarInt}; #[derive(Serialize)] #[packet(0x09)] -pub struct CBlockUpdate { - location: WorldPosition, +pub struct CBlockUpdate<'a> { + location: &'a WorldPosition, block_id: VarInt, } -impl CBlockUpdate { - pub fn new(location: WorldPosition, block_id: VarInt) -> Self { +impl<'a> CBlockUpdate<'a> { + pub fn new(location: &'a WorldPosition, block_id: VarInt) -> Self { Self { location, block_id } } } diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/client/player_packet.rs index b242f2b6b..fd9ef9c94 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/client/player_packet.rs @@ -329,6 +329,10 @@ impl Player { pub fn handle_player_action(&mut self, server: &mut Server, player_action: SPlayerAction) { match Status::from_i32(player_action.status.0).unwrap() { Status::StartedDigging => { + if !self.can_interact_with_block_at(&player_action.location, 1.0) { + // TODO: maybe log? + return; + } // TODO: do validation // TODO: Config if self.gamemode == GameMode::Creative { @@ -337,20 +341,28 @@ impl Player { // TODO: currently this is always dirt replace it server.broadcast_packet(self, &CWorldEvent::new(2001, &location, 11, false)); // AIR - server.broadcast_packet(self, &CBlockUpdate::new(location, 0.into())); + server.broadcast_packet(self, &CBlockUpdate::new(&location, 0.into())); } } Status::CancelledDigging => { + if !self.can_interact_with_block_at(&player_action.location, 1.0) { + // TODO: maybe log? + return; + } self.current_block_destroy_stage = 0; } Status::FinishedDigging => { // TODO: do validation let location = player_action.location; + if !self.can_interact_with_block_at(&location, 1.0) { + // TODO: maybe log? + return; + } // Block break & block break sound // TODO: currently this is always dirt replace it server.broadcast_packet(self, &CWorldEvent::new(2001, &location, 11, false)); // AIR - server.broadcast_packet(self, &CBlockUpdate::new(location, 0.into())); + server.broadcast_packet(self, &CBlockUpdate::new(&location, 0.into())); // TODO: Send this every tick self.client .send_packet(&CAcknowledgeBlockChange::new(player_action.sequence)); @@ -376,12 +388,14 @@ impl Player { } pub fn handle_use_item_on(&mut self, server: &mut Server, use_item_on: SUseItemOn) { - self.client - .send_packet(&CAcknowledgeBlockChange::new(use_item_on.sequence)); - let location = use_item_on.location; + + if !self.can_interact_with_block_at(&location, 1.0) { + // TODO: maybe log? + return; + } + let face = BlockFace::from_i32(use_item_on.face.0).unwrap(); - let location = WorldPosition(location.0 + face.to_offset()); if let Some(item) = self.inventory.held_item() { let minecraft_id = global_registry::find_minecraft_id(global_registry::ITEM_REGISTRY, item.item_id) @@ -394,10 +408,19 @@ impl Player { { server.broadcast_packet( self, - &CBlockUpdate::new(location, (block_state_id as i32).into()), + &CBlockUpdate::new(&location, (block_state_id as i32).into()), + ); + server.broadcast_packet( + self, + &CBlockUpdate::new( + &WorldPosition(location.0 + face.to_offset()), + (block_state_id as i32).into(), + ), ); } } + self.client + .send_packet(&CAcknowledgeBlockChange::new(use_item_on.sequence)); } pub fn handle_set_held_item(&mut self, _server: &mut Server, held: SSetHeldItem) { diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index d7d1b20bf..aa0d28da9 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -11,6 +11,7 @@ use pumpkin_protocol::{ CGameEvent, CPlayDisconnect, CPlayerInfoUpdate, CSyncPlayerPosition, CSystemChatMessage, PlayerAction, }, + position::WorldPosition, server::play::{ SChatCommand, SChatMessage, SClientInformationPlay, SConfirmTeleport, SInteract, SPlayPingRequest, SPlayerAction, SPlayerCommand, SPlayerPosition, SPlayerPositionRotation, @@ -21,7 +22,7 @@ use pumpkin_protocol::{ use pumpkin_world::vector3::Vector3; use serde::{Deserialize, Serialize}; -use crate::{client::Client, server::Server}; +use crate::{client::Client, server::Server, util::boundingbox::BoundingBox}; pub struct Player { pub client: Client, @@ -55,7 +56,7 @@ impl Player { pub fn new(client: Client, entity_id: EntityId, gamemode: GameMode) -> Self { Self { client, - entity: Entity::new(entity_id, EntityType::Player), + entity: Entity::new(entity_id, EntityType::Player, 1.62), on_ground: false, awaiting_teleport: None, sneaking: false, @@ -125,6 +126,24 @@ impl Player { )); } + pub fn block_interaction_range(&self) -> f64 { + if self.gamemode == GameMode::Creative { + 5.0 + } else { + 4.5 + } + } + + pub fn can_interact_with_block_at(&self, pos: &WorldPosition, additional_range: f64) -> bool { + let d = self.block_interaction_range() + additional_range; + let box_pos = BoundingBox::from_block(pos); + box_pos.squared_magnitude(Vector3 { + x: self.entity.x, + y: self.entity.y + self.entity.standing_eye_height as f64, + z: self.entity.z, + }) < d * d + } + /// Kicks the Client with a reason depending on the connection state pub fn kick(&mut self, reason: TextComponent) { assert!(self.client.connection_state == ConnectionState::Play); diff --git a/pumpkin/src/util/boundingbox.rs b/pumpkin/src/util/boundingbox.rs new file mode 100644 index 000000000..cc32ebbfa --- /dev/null +++ b/pumpkin/src/util/boundingbox.rs @@ -0,0 +1,43 @@ +use pumpkin_protocol::position::WorldPosition; +use pumpkin_world::vector3::Vector3; + +pub struct BoundingBox { + pub min_x: f64, + pub min_y: f64, + pub min_z: f64, + pub max_x: f64, + pub max_y: f64, + pub max_z: f64, +} + +impl BoundingBox { + pub fn new(min_x: f64, min_y: f64, min_z: f64, max_x: f64, max_y: f64, max_z: f64) -> Self { + Self { + min_x, + min_y, + min_z, + max_x, + max_y, + max_z, + } + } + + pub fn from_block(position: &WorldPosition) -> Self { + let position = position.0; + Self { + min_x: position.x as f64, + min_y: position.y as f64, + min_z: position.z as f64, + max_x: (position.x as f64) + 1.0, + max_y: (position.y as f64) + 1.0, + max_z: (position.z as f64) + 1.0, + } + } + + pub fn squared_magnitude(&self, pos: Vector3) -> f64 { + let d = f64::max(f64::max(self.min_x - pos.x, pos.x - self.max_x), 0.0); + let e = f64::max(f64::max(self.min_y - pos.y, pos.y - self.max_y), 0.0); + let f = f64::max(f64::max(self.min_z - pos.z, pos.z - self.max_z), 0.0); + super::math::squared_magnitude(d, e, f) + } +} diff --git a/pumpkin/src/util/math.rs b/pumpkin/src/util/math.rs index 2d90277d4..ec4ff43d8 100644 --- a/pumpkin/src/util/math.rs +++ b/pumpkin/src/util/math.rs @@ -10,3 +10,7 @@ pub fn wrap_degrees(var: f32) -> f32 { var1 } + +pub fn squared_magnitude(a: f64, b: f64, c: f64) -> f64 { + a * a + b * b + c * c +} diff --git a/pumpkin/src/util/mod.rs b/pumpkin/src/util/mod.rs index b8135c3c3..59887f233 100644 --- a/pumpkin/src/util/mod.rs +++ b/pumpkin/src/util/mod.rs @@ -1 +1,2 @@ +pub mod boundingbox; pub mod math;