diff --git a/Cargo.lock b/Cargo.lock index 6991ec1..94fddce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1507,7 +1507,7 @@ dependencies = [ "itertools", "lz4_flex", "mlua", - "mlua-luau-scheduler", + "mlua-luau-scheduler 0.0.2", "once_cell", "os_str_bytes", "path-clean", @@ -1592,6 +1592,12 @@ version = "0.8.3" [[package]] name = "lune-std-task" version = "0.8.3" +dependencies = [ + "lune-utils", + "mlua", + "mlua-luau-scheduler 0.0.1", + "tokio", +] [[package]] name = "lune-utils" @@ -1698,6 +1704,23 @@ dependencies = [ "serde-value", ] +[[package]] +name = "mlua-luau-scheduler" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c7ba4c0a49d3549cbb152c72cb447a0e733de8f1b70bb276a010e13addbd72b" +dependencies = [ + "async-executor", + "blocking", + "concurrent-queue", + "derive_more", + "event-listener 4.0.3", + "futures-lite", + "mlua", + "rustc-hash", + "tracing", +] + [[package]] name = "mlua-luau-scheduler" version = "0.0.2" diff --git a/crates/lune-std-task/Cargo.toml b/crates/lune-std-task/Cargo.toml index ee737ca..127a664 100644 --- a/crates/lune-std-task/Cargo.toml +++ b/crates/lune-std-task/Cargo.toml @@ -9,3 +9,11 @@ path = "src/lib.rs" [lints] workspace = true + +[dependencies] +mlua = "0.9.7" +mlua-luau-scheduler = "0.0.1" + +tokio = { version = "1", features = ["time"] } + +lune-utils = { version = "0.8.3", path = "../lune-utils" } diff --git a/crates/lune-std-task/src/lib.rs b/crates/lune-std-task/src/lib.rs index 2e802e7..9909281 100644 --- a/crates/lune-std-task/src/lib.rs +++ b/crates/lune-std-task/src/lib.rs @@ -1 +1,66 @@ #![allow(clippy::cargo_common_metadata)] + +use std::time::Duration; + +use mlua::prelude::*; +use mlua_luau_scheduler::Functions; + +use tokio::time::{sleep, Instant}; + +use lune_utils::TableBuilder; + +/** + Creates the `task` standard library module. + + # Errors + + Errors when out of memory, or if default Lua globals are missing. +*/ +pub fn module(lua: &Lua) -> LuaResult> { + let fns = Functions::new(lua)?; + + // Create wait & delay functions + let task_wait = lua.create_async_function(wait)?; + let task_delay_env = TableBuilder::new(lua)? + .with_value("select", lua.globals().get::<_, LuaFunction>("select")?)? + .with_value("spawn", fns.spawn.clone())? + .with_value("defer", fns.defer.clone())? + .with_value("wait", task_wait.clone())? + .build_readonly()?; + let task_delay = lua + .load(DELAY_IMPL_LUA) + .set_name("task.delay") + .set_environment(task_delay_env) + .into_function()?; + + // Overwrite resume & wrap functions on the coroutine global + // with ones that are compatible with our scheduler + let co = lua.globals().get::<_, LuaTable>("coroutine")?; + co.set("resume", fns.resume.clone())?; + co.set("wrap", fns.wrap.clone())?; + + TableBuilder::new(lua)? + .with_value("cancel", fns.cancel)? + .with_value("defer", fns.defer)? + .with_value("delay", task_delay)? + .with_value("spawn", fns.spawn)? + .with_value("wait", task_wait)? + .build_readonly() +} + +const DELAY_IMPL_LUA: &str = r" +return defer(function(...) + wait(select(1, ...)) + spawn(select(2, ...)) +end, ...) +"; + +async fn wait(_: &Lua, secs: Option) -> LuaResult { + let duration = Duration::from_secs_f64(secs.unwrap_or_default()); + + let before = Instant::now(); + sleep(duration).await; + let after = Instant::now(); + + Ok((after - before).as_secs_f64()) +}