1
Fork 0

fix the fact that I barely use any memory (in progress)

This commit is contained in:
Andy Killorin 2023-12-25 22:04:58 -06:00
parent 48d7ea164d
commit 8adcfdea38
Signed by: ank
GPG key ID: B6241CA3B552BCA4
5 changed files with 113 additions and 30 deletions

View file

@ -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"

View file

@ -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()
} }

View file

@ -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,

View file

@ -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

View file

@ -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()
} }