Add `BlockPos` struct

main
copygirl 2 weeks ago
parent 7c235d6d61
commit 536ff0ebb6
  1. 45
      common/src/block.rs
  2. 2
      common/src/lib.rs
  3. 110
      common/src/math/block_pos.rs
  4. 3
      common/src/math/mod.rs
  5. 6
      common/src/network/protocol.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::<BlockMap>();
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<IVec3, Entity>);
pub struct BlockMap(HashMap<BlockPos, Entity>);
#[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<Entity> {
self.map.0.get(&pos).copied()
pub fn get(&self, pos: impl Into<BlockPos>) -> Option<Entity> {
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<EntityCommands> {
pub fn entity(&mut self, pos: impl Into<BlockPos>) -> Option<EntityCommands> {
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<BlockPos>) -> 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<EntityCommands, Entity> {
pub fn try_spawn(&mut self, pos: impl Into<BlockPos>) -> Result<EntityCommands, Entity> {
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<BlockPos>) -> 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<IVec3> {
self.blocks.get(entity).ok().map(|block| block.0)
pub fn position(&self, entity: Entity) -> Option<BlockPos> {
self.blocks.get(entity).ok().map(|block| *block.1)
}
}
fn block_added(
event: On<Add, Block>,
blocks: Query<&Block>,
blocks: Query<(&Block, &BlockPos)>,
mut map: ResMut<BlockMap>,
server: Option<Single<&Server>>,
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<Remove, Block>, blocks: Query<&Block>, mut map: ResMut<BlockMap>) {
fn block_removed(
event: On<Remove, Block>,
blocks: Query<(&Block, &BlockPos)>,
mut map: ResMut<BlockMap>,
) {
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);
}

@ -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::*;

@ -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<BlockPos> 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<BlockPos> for [i32; 3] {
fn from(BlockPos { x, y, z }: BlockPos) -> Self {
[x, y, z]
}
}
impl From<IVec3> for BlockPos {
fn from(IVec3 { x, y, z }: IVec3) -> Self {
Self::new(x, y, z)
}
}
impl From<BlockPos> for IVec3 {
fn from(BlockPos { x, y, z }: BlockPos) -> Self {
IVec3::new(x, y, z)
}
}
// addition / subtraction
impl Add<IVec3> 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<IVec3> for BlockPos {
fn add_assign(&mut self, rhs: IVec3) {
self.x += rhs.x;
self.y += rhs.y;
self.z += rhs.z;
}
}
impl Sub<IVec3> 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<IVec3> for BlockPos {
fn sub_assign(&mut self, rhs: IVec3) {
self.x -= rhs.x;
self.y -= rhs.y;
self.z -= rhs.z;
}
}
impl Sub<BlockPos> 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)
}
}

@ -0,0 +1,3 @@
mod block_pos;
pub use block_pos::BlockPos;

@ -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::<Player>();
app.register_component::<Block>();
app.register_component::<BlockPos>();
app.register_component::<Transform>()
.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 {

Loading…
Cancel
Save