diff --git a/server/src/blocks.rs b/server/src/blocks.rs index 5dc9b51..44ff8f6 100644 --- a/server/src/blocks.rs +++ b/server/src/blocks.rs @@ -1,6 +1,6 @@ -use rstar::{self, RTree, RTreeObject, AABB, PointDistance}; -use serde::{Deserialize, Serialize}; use pathfinding::prelude::astar; +use rstar::{self, PointDistance, RTree, RTreeObject, AABB}; +use serde::{Deserialize, Serialize}; use crate::Vec3; @@ -12,9 +12,8 @@ pub struct Block { pub pos: super::Vec3, } - impl RTreeObject for Block { - type Envelope = AABB<[i32;3]>; + type Envelope = AABB<[i32; 3]>; fn envelope(&self) -> Self::Envelope { AABB::from_point(self.pos.into()) @@ -22,11 +21,7 @@ impl RTreeObject for Block { } impl PointDistance for Block { - fn distance_2( - &self, - point: &[i32;3], - ) -> i32 { + fn distance_2(&self, point: &[i32; 3]) -> i32 { (self.pos - Vec3::from(*point)).abs().sum() } - } diff --git a/server/src/main.rs b/server/src/main.rs index d021894..10894f9 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -1,22 +1,27 @@ -use std::{env::args, sync::Arc, fs, io::ErrorKind, collections::VecDeque}; +use std::{collections::VecDeque, env::args, fs, io::ErrorKind, sync::Arc}; +use anyhow::{Context, Error, Ok}; use axum::{ + extract::{Path, State}, + http::request, routing::{get, post}, - Router, extract::{State, Path}, Json, http::{request}, + Json, Router, }; -use anyhow::{Error, Ok, Context}; use blocks::World; use rstar::{self, AABB}; mod names; -use names::Name; -use tokio::{sync::{Mutex, RwLock, watch::{self}}}; -use serde::{Serialize, Deserialize}; use const_format::formatcp; -use hyper_util::rt::TokioIo; -use tower::Service; use hyper::body::Incoming; +use hyper_util::rt::TokioIo; use nalgebra::Vector3; +use names::Name; +use serde::{Deserialize, Serialize}; +use tokio::sync::{ + watch::{self}, + Mutex, RwLock, +}; +use tower::Service; use crate::{blocks::Block, paths::route}; mod blocks; @@ -42,7 +47,7 @@ impl Direction { Direction::West => Direction::South, } } - + fn right(self) -> Self { match self { Direction::North => Direction::East, @@ -72,17 +77,19 @@ type SharedControl = Arc>; #[tokio::main] async fn main() -> Result<(), Error> { - - let state = match tokio::fs::OpenOptions::new().read(true).open("state.json").await { - tokio::io::Result::Ok(file) => { - serde_json::from_reader(file.into_std().await)? - }, + let state = match tokio::fs::OpenOptions::new() + .read(true) + .open("state.json") + .await + { + tokio::io::Result::Ok(file) => serde_json::from_reader(file.into_std().await)?, tokio::io::Result::Err(e) => match e.kind() { - ErrorKind::NotFound => { - ControlState { turtles:Vec::new(), world: World::new() } + ErrorKind::NotFound => ControlState { + turtles: Vec::new(), + world: World::new(), }, - _ => panic!() - } + _ => panic!(), + }, }; let state = SharedControl::new(RwLock::new(state)); @@ -95,8 +102,7 @@ async fn main() -> Result<(), Error> { .route("/turtle/:id/info", get(turtle_info)) .route("/turtle/updateAll", get(update_turtles)) .route("/flush", get(flush)) - .with_state(state.clone()); - + .with_state(state.clone()); let server = safe_kill::serve(server).await; @@ -108,7 +114,7 @@ async fn main() -> Result<(), Error> { Ok(()) } -mod safe_kill; +mod safe_kill; async fn write_to_disk(state: SharedControl) -> anyhow::Result<()> { let json = serde_json::to_string_pretty(&(*state.read().await))?; @@ -125,42 +131,52 @@ async fn flush(State(state): State) -> &'static str { async fn create_turtle( State(state): State, Json(req): Json, - ) -> Json { +) -> Json { let turtles = &mut state.write().await.turtles; let id = turtles.len() as u32; turtles.push(turtle::Turtle::new(id, req.position, req.facing, req.fuel)); println!("turt {id}"); - Json(turtle::TurtleResponse {name: Name::from_num(id).to_str(), id, command: turtle::TurtleCommand::Update}) + Json(turtle::TurtleResponse { + name: Name::from_num(id).to_str(), + id, + command: turtle::TurtleCommand::Update, + }) } async fn set_goal( Path(id): Path, State(state): State, Json(req): Json, - ) -> &'static str { +) -> &'static str { state.write().await.turtles[id as usize].goal = Some(req); "ACK" } -async fn update_turtles( - State(state): State, - ) -> &'static str { - state.write().await.turtles.iter_mut().for_each(|t| t.pending_update = true); +async fn update_turtles(State(state): State) -> &'static str { + state + .write() + .await + .turtles + .iter_mut() + .for_each(|t| t.pending_update = true); "ACK" } async fn turtle_info( Path(id): Path, State(state): State, - ) -> Json { +) -> Json { let state = &mut state.read().await; let turtle = &state.turtles[id as usize]; let mut pseudomoves: VecDeque = VecDeque::new(); - turtle.moves.front().map(|m| pseudomoves.push_front(m.clone())); + turtle + .moves + .front() + .map(|m| pseudomoves.push_front(m.clone())); let cloned = turtle::Turtle { name: turtle.name.clone(), @@ -179,7 +195,7 @@ async fn command( Path(id): Path, State(state): State, Json(req): Json, - ) -> Json { +) -> Json { let mut state = &mut state.write().await; println!("{:?}", &req); @@ -219,7 +235,6 @@ fn difference(from: Position, to: Position) -> Option { } else { None } - } else { None } @@ -227,7 +242,7 @@ fn difference(from: Position, to: Position) -> Option { #[derive(Serialize, Deserialize)] struct TurtleMineJobParams { - region: AABB<[i32;3]>, + region: AABB<[i32; 3]>, to_mine: Vec, method: TurtleMineMethod, refuel: Position, @@ -237,18 +252,21 @@ struct TurtleMineJobParams { #[derive(Serialize, Deserialize)] struct TurtleMineJob { to_mine: VecDeque, - mined: AABB<[i32;3]>, + mined: AABB<[i32; 3]>, params: TurtleMineJobParams, } - #[derive(Serialize, Deserialize)] enum TurtleMineMethod { Clear, Strip, } async fn client() -> &'static str { - formatcp!("local ipaddr = {}\n{}", include_str!("../ipaddr.txt"), include_str!("../../client/client.lua")) + formatcp!( + "local ipaddr = {}\n{}", + include_str!("../ipaddr.txt"), + include_str!("../../client/client.lua") + ) } mod turtle; diff --git a/server/src/names.rs b/server/src/names.rs index 5be5515..5293c8d 100644 --- a/server/src/names.rs +++ b/server/src/names.rs @@ -1,6 +1,6 @@ use anyhow::Context; use bit_struct::*; -use feistel_rs::{feistel_encrypt, feistel_decrypt}; +use feistel_rs::{feistel_decrypt, feistel_encrypt}; bit_struct! { pub struct Name(u32) { @@ -24,48 +24,81 @@ impl Name { pub fn from_str(name: &str) -> anyhow::Result { let parts: Vec<&str> = name.splitn(4, ' ').collect(); let (first, last, order, mut pronouns) = (parts[0], parts[1], parts[2], parts[3]); - pronouns = &pronouns[1..pronouns.len()-1]; + pronouns = &pronouns[1..pronouns.len() - 1]; - - let first = FIRST_NAMES.lines().position(|x| *x == *first).map(|n| u13::new(n as u16)).context("unknown first name")?; - let last = LAST_NAMES.lines().position(|x| *x == *last).map(|n| u14::new(n as u16)).context("unknown last name")?; - let order = ORDER.lines().position(|x| *x == *order).map(|n| u3::new(n as u8)).context("unknown title")?; - let pronouns = PRONOUNS.lines().position(|x| *x == *pronouns).map(|n| u2::new(n as u8)).context("invalid gender")?; + let first = FIRST_NAMES + .lines() + .position(|x| *x == *first) + .map(|n| u13::new(n as u16)) + .context("unknown first name")?; + let last = LAST_NAMES + .lines() + .position(|x| *x == *last) + .map(|n| u14::new(n as u16)) + .context("unknown last name")?; + let order = ORDER + .lines() + .position(|x| *x == *order) + .map(|n| u3::new(n as u8)) + .context("unknown title")?; + let pronouns = PRONOUNS + .lines() + .position(|x| *x == *pronouns) + .map(|n| u2::new(n as u8)) + .context("invalid gender")?; // first name 14 bits // last name 13 bits // order 3 bits // pronouns 2 bits - // TODO: trim tables to avoid "zach" crashes - Ok(Name::new(first.unwrap(), last.unwrap(), order.unwrap(), pronouns.unwrap())) + Ok(Name::new( + first.unwrap(), + last.unwrap(), + order.unwrap(), + pronouns.unwrap(), + )) } pub fn from_num(name: u32) -> Self { let encrypted = u32::from_le_bytes( - feistel_encrypt(&name.to_le_bytes(), &FEISTEL_KEY, FEISTEL_ROUNDS).try_into().unwrap() + feistel_encrypt(&name.to_le_bytes(), &FEISTEL_KEY, FEISTEL_ROUNDS) + .try_into() + .unwrap(), ); // This is ok because every possible value is valid (see tests) - unsafe { - Name(UnsafeStorage::new_unsafe(encrypted)) - } + unsafe { Name(UnsafeStorage::new_unsafe(encrypted)) } } pub fn to_num(self) -> u32 { let num = self.raw(); u32::from_le_bytes( - feistel_decrypt(&num.to_le_bytes(), &FEISTEL_KEY, FEISTEL_ROUNDS).try_into().unwrap() + feistel_decrypt(&num.to_le_bytes(), &FEISTEL_KEY, FEISTEL_ROUNDS) + .try_into() + .unwrap(), ) } pub fn to_str(&mut self) -> String { - let first = FIRST_NAMES.lines().nth(self.first().get().value() as usize).unwrap(); - let last = LAST_NAMES.lines().nth(self.last().get().value() as usize).unwrap(); - let order = ORDER.lines().nth(self.order().get().value() as usize).unwrap(); - let pronouns = PRONOUNS.lines().nth(self.pronouns().get().value() as usize).unwrap(); + let first = FIRST_NAMES + .lines() + .nth(self.first().get().value() as usize) + .unwrap(); + let last = LAST_NAMES + .lines() + .nth(self.last().get().value() as usize) + .unwrap(); + let order = ORDER + .lines() + .nth(self.order().get().value() as usize) + .unwrap(); + let pronouns = PRONOUNS + .lines() + .nth(self.pronouns().get().value() as usize) + .unwrap(); format!("{first} {last} {order} ({pronouns})") } diff --git a/server/src/paths.rs b/server/src/paths.rs index 3eecbb4..619a28d 100644 --- a/server/src/paths.rs +++ b/server/src/paths.rs @@ -1,28 +1,45 @@ use std::rc::Rc; +use crate::{ + blocks::{Block, World}, + Direction, Position, +}; use pathfinding::prelude::astar; -use crate::{blocks::{World, Block}, Position, Direction}; use super::Vec3; pub fn route(from: Position, to: Position, world: &World) -> Option> { // attempt at not crashing by looking infinitely into the abyss - if world.locate_at_point(&to.0.into()) - .is_some_and(|b| difficulty(&b.name).is_none()) { + if world + .locate_at_point(&to.0.into()) + .is_some_and(|b| difficulty(&b.name).is_none()) + { return None; - } - let route = astar(&from, move |p| {next(p, world)} , |p1| {(p1.0 - &to.0).abs().sum() as u32}, |p| {p == &to}).unwrap(); + let route = astar( + &from, + move |p| next(p, world), + |p1| (p1.0 - &to.0).abs().sum() as u32, + |p| p == &to, + ) + .unwrap(); Some(route.0) } fn next(from: &Position, world: &World) -> Vec<(Position, u32)> { let mut vec: Vec<(Position, u32)> = Vec::new(); - vec.push(((from.0, from.1.left()),1)); - vec.push(((from.0, from.1.right()),1)); + vec.push(((from.0, from.1.left()), 1)); + vec.push(((from.0, from.1.right()), 1)); - fn insert(vec: &mut Vec<(Position, u32)>, point: Vec3, orientation: Direction, world: &World, unknown: Option) { - world.locate_at_point(&point.into()) + fn insert( + vec: &mut Vec<(Position, u32)>, + point: Vec3, + orientation: Direction, + world: &World, + unknown: Option, + ) { + world + .locate_at_point(&point.into()) .map_or(unknown, |b| difficulty(&b.name)) .map(|d| vec.push(((point, orientation), d))); } @@ -32,12 +49,12 @@ fn next(from: &Position, world: &World) -> Vec<(Position, u32)> { //let behind = from.0 - from.1.unit(); //insert(&mut vec, behind, from.1, world, None); - + let above = from.0 + Vec3::y(); - insert(&mut vec, above, from.1, world,UNKNOWN); + insert(&mut vec, above, from.1, world, UNKNOWN); let below = from.0 - Vec3::y(); - insert(&mut vec, below, from.1, world,UNKNOWN); + insert(&mut vec, below, from.1, world, UNKNOWN); vec } @@ -56,7 +73,11 @@ const UNKNOWN: Option = Some(2); // time to go somewhere fn difficulty(name: &str) -> Option { - if name == "minecraft:air" { return Some(1) }; - if GARBAGE.contains(&name) { return Some(2)}; + if name == "minecraft:air" { + return Some(1); + }; + if GARBAGE.contains(&name) { + return Some(2); + }; None } diff --git a/server/src/safe_kill.rs b/server/src/safe_kill.rs index 39b3722..7bb1292 100644 --- a/server/src/safe_kill.rs +++ b/server/src/safe_kill.rs @@ -19,7 +19,9 @@ use tokio::sync::watch::Sender; use axum::Router; pub(crate) async fn serve(server: Router) -> Sender<()> { - let listener = tokio::net::TcpListener::bind("0.0.0.0:48228").await.unwrap(); + let listener = tokio::net::TcpListener::bind("0.0.0.0:48228") + .await + .unwrap(); let (close_tx, close_rx) = watch::channel(()); @@ -66,7 +68,7 @@ pub(crate) async fn serve(server: Router) -> Sender<()> { drop(close_rx); }); - }; + } drop(listener); @@ -75,8 +77,7 @@ pub(crate) async fn serve(server: Router) -> Sender<()> { pub(crate) async fn shutdown_signal() { let ctrl_c = async { - signal::ctrl_c() - .await.unwrap(); + signal::ctrl_c().await.unwrap(); }; ctrl_c.await diff --git a/server/src/turtle.rs b/server/src/turtle.rs index 142c1fb..1538256 100644 --- a/server/src/turtle.rs +++ b/server/src/turtle.rs @@ -21,8 +21,8 @@ use super::Vec3; use super::names::Name; -use serde::Serialize; use serde::Deserialize; +use serde::Serialize; use super::paths::route; @@ -40,8 +40,15 @@ pub(crate) struct Turtle { impl Turtle { pub(crate) fn new(id: u32, position: Vec3, facing: Direction, fuel: usize) -> Self { - Self { name: Name::from_num(id), fuel, queued_movement: Vec3::new(0, 0, 0), position: (position, facing), goal: None, pending_update: true, moves: VecDeque::new() } - + Self { + name: Name::from_num(id), + fuel, + queued_movement: Vec3::new(0, 0, 0), + position: (position, facing), + goal: None, + pending_update: true, + moves: VecDeque::new(), + } } } @@ -49,8 +56,11 @@ pub(crate) fn process_turtle_update( id: u32, state: &mut ControlState, update: TurtleUpdate, - ) -> anyhow::Result { - let turtle = state.turtles.get_mut(id as usize).context("nonexisting turtle")?; +) -> anyhow::Result { + let turtle = state + .turtles + .get_mut(id as usize) + .context("nonexisting turtle")?; let world = &mut state.world; if turtle.pending_update { @@ -58,7 +68,10 @@ pub(crate) fn process_turtle_update( return Ok(TurtleCommand::Update); } - println!("above: {}, below: {}, ahead: {}", update.above, update.below, update.ahead); + println!( + "above: {}, below: {}, ahead: {}", + update.above, update.below, update.ahead + ); if turtle.fuel != update.fuel { turtle.fuel = update.fuel; @@ -96,20 +109,22 @@ pub(crate) fn process_turtle_update( let route = route(turtle.position, goal, world).unwrap(); println!("route: {:?}", route); let mut next_move = difference(route[0], route[1]).unwrap(); - if world.locate_at_point(&route[1].0.into()).is_some_and(|b| b.name != "minecraft:air") { + if world + .locate_at_point(&route[1].0.into()) + .is_some_and(|b| b.name != "minecraft:air") + { next_move = match next_move { TurtleCommand::Up(_) => TurtleCommand::DigUp, TurtleCommand::Down(_) => TurtleCommand::DigDown, TurtleCommand::Forward(_) => TurtleCommand::Dig, _ => next_move, - } } turtle.queued_movement = next_move.delta(turtle.position.1); match next_move { TurtleCommand::Left => turtle.position.1 = turtle.position.1.left(), TurtleCommand::Right => turtle.position.1 = turtle.position.1.right(), - _ => {}, + _ => {} } return Ok(next_move); } @@ -165,16 +180,16 @@ pub(crate) enum TurtleCommandResponse { } impl TurtleCommand { - pub(crate) fn delta(&self, direction: Direction) -> Vec3 { - let dir = direction.unit(); - match self { - TurtleCommand::Forward(count) => dir * *count as i32, - TurtleCommand::Backward(count) => -dir * *count as i32, - TurtleCommand::Up(count) => Vec3::y() * *count as i32, - TurtleCommand::Down(count) => -Vec3::y() * *count as i32, - _ => Vec3::zeros(), + pub(crate) fn delta(&self, direction: Direction) -> Vec3 { + let dir = direction.unit(); + match self { + TurtleCommand::Forward(count) => dir * *count as i32, + TurtleCommand::Backward(count) => -dir * *count as i32, + TurtleCommand::Up(count) => Vec3::y() * *count as i32, + TurtleCommand::Down(count) => -Vec3::y() * *count as i32, + _ => Vec3::zeros(), + } } - } } #[derive(Serialize, Deserialize, Debug)]