From 732435d3d5b3610c29e3715ef4cce87cc7a82952 Mon Sep 17 00:00:00 2001 From: Andy Killorin <37423245+Speedy6451@users.noreply.github.com> Date: Thu, 28 Dec 2023 11:54:57 -0600 Subject: [PATCH] allow turtles to control one another (with even more mutexes) --- client/client.lua | 10 ++++++++ server/src/depot.rs | 54 ++++++++++++++++++++++++-------------------- server/src/mine.rs | 30 +++++++++++++++++++++--- server/src/tasks.rs | 27 ++++++++++++++++++++++ server/src/turtle.rs | 5 ++++ 5 files changed, 99 insertions(+), 27 deletions(-) diff --git a/client/client.lua b/client/client.lua index 1aae16d..fa2f95c 100644 --- a/client/client.lua +++ b/client/client.lua @@ -68,6 +68,14 @@ local function restartfront() return front.isOn() end +local function namefront() + local front = peripheral.wrap("front") + if not front or not front.shutdown then + return false + end + return front.getLabel() +end + local function inventoryinfo() return { ["Inventory"] = peripheral.wrap("front").list() } end @@ -143,6 +151,8 @@ local commands = { ["CycleFront"] = restartfront, ["Poweroff"] = os.shutdown, ["GetFuelLimit"] = turtle.getFuelLimit, + ["Name"] = os.computerLabel, + ["NameFront"] = namefront, }; if not ipaddr then diff --git a/server/src/depot.rs b/server/src/depot.rs index 545a8aa..8872533 100644 --- a/server/src/depot.rs +++ b/server/src/depot.rs @@ -51,29 +51,8 @@ impl Depots { trace!("depot at {:?}", depot.position()); turtle.goto(*depot.position()).await?; - // dump inventory - for (i, _) in turtle.inventory().await.into_iter().enumerate().filter(|(_,n)| n.is_some()) { - turtle.execute(Select((i+1) as u32)).await; - turtle.execute(DropDown(64)).await; - } - - // refuel - turtle.execute(Select(1)).await; - let limit = turtle.fuel_limit(); - while turtle.fuel() + 1000 < limit { - turtle.execute(SuckFront(64)).await; - let re = turtle.execute(Refuel).await; - turtle.execute(DropDown(64)).await; - if let TurtleCommandResponse::Failure = re.ret { - // partial refuel, good enough - warn!("only received {} fuel", turtle.fuel()); - if turtle.fuel() > 1500 { - break; - } else { - turtle.execute(Wait(15)).await; - } - } - } + dump(&turtle).await; + refuel(&turtle).await; // This can fail, we don't really care (as long as it executes once) turtle.execute(Backward(4)).await; @@ -82,7 +61,7 @@ impl Depots { // lava bucket fix for (i, _) in turtle.inventory().await.into_iter().enumerate().filter(|(_,n)| n.is_some()) { - turtle.execute(Select(i as u32)).await; + turtle.execute(Select((i+1) as u32)).await; turtle.execute(DropDown(64)).await; } @@ -115,3 +94,30 @@ impl Depots { depots } } + +pub async fn dump(turtle: &TurtleCommander) { + for (i, _) in turtle.inventory().await.into_iter().enumerate().filter(|(_,n)| n.is_some()) { + turtle.execute(Select((i+1) as u32)).await; + turtle.execute(DropDown(64)).await; + } +} + + +pub async fn refuel(turtle: &TurtleCommander) { + turtle.execute(Select(1)).await; + let limit = turtle.fuel_limit(); + while turtle.fuel() + 1000 < limit { + turtle.execute(SuckFront(64)).await; + let re = turtle.execute(Refuel).await; + turtle.execute(DropDown(64)).await; + if let TurtleCommandResponse::Failure = re.ret { + // partial refuel, good enough + warn!("only received {} fuel", turtle.fuel()); + if turtle.fuel() > 1500 { + break; + } else { + turtle.execute(Wait(15)).await; + } + } + } +} diff --git a/server/src/mine.rs b/server/src/mine.rs index 1f26c97..2785284 100644 --- a/server/src/mine.rs +++ b/server/src/mine.rs @@ -6,7 +6,7 @@ use serde::{Serialize, Deserialize}; use tokio::{task::{JoinHandle, AbortHandle}, sync::RwLock}; use typetag::serde; -use crate::{blocks::{Position, Vec3, Direction}, turtle::{TurtleCommand, TurtleCommander, TurtleCommandResponse, InventorySlot}, paths::TRANSPARENT, tasks::{Task, TaskState}}; +use crate::{blocks::{Position, Vec3, Direction}, turtle::{TurtleCommand, TurtleCommander, TurtleCommandResponse, InventorySlot}, paths::TRANSPARENT, tasks::{Task, TaskState}, names::Name, depot}; use TurtleCommand::*; /// Things to leave in the field (not worth fuel) @@ -86,7 +86,7 @@ async fn devore(turtle: &TurtleCommander) { let depot = turtle.get_depot().await; for i in turtles { - let position = depot.position(); + let position = depot.position().clone(); let staging = position.pos - position.dir.unit(); @@ -94,7 +94,30 @@ async fn devore(turtle: &TurtleCommander) { warn!("devoring {i}"); turtle.execute(Select(i)).await; turtle.execute(Place).await; - turtle.execute(CycleFront).await; + + loop { // cancel the task of the turtle ahead so that it doesn't go wild in the depot + if let TurtleCommandResponse::Name(name) = turtle.execute(NameFront).await.ret { + match Name::from_str(&name) { + Ok(name) => { + let mut scheduler = turtle.scheduler().await; + scheduler.cancel(name).await; + scheduler.do_on(move |turtle| tokio::spawn(async move { + depot::dump(&turtle).await; + depot::refuel(&turtle).await; + // *teleports behind you* + turtle.goto(Position::new(staging - position.dir.unit(), position.dir)).await; + }).abort_handle(), name).unwrap(); + break; + }, + Err(_) => error!("bad turtle name: {name}"), + } + } else { + error!("could not get name"); + } + } + + turtle.execute(CycleFront).await; // boot child + loop { let ret = turtle.execute(Wait(3)).await; // this won't do well with dead (energy-lacking) turtles, perhaps obtaining @@ -106,6 +129,7 @@ async fn devore(turtle: &TurtleCommander) { break; } warn!("devored turtle still inactive"); + turtle.execute(CycleFront).await; // rebot child } } } diff --git a/server/src/tasks.rs b/server/src/tasks.rs index 99376eb..30751d3 100644 --- a/server/src/tasks.rs +++ b/server/src/tasks.rs @@ -120,6 +120,33 @@ impl Scheduler { }); } + // TODO: make awaiting this feasible + pub fn do_on(&mut self, mut task: T, turtle: Name) -> Option<()> + where T: FnMut(TurtleCommander) -> AbortHandle + { + let turtle = self.turtles.iter_mut().filter(|t| t.0.name() == turtle).next()?; + match turtle.1 { + Some(_) => None, + None => { + trace!("new adhoc task on {}", turtle.0.name().to_str()); + turtle.1 = Some(task(turtle.0.clone())); + Some(()) + }, + } + } + + pub fn task_on(&mut self, mut task: Box, turtle: Name) -> Option<()> { + trace!("new {} task on {}", task.typetag_name(), turtle.clone().to_str()); + let turtle = self.turtles.iter_mut().filter(|t| t.0.name() == turtle).next()?; + match turtle.1 { + Some(_) => None, + None => { + turtle.1 = Some(task.run(turtle.0.clone())); + Some(()) + }, + } + } + pub async fn cancel(&mut self, turtle: Name) -> Option<()> { if let Some(task) = self.turtles.iter_mut().find(|t| t.0.name() == turtle)?.1.as_ref() { task.abort(); diff --git a/server/src/turtle.rs b/server/src/turtle.rs index 77667ce..213a0a9 100644 --- a/server/src/turtle.rs +++ b/server/src/turtle.rs @@ -12,6 +12,7 @@ use anyhow::Ok; use anyhow; use anyhow::Context; +use tokio::sync::OwnedMutexGuard; use tracing::error; use tracing::trace; use tracing::warn; @@ -256,6 +257,10 @@ impl TurtleCommander { self.world.clone() } + pub async fn scheduler(&self) -> OwnedMutexGuard { + self.tasks.clone().lock_owned().await + } + pub async fn inventory(&self) -> Vec> { let mut inventory = self.inventory.write().await;