message passing
This commit is contained in:
parent
5f00bb9526
commit
b7e33ae057
4 changed files with 78 additions and 32 deletions
|
@ -146,6 +146,8 @@ repeat
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
command = nil
|
||||||
|
|
||||||
local ret_table = nil
|
local ret_table = nil
|
||||||
if type(ret) == "boolean" then
|
if type(ret) == "boolean" then
|
||||||
if ret then
|
if ret then
|
||||||
|
|
|
@ -18,10 +18,10 @@ use names::Name;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tokio::sync::{
|
use tokio::sync::{
|
||||||
watch::{self},
|
watch::{self},
|
||||||
Mutex, RwLock,
|
Mutex, RwLock, mpsc
|
||||||
};
|
};
|
||||||
use tower::Service;
|
use tower::Service;
|
||||||
use turtle::TurtleTask;
|
use turtle::{TurtleTask, Iota, Receiver, Sender, Turtle};
|
||||||
|
|
||||||
use crate::{blocks::Block, paths::route};
|
use crate::{blocks::Block, paths::route};
|
||||||
|
|
||||||
|
@ -33,13 +33,19 @@ mod safe_kill;
|
||||||
mod turtle;
|
mod turtle;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
struct ControlState {
|
struct SavedState {
|
||||||
turtles: Vec<turtle::Turtle>,
|
turtles: Vec<turtle::Turtle>,
|
||||||
tasks: Vec<VecDeque<TurtleMineJob>>,
|
tasks: Vec<VecDeque<TurtleMineJob>>,
|
||||||
world: blocks::World,
|
world: blocks::World,
|
||||||
//chunkloaders: unimplemented!(),
|
//chunkloaders: unimplemented!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ControlState {
|
||||||
|
saved: SavedState,
|
||||||
|
turtle_senders: Vec<Mutex<Sender>>,
|
||||||
|
turtle_receivers: Vec<Mutex<Receiver>>
|
||||||
|
}
|
||||||
|
|
||||||
type SharedControl = Arc<RwLock<ControlState>>;
|
type SharedControl = Arc<RwLock<ControlState>>;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
|
@ -51,7 +57,7 @@ async fn main() -> Result<(), Error> {
|
||||||
{
|
{
|
||||||
tokio::io::Result::Ok(file) => serde_json::from_reader(file.into_std().await)?,
|
tokio::io::Result::Ok(file) => serde_json::from_reader(file.into_std().await)?,
|
||||||
tokio::io::Result::Err(e) => match e.kind() {
|
tokio::io::Result::Err(e) => match e.kind() {
|
||||||
ErrorKind::NotFound => ControlState {
|
ErrorKind::NotFound => SavedState {
|
||||||
turtles: Vec::new(),
|
turtles: Vec::new(),
|
||||||
world: World::new(),
|
world: World::new(),
|
||||||
tasks: Vec::new(),
|
tasks: Vec::new(),
|
||||||
|
@ -60,6 +66,12 @@ async fn main() -> Result<(), Error> {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let state = ControlState { saved: state, turtle_senders:
|
||||||
|
Vec::new()
|
||||||
|
, turtle_receivers:
|
||||||
|
Vec::new()
|
||||||
|
};
|
||||||
|
|
||||||
let state = SharedControl::new(RwLock::new(state));
|
let state = SharedControl::new(RwLock::new(state));
|
||||||
|
|
||||||
let server = Router::new()
|
let server = Router::new()
|
||||||
|
@ -67,6 +79,7 @@ async fn main() -> Result<(), Error> {
|
||||||
.route("/turtle/:id/update", post(command))
|
.route("/turtle/:id/update", post(command))
|
||||||
.route("/turtle/client.lua", get(client))
|
.route("/turtle/client.lua", get(client))
|
||||||
.route("/turtle/:id/setGoal", post(set_goal))
|
.route("/turtle/:id/setGoal", post(set_goal))
|
||||||
|
.route("/turtle/:id/cancelTask", post(cancel))
|
||||||
.route("/turtle/:id/info", get(turtle_info))
|
.route("/turtle/:id/info", get(turtle_info))
|
||||||
.route("/turtle/updateAll", get(update_turtles))
|
.route("/turtle/updateAll", get(update_turtles))
|
||||||
.route("/flush", get(flush))
|
.route("/flush", get(flush))
|
||||||
|
@ -84,7 +97,7 @@ async fn main() -> Result<(), Error> {
|
||||||
|
|
||||||
|
|
||||||
async fn write_to_disk(state: SharedControl) -> anyhow::Result<()> {
|
async fn write_to_disk(state: SharedControl) -> anyhow::Result<()> {
|
||||||
let json = serde_json::to_string_pretty(&(*state.read().await))?;
|
let json = serde_json::to_string_pretty(&state.read().await.saved)?;
|
||||||
tokio::fs::write("state.json", json).await?;
|
tokio::fs::write("state.json", json).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -100,13 +113,15 @@ async fn create_turtle(
|
||||||
Json(req): Json<turtle::TurtleRegister>,
|
Json(req): Json<turtle::TurtleRegister>,
|
||||||
) -> Json<turtle::TurtleResponse> {
|
) -> Json<turtle::TurtleResponse> {
|
||||||
let state = &mut state.write().await;
|
let state = &mut state.write().await;
|
||||||
let turtles = &mut state.turtles;
|
let id = state.saved.turtles.len() as u32;
|
||||||
let id = turtles.len() as u32;
|
let (send, receive) = mpsc::channel(1);
|
||||||
turtles.push(turtle::Turtle::new(id, req.position, req.facing, req.fuel));
|
state.turtle_senders.push(Mutex::new(send));
|
||||||
state.tasks.push(VecDeque::new());
|
state.turtle_receivers.push(Mutex::new(receive));
|
||||||
|
state.saved.turtles.push(turtle::Turtle::new(id, req.position, req.facing, req.fuel));
|
||||||
|
state.saved.tasks.push(VecDeque::new());
|
||||||
|
|
||||||
|
|
||||||
println!("turt {id}");
|
println!("new turtle: {id}");
|
||||||
|
|
||||||
Json(turtle::TurtleResponse {
|
Json(turtle::TurtleResponse {
|
||||||
name: Name::from_num(id).to_str(),
|
name: Name::from_num(id).to_str(),
|
||||||
|
@ -120,18 +135,27 @@ async fn set_goal(
|
||||||
State(state): State<SharedControl>,
|
State(state): State<SharedControl>,
|
||||||
Json(req): Json<Position>,
|
Json(req): Json<Position>,
|
||||||
) -> &'static str {
|
) -> &'static str {
|
||||||
state.write().await.tasks[id as usize].push_back(
|
state.write().await.saved.tasks[id as usize].push_back(
|
||||||
TurtleMineJob::chunk(req.0)
|
TurtleMineJob::chunk(req.0)
|
||||||
);
|
);
|
||||||
|
|
||||||
"ACK"
|
"ACK"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn cancel(
|
||||||
|
Path(id): Path<u32>,
|
||||||
|
State(state): State<SharedControl>,
|
||||||
|
) -> &'static str {
|
||||||
|
state.write().await.saved.tasks[id as usize].pop_front();
|
||||||
|
|
||||||
|
"ACK"
|
||||||
|
}
|
||||||
|
|
||||||
async fn update_turtles(State(state): State<SharedControl>) -> &'static str {
|
async fn update_turtles(State(state): State<SharedControl>) -> &'static str {
|
||||||
state
|
state
|
||||||
.write()
|
.write()
|
||||||
.await
|
.await
|
||||||
.turtles
|
.saved.turtles
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.for_each(|t| t.pending_update = true);
|
.for_each(|t| t.pending_update = true);
|
||||||
"ACK"
|
"ACK"
|
||||||
|
@ -142,16 +166,14 @@ async fn turtle_info(
|
||||||
State(state): State<SharedControl>,
|
State(state): State<SharedControl>,
|
||||||
) -> Json<turtle::Turtle> {
|
) -> Json<turtle::Turtle> {
|
||||||
let state = &mut state.read().await;
|
let state = &mut state.read().await;
|
||||||
let turtle = &state.turtles[id as usize];
|
let turtle = &state.saved.turtles[id as usize];
|
||||||
|
|
||||||
let cloned = turtle::Turtle {
|
let cloned = Turtle::new(
|
||||||
name: turtle.name.clone(),
|
turtle.name.to_num(),
|
||||||
fuel: turtle.fuel,
|
turtle.position.to_owned().0,
|
||||||
queued_movement: turtle.queued_movement.clone(),
|
turtle.position.to_owned().1,
|
||||||
position: turtle.position.clone(),
|
turtle.fuel
|
||||||
goal: turtle.goal.clone(),
|
);
|
||||||
pending_update: turtle.pending_update,
|
|
||||||
};
|
|
||||||
|
|
||||||
Json(cloned)
|
Json(cloned)
|
||||||
}
|
}
|
||||||
|
@ -165,12 +187,12 @@ async fn command(
|
||||||
|
|
||||||
println!("{:?}", &req);
|
println!("{:?}", &req);
|
||||||
|
|
||||||
if id as usize > state.turtles.len() {
|
if id as usize > state.saved.turtles.len() {
|
||||||
return Json(turtle::TurtleCommand::Update);
|
return Json(turtle::TurtleCommand::Update);
|
||||||
}
|
}
|
||||||
|
|
||||||
Json(
|
Json(
|
||||||
turtle::process_turtle_update(id, &mut state, req).unwrap_or(turtle::TurtleCommand::Update),
|
turtle::process_turtle_update(id, &mut state, req).await.unwrap_or(turtle::TurtleCommand::Update),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,12 +58,13 @@ fn next(from: &Position, world: &World) -> Vec<(Position, u32)> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Blocks that are fine to tunnel through
|
/// Blocks that are fine to tunnel through
|
||||||
const GARBAGE: [&str; 5] = [
|
const GARBAGE: [&str; 6] = [
|
||||||
"minecraft:stone",
|
"minecraft:stone",
|
||||||
"minecraft:dirt",
|
"minecraft:dirt",
|
||||||
"minecraft:andesite",
|
"minecraft:andesite",
|
||||||
"minecraft:sand",
|
"minecraft:sand",
|
||||||
"minecraft:gravel",
|
"minecraft:gravel",
|
||||||
|
"minecraft:sandstone",
|
||||||
];
|
];
|
||||||
|
|
||||||
/// time taken to go through uncharted territory (in turtle. calls)
|
/// time taken to go through uncharted territory (in turtle. calls)
|
||||||
|
|
|
@ -11,10 +11,13 @@ use anyhow::Ok;
|
||||||
use anyhow;
|
use anyhow;
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
use tokio::sync::mpsc;
|
||||||
|
use tokio::sync::oneshot;
|
||||||
|
|
||||||
use super::ControlState;
|
use super::ControlState;
|
||||||
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
use std::future::Ready;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,8 +37,13 @@ pub(crate) struct Turtle {
|
||||||
pub(crate) position: Position,
|
pub(crate) position: Position,
|
||||||
pub(crate) goal: Option<Iota>,
|
pub(crate) goal: Option<Iota>,
|
||||||
pub(crate) pending_update: bool,
|
pub(crate) pending_update: bool,
|
||||||
|
#[serde(skip)]
|
||||||
|
callback: Option<oneshot::Sender<TurtleUpdate>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type Sender = mpsc::Sender<(Iota, oneshot::Sender<TurtleUpdate>)>;
|
||||||
|
pub type Receiver = mpsc::Receiver<(Iota, oneshot::Sender<TurtleUpdate>)>;
|
||||||
|
|
||||||
impl Default for Turtle {
|
impl Default for Turtle {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -45,6 +53,7 @@ impl Default for Turtle {
|
||||||
position: (Vec3::zeros(), Direction::North),
|
position: (Vec3::zeros(), Direction::North),
|
||||||
goal: Default::default(),
|
goal: Default::default(),
|
||||||
pending_update: Default::default(),
|
pending_update: Default::default(),
|
||||||
|
callback: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,24 +67,25 @@ impl Turtle {
|
||||||
position: (position, facing),
|
position: (position, facing),
|
||||||
goal: None,
|
goal: None,
|
||||||
pending_update: true,
|
pending_update: true,
|
||||||
|
callback: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn process_turtle_update(
|
pub(crate) async fn process_turtle_update(
|
||||||
id: u32,
|
id: u32,
|
||||||
state: &mut ControlState,
|
state: &mut ControlState,
|
||||||
update: TurtleUpdate,
|
update: TurtleUpdate,
|
||||||
) -> anyhow::Result<TurtleCommand> {
|
) -> anyhow::Result<TurtleCommand> {
|
||||||
let turtle = state
|
let turtle = state
|
||||||
.turtles
|
.saved.turtles
|
||||||
.get_mut(id as usize)
|
.get_mut(id as usize)
|
||||||
.context("nonexisting turtle")?;
|
.context("nonexisting turtle")?;
|
||||||
let tasks = state
|
let tasks = state
|
||||||
.tasks
|
.saved.tasks
|
||||||
.get_mut(id as usize)
|
.get_mut(id as usize)
|
||||||
.context("state gone?????").unwrap();
|
.context("state gone?????").unwrap();
|
||||||
let world = &mut state.world;
|
let world = &mut state.saved.world;
|
||||||
|
|
||||||
if turtle.pending_update {
|
if turtle.pending_update {
|
||||||
turtle.pending_update = false;
|
turtle.pending_update = false;
|
||||||
|
@ -90,21 +100,21 @@ pub(crate) fn process_turtle_update(
|
||||||
}
|
}
|
||||||
|
|
||||||
let above = Block {
|
let above = Block {
|
||||||
name: update.above,
|
name: update.above.clone(),
|
||||||
pos: turtle.position.0 + Vec3::y(),
|
pos: turtle.position.0 + Vec3::y(),
|
||||||
};
|
};
|
||||||
world.remove_at_point(&above.pos.into());
|
world.remove_at_point(&above.pos.into());
|
||||||
world.insert(above.clone());
|
world.insert(above.clone());
|
||||||
|
|
||||||
let ahead = Block {
|
let ahead = Block {
|
||||||
name: update.ahead,
|
name: update.ahead.clone(),
|
||||||
pos: turtle.position.0 + turtle.position.1.clone().unit(),
|
pos: turtle.position.0 + turtle.position.1.clone().unit(),
|
||||||
};
|
};
|
||||||
world.remove_at_point(&ahead.pos.into());
|
world.remove_at_point(&ahead.pos.into());
|
||||||
world.insert(ahead.clone());
|
world.insert(ahead.clone());
|
||||||
|
|
||||||
let below = Block {
|
let below = Block {
|
||||||
name: update.below,
|
name: update.below.clone(),
|
||||||
pos: turtle.position.0 - Vec3::y(),
|
pos: turtle.position.0 - Vec3::y(),
|
||||||
};
|
};
|
||||||
world.remove_at_point(&below.pos.into());
|
world.remove_at_point(&below.pos.into());
|
||||||
|
@ -116,13 +126,24 @@ pub(crate) fn process_turtle_update(
|
||||||
task.handle_block(ahead);
|
task.handle_block(ahead);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(goal) = tasks.front_mut().map(|t| t.next(&turtle)) {
|
if let Some(send) = turtle.callback.take() {
|
||||||
|
send.send(update).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((cmd, ret)) = state.turtle_receivers.get(id as usize).unwrap().lock().await.try_recv().ok() {
|
||||||
|
turtle.callback = Some(ret);
|
||||||
|
|
||||||
|
turtle.goal = Some(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(goal) = turtle.goal.take().or_else(|| tasks.front_mut().map(|t| t.next(&turtle))) {
|
||||||
let command = match goal {
|
let command = match goal {
|
||||||
Iota::End => {
|
Iota::End => {
|
||||||
tasks.pop_front();
|
tasks.pop_front();
|
||||||
TurtleCommand::Wait(0) // TODO: fix
|
TurtleCommand::Wait(0) // TODO: fix
|
||||||
},
|
},
|
||||||
Iota::Goto(pos) => {
|
Iota::Goto(pos) => {
|
||||||
|
println!("gogto: {:?}", pos);
|
||||||
pathstep(turtle, world, pos).unwrap()
|
pathstep(turtle, world, pos).unwrap()
|
||||||
},
|
},
|
||||||
Iota::Mine(pos) => {
|
Iota::Mine(pos) => {
|
||||||
|
|
Loading…
Reference in a new issue