1
Fork 0
avarus/server/src/blocks.rs

187 lines
5.3 KiB
Rust

use std::sync::Arc;
use nalgebra::Vector3;
use rstar::{PointDistance, RTree, RTreeObject, AABB};
use serde::{Deserialize, Serialize};
use tokio::sync::{RwLock, OwnedRwLockReadGuard};
use crate::{turtle::TurtleCommand, paths::{self, TRANSPARENT}};
pub type WorldReadLock = OwnedRwLockReadGuard<RTree<Block>>;
#[derive(Clone)]
pub struct World {
state: Arc<RwLock<RTree<Block>>>, // interior mutability to get around the
// questionable architecture of this project
}
impl World {
pub fn new() -> Self { Self { state: Arc::new(RwLock::new(RTree::new())) } }
pub fn from_tree(tree: RTree<Block>) -> Self { Self { state: Arc::new(RwLock::new(tree)) } }
pub async fn to_tree(self) -> RTree<Block> { self.state.write().await.to_owned() }
pub async fn tree(&self) -> RTree<Block> { self.state.read().await.clone() }
pub async fn get(&self, block: Vec3) -> Option<Block> {
self.state.read().await.locate_at_point(&block.into()).map(|b| b.to_owned())
}
pub async fn set(&self, block: Block) {
self.state.write().await.remove_at_point(&block.pos.into());
self.state.write().await.insert(block);
}
/// Returns true if a known non-traversable block exists at the point
pub async fn occupied(&self, block: Vec3) -> bool {
self.state.read().await.locate_at_point(&block.into()).is_some_and(|b| !TRANSPARENT.contains(&b.name.as_str()))
}
/// Returns true if a "garbage" block exists at the given point which you are free to destroy
pub async fn garbage(&self, block: Vec3) -> bool {
self.state.read().await.locate_at_point(&block.into()).is_some_and(|b| paths::difficulty(&b.name).is_some())
}
pub async fn lock(self) -> WorldReadLock {
self.state.read_owned().await
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Block {
pub name: String,
pub pos: Vec3,
}
impl RTreeObject for Block {
type Envelope = AABB<[i32; 3]>;
fn envelope(&self) -> Self::Envelope {
AABB::from_point(self.pos.into())
}
}
impl PointDistance for Block {
fn distance_2(&self, point: &[i32; 3]) -> i32 {
(self.pos - Vec3::from(*point)).abs().sum()
}
}
pub type Vec3 = Vector3<i32>;
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct Position {
pub pos: Vec3,
pub dir: Direction,
}
impl Position {
pub fn new(pos: Vec3, dir: Direction) -> Self { Self { pos, dir } }
/// Get a turtle command to map two adjacent positions
pub fn difference(self, to: Position) -> Option<TurtleCommand> {
use crate::turtle::TurtleCommand::*;
if self.pos == to.pos {
if to.dir == self.dir.left() {
Some(Left)
} else if to.dir == self.dir.right() {
Some(Right)
} else {
None
}
} else if to.dir == self.dir {
if to.pos == self.pos + self.dir.unit() {
Some(Forward(1))
} else if to.pos == self.pos - self.dir.unit() {
Some(Backward(1))
} else if to.pos == self.pos + Vec3::y() {
Some(Up(1))
} else if to.pos == self.pos - Vec3::y() {
Some(Down(1))
} else {
None
}
} else {
None
}
}
/// Command to dig
/// Assumes that "to" can be dug from your position
pub fn dig(&self, to: Vec3) -> Option<TurtleCommand> {
// manhattan distance of 1 required to dig
if (self.pos-to).abs().sum()!=1 {
return None;
}
// not covered: pointing away from to
Some(match self.pos.y - to.y {
0 => TurtleCommand::Dig,
1 => TurtleCommand::DigDown,
-1 => TurtleCommand::DigUp,
_ => None?
})
}
}
#[derive(Serialize, Deserialize, Clone, Hash, PartialEq, Eq, Copy, Debug)]
pub enum Direction {
North,
South,
East,
West,
}
impl Direction {
pub fn left(self) -> Self {
match self {
Direction::North => Direction::West,
Direction::South => Direction::East,
Direction::East => Direction::North,
Direction::West => Direction::South,
}
}
pub fn right(self) -> Self {
match self {
Direction::North => Direction::East,
Direction::South => Direction::West,
Direction::East => Direction::South,
Direction::West => Direction::North,
}
}
pub fn unit(self) -> Vec3 {
match self {
Direction::North => Vec3::new(0, 0, -1),
Direction::South => Vec3::new(0, 0, 1),
Direction::East => Vec3::new(1, 0, 0),
Direction::West => Vec3::new(-1, 0, 0),
}
}
}
/// closest valid state to the given point from where you are
pub fn nearest(from: Vec3, to: Vec3) -> Position {
let diff = to.xz()-from.xz();
let dir = if diff.x.abs() > diff.y.abs() {
if diff.x > 0 {
Direction::East
} else {
Direction::West
}
} else {
if diff.y > 0 {
Direction::South
} else {
Direction::South
}
};
Position {
pos: to - dir.unit(),
dir
}
}