diff --git a/Cargo.lock b/Cargo.lock index c483d2fe..e85dcfee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1547,7 +1547,6 @@ dependencies = [ "once_cell", "rand", "rbx_binary", - "rbx_cookie", "rbx_dom_weak", "rbx_reflection", "rbx_reflection_database", @@ -1640,8 +1639,12 @@ dependencies = [ name = "lune-std-roblox" version = "0.1.0" dependencies = [ + "lune-roblox", "lune-utils", "mlua", + "mlua-luau-scheduler 0.0.1", + "once_cell", + "rbx_cookie", ] [[package]] diff --git a/crates/lune-roblox/Cargo.toml b/crates/lune-roblox/Cargo.toml index 2f0ccdda..88eb441b 100644 --- a/crates/lune-roblox/Cargo.toml +++ b/crates/lune-roblox/Cargo.toml @@ -18,8 +18,6 @@ rand = "0.8" thiserror = "1.0" once_cell = "1.17" -rbx_cookie = { version = "0.1.4", default-features = false } - rbx_binary = "0.7.3" rbx_dom_weak = "2.6.0" rbx_reflection = "4.4.0" diff --git a/crates/lune-std-roblox/Cargo.toml b/crates/lune-std-roblox/Cargo.toml index 58a4200c..3a2aef26 100644 --- a/crates/lune-std-roblox/Cargo.toml +++ b/crates/lune-std-roblox/Cargo.toml @@ -12,5 +12,10 @@ workspace = true [dependencies] mlua = { version = "0.9.7", features = ["luau"] } +mlua-luau-scheduler = "0.0.1" + +once_cell = "1.17" +rbx_cookie = { version = "0.1.4", default-features = false } lune-utils = { version = "0.1.0", path = "../lune-utils" } +lune-roblox = { version = "0.1.0", path = "../lune-roblox" } diff --git a/crates/lune-std-roblox/src/lib.rs b/crates/lune-std-roblox/src/lib.rs index 5f26a9e7..d1f6a979 100644 --- a/crates/lune-std-roblox/src/lib.rs +++ b/crates/lune-std-roblox/src/lib.rs @@ -1,6 +1,16 @@ #![allow(clippy::cargo_common_metadata)] use mlua::prelude::*; +use mlua_luau_scheduler::LuaSpawnExt; +use once_cell::sync::OnceCell; + +use lune_roblox::{ + document::{Document, DocumentError, DocumentFormat, DocumentKind}, + instance::{registry::InstanceRegistry, Instance}, + reflection::Database as ReflectionDatabase, +}; + +static REFLECTION_DATABASE: OnceCell = OnceCell::new(); use lune_utils::TableBuilder; @@ -12,5 +22,128 @@ use lune_utils::TableBuilder; Errors when out of memory. */ pub fn module(lua: &Lua) -> LuaResult { - TableBuilder::new(lua)?.build_readonly() + let mut roblox_constants = Vec::new(); + + let roblox_module = lune_roblox::module(lua)?; + for pair in roblox_module.pairs::() { + roblox_constants.push(pair?); + } + + TableBuilder::new(lua)? + .with_values(roblox_constants)? + .with_async_function("deserializePlace", deserialize_place)? + .with_async_function("deserializeModel", deserialize_model)? + .with_async_function("serializePlace", serialize_place)? + .with_async_function("serializeModel", serialize_model)? + .with_function("getAuthCookie", get_auth_cookie)? + .with_function("getReflectionDatabase", get_reflection_database)? + .with_function("implementProperty", implement_property)? + .with_function("implementMethod", implement_method)? + .build_readonly() +} + +async fn deserialize_place<'lua>( + lua: &'lua Lua, + contents: LuaString<'lua>, +) -> LuaResult> { + let bytes = contents.as_bytes().to_vec(); + let fut = lua.spawn_blocking(move || { + let doc = Document::from_bytes(bytes, DocumentKind::Place)?; + let data_model = doc.into_data_model_instance()?; + Ok::<_, DocumentError>(data_model) + }); + fut.await.into_lua_err()?.into_lua(lua) +} + +async fn deserialize_model<'lua>( + lua: &'lua Lua, + contents: LuaString<'lua>, +) -> LuaResult> { + let bytes = contents.as_bytes().to_vec(); + let fut = lua.spawn_blocking(move || { + let doc = Document::from_bytes(bytes, DocumentKind::Model)?; + let instance_array = doc.into_instance_array()?; + Ok::<_, DocumentError>(instance_array) + }); + fut.await.into_lua_err()?.into_lua(lua) +} + +async fn serialize_place<'lua>( + lua: &'lua Lua, + (data_model, as_xml): (LuaUserDataRef<'lua, Instance>, Option), +) -> LuaResult> { + let data_model = (*data_model).clone(); + let fut = lua.spawn_blocking(move || { + let doc = Document::from_data_model_instance(data_model)?; + let bytes = doc.to_bytes_with_format(match as_xml { + Some(true) => DocumentFormat::Xml, + _ => DocumentFormat::Binary, + })?; + Ok::<_, DocumentError>(bytes) + }); + let bytes = fut.await.into_lua_err()?; + lua.create_string(bytes) +} + +async fn serialize_model<'lua>( + lua: &'lua Lua, + (instances, as_xml): (Vec>, Option), +) -> LuaResult> { + let instances = instances.iter().map(|i| (*i).clone()).collect(); + let fut = lua.spawn_blocking(move || { + let doc = Document::from_instance_array(instances)?; + let bytes = doc.to_bytes_with_format(match as_xml { + Some(true) => DocumentFormat::Xml, + _ => DocumentFormat::Binary, + })?; + Ok::<_, DocumentError>(bytes) + }); + let bytes = fut.await.into_lua_err()?; + lua.create_string(bytes) +} + +fn get_auth_cookie(_: &Lua, raw: Option) -> LuaResult> { + if matches!(raw, Some(true)) { + Ok(rbx_cookie::get_value()) + } else { + Ok(rbx_cookie::get()) + } +} + +fn get_reflection_database(_: &Lua, _: ()) -> LuaResult { + Ok(*REFLECTION_DATABASE.get_or_init(ReflectionDatabase::new)) +} + +fn implement_property( + lua: &Lua, + (class_name, property_name, property_getter, property_setter): ( + String, + String, + LuaFunction, + Option, + ), +) -> LuaResult<()> { + let property_setter = if let Some(setter) = property_setter { + setter + } else { + let property_name = property_name.clone(); + lua.create_function(move |_, _: LuaMultiValue| { + Err::<(), _>(LuaError::runtime(format!( + "Property '{property_name}' is read-only" + ))) + })? + }; + InstanceRegistry::insert_property_getter(lua, &class_name, &property_name, property_getter) + .into_lua_err()?; + InstanceRegistry::insert_property_setter(lua, &class_name, &property_name, property_setter) + .into_lua_err()?; + Ok(()) +} + +fn implement_method( + lua: &Lua, + (class_name, method_name, method): (String, String, LuaFunction), +) -> LuaResult<()> { + InstanceRegistry::insert_method(lua, &class_name, &method_name, method).into_lua_err()?; + Ok(()) }