diff --git a/client/client.lua b/client/client.lua index 24afb08..b6dcb8c 100644 --- a/client/client.lua +++ b/client/client.lua @@ -89,7 +89,7 @@ end local commands = { ["Wait"] = sleep, ["Forward"] = cyclefn(turtle.forward), - ["Backward"] = cyclefn(turtle.backward), + ["Backward"] = cyclefn(turtle.back), ["Up"] = cyclefn(turtle.up), ["Down"] = cyclefn(turtle.down), ["DropFront"] = turtle.dropfront, diff --git a/server/src/blocks.rs b/server/src/blocks.rs index f4c942d..043fc6c 100644 --- a/server/src/blocks.rs +++ b/server/src/blocks.rs @@ -30,6 +30,7 @@ impl World { self.state.write().await.insert(block); } + /// Returns true if a known non-air 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| b.name != "minecraft:air") } diff --git a/server/src/main.rs b/server/src/main.rs index 9fa1fa5..ae66ec3 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -171,8 +171,7 @@ async fn dig( let turtle = state.read().await.get_turtle(id).await.unwrap(); tokio::spawn( async move { - let pos = turtle.goto_adjacent(req).await.unwrap(); - turtle.execute(pos.dig(req).unwrap()).await + mine::mine_chunk(turtle.clone(), req).await } ); @@ -230,8 +229,6 @@ async fn command( ) -> Json { let mut state = &mut state.write().await; - println!("{:?}", &req); - if id as usize > state.turtles.len() { return Json(turtle::TurtleCommand::Update); } diff --git a/server/src/paths.rs b/server/src/paths.rs index 19f1375..7b4598f 100644 --- a/server/src/paths.rs +++ b/server/src/paths.rs @@ -16,6 +16,12 @@ pub async fn route_facing(from: Position, to: Vec3, world: &World) -> Option Option> { + // attempt at not crashing by looking infinitely into the abyss + if world.get(to.pos).await + .is_some_and(|b| difficulty(&b.name).is_none()) + { + return None; + } route_to(from, to.pos, |p| p == &to, world).await } @@ -24,13 +30,6 @@ where D: FnMut(&Position) -> bool { // lock once, we'll be doing a lot of lookups let world = world.clone().lock().await; - // attempt at not crashing by looking infinitely into the abyss - if world - .locate_at_point(&to.into()) - .is_some_and(|b| difficulty(&b.name).is_none()) - { - return None; - } let route = astar( &from, move |p| next(p, &world), @@ -63,8 +62,8 @@ fn next(from: &Position, world: &WorldReadLock) -> Vec<(Position, u32)> { let ahead = from.pos + from.dir.unit(); insert(&mut vec, ahead, from.dir, world, UNKNOWN); - //let behind = from.pos - from.dir.unit(); - //insert(&mut vec, behind, from.dir, world, None); + let behind = from.pos - from.dir.unit(); + insert(&mut vec, behind, from.dir, world, None); let above = from.pos + Vec3::y(); insert(&mut vec, above, from.dir, world, UNKNOWN); diff --git a/server/src/turtle.rs b/server/src/turtle.rs index 82d2d5a..25a64ca 100644 --- a/server/src/turtle.rs +++ b/server/src/turtle.rs @@ -25,6 +25,7 @@ use super::LiveState; use std::collections::VecDeque; use std::future::Ready; use std::sync::Arc; +use std::sync::atomic::AtomicUsize; use std::time::Duration; @@ -143,16 +144,24 @@ impl Turtle { pub struct TurtleCommander { sender: Arc, world: World, - turtle: Arc>, + // everything below is best-effort + // TODO: make not bad + pos: Arc>, + fuel: Arc, + max_fuel: Arc, + } impl TurtleCommander { pub async fn new(turtle: Name, state: &LiveState) -> Option { let turtle = state.turtles.get(turtle.to_num() as usize)?.clone(); + let turtle = turtle.read().await; Some(TurtleCommander { - sender: turtle.clone().read().await.sender.as_ref().unwrap().clone(), + sender: turtle.sender.as_ref().unwrap().clone(), world: state.world.clone(), - turtle, + pos: Arc::new(RwLock::new(turtle.position)), + fuel: Arc::new(AtomicUsize::new(turtle.fuel)), + max_fuel: Arc::new(AtomicUsize::new(turtle.fuel_limit)), }) } @@ -161,22 +170,26 @@ impl TurtleCommander { self.sender.to_owned().send((command,send)).await.unwrap(); - recv.await.unwrap() + let resp = recv.await.unwrap(); + let mut pos = self.pos.write().await; + *pos = resp.pos; + self.fuel.store(resp.fuel, std::sync::atomic::Ordering::SeqCst); + resp } pub async fn pos(&self) -> Position { - self.turtle.read().await.position + self.pos.read().await.clone() } pub async fn fuel(&self) -> usize { - self.turtle.read().await.fuel + self.fuel.load(std::sync::atomic::Ordering::SeqCst) } pub async fn fuel_limit(&self) -> usize { - self.turtle.read().await.fuel_limit + self.max_fuel.load(std::sync::atomic::Ordering::SeqCst) } - pub async fn world(&self) -> World { + pub fn world(&self) -> World { self.world.clone() } @@ -188,7 +201,9 @@ impl TurtleCommander { break; } - let route = route(recent, pos, &world).await?; + // easiest way to not eventually take over all memory + let routing = timeout(Duration::from_secs(1), route(recent, pos, &world)); + let route = routing.await.ok()??; let steps: Vec = route.iter().map_windows(|[from,to]| from.difference(**to).unwrap()).collect(); @@ -212,18 +227,19 @@ impl TurtleCommander { let world = self.world.clone(); loop { - if pos == recent.dir.unit() + recent.pos { + if pos == recent.dir.unit() + recent.pos + || pos == recent.pos + Vec3::y() + || pos == recent.pos - Vec3::y() + { break; } - let route = route_facing(recent, pos, &world).await?; + let routing = timeout(Duration::from_secs(1), route_facing(recent, pos, &world)); + let route = routing.await.ok()??; let steps: Vec = route.iter().map_windows(|[from,to]| from.difference(**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.occupied(next_position.pos).await { if world.garbage(next_position.pos).await { self.execute(dbg!(recent.dig(next_position.pos))?).await; @@ -232,9 +248,13 @@ impl TurtleCommander { } } - let state = self.execute(command).await; + let state = self.execute(command.clone()).await; if let TurtleCommandResponse::Failure = state.ret { + if let TurtleCommand::Backward(_) = command { + // turn around if you bump your rear on something + self.goto(Position::new(recent.pos, recent.dir.left().left())).await? + } break 'route; } @@ -309,7 +329,7 @@ pub(crate) async fn process_turtle_update( _ => {} } turtle.queued_movement = cmd.unit(turtle.position.dir); - println!("Turtle {}: {cmd:?}", turtle.name.to_str()); + println!("{}: {cmd:?}", turtle.name.to_str()); return Ok(cmd); } }