diff --git a/roboscapesim-api/Cargo.toml b/roboscapesim-api/Cargo.toml index dc1e749..9727e18 100644 --- a/roboscapesim-api/Cargo.toml +++ b/roboscapesim-api/Cargo.toml @@ -5,8 +5,8 @@ edition = "2021" [dependencies] async-once-cell = "0.5.3" -axum = "0.6.20" -axum-macros = "0.3.8" +axum = "0.7.1" +axum-macros = "0.4.0" dashmap = "5.5.3" futures-executor = "0.3.29" log = "0.4.20" @@ -18,4 +18,4 @@ serde = "1.0.188" serde_json = "1.0.107" simple_logger = "4.2.0" tokio = { version = "1", features = ["full"] } -tower-http = { version = "0.4", features = ["cors"] } +tower-http = { version = "0.5.0", features = ["cors"] } diff --git a/roboscapesim-api/src/main.rs b/roboscapesim-api/src/main.rs index ccd82f9..18d8696 100644 --- a/roboscapesim-api/src/main.rs +++ b/roboscapesim-api/src/main.rs @@ -58,7 +58,8 @@ async fn main() { let addr = SocketAddr::from(([0, 0, 0, 0], 5001)); debug!("listening on {}", addr); - let server = axum::Server::bind(&addr).serve(app.into_make_service()); + let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); + let server = axum::serve(listener, app.into_make_service()); // Clean up servers not updated in 5 minutes tokio::spawn(async move { diff --git a/roboscapesim-server/Cargo.toml b/roboscapesim-server/Cargo.toml index 8d0576b..e343a36 100644 --- a/roboscapesim-server/Cargo.toml +++ b/roboscapesim-server/Cargo.toml @@ -15,8 +15,8 @@ tokio = { version = "1", features = ["full"] } serde = { version = "1.0" } serde_json = "1.0" rand = "0.8" -axum = {version = "0.6", features = ["headers"]} -tower-http = { version = "0.4", features = ["cors"] } +axum = {version = "0.7.1"} +tower-http = { version = "0.5.0", features = ["cors"] } dashmap = { version = "5.5", features = ["serde", "rayon", "inline"] } once_cell = { version = "1.18", features = ["parking_lot"] } nalgebra = { version = "0.32", features = ["serde", "serde-serialize", "rand"] } @@ -25,8 +25,8 @@ log = "0.4" derivative = "2.2" iotscape = "0.4.0" dotenvy = "0.15" -netsblox-vm = { version = "=0.2.20", default-features = false, features = ["std", "native-tls"] } -axum-macros = "0.3.8" +netsblox-vm = { version = "=0.3.4", default-features = false, features = ["std", "native-tls", "std-system"] } +axum-macros = "0.4.0" reqwest = "0.11" futures = "0.3" rayon = "1.8" diff --git a/roboscapesim-server/src/api.rs b/roboscapesim-server/src/api.rs index a2d9c41..126ad1b 100644 --- a/roboscapesim-server/src/api.rs +++ b/roboscapesim-server/src/api.rs @@ -78,8 +78,8 @@ pub async fn create_api() { .allow_origin(Any) .allow_headers([header::CONTENT_TYPE])); - let server = axum::Server::bind(&addr) - .serve(app.into_make_service()); + let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); + let server = axum::serve(listener, app.into_make_service()); info!("API server listening on {}", addr); diff --git a/roboscapesim-server/src/main.rs b/roboscapesim-server/src/main.rs index 7e09ad8..e0c9baa 100644 --- a/roboscapesim-server/src/main.rs +++ b/roboscapesim-server/src/main.rs @@ -83,6 +83,7 @@ async fn update_fn() { let update_time = get_timestamp(); SHARED_CLOCK.update(); + // Perform updates for kvp in ROOMS.iter() { let m = kvp.value().clone(); diff --git a/roboscapesim-server/src/room.rs b/roboscapesim-server/src/room.rs index 484b291..021d222 100644 --- a/roboscapesim-server/src/room.rs +++ b/roboscapesim-server/src/room.rs @@ -1,21 +1,19 @@ use std::collections::{HashMap, BTreeMap}; use std::rc::Rc; use std::thread::{self, JoinHandle}; -use std::time::{Duration, Instant}; +use std::time::Duration; use std::sync::atomic::{AtomicBool, Ordering}; use dashmap::{DashMap, DashSet}; use derivative::Derivative; use log::{error, info, trace, warn}; use nalgebra::{vector, Vector3, UnitQuaternion}; -use netsblox_vm::runtime::{SimpleValue, ErrorCause, CommandStatus, Command}; -use netsblox_vm::std_system::Clock; -use netsblox_vm::{project::{ProjectStep, IdleAction}, real_time::UtcOffset, runtime::{RequestStatus, Config, Key, System}, std_system::StdSystem}; +use netsblox_vm::real_time::OffsetDateTime; +use netsblox_vm::{runtime::{SimpleValue, ErrorCause, CommandStatus, Command, RequestStatus, Config, Key, System}, std_util::Clock, project::{ProjectStep, IdleAction}, real_time::UtcOffset, std_system::StdSystem}; use once_cell::sync::Lazy; use rand::Rng; use rapier3d::prelude::{ColliderBuilder, RigidBodyBuilder, AngVector, Real, RigidBodyHandle}; -use roboscapesim_common::*; -use roboscapesim_common::api::RoomInfo; +use roboscapesim_common::{*, api::RoomInfo}; use tokio::{spawn, time::sleep}; use std::sync::{mpsc, Arc, Mutex}; @@ -33,6 +31,8 @@ use crate::util::traits::resettable::{Resettable, RigidBodyResetter}; use crate::vm::{STEPS_PER_IO_ITER, open_project, YIELDS_BEFORE_IDLE_SLEEP, IDLE_SLEEP_TIME, DEFAULT_BASE_URL, C, get_env}; pub(crate) mod netsblox_api; +const COLLECT_PERIOD: Duration = Duration::from_secs(60); + #[derive(Derivative)] #[derivative(Debug)] /// Holds the data for a single room @@ -47,7 +47,7 @@ pub struct RoomData { pub sockets: DashMap>, /// List of usernames of users who have visited the room pub visitors: DashSet, - pub last_update: Instant, + pub last_update: OffsetDateTime, pub last_full_update: i64, pub hibernating_since: Arc>>, pub roomtime: f64, @@ -102,7 +102,7 @@ impl RoomData { last_full_update: 0, roomtime: 0.0, sim: Arc::new(Mutex::new(Simulation::new())), - last_update: Instant::now(), + last_update: SHARED_CLOCK.read(netsblox_vm::runtime::Precision::Medium), robots: Arc::new(DashMap::new()), reseters: HashMap::new(), services: Arc::new(DashMap::new()), @@ -157,6 +157,7 @@ impl RoomData { let hibernating = obj.hibernating.clone(); let hibernating_since = obj.hibernating_since.clone(); let id_clone = obj.name.clone(); + let id_clone2 = obj.name.clone(); let robots = obj.robots.clone(); obj.vm_thread = Some(thread::spawn(move || { tokio::runtime::Builder::new_current_thread() @@ -165,13 +166,13 @@ impl RoomData { .unwrap() .block_on(async { let project = load_environment(environment).await; - + // Setup VM let (project_name, role) = open_project(&project).unwrap_or_else(|_| panic!("failed to read file")); let mut idle_sleeper = IdleAction::new(YIELDS_BEFORE_IDLE_SLEEP, Box::new(|| thread::sleep(IDLE_SLEEP_TIME))); info!("Loading project {}", project_name); let system = Rc::new(StdSystem::new_async(DEFAULT_BASE_URL.to_owned(), Some(&project_name), Config { - request: Some(Rc::new(move |_system: &StdSystem, _, key, request, _| { + request: Some(Rc::new(move |_mc, key, request: netsblox_vm::runtime::Request<'_, C, StdSystem>, _proc| { match &request { netsblox_vm::runtime::Request::Rpc { service, rpc, args } => { match args.iter().map(|(_k, v)| Ok(v.to_simple()?.into_json()?)).collect::,ErrorCause<_,_>>>() { @@ -211,7 +212,7 @@ impl RoomData { _ => RequestStatus::UseDefault { key, request }, } })), - command: Some(Rc::new(move |_, _, key, command, proc| match command { + command: Some(Rc::new(move |_mc, key, command, proc| match command { Command::Print { style: _, value } => { let entity = &*proc.get_call_stack().last().unwrap().entity.borrow(); if let Some(value) = value { info!("{entity:?} > {value:?}") } @@ -231,7 +232,7 @@ impl RoomData { } }; - let env = env.unwrap(); + let mut env = env.unwrap(); info!("Loaded"); // Start program @@ -240,6 +241,8 @@ impl RoomData { proj.input(mc, netsblox_vm::project::Input::Start); }); + let mut last_collect_time = SHARED_CLOCK.read(netsblox_vm::runtime::Precision::Medium); + // Run program loop { if hibernating.load(Ordering::Relaxed) && hibernating_since.lock().unwrap().clone().unwrap_or(0) < get_timestamp() + 2 { @@ -266,6 +269,12 @@ impl RoomData { idle_sleeper.consume(&res); } }); + + if SHARED_CLOCK.read(netsblox_vm::runtime::Precision::Medium) > last_collect_time + COLLECT_PERIOD { + trace!("Collecting garbage for room {}", id_clone2); + env.collect_all(); + last_collect_time = SHARED_CLOCK.read(netsblox_vm::runtime::Precision::Medium); + } } } }); @@ -414,7 +423,7 @@ impl RoomData { } pub fn update(&mut self) { - let now = Instant::now(); + let now = SHARED_CLOCK.read(netsblox_vm::runtime::Precision::Medium); let delta_time = 1.0 / 60.0; if !self.hibernating.load(Ordering::Relaxed) { diff --git a/roboscapesim-server/src/vm.rs b/roboscapesim-server/src/vm.rs index 34ca74e..48490f3 100644 --- a/roboscapesim-server/src/vm.rs +++ b/roboscapesim-server/src/vm.rs @@ -1,7 +1,7 @@ use std::{fmt, rc::Rc}; use std::time::Duration; -use netsblox_vm::runtime::{GetType, SimpleValue, Entity}; -use netsblox_vm::{ast, runtime::{CustomTypes, Value, EntityKind, ErrorCause, FromAstError, Settings}, gc::{Mutation, Collect, RefLock, Gc, Arena, Rootable}, project::Project, bytecode::{Locations, ByteCode}, std_system::StdSystem}; +use netsblox_vm::runtime::{GetType, SimpleValue, ProcessKind}; +use netsblox_vm::{ast, runtime::{CustomTypes, Value, EntityKind, FromAstError, Settings}, gc::{Mutation, Collect, RefLock, Gc, Arena, Rootable}, project::Project, bytecode::{Locations, ByteCode}, std_system::StdSystem}; pub const DEFAULT_BASE_URL: &str = "https://cloud.netsblox.org"; pub const STEPS_PER_IO_ITER: usize = 64; @@ -17,8 +17,8 @@ pub struct Env<'gc, C: CustomTypes>> { pub type EnvArena = Arena]>; pub struct ProcessState; -impl From<&Entity<'_, C, StdSystem>> for ProcessState { - fn from(_: &Entity<'_, C, StdSystem>) -> Self { +impl From>> for ProcessState { + fn from(_: ProcessKind<'_, '_, C, StdSystem>) -> Self { ProcessState } } @@ -60,8 +60,8 @@ impl CustomTypes> for C { type EntityState = EntityState; type ProcessState = ProcessState; - fn from_intermediate<'gc>(mc: &Mutation<'gc>, value: Self::Intermediate) -> Result>, ErrorCause>> { - Ok(Value::from_simple(mc, value)) + fn from_intermediate<'gc>(mc: &Mutation<'gc>, value: Self::Intermediate) -> Value<'gc, C, StdSystem> { + Value::from_simple(mc, value) } }