fix the fact that I barely use any memory (in progress)
This commit is contained in:
parent
48d7ea164d
commit
8adcfdea38
5 changed files with 113 additions and 30 deletions
|
@ -40,3 +40,4 @@ opentelemetry = "0.21.0"
|
||||||
tracing-opentelemetry = "0.22"
|
tracing-opentelemetry = "0.22"
|
||||||
opentelemetry-jaeger = { version = "0.20", features = ["rt-tokio"] }
|
opentelemetry-jaeger = { version = "0.20", features = ["rt-tokio"] }
|
||||||
opentelemetry_sdk = { version = "0.21.1", features = ["trace"] }
|
opentelemetry_sdk = { version = "0.21.1", features = ["trace"] }
|
||||||
|
memoize = "0.4.2"
|
||||||
|
|
|
@ -1,46 +1,84 @@
|
||||||
use std::{sync::Arc, ops::Sub};
|
use std::{sync::Arc, ops::Sub};
|
||||||
|
|
||||||
|
use anyhow::Ok;
|
||||||
use nalgebra::Vector3;
|
use nalgebra::Vector3;
|
||||||
use rstar::{PointDistance, RTree, RTreeObject, AABB};
|
use rstar::{PointDistance, RTree, RTreeObject, AABB, Envelope};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tokio::sync::{RwLock, OwnedRwLockReadGuard};
|
use tokio::sync::{RwLock, OwnedRwLockReadGuard};
|
||||||
|
use memoize::memoize;
|
||||||
|
|
||||||
use crate::{turtle::TurtleCommand, paths::{self, TRANSPARENT}};
|
use crate::{turtle::TurtleCommand, paths::{self, TRANSPARENT}};
|
||||||
|
|
||||||
pub type WorldReadLock = OwnedRwLockReadGuard<RTree<Block>>;
|
const CHUNK_SIZE: usize = 16;
|
||||||
|
const CHUNK_VOLUME: usize = CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE;
|
||||||
|
const CHUNK_VEC: Vec3 = Vec3::new(CHUNK_SIZE as i32, CHUNK_SIZE as i32, CHUNK_SIZE as i32);
|
||||||
|
|
||||||
#[derive(Clone)]
|
pub struct World(RTree<Chunk>);
|
||||||
pub struct World {
|
|
||||||
state: Arc<RwLock<RTree<Block>>>, // interior mutability to get around the
|
|
||||||
// questionable architecture of this project
|
|
||||||
}
|
|
||||||
|
|
||||||
impl World {
|
impl World {
|
||||||
|
pub fn get(&self, block: Vec3) -> Option<Block> {
|
||||||
|
let chunk = block.component_div(&CHUNK_VEC);
|
||||||
|
let chunk = self.get_chunk(chunk)?;
|
||||||
|
chunk.get(block)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set(&mut self, block: Block) -> Option<Block> {
|
||||||
|
let chunk = block.pos.component_div(&CHUNK_VEC);
|
||||||
|
let chunk = self.get_chunk(chunk)?;
|
||||||
|
chunk.set(block)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_chunk(&self, block: Vec3) -> &Chunk {
|
||||||
|
let block = block.component_div(&CHUNK_VEC);
|
||||||
|
if let Some(chunk) = self.0.locate_at_point(&block.into()) {
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
self.0.insert(Chunk::new(block));
|
||||||
|
&Chunk::new(block)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_bulk<const COUNT:usize>(&self, blocks: [Vec3;COUNT]) -> [Option<&Block>;COUNT] {
|
||||||
|
let mut chunk: Option<&Chunk> = None;
|
||||||
|
|
||||||
|
blocks.iter().map(|b|{
|
||||||
|
if !chunk.is_some_and(|c| c.contains(b)) {
|
||||||
|
chunk = Some(self.get_chunk(b));
|
||||||
|
}
|
||||||
|
chunk.unwrap().get(b)
|
||||||
|
}).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SharedWorld {
|
||||||
|
state: Arc<RwLock<World>>, // interior mutability to get around the
|
||||||
|
// questionable architecture of this project
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SharedWorld {
|
||||||
pub fn new() -> Self { Self { state: Arc::new(RwLock::new(RTree::new())) } }
|
pub fn new() -> Self { Self { state: Arc::new(RwLock::new(RTree::new())) } }
|
||||||
pub fn from_tree(tree: RTree<Block>) -> Self { Self { state: Arc::new(RwLock::new(tree)) } }
|
pub fn from_world(tree: World) -> Self { Self { state: Arc::new(RwLock::new(tree)) } }
|
||||||
pub async fn to_tree(self) -> RTree<Block> { self.state.write().await.to_owned() }
|
|
||||||
pub async fn tree(&self) -> RTree<Block> { self.state.read().await.clone() }
|
|
||||||
|
|
||||||
pub async fn get(&self, block: Vec3) -> Option<Block> {
|
pub async fn get(&self, block: Vec3) -> Option<Block> {
|
||||||
self.state.read().await.locate_at_point(&block.into()).map(|b| b.to_owned())
|
self.state.read().await.get(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn set(&self, block: Block) {
|
pub async fn set(&self, block: Block) {
|
||||||
self.state.write().await.remove_at_point(&block.pos.into());
|
self.state.write().await.set(block);
|
||||||
self.state.write().await.insert(block);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if a known non-traversable block exists at the point
|
/// Returns true if a known non-traversable block exists at the point
|
||||||
pub async fn occupied(&self, block: Vec3) -> bool {
|
pub async fn occupied(&self, block: Vec3) -> bool {
|
||||||
self.state.read().await.locate_at_point(&block.into()).is_some_and(|b| !TRANSPARENT.contains(&b.name.as_str()))
|
self.get(block).await.is_some_and(|b| !TRANSPARENT.contains(&b.name.as_str()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if a "garbage" block exists at the given point which you are free to destroy
|
/// Returns true if a "garbage" block exists at the given point which you are free to destroy
|
||||||
pub async fn garbage(&self, block: Vec3) -> bool {
|
pub async fn garbage(&self, block: Vec3) -> bool {
|
||||||
self.state.read().await.locate_at_point(&block.into()).is_some_and(|b| paths::difficulty(&b.name).is_some())
|
self.get(block).await.is_some_and(|b| paths::difficulty(&b.name).is_some())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn lock(self) -> WorldReadLock {
|
pub async fn lock(self) -> OwnedRwLockReadGuard<World> {
|
||||||
self.state.read_owned().await
|
self.state.read_owned().await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,7 +89,50 @@ pub struct Block {
|
||||||
pub pos: Vec3,
|
pub pos: Vec3,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RTreeObject for Block {
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
pub struct Chunk {
|
||||||
|
pos: Vec3, /// position in chunk coordinates (world/16)
|
||||||
|
data: [[[Option<Block>;CHUNK_SIZE];CHUNK_SIZE];CHUNK_SIZE]
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Chunk {
|
||||||
|
fn new(pos: Vec3) -> Self {
|
||||||
|
Self {
|
||||||
|
pos,
|
||||||
|
data:[[[None;CHUNK_SIZE];CHUNK_SIZE];CHUNK_SIZE]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set(&mut self, pos: Block) -> Result<(), ()> {
|
||||||
|
let chunk = self.pos.component_mul(&CHUNK_VEC);
|
||||||
|
let local = pos.pos - chunk;
|
||||||
|
if !self.contains(&pos) {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.data[local.x][local.y][local.z] = pos;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(&self, pos: Position) -> Option<&Block> {
|
||||||
|
let chunk = self.pos.component_mul(&CHUNK_VEC);
|
||||||
|
let local = pos.pos - chunk;
|
||||||
|
if !self.contains(&pos) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(self.data[local.x][local.y][local.z])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains(&self, pos:&Position) -> bool {
|
||||||
|
let chunk = self.pos.component_mul(&CHUNK_VEC);
|
||||||
|
let local = pos.pos - chunk;
|
||||||
|
AABB::from_corners(chunk, chunk+CHUNK_VEC).contains_point(&local)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RTreeObject for Chunk {
|
||||||
type Envelope = AABB<[i32; 3]>;
|
type Envelope = AABB<[i32; 3]>;
|
||||||
|
|
||||||
fn envelope(&self) -> Self::Envelope {
|
fn envelope(&self) -> Self::Envelope {
|
||||||
|
@ -59,7 +140,7 @@ impl RTreeObject for Block {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PointDistance for Block {
|
impl PointDistance for Chunk {
|
||||||
fn distance_2(&self, point: &[i32; 3]) -> i32 {
|
fn distance_2(&self, point: &[i32; 3]) -> i32 {
|
||||||
(self.pos - Vec3::from(*point)).abs().sum()
|
(self.pos - Vec3::from(*point)).abs().sum()
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ use axum::{
|
||||||
routing::{get},
|
routing::{get},
|
||||||
Router,
|
Router,
|
||||||
};
|
};
|
||||||
use blocks::{World, Position, };
|
use blocks::{SharedWorld, Position, };
|
||||||
use depot::Depots;
|
use depot::Depots;
|
||||||
use opentelemetry::global;
|
use opentelemetry::global;
|
||||||
use opentelemetry_sdk::{runtime::Tokio, trace::BatchConfig};
|
use opentelemetry_sdk::{runtime::Tokio, trace::BatchConfig};
|
||||||
|
@ -220,7 +220,7 @@ struct SavedState {
|
||||||
struct LiveState {
|
struct LiveState {
|
||||||
turtles: Vec<Arc<RwLock<turtle::Turtle>>>,
|
turtles: Vec<Arc<RwLock<turtle::Turtle>>>,
|
||||||
tasks: Scheduler,
|
tasks: Scheduler,
|
||||||
world: blocks::World,
|
world: blocks::SharedWorld,
|
||||||
depots: Depots,
|
depots: Depots,
|
||||||
started: Instant,
|
started: Instant,
|
||||||
kill: watch::Sender<bool>,
|
kill: watch::Sender<bool>,
|
||||||
|
@ -244,7 +244,7 @@ impl LiveState {
|
||||||
};
|
};
|
||||||
let depots = Depots::from_vec(save.depots);
|
let depots = Depots::from_vec(save.depots);
|
||||||
|
|
||||||
Self { turtles: turtles.into_iter().map(|t| Arc::new(RwLock::new(t))).collect(), tasks: scheduler, world: World::from_tree(save.world),
|
Self { turtles: turtles.into_iter().map(|t| Arc::new(RwLock::new(t))).collect(), tasks: scheduler, world: SharedWorld::from_tree(save.world),
|
||||||
depots,
|
depots,
|
||||||
started: Instant::now(),
|
started: Instant::now(),
|
||||||
kill:sender,
|
kill:sender,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
blocks::{World, Position, Direction, Vec3, WorldReadLock},
|
blocks::{SharedWorld, Position, Direction, Vec3, World, nearest, Block},
|
||||||
};
|
};
|
||||||
|
use rstar::{AABB, Envelope};
|
||||||
use tokio::task::spawn_blocking;
|
use tokio::task::spawn_blocking;
|
||||||
use tracing::{trace, error};
|
use tracing::{trace, error};
|
||||||
use pathfinding::prelude::astar;
|
use pathfinding::prelude::astar;
|
||||||
|
@ -8,7 +9,7 @@ use pathfinding::prelude::astar;
|
||||||
const LOOKUP_LIMIT: usize = 10_000_000;
|
const LOOKUP_LIMIT: usize = 10_000_000;
|
||||||
|
|
||||||
#[tracing::instrument(skip(world))]
|
#[tracing::instrument(skip(world))]
|
||||||
pub async fn route_facing(from: Position, to: Vec3, world: &World) -> Option<Vec<Position>> {
|
pub async fn route_facing(from: Position, to: Vec3, world: &SharedWorld) -> Option<Vec<Position>> {
|
||||||
let facing = move |p: &Position| {
|
let facing = move |p: &Position| {
|
||||||
let ahead = p.dir.unit() + p.pos;
|
let ahead = p.dir.unit() + p.pos;
|
||||||
let above = Vec3::y() + p.pos;
|
let above = Vec3::y() + p.pos;
|
||||||
|
@ -19,7 +20,7 @@ pub async fn route_facing(from: Position, to: Vec3, world: &World) -> Option<Vec
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(world))]
|
#[tracing::instrument(skip(world))]
|
||||||
pub async fn route(from: Position, to: Position, world: &World) -> Option<Vec<Position>> {
|
pub async fn route(from: Position, to: Position, world: &SharedWorld) -> Option<Vec<Position>> {
|
||||||
trace!("routing from {from:?} to {to:?}");
|
trace!("routing from {from:?} to {to:?}");
|
||||||
// attempt at not crashing by looking infinitely into the abyss
|
// attempt at not crashing by looking infinitely into the abyss
|
||||||
if world.get(to.pos).await
|
if world.get(to.pos).await
|
||||||
|
@ -30,7 +31,7 @@ pub async fn route(from: Position, to: Position, world: &World) -> Option<Vec<Po
|
||||||
route_to(from, to.pos, move |p| p == &to, world).await
|
route_to(from, to.pos, move |p| p == &to, world).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn route_to<D>(from: Position, to: Vec3, mut done: D, world: &World) -> Option<Vec<Position>>
|
async fn route_to<D>(from: Position, to: Vec3, mut done: D, world: &SharedWorld) -> Option<Vec<Position>>
|
||||||
where D: FnMut(&Position) -> bool + Send + 'static {
|
where D: FnMut(&Position) -> bool + Send + 'static {
|
||||||
// lock once, we'll be doing a lot of lookups
|
// lock once, we'll be doing a lot of lookups
|
||||||
let world = world.clone().lock().await;
|
let world = world.clone().lock().await;
|
||||||
|
@ -62,14 +63,14 @@ where D: FnMut(&Position) -> bool + Send + 'static {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next(from: &Position, world: &WorldReadLock) -> Vec<(Position, u32)> {
|
fn next(from: &Position, world: &World) -> Vec<(Position, u32)> {
|
||||||
let mut vec: Vec<(Position, u32)> = Vec::new();
|
let mut vec: Vec<(Position, u32)> = Vec::new();
|
||||||
|
|
||||||
fn insert(
|
fn insert(
|
||||||
vec: &mut Vec<(Position, u32)>,
|
vec: &mut Vec<(Position, u32)>,
|
||||||
point: Vec3,
|
point: Vec3,
|
||||||
orientation: Direction,
|
orientation: Direction,
|
||||||
world: &WorldReadLock,
|
world: &World,
|
||||||
unknown: Option<u32>,
|
unknown: Option<u32>,
|
||||||
) {
|
) {
|
||||||
world
|
world
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::blocks::Block;
|
||||||
use crate::blocks::Direction;
|
use crate::blocks::Direction;
|
||||||
use crate::blocks::Position;
|
use crate::blocks::Position;
|
||||||
use crate::blocks::Vec3;
|
use crate::blocks::Vec3;
|
||||||
use crate::blocks::World;
|
use crate::blocks::SharedWorld;
|
||||||
use crate::depot::Depots;
|
use crate::depot::Depots;
|
||||||
use crate::paths::route_facing;
|
use crate::paths::route_facing;
|
||||||
|
|
||||||
|
@ -144,7 +144,7 @@ impl Turtle {
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct TurtleCommander {
|
pub struct TurtleCommander {
|
||||||
sender: Arc<Sender>,
|
sender: Arc<Sender>,
|
||||||
world: World,
|
world: SharedWorld,
|
||||||
depots: Depots,
|
depots: Depots,
|
||||||
// everything below is best-effort
|
// everything below is best-effort
|
||||||
// TODO: make not bad
|
// TODO: make not bad
|
||||||
|
@ -221,7 +221,7 @@ impl TurtleCommander {
|
||||||
self.max_fuel.load(std::sync::atomic::Ordering::SeqCst)
|
self.max_fuel.load(std::sync::atomic::Ordering::SeqCst)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn world(&self) -> World {
|
pub fn world(&self) -> SharedWorld {
|
||||||
self.world.clone()
|
self.world.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue