From 536ff0ebb6031699bcf5c26d86bb09d4849703b7 Mon Sep 17 00:00:00 2001 From: copygirl Date: Wed, 19 Nov 2025 13:26:30 +0100 Subject: [PATCH] Add `BlockPos` struct --- common/src/block.rs | 45 ++++++++------ common/src/lib.rs | 2 + common/src/math/block_pos.rs | 110 +++++++++++++++++++++++++++++++++ common/src/math/mod.rs | 3 + common/src/network/protocol.rs | 6 +- 5 files changed, 145 insertions(+), 21 deletions(-) create mode 100644 common/src/math/block_pos.rs create mode 100644 common/src/math/mod.rs diff --git a/common/src/block.rs b/common/src/block.rs index ba2659c..f873eec 100644 --- a/common/src/block.rs +++ b/common/src/block.rs @@ -5,6 +5,8 @@ use bevy::ecs::system::SystemParam; use bevy::platform::collections::HashMap; use serde::{Deserialize, Serialize}; +use crate::math::BlockPos; + pub(crate) fn plugin(app: &mut App) { app.init_resource::(); app.add_observer(block_added); @@ -12,37 +14,38 @@ pub(crate) fn plugin(app: &mut App) { } #[derive(Component, Deserialize, Serialize, PartialEq)] -pub struct Block(pub IVec3); +pub struct Block; #[derive(Resource, Default)] -pub struct BlockMap(HashMap); +pub struct BlockMap(HashMap); #[derive(SystemParam)] pub struct Blocks<'w, 's> { map: Res<'w, BlockMap>, - blocks: Query<'w, 's, &'static Block>, + blocks: Query<'w, 's, (&'static Block, &'static BlockPos)>, commands: Commands<'w, 's>, } impl Blocks<'_, '_> { /// Gets the block [`Entity`] at the given position, if any. - pub fn get(&self, pos: IVec3) -> Option { - self.map.0.get(&pos).copied() + pub fn get(&self, pos: impl Into) -> Option { + self.map.0.get(&pos.into()).copied() } /// Gets an [`EntityCommands`] for the block at the given position, if any. - pub fn entity(&mut self, pos: IVec3) -> Option { + pub fn entity(&mut self, pos: impl Into) -> Option { self.get(pos).map(|block| self.commands.entity(block)) } /// Spawns a block at the given position. - pub fn spawn(&mut self, pos: IVec3) -> EntityCommands { - self.commands.spawn(Block(pos)) + pub fn spawn(&mut self, pos: impl Into) -> EntityCommands { + self.commands.spawn((Block, pos.into())) } /// Tries to spawn a block at the given position, as long as there isn't one already. /// If there already is a block, its [`Entity`] is returned as the `Err` variant. - pub fn try_spawn(&mut self, pos: IVec3) -> Result { + pub fn try_spawn(&mut self, pos: impl Into) -> Result { + let pos = pos.into(); // required because we use it twice if let Some(existing) = self.get(pos) { Err(existing) } else { @@ -51,39 +54,43 @@ impl Blocks<'_, '_> { } /// Despawns the block at the given position, returning if successful. - pub fn despawn(&mut self, pos: IVec3) -> bool { + pub fn despawn(&mut self, pos: impl Into) -> bool { self.entity(pos).map(|mut block| block.despawn()).is_some() } /// Gets the position of the given block [`Entity`], if it exists and is a [`Block`]. - pub fn position(&self, entity: Entity) -> Option { - self.blocks.get(entity).ok().map(|block| block.0) + pub fn position(&self, entity: Entity) -> Option { + self.blocks.get(entity).ok().map(|block| *block.1) } } fn block_added( event: On, - blocks: Query<&Block>, + blocks: Query<(&Block, &BlockPos)>, mut map: ResMut, server: Option>, mut commands: Commands, ) { let block = event.entity; - let pos = blocks.get(block).unwrap().0; - if map.0.insert(pos, block).is_some() { + let (_, pos) = blocks.get(block).expect("Block is missing BlockPos"); + if map.0.insert(*pos, block).is_some() { // TODO: This IS going to happen occasionally. warn!("Duplicate block at pos {pos}"); } let mut entity = commands.entity(block); - entity.insert(Transform::from_translation(pos.as_vec3() + Vec3::ONE / 2.)); + entity.insert(Transform::from_translation(pos.center())); if server.is_some() { entity.insert(Replicate::to_clients(NetworkTarget::All)); } } -fn block_removed(event: On, blocks: Query<&Block>, mut map: ResMut) { +fn block_removed( + event: On, + blocks: Query<(&Block, &BlockPos)>, + mut map: ResMut, +) { let block = event.entity; - let pos = blocks.get(block).unwrap().0; - map.0.remove(&pos); + let (_, pos) = blocks.get(block).expect("Block is missing BlockPos"); + map.0.remove(pos); } diff --git a/common/src/lib.rs b/common/src/lib.rs index 53ab0ac..8c3e7ef 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -1,5 +1,6 @@ pub mod asset_loading; pub mod block; +pub mod math; pub mod network; pub mod player; @@ -7,6 +8,7 @@ pub mod prelude { pub use crate::network; pub use crate::block::*; + pub use crate::math::*; pub use crate::network::protocol::*; pub use crate::player::*; diff --git a/common/src/math/block_pos.rs b/common/src/math/block_pos.rs new file mode 100644 index 0000000..ee37e62 --- /dev/null +++ b/common/src/math/block_pos.rs @@ -0,0 +1,110 @@ +use std::ops::{Add, AddAssign, Sub, SubAssign}; + +use bevy::prelude::*; + +use serde::{Deserialize, Serialize}; + +#[derive(Component, Reflect, Deserialize, Serialize, Clone, Copy, PartialEq, Eq, Hash, Debug)] +pub struct BlockPos { + pub x: i32, + pub y: i32, + pub z: i32, +} + +impl BlockPos { + pub fn new(x: i32, y: i32, z: i32) -> Self { + Self { x, y, z } + } + + pub fn distance_squared(self, other: BlockPos) -> i32 { + (self - other).length_squared() + } + + pub fn distance(self, other: BlockPos) -> f32 { + (self.distance_squared(other) as f32).sqrt() + } + + pub fn center(self) -> Vec3 { + IVec3::from(self).as_vec3() + Vec3::ONE / 2. + } +} + +impl std::fmt::Display for BlockPos { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "[{}, {}, {}]", self.x, self.y, self.z) + } +} + +// conversion + +impl From<(i32, i32, i32)> for BlockPos { + fn from((x, y, z): (i32, i32, i32)) -> Self { + Self::new(x, y, z) + } +} +impl From for (i32, i32, i32) { + fn from(BlockPos { x, y, z }: BlockPos) -> Self { + (x, y, z) + } +} + +impl From<[i32; 3]> for BlockPos { + fn from([x, y, z]: [i32; 3]) -> Self { + Self::new(x, y, z) + } +} +impl From for [i32; 3] { + fn from(BlockPos { x, y, z }: BlockPos) -> Self { + [x, y, z] + } +} + +impl From for BlockPos { + fn from(IVec3 { x, y, z }: IVec3) -> Self { + Self::new(x, y, z) + } +} +impl From for IVec3 { + fn from(BlockPos { x, y, z }: BlockPos) -> Self { + IVec3::new(x, y, z) + } +} + +// addition / subtraction + +impl Add for BlockPos { + type Output = BlockPos; + fn add(self, rhs: IVec3) -> Self::Output { + Self::new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z) + } +} + +impl AddAssign for BlockPos { + fn add_assign(&mut self, rhs: IVec3) { + self.x += rhs.x; + self.y += rhs.y; + self.z += rhs.z; + } +} + +impl Sub for BlockPos { + type Output = BlockPos; + fn sub(self, rhs: IVec3) -> Self::Output { + Self::new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z) + } +} + +impl SubAssign for BlockPos { + fn sub_assign(&mut self, rhs: IVec3) { + self.x -= rhs.x; + self.y -= rhs.y; + self.z -= rhs.z; + } +} + +impl Sub for BlockPos { + type Output = IVec3; + fn sub(self, rhs: BlockPos) -> Self::Output { + IVec3::new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z) + } +} diff --git a/common/src/math/mod.rs b/common/src/math/mod.rs new file mode 100644 index 0000000..983b420 --- /dev/null +++ b/common/src/math/mod.rs @@ -0,0 +1,3 @@ +mod block_pos; + +pub use block_pos::BlockPos; diff --git a/common/src/network/protocol.rs b/common/src/network/protocol.rs index 4a2b136..a9136b2 100644 --- a/common/src/network/protocol.rs +++ b/common/src/network/protocol.rs @@ -6,6 +6,7 @@ use lightyear::input::native::plugin::InputPlugin; use serde::{Deserialize, Serialize}; use crate::block::Block; +use crate::math::BlockPos; use crate::player::{HeadOrientation, Player}; pub(super) fn plugin(app: &mut App) { @@ -16,6 +17,7 @@ pub(super) fn plugin(app: &mut App) { // marker components app.register_component::(); app.register_component::(); + app.register_component::(); app.register_component::() .add_prediction() @@ -38,8 +40,8 @@ pub struct Inputs { pub enum Action { #[default] None, - PlaceBlock(IVec3), - BreakBlock(IVec3), + PlaceBlock(BlockPos), + BreakBlock(BlockPos), } fn interpolate_transform(start: Transform, other: Transform, t: f32) -> Transform {