diff --git a/server/Cargo.toml b/server/Cargo.toml index f14e2a2..17ae058 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -12,10 +12,12 @@ bit-struct = "0.3.2" const_format = "0.2.32" erased-serde = "0.4.1" feistel_rs = "0.1.0" +future-parking_lot = "0.3.3" hilbert_index = "0.2.0" hyper = "1.0.1" hyper-util = "0.1.1" nalgebra = { version = "0.32.3", features = ["serde-serialize"] } +parking_lot = { version = "0.11", features = ["serde"] } pathfinding = "4.6.0" rstar = { version = "0.11.0", features = ["serde"] } rustmatica = "0.1.1" diff --git a/server/src/main.rs b/server/src/main.rs index ac57a79..810a554 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -1,3 +1,5 @@ +#![feature(iter_map_windows)] + use std::{collections::VecDeque, io::ErrorKind, sync::Arc}; use anyhow::{Context, Error, Ok}; @@ -21,7 +23,7 @@ use tokio::sync::{ Mutex, RwLock, mpsc }; use tower::Service; -use turtle::{TurtleTask, Iota, Receiver, Sender, Turtle, TurtleUpdate}; +use turtle::{TurtleTask, Iota, Receiver, Sender, Turtle, TurtleUpdate, TurtleInfo}; use crate::{blocks::Block, paths::route}; @@ -127,11 +129,11 @@ async fn create_turtle( async fn place_up( Path(id): Path, State(state): State, -) -> Json { +) -> Json { let turtle = state.write().await.saved.turtles.get(id as usize).unwrap() .cmd(); - Json(turtle.execute(Iota::Execute(turtle::TurtleCommand::PlaceUp)).await) + Json(turtle.execute(turtle::TurtleCommand::PlaceUp).await) } async fn set_goal( @@ -139,9 +141,10 @@ async fn set_goal( State(state): State, Json(req): Json, ) -> &'static str { - state.write().await.saved.tasks[id as usize].push_back( - TurtleMineJob::chunk(req.0) - ); + state.read().await.saved.turtles[id as usize].cmd().goto(req).await; + //state.write().await.saved.tasks[id as usize].push_back( + // TurtleMineJob::chunk(req.0) + //); "ACK" } diff --git a/server/src/turtle.rs b/server/src/turtle.rs index 5268940..17926da 100644 --- a/server/src/turtle.rs +++ b/server/src/turtle.rs @@ -38,15 +38,34 @@ pub(crate) struct Turtle { pub(crate) goal: Option, pub(crate) pending_update: bool, #[serde(skip)] - callback: Option>, + callback: Option>, #[serde(skip)] sender: Option>, #[serde(skip)] receiver: Option, } -pub type Sender = mpsc::Sender<(Iota, oneshot::Sender)>; -pub type Receiver = mpsc::Receiver<(Iota, oneshot::Sender)>; +#[derive(Debug)] +pub struct TurtleInfo { + pub name: Name, + pub pos: Position, + pub fuel: usize, + /// Block name + pub ahead: String, + pub above: String, + pub below: String, + pub ret: TurtleCommandResponse, +} + +impl TurtleInfo { + fn from_update(update: TurtleUpdate, name: Name, pos: Position) -> Self { + Self { name, pos, + fuel: update.fuel, ahead: update.ahead, above: update.above, below: update.below, ret: update.ret } + } +} + +pub type Sender = mpsc::Sender<(TurtleCommand, oneshot::Sender)>; +pub type Receiver = mpsc::Receiver<(TurtleCommand, oneshot::Sender)>; impl Default for Turtle { fn default() -> Self { @@ -102,25 +121,40 @@ pub struct TurtleCommander { } impl TurtleCommander { - pub async fn execute(&self, command: Iota) -> TurtleUpdate { - let (send, recv) = oneshot::channel::(); + pub async fn execute(&self, command: TurtleCommand) -> TurtleInfo { + let (send, recv) = oneshot::channel::(); self.sender.to_owned().send((command,send)).await.unwrap(); recv.await.unwrap() } +} - pub async fn command(&self, command: TurtleCommand) -> TurtleUpdate { - self.execute(Iota::Execute(command)).await - } +async fn goto(cmd: &TurtleCommander, recent: TurtleInfo, pos: Position, world: &rstar::RTree) -> Option<()> { + let mut recent = recent.pos; + loop { + if recent == pos { + break; + } - pub async fn goto(&self, pos: Position) -> TurtleUpdate { - self.execute(Iota::Goto(pos)).await - } + let route = route(recent, pos, world)?; - pub async fn mine(&self, pos: Vec3) -> TurtleUpdate { - self.execute(Iota::Mine(pos)).await + let steps: Vec = route.iter().map_windows(|[from,to]| difference(**from,**to).unwrap()).collect(); + + 'route: for (next_position, command) in route.into_iter().skip(1).zip(steps) { + // reroute if the goal point is not empty before moving + // valid routes will explicitly tell you to break ground + + if world.locate_at_point(&next_position.0.into()).unwrap().name != "minecraft:air" { + break 'route; + } + + let state = cmd.execute(command).await; + recent = state.pos; + + } } + Some(()) } pub(crate) async fn process_turtle_update( @@ -171,21 +205,17 @@ pub(crate) async fn process_turtle_update( world.remove_at_point(&below.pos.into()); world.insert(below.clone()); - if let Some(task) = tasks.front_mut() { - task.handle_block(above); - task.handle_block(below); - task.handle_block(ahead); - } + let info = TurtleInfo::from_update(update, turtle.name.clone(), turtle.position.clone()); if let Some(send) = turtle.callback.take() { - send.send(update).unwrap(); + send.send(info).unwrap(); } if let Some(recv) = turtle.receiver.as_mut() { if let Some((cmd, ret)) = recv.try_recv().ok() { turtle.callback = Some(ret); - turtle.goal = Some(cmd); + return Ok(cmd); } }