Refactor export tables for roblox builtin library

This commit is contained in:
Filip Tibell 2023-08-23 14:53:52 -05:00
parent 57e2072bc0
commit de0f017540
No known key found for this signature in database
27 changed files with 999 additions and 837 deletions

View file

@ -16,6 +16,8 @@ pub struct LuneError {
disable_colors: bool,
}
// TODO: Rename this struct to "RuntimeError" instead for
// the next breaking release, it's a more fitting name
impl LuneError {
/**
Enables colorization of the error message when formatted using the [`Display`] trait.

View file

@ -6,12 +6,16 @@ mod builtins;
mod error;
mod globals;
mod scheduler;
mod util;
pub(crate) mod util;
use self::scheduler::{LuaSchedulerExt, Scheduler};
pub use error::LuneError;
// TODO: Rename this struct to "Runtime" instead for the
// next breaking release, it's a more fitting name and
// will probably be more obvious when browsing files
#[derive(Debug, Clone)]
pub struct Lune {
lua: &'static Lua,

View file

@ -3,6 +3,8 @@ use core::fmt;
use mlua::prelude::*;
use rbx_dom_weak::types::Axes as DomAxes;
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable};
use super::{super::*, EnumItem};
/**
@ -17,53 +19,58 @@ pub struct Axes {
pub(crate) z: bool,
}
impl Axes {
pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> {
datatype_table.set(
"new",
lua.create_function(|_, args: LuaMultiValue| {
let mut x = false;
let mut y = false;
let mut z = false;
let mut check = |e: &EnumItem| {
if e.parent.desc.name == "Axis" {
match &e.name {
name if name == "X" => x = true,
name if name == "Y" => y = true,
name if name == "Z" => z = true,
_ => {}
}
} else if e.parent.desc.name == "NormalId" {
match &e.name {
name if name == "Left" || name == "Right" => x = true,
name if name == "Top" || name == "Bottom" => y = true,
name if name == "Front" || name == "Back" => z = true,
_ => {}
}
impl LuaExportsTable<'_> for Axes {
const EXPORT_NAME: &'static str = "Axes";
fn create_exports_table(lua: &Lua) -> LuaResult<LuaTable> {
let axes_new = |_, args: LuaMultiValue| {
let mut x = false;
let mut y = false;
let mut z = false;
let mut check = |e: &EnumItem| {
if e.parent.desc.name == "Axis" {
match &e.name {
name if name == "X" => x = true,
name if name == "Y" => y = true,
name if name == "Z" => z = true,
_ => {}
}
};
for (index, arg) in args.into_iter().enumerate() {
if let LuaValue::UserData(u) = arg {
if let Ok(e) = u.borrow::<EnumItem>() {
check(&e);
} else {
return Err(LuaError::RuntimeError(format!(
"Expected argument #{} to be an EnumItem, got userdata",
index
)));
}
} else {
return Err(LuaError::RuntimeError(format!(
"Expected argument #{} to be an EnumItem, got {}",
index,
arg.type_name()
)));
} else if e.parent.desc.name == "NormalId" {
match &e.name {
name if name == "Left" || name == "Right" => x = true,
name if name == "Top" || name == "Bottom" => y = true,
name if name == "Front" || name == "Back" => z = true,
_ => {}
}
}
Ok(Axes { x, y, z })
})?,
)?;
Ok(())
};
for (index, arg) in args.into_iter().enumerate() {
if let LuaValue::UserData(u) = arg {
if let Ok(e) = u.borrow::<EnumItem>() {
check(&e);
} else {
return Err(LuaError::RuntimeError(format!(
"Expected argument #{} to be an EnumItem, got userdata",
index
)));
}
} else {
return Err(LuaError::RuntimeError(format!(
"Expected argument #{} to be an EnumItem, got {}",
index,
arg.type_name()
)));
}
}
Ok(Axes { x, y, z })
};
TableBuilder::new(lua)?
.with_function("new", axes_new)?
.build_readonly()
}
}

View file

@ -4,6 +4,8 @@ use mlua::prelude::*;
use rand::seq::SliceRandom;
use rbx_dom_weak::types::BrickColor as DomBrickColor;
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable};
use super::{super::*, Color3};
/**
@ -20,57 +22,58 @@ pub struct BrickColor {
pub(crate) rgb: (u8, u8, u8),
}
impl BrickColor {
pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> {
impl LuaExportsTable<'_> for BrickColor {
const EXPORT_NAME: &'static str = "BrickColor";
fn create_exports_table(lua: &Lua) -> LuaResult<LuaTable> {
type ArgsNumber = u16;
type ArgsName = String;
type ArgsRgb = (u8, u8, u8);
type ArgsColor3<'lua> = LuaUserDataRef<'lua, Color3>;
datatype_table.set(
"new",
lua.create_function(|lua, args: LuaMultiValue| {
if let Ok(number) = ArgsNumber::from_lua_multi(args.clone(), lua) {
Ok(color_from_number(number))
} else if let Ok(name) = ArgsName::from_lua_multi(args.clone(), lua) {
Ok(color_from_name(name))
} else if let Ok((r, g, b)) = ArgsRgb::from_lua_multi(args.clone(), lua) {
Ok(color_from_rgb(r, g, b))
} else if let Ok(color) = ArgsColor3::from_lua_multi(args.clone(), lua) {
Ok(Self::from(*color))
} else {
// FUTURE: Better error message here using given arg types
Err(LuaError::RuntimeError(
"Invalid arguments to constructor".to_string(),
))
}
})?,
)?;
datatype_table.set(
"palette",
lua.create_function(|_, index: u16| {
if index == 0 {
Err(LuaError::RuntimeError("Invalid index".to_string()))
} else if let Some(number) = BRICK_COLOR_PALETTE.get((index - 1) as usize) {
Ok(color_from_number(*number))
} else {
Err(LuaError::RuntimeError("Invalid index".to_string()))
}
})?,
)?;
datatype_table.set(
"random",
lua.create_function(|_, ()| {
let number = BRICK_COLOR_PALETTE.choose(&mut rand::thread_rng());
Ok(color_from_number(*number.unwrap()))
})?,
)?;
let brick_color_new = |lua, args: LuaMultiValue| {
if let Ok(number) = ArgsNumber::from_lua_multi(args.clone(), lua) {
Ok(color_from_number(number))
} else if let Ok(name) = ArgsName::from_lua_multi(args.clone(), lua) {
Ok(color_from_name(name))
} else if let Ok((r, g, b)) = ArgsRgb::from_lua_multi(args.clone(), lua) {
Ok(color_from_rgb(r, g, b))
} else if let Ok(color) = ArgsColor3::from_lua_multi(args.clone(), lua) {
Ok(Self::from(*color))
} else {
// FUTURE: Better error message here using given arg types
Err(LuaError::RuntimeError(
"Invalid arguments to constructor".to_string(),
))
}
};
let brick_color_palette = |_, index: u16| {
if index == 0 {
Err(LuaError::RuntimeError("Invalid index".to_string()))
} else if let Some(number) = BRICK_COLOR_PALETTE.get((index - 1) as usize) {
Ok(color_from_number(*number))
} else {
Err(LuaError::RuntimeError("Invalid index".to_string()))
}
};
let brick_color_random = |_, ()| {
let number = BRICK_COLOR_PALETTE.choose(&mut rand::thread_rng());
Ok(color_from_number(*number.unwrap()))
};
let mut builder = TableBuilder::new(lua)?
.with_function("new", brick_color_new)?
.with_function("palette", brick_color_palette)?
.with_function("random", brick_color_random)?;
for (name, number) in BRICK_COLOR_CONSTRUCTORS {
datatype_table.set(
*name,
lua.create_function(|_, ()| Ok(color_from_number(*number)))?,
)?;
let f = |_, ()| Ok(color_from_number(*number));
builder = builder.with_function(*name, f)?;
}
Ok(())
builder.build_readonly()
}
}

View file

@ -5,6 +5,8 @@ use glam::{EulerRot, Mat4, Quat, Vec3};
use mlua::prelude::*;
use rbx_dom_weak::types::{CFrame as DomCFrame, Matrix3 as DomMatrix3, Vector3 as DomVector3};
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable};
use super::{super::*, Vector3};
/**
@ -35,79 +37,61 @@ impl CFrame {
fn inverse(&self) -> Self {
Self(self.0.inverse())
}
}
impl LuaExportsTable<'_> for CFrame {
const EXPORT_NAME: &'static str = "CFrame";
fn create_exports_table(lua: &Lua) -> LuaResult<LuaTable> {
let cframe_angles = |_, (rx, ry, rz): (f32, f32, f32)| {
Ok(CFrame(Mat4::from_euler(EulerRot::XYZ, rx, ry, rz)))
};
let cframe_from_axis_angle =
|_, (v, r): (LuaUserDataRef<Vector3>, f32)| Ok(CFrame(Mat4::from_axis_angle(v.0, r)));
let cframe_from_euler_angles_xyz = |_, (rx, ry, rz): (f32, f32, f32)| {
Ok(CFrame(Mat4::from_euler(EulerRot::XYZ, rx, ry, rz)))
};
let cframe_from_euler_angles_yxz = |_, (rx, ry, rz): (f32, f32, f32)| {
Ok(CFrame(Mat4::from_euler(EulerRot::YXZ, ry, rx, rz)))
};
let cframe_from_matrix = |_,
(pos, rx, ry, rz): (
LuaUserDataRef<Vector3>,
LuaUserDataRef<Vector3>,
LuaUserDataRef<Vector3>,
Option<LuaUserDataRef<Vector3>>,
)| {
Ok(CFrame(Mat4::from_cols(
rx.0.extend(0.0),
ry.0.extend(0.0),
rz.map(|r| r.0)
.unwrap_or_else(|| rx.0.cross(ry.0).normalize())
.extend(0.0),
pos.0.extend(1.0),
)))
};
let cframe_from_orientation = |_, (rx, ry, rz): (f32, f32, f32)| {
Ok(CFrame(Mat4::from_euler(EulerRot::YXZ, ry, rx, rz)))
};
let cframe_look_at = |_,
(from, to, up): (
LuaUserDataRef<Vector3>,
LuaUserDataRef<Vector3>,
Option<LuaUserDataRef<Vector3>>,
)| {
Ok(CFrame(look_at(
from.0,
to.0,
up.as_deref().unwrap_or(&Vector3(Vec3::Y)).0,
)))
};
pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> {
// Constants
datatype_table.set("identity", CFrame(Mat4::IDENTITY))?;
// Strict args constructors
datatype_table.set(
"lookAt",
lua.create_function(
|_,
(from, to, up): (
LuaUserDataRef<Vector3>,
LuaUserDataRef<Vector3>,
Option<LuaUserDataRef<Vector3>>,
)| {
Ok(CFrame(look_at(
from.0,
to.0,
up.as_deref().unwrap_or(&Vector3(Vec3::Y)).0,
)))
},
)?,
)?;
datatype_table.set(
"fromEulerAnglesXYZ",
lua.create_function(|_, (rx, ry, rz): (f32, f32, f32)| {
Ok(CFrame(Mat4::from_euler(EulerRot::XYZ, rx, ry, rz)))
})?,
)?;
datatype_table.set(
"fromEulerAnglesYXZ",
lua.create_function(|_, (rx, ry, rz): (f32, f32, f32)| {
Ok(CFrame(Mat4::from_euler(EulerRot::YXZ, ry, rx, rz)))
})?,
)?;
datatype_table.set(
"Angles",
lua.create_function(|_, (rx, ry, rz): (f32, f32, f32)| {
Ok(CFrame(Mat4::from_euler(EulerRot::XYZ, rx, ry, rz)))
})?,
)?;
datatype_table.set(
"fromOrientation",
lua.create_function(|_, (rx, ry, rz): (f32, f32, f32)| {
Ok(CFrame(Mat4::from_euler(EulerRot::YXZ, ry, rx, rz)))
})?,
)?;
datatype_table.set(
"fromAxisAngle",
lua.create_function(|_, (v, r): (LuaUserDataRef<Vector3>, f32)| {
Ok(CFrame(Mat4::from_axis_angle(v.0, r)))
})?,
)?;
datatype_table.set(
"fromMatrix",
lua.create_function(
|_,
(pos, rx, ry, rz): (
LuaUserDataRef<Vector3>,
LuaUserDataRef<Vector3>,
LuaUserDataRef<Vector3>,
Option<LuaUserDataRef<Vector3>>,
)| {
Ok(CFrame(Mat4::from_cols(
rx.0.extend(0.0),
ry.0.extend(0.0),
rz.map(|r| r.0)
.unwrap_or_else(|| rx.0.cross(ry.0).normalize())
.extend(0.0),
pos.0.extend(1.0),
)))
},
)?,
)?;
// Dynamic args constructor
type ArgsPos<'lua> = LuaUserDataRef<'lua, Vector3>;
type ArgsLook<'lua> = (
@ -115,48 +99,59 @@ impl CFrame {
LuaUserDataRef<'lua, Vector3>,
Option<LuaUserDataRef<'lua, Vector3>>,
);
type ArgsPosXYZ = (f32, f32, f32);
type ArgsPosXYZQuat = (f32, f32, f32, f32, f32, f32, f32);
type ArgsMatrix = (f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32, f32);
datatype_table.set(
"new",
lua.create_function(|lua, args: LuaMultiValue| {
if args.clone().into_vec().is_empty() {
Ok(CFrame(Mat4::IDENTITY))
} else if let Ok(pos) = ArgsPos::from_lua_multi(args.clone(), lua) {
Ok(CFrame(Mat4::from_translation(pos.0)))
} else if let Ok((from, to, up)) = ArgsLook::from_lua_multi(args.clone(), lua) {
Ok(CFrame(look_at(
from.0,
to.0,
up.as_deref().unwrap_or(&Vector3(Vec3::Y)).0,
)))
} else if let Ok((x, y, z)) = ArgsPosXYZ::from_lua_multi(args.clone(), lua) {
Ok(CFrame(Mat4::from_translation(Vec3::new(x, y, z))))
} else if let Ok((x, y, z, qx, qy, qz, qw)) =
ArgsPosXYZQuat::from_lua_multi(args.clone(), lua)
{
Ok(CFrame(Mat4::from_rotation_translation(
Quat::from_array([qx, qy, qz, qw]),
Vec3::new(x, y, z),
)))
} else if let Ok((x, y, z, r00, r01, r02, r10, r11, r12, r20, r21, r22)) =
ArgsMatrix::from_lua_multi(args, lua)
{
Ok(CFrame(Mat4::from_cols_array_2d(&[
[r00, r01, r02, 0.0],
[r10, r11, r12, 0.0],
[r20, r21, r22, 0.0],
[x, y, z, 1.0],
])))
} else {
// FUTURE: Better error message here using given arg types
Err(LuaError::RuntimeError(
"Invalid arguments to constructor".to_string(),
))
}
})?,
)
let cframe_new = |lua, args: LuaMultiValue| {
if args.clone().into_vec().is_empty() {
Ok(CFrame(Mat4::IDENTITY))
} else if let Ok(pos) = ArgsPos::from_lua_multi(args.clone(), lua) {
Ok(CFrame(Mat4::from_translation(pos.0)))
} else if let Ok((from, to, up)) = ArgsLook::from_lua_multi(args.clone(), lua) {
Ok(CFrame(look_at(
from.0,
to.0,
up.as_deref().unwrap_or(&Vector3(Vec3::Y)).0,
)))
} else if let Ok((x, y, z)) = ArgsPosXYZ::from_lua_multi(args.clone(), lua) {
Ok(CFrame(Mat4::from_translation(Vec3::new(x, y, z))))
} else if let Ok((x, y, z, qx, qy, qz, qw)) =
ArgsPosXYZQuat::from_lua_multi(args.clone(), lua)
{
Ok(CFrame(Mat4::from_rotation_translation(
Quat::from_array([qx, qy, qz, qw]),
Vec3::new(x, y, z),
)))
} else if let Ok((x, y, z, r00, r01, r02, r10, r11, r12, r20, r21, r22)) =
ArgsMatrix::from_lua_multi(args, lua)
{
Ok(CFrame(Mat4::from_cols_array_2d(&[
[r00, r01, r02, 0.0],
[r10, r11, r12, 0.0],
[r20, r21, r22, 0.0],
[x, y, z, 1.0],
])))
} else {
// FUTURE: Better error message here using given arg types
Err(LuaError::RuntimeError(
"Invalid arguments to constructor".to_string(),
))
}
};
TableBuilder::new(lua)?
.with_function("Angles", cframe_angles)?
.with_value("identity", CFrame(Mat4::IDENTITY))?
.with_function("fromAxisAngle", cframe_from_axis_angle)?
.with_function("fromEulerAnglesXYZ", cframe_from_euler_angles_xyz)?
.with_function("fromEulerAnglesYXZ", cframe_from_euler_angles_yxz)?
.with_function("fromMatrix", cframe_from_matrix)?
.with_function("fromOrientation", cframe_from_orientation)?
.with_function("lookAt", cframe_look_at)?
.with_function("new", cframe_new)?
.build_readonly()
}
}

View file

@ -5,6 +5,8 @@ use glam::Vec3;
use mlua::prelude::*;
use rbx_dom_weak::types::{Color3 as DomColor3, Color3uint8 as DomColor3uint8};
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable};
use super::super::*;
/**
@ -22,88 +24,87 @@ pub struct Color3 {
pub(crate) b: f32,
}
impl Color3 {
pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> {
datatype_table.set(
"new",
lua.create_function(|_, (r, g, b): (Option<f32>, Option<f32>, Option<f32>)| {
Ok(Color3 {
r: r.unwrap_or_default(),
g: g.unwrap_or_default(),
b: b.unwrap_or_default(),
})
})?,
)?;
datatype_table.set(
"fromRGB",
lua.create_function(|_, (r, g, b): (Option<u8>, Option<u8>, Option<u8>)| {
Ok(Color3 {
r: (r.unwrap_or_default() as f32) / 255f32,
g: (g.unwrap_or_default() as f32) / 255f32,
b: (b.unwrap_or_default() as f32) / 255f32,
})
})?,
)?;
datatype_table.set(
"fromHSV",
lua.create_function(|_, (h, s, v): (f32, f32, f32)| {
// https://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c
let i = (h * 6.0).floor();
let f = h * 6.0 - i;
let p = v * (1.0 - s);
let q = v * (1.0 - f * s);
let t = v * (1.0 - (1.0 - f) * s);
impl LuaExportsTable<'_> for Color3 {
const EXPORT_NAME: &'static str = "Color3";
let (r, g, b) = match (i % 6.0) as u8 {
0 => (v, t, p),
1 => (q, v, p),
2 => (p, v, t),
3 => (p, q, v),
4 => (t, p, v),
5 => (v, p, q),
_ => unreachable!(),
};
fn create_exports_table(lua: &Lua) -> LuaResult<LuaTable> {
let color3_from_rgb = |_, (r, g, b): (Option<u8>, Option<u8>, Option<u8>)| {
Ok(Color3 {
r: (r.unwrap_or_default() as f32) / 255f32,
g: (g.unwrap_or_default() as f32) / 255f32,
b: (b.unwrap_or_default() as f32) / 255f32,
})
};
Ok(Color3 { r, g, b })
})?,
)?;
datatype_table.set(
"fromHex",
lua.create_function(|_, hex: String| {
let trimmed = hex.trim_start_matches('#').to_ascii_uppercase();
let chars = if trimmed.len() == 3 {
(
u8::from_str_radix(&trimmed[..1].repeat(2), 16),
u8::from_str_radix(&trimmed[1..2].repeat(2), 16),
u8::from_str_radix(&trimmed[2..3].repeat(2), 16),
)
} else if trimmed.len() == 6 {
(
u8::from_str_radix(&trimmed[..2], 16),
u8::from_str_radix(&trimmed[2..4], 16),
u8::from_str_radix(&trimmed[4..6], 16),
)
} else {
return Err(LuaError::RuntimeError(format!(
"Hex color string must be 3 or 6 characters long, got {} character{}",
trimmed.len(),
if trimmed.len() == 1 { "" } else { "s" }
)));
};
match chars {
(Ok(r), Ok(g), Ok(b)) => Ok(Color3 {
r: (r as f32) / 255f32,
g: (g as f32) / 255f32,
b: (b as f32) / 255f32,
}),
_ => Err(LuaError::RuntimeError(format!(
"Hex color string '{}' contains invalid character",
trimmed
))),
}
})?,
)?;
Ok(())
let color3_from_hsv = |_, (h, s, v): (f32, f32, f32)| {
// https://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c
let i = (h * 6.0).floor();
let f = h * 6.0 - i;
let p = v * (1.0 - s);
let q = v * (1.0 - f * s);
let t = v * (1.0 - (1.0 - f) * s);
let (r, g, b) = match (i % 6.0) as u8 {
0 => (v, t, p),
1 => (q, v, p),
2 => (p, v, t),
3 => (p, q, v),
4 => (t, p, v),
5 => (v, p, q),
_ => unreachable!(),
};
Ok(Color3 { r, g, b })
};
let color3_from_hex = |_, hex: String| {
let trimmed = hex.trim_start_matches('#').to_ascii_uppercase();
let chars = if trimmed.len() == 3 {
(
u8::from_str_radix(&trimmed[..1].repeat(2), 16),
u8::from_str_radix(&trimmed[1..2].repeat(2), 16),
u8::from_str_radix(&trimmed[2..3].repeat(2), 16),
)
} else if trimmed.len() == 6 {
(
u8::from_str_radix(&trimmed[..2], 16),
u8::from_str_radix(&trimmed[2..4], 16),
u8::from_str_radix(&trimmed[4..6], 16),
)
} else {
return Err(LuaError::RuntimeError(format!(
"Hex color string must be 3 or 6 characters long, got {} character{}",
trimmed.len(),
if trimmed.len() == 1 { "" } else { "s" }
)));
};
match chars {
(Ok(r), Ok(g), Ok(b)) => Ok(Color3 {
r: (r as f32) / 255f32,
g: (g as f32) / 255f32,
b: (b as f32) / 255f32,
}),
_ => Err(LuaError::RuntimeError(format!(
"Hex color string '{}' contains invalid character",
trimmed
))),
}
};
let color3_new = |_, (r, g, b): (Option<f32>, Option<f32>, Option<f32>)| {
Ok(Color3 {
r: r.unwrap_or_default(),
g: g.unwrap_or_default(),
b: b.unwrap_or_default(),
})
};
TableBuilder::new(lua)?
.with_function("fromRGB", color3_from_rgb)?
.with_function("fromHSV", color3_from_hsv)?
.with_function("fromHex", color3_from_hex)?
.with_function("new", color3_new)?
.build_readonly()
}
}

View file

@ -5,6 +5,8 @@ use rbx_dom_weak::types::{
ColorSequence as DomColorSequence, ColorSequenceKeypoint as DomColorSequenceKeypoint,
};
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable};
use super::{super::*, Color3, ColorSequenceKeypoint};
/**
@ -17,52 +19,56 @@ pub struct ColorSequence {
pub(crate) keypoints: Vec<ColorSequenceKeypoint>,
}
impl ColorSequence {
pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> {
impl LuaExportsTable<'_> for ColorSequence {
const EXPORT_NAME: &'static str = "ColorSequence";
fn create_exports_table(lua: &Lua) -> LuaResult<LuaTable> {
type ArgsColor<'lua> = LuaUserDataRef<'lua, Color3>;
type ArgsColors<'lua> = (LuaUserDataRef<'lua, Color3>, LuaUserDataRef<'lua, Color3>);
type ArgsKeypoints<'lua> = Vec<LuaUserDataRef<'lua, ColorSequenceKeypoint>>;
datatype_table.set(
"new",
lua.create_function(|lua, args: LuaMultiValue| {
if let Ok(color) = ArgsColor::from_lua_multi(args.clone(), lua) {
Ok(ColorSequence {
keypoints: vec![
ColorSequenceKeypoint {
time: 0.0,
color: *color,
},
ColorSequenceKeypoint {
time: 1.0,
color: *color,
},
],
})
} else if let Ok((c0, c1)) = ArgsColors::from_lua_multi(args.clone(), lua) {
Ok(ColorSequence {
keypoints: vec![
ColorSequenceKeypoint {
time: 0.0,
color: *c0,
},
ColorSequenceKeypoint {
time: 1.0,
color: *c1,
},
],
})
} else if let Ok(keypoints) = ArgsKeypoints::from_lua_multi(args, lua) {
Ok(ColorSequence {
keypoints: keypoints.iter().map(|k| **k).collect(),
})
} else {
// FUTURE: Better error message here using given arg types
Err(LuaError::RuntimeError(
"Invalid arguments to constructor".to_string(),
))
}
})?,
)
let color_sequence_new = |lua, args: LuaMultiValue| {
if let Ok(color) = ArgsColor::from_lua_multi(args.clone(), lua) {
Ok(ColorSequence {
keypoints: vec![
ColorSequenceKeypoint {
time: 0.0,
color: *color,
},
ColorSequenceKeypoint {
time: 1.0,
color: *color,
},
],
})
} else if let Ok((c0, c1)) = ArgsColors::from_lua_multi(args.clone(), lua) {
Ok(ColorSequence {
keypoints: vec![
ColorSequenceKeypoint {
time: 0.0,
color: *c0,
},
ColorSequenceKeypoint {
time: 1.0,
color: *c1,
},
],
})
} else if let Ok(keypoints) = ArgsKeypoints::from_lua_multi(args, lua) {
Ok(ColorSequence {
keypoints: keypoints.iter().map(|k| **k).collect(),
})
} else {
// FUTURE: Better error message here using given arg types
Err(LuaError::RuntimeError(
"Invalid arguments to constructor".to_string(),
))
}
};
TableBuilder::new(lua)?
.with_function("new", color_sequence_new)?
.build_readonly()
}
}

View file

@ -3,6 +3,8 @@ use core::fmt;
use mlua::prelude::*;
use rbx_dom_weak::types::ColorSequenceKeypoint as DomColorSequenceKeypoint;
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable};
use super::{super::*, Color3};
/**
@ -16,18 +18,20 @@ pub struct ColorSequenceKeypoint {
pub(crate) color: Color3,
}
impl ColorSequenceKeypoint {
pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> {
datatype_table.set(
"new",
lua.create_function(|_, (time, color): (f32, LuaUserDataRef<Color3>)| {
Ok(ColorSequenceKeypoint {
time,
color: *color,
})
})?,
)?;
Ok(())
impl LuaExportsTable<'_> for ColorSequenceKeypoint {
const EXPORT_NAME: &'static str = "ColorSequenceKeypoint";
fn create_exports_table(lua: &Lua) -> LuaResult<LuaTable> {
let color_sequence_keypoint_new = |_, (time, color): (f32, LuaUserDataRef<Color3>)| {
Ok(ColorSequenceKeypoint {
time,
color: *color,
})
};
TableBuilder::new(lua)?
.with_function("new", color_sequence_keypoint_new)?
.build_readonly()
}
}

View file

@ -3,6 +3,8 @@ use core::fmt;
use mlua::prelude::*;
use rbx_dom_weak::types::Faces as DomFaces;
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable};
use super::{super::*, EnumItem};
/**
@ -20,59 +22,64 @@ pub struct Faces {
pub(crate) front: bool,
}
impl Faces {
pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> {
datatype_table.set(
"new",
lua.create_function(|_, args: LuaMultiValue| {
let mut right = false;
let mut top = false;
let mut back = false;
let mut left = false;
let mut bottom = false;
let mut front = false;
let mut check = |e: &EnumItem| {
if e.parent.desc.name == "NormalId" {
match &e.name {
name if name == "Right" => right = true,
name if name == "Top" => top = true,
name if name == "Back" => back = true,
name if name == "Left" => left = true,
name if name == "Bottom" => bottom = true,
name if name == "Front" => front = true,
_ => {}
}
}
};
for (index, arg) in args.into_iter().enumerate() {
if let LuaValue::UserData(u) = arg {
if let Ok(e) = u.borrow::<EnumItem>() {
check(&e);
} else {
return Err(LuaError::RuntimeError(format!(
"Expected argument #{} to be an EnumItem, got userdata",
index
)));
}
} else {
return Err(LuaError::RuntimeError(format!(
"Expected argument #{} to be an EnumItem, got {}",
index,
arg.type_name()
)));
impl LuaExportsTable<'_> for Faces {
const EXPORT_NAME: &'static str = "Faces";
fn create_exports_table(lua: &Lua) -> LuaResult<LuaTable> {
let faces_new = |_, args: LuaMultiValue| {
let mut right = false;
let mut top = false;
let mut back = false;
let mut left = false;
let mut bottom = false;
let mut front = false;
let mut check = |e: &EnumItem| {
if e.parent.desc.name == "NormalId" {
match &e.name {
name if name == "Right" => right = true,
name if name == "Top" => top = true,
name if name == "Back" => back = true,
name if name == "Left" => left = true,
name if name == "Bottom" => bottom = true,
name if name == "Front" => front = true,
_ => {}
}
}
Ok(Faces {
right,
top,
back,
left,
bottom,
front,
})
})?,
)?;
Ok(())
};
for (index, arg) in args.into_iter().enumerate() {
if let LuaValue::UserData(u) = arg {
if let Ok(e) = u.borrow::<EnumItem>() {
check(&e);
} else {
return Err(LuaError::RuntimeError(format!(
"Expected argument #{} to be an EnumItem, got userdata",
index
)));
}
} else {
return Err(LuaError::RuntimeError(format!(
"Expected argument #{} to be an EnumItem, got {}",
index,
arg.type_name()
)));
}
}
Ok(Faces {
right,
top,
back,
left,
bottom,
front,
})
};
TableBuilder::new(lua)?
.with_function("new", faces_new)?
.build_readonly()
}
}

View file

@ -6,6 +6,8 @@ use rbx_dom_weak::types::{
Font as DomFont, FontStyle as DomFontStyle, FontWeight as DomFontWeight,
};
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable};
use super::{super::*, EnumItem};
/**
@ -34,66 +36,65 @@ impl Font {
cached_id: None,
})
}
}
pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> {
datatype_table.set(
"new",
lua.create_function(
|_, (family, weight, style): (String, Option<FontWeight>, Option<FontStyle>)| {
Ok(Font {
family,
weight: weight.unwrap_or_default(),
style: style.unwrap_or_default(),
cached_id: None,
})
},
)?,
)?;
datatype_table.set(
"fromEnum",
lua.create_function(|_, value: LuaUserDataRef<EnumItem>| {
if value.parent.desc.name == "Font" {
match Font::from_enum_item(&value) {
Some(props) => Ok(props),
None => Err(LuaError::RuntimeError(format!(
"Found unknown Font '{}'",
value.name
))),
}
} else {
Err(LuaError::RuntimeError(format!(
"Expected argument #1 to be a Font, got {}",
value.parent.desc.name
)))
impl LuaExportsTable<'_> for Font {
const EXPORT_NAME: &'static str = "Font";
fn create_exports_table(lua: &Lua) -> LuaResult<LuaTable> {
let font_from_enum = |_, value: LuaUserDataRef<EnumItem>| {
if value.parent.desc.name == "Font" {
match Font::from_enum_item(&value) {
Some(props) => Ok(props),
None => Err(LuaError::RuntimeError(format!(
"Found unknown Font '{}'",
value.name
))),
}
})?,
)?;
datatype_table.set(
"fromName",
lua.create_function(
|_, (file, weight, style): (String, Option<FontWeight>, Option<FontStyle>)| {
Ok(Font {
family: format!("rbxasset://fonts/families/{}.json", file),
weight: weight.unwrap_or_default(),
style: style.unwrap_or_default(),
cached_id: None,
})
},
)?,
)?;
datatype_table.set(
"fromId",
lua.create_function(
|_, (id, weight, style): (i32, Option<FontWeight>, Option<FontStyle>)| {
Ok(Font {
family: format!("rbxassetid://{}", id),
weight: weight.unwrap_or_default(),
style: style.unwrap_or_default(),
cached_id: None,
})
},
)?,
)
} else {
Err(LuaError::RuntimeError(format!(
"Expected argument #1 to be a Font, got {}",
value.parent.desc.name
)))
}
};
let font_from_name =
|_, (file, weight, style): (String, Option<FontWeight>, Option<FontStyle>)| {
Ok(Font {
family: format!("rbxasset://fonts/families/{}.json", file),
weight: weight.unwrap_or_default(),
style: style.unwrap_or_default(),
cached_id: None,
})
};
let font_from_id =
|_, (id, weight, style): (i32, Option<FontWeight>, Option<FontStyle>)| {
Ok(Font {
family: format!("rbxassetid://{}", id),
weight: weight.unwrap_or_default(),
style: style.unwrap_or_default(),
cached_id: None,
})
};
let font_new =
|_, (family, weight, style): (String, Option<FontWeight>, Option<FontStyle>)| {
Ok(Font {
family,
weight: weight.unwrap_or_default(),
style: style.unwrap_or_default(),
cached_id: None,
})
};
TableBuilder::new(lua)?
.with_function("fromEnum", font_from_enum)?
.with_function("fromName", font_from_name)?
.with_function("fromId", font_from_id)?
.with_function("new", font_new)?
.build_readonly()
}
}

View file

@ -3,6 +3,8 @@ use core::fmt;
use mlua::prelude::*;
use rbx_dom_weak::types::NumberRange as DomNumberRange;
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable};
use super::super::*;
/**
@ -16,20 +18,23 @@ pub struct NumberRange {
pub(crate) max: f32,
}
impl NumberRange {
pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> {
datatype_table.set(
"new",
lua.create_function(|_, (min, max): (f32, Option<f32>)| {
Ok(match max {
Some(max) => NumberRange {
min: min.min(max),
max: min.max(max),
},
None => NumberRange { min, max: min },
})
})?,
)
impl LuaExportsTable<'_> for NumberRange {
const EXPORT_NAME: &'static str = "NumberRange";
fn create_exports_table(lua: &Lua) -> LuaResult<LuaTable> {
let number_range_new = |_, (min, max): (f32, Option<f32>)| {
Ok(match max {
Some(max) => NumberRange {
min: min.min(max),
max: min.max(max),
},
None => NumberRange { min, max: min },
})
};
TableBuilder::new(lua)?
.with_function("new", number_range_new)?
.build_readonly()
}
}

View file

@ -5,6 +5,8 @@ use rbx_dom_weak::types::{
NumberSequence as DomNumberSequence, NumberSequenceKeypoint as DomNumberSequenceKeypoint,
};
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable};
use super::{super::*, NumberSequenceKeypoint};
/**
@ -17,56 +19,60 @@ pub struct NumberSequence {
pub(crate) keypoints: Vec<NumberSequenceKeypoint>,
}
impl NumberSequence {
pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> {
impl LuaExportsTable<'_> for NumberSequence {
const EXPORT_NAME: &'static str = "NumberSequence";
fn create_exports_table(lua: &Lua) -> LuaResult<LuaTable> {
type ArgsColor = f32;
type ArgsColors = (f32, f32);
type ArgsKeypoints<'lua> = Vec<LuaUserDataRef<'lua, NumberSequenceKeypoint>>;
datatype_table.set(
"new",
lua.create_function(|lua, args: LuaMultiValue| {
if let Ok(value) = ArgsColor::from_lua_multi(args.clone(), lua) {
Ok(NumberSequence {
keypoints: vec![
NumberSequenceKeypoint {
time: 0.0,
value,
envelope: 0.0,
},
NumberSequenceKeypoint {
time: 1.0,
value,
envelope: 0.0,
},
],
})
} else if let Ok((v0, v1)) = ArgsColors::from_lua_multi(args.clone(), lua) {
Ok(NumberSequence {
keypoints: vec![
NumberSequenceKeypoint {
time: 0.0,
value: v0,
envelope: 0.0,
},
NumberSequenceKeypoint {
time: 1.0,
value: v1,
envelope: 0.0,
},
],
})
} else if let Ok(keypoints) = ArgsKeypoints::from_lua_multi(args, lua) {
Ok(NumberSequence {
keypoints: keypoints.iter().map(|k| **k).collect(),
})
} else {
// FUTURE: Better error message here using given arg types
Err(LuaError::RuntimeError(
"Invalid arguments to constructor".to_string(),
))
}
})?,
)
let number_sequence_new = |lua, args: LuaMultiValue| {
if let Ok(value) = ArgsColor::from_lua_multi(args.clone(), lua) {
Ok(NumberSequence {
keypoints: vec![
NumberSequenceKeypoint {
time: 0.0,
value,
envelope: 0.0,
},
NumberSequenceKeypoint {
time: 1.0,
value,
envelope: 0.0,
},
],
})
} else if let Ok((v0, v1)) = ArgsColors::from_lua_multi(args.clone(), lua) {
Ok(NumberSequence {
keypoints: vec![
NumberSequenceKeypoint {
time: 0.0,
value: v0,
envelope: 0.0,
},
NumberSequenceKeypoint {
time: 1.0,
value: v1,
envelope: 0.0,
},
],
})
} else if let Ok(keypoints) = ArgsKeypoints::from_lua_multi(args, lua) {
Ok(NumberSequence {
keypoints: keypoints.iter().map(|k| **k).collect(),
})
} else {
// FUTURE: Better error message here using given arg types
Err(LuaError::RuntimeError(
"Invalid arguments to constructor".to_string(),
))
}
};
TableBuilder::new(lua)?
.with_function("new", number_sequence_new)?
.build_readonly()
}
}

View file

@ -3,6 +3,8 @@ use core::fmt;
use mlua::prelude::*;
use rbx_dom_weak::types::NumberSequenceKeypoint as DomNumberSequenceKeypoint;
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable};
use super::super::*;
/**
@ -17,19 +19,21 @@ pub struct NumberSequenceKeypoint {
pub(crate) envelope: f32,
}
impl NumberSequenceKeypoint {
pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> {
datatype_table.set(
"new",
lua.create_function(|_, (time, value, envelope): (f32, f32, Option<f32>)| {
Ok(NumberSequenceKeypoint {
time,
value,
envelope: envelope.unwrap_or_default(),
})
})?,
)?;
Ok(())
impl LuaExportsTable<'_> for NumberSequenceKeypoint {
const EXPORT_NAME: &'static str = "NumberSequenceKeypoint";
fn create_exports_table(lua: &Lua) -> LuaResult<LuaTable> {
let number_sequence_keypoint_new = |_, (time, value, envelope): (f32, f32, Option<f32>)| {
Ok(NumberSequenceKeypoint {
time,
value,
envelope: envelope.unwrap_or_default(),
})
};
TableBuilder::new(lua)?
.with_function("new", number_sequence_keypoint_new)?
.build_readonly()
}
}

View file

@ -3,6 +3,8 @@ use core::fmt;
use mlua::prelude::*;
use rbx_dom_weak::types::CustomPhysicalProperties as DomCustomPhysicalProperties;
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable};
use super::{super::*, EnumItem};
/**
@ -32,51 +34,52 @@ impl PhysicalProperties {
elasticity_weight: props.5,
})
}
}
pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> {
impl LuaExportsTable<'_> for PhysicalProperties {
const EXPORT_NAME: &'static str = "PhysicalProperties";
fn create_exports_table(lua: &Lua) -> LuaResult<LuaTable> {
type ArgsMaterial<'lua> = LuaUserDataRef<'lua, EnumItem>;
type ArgsNumbers = (f32, f32, f32, Option<f32>, Option<f32>);
datatype_table.set(
"new",
lua.create_function(|lua, args: LuaMultiValue| {
if let Ok(value) = ArgsMaterial::from_lua_multi(args.clone(), lua) {
if value.parent.desc.name == "Material" {
match PhysicalProperties::from_material(&value) {
Some(props) => Ok(props),
None => Err(LuaError::RuntimeError(format!(
"Found unknown Material '{}'",
value.name
))),
}
} else {
Err(LuaError::RuntimeError(format!(
"Expected argument #1 to be a Material, got {}",
value.parent.desc.name
)))
let physical_properties_new = |lua, args: LuaMultiValue| {
if let Ok(value) = ArgsMaterial::from_lua_multi(args.clone(), lua) {
if value.parent.desc.name == "Material" {
match PhysicalProperties::from_material(&value) {
Some(props) => Ok(props),
None => Err(LuaError::RuntimeError(format!(
"Found unknown Material '{}'",
value.name
))),
}
} else if let Ok((
} else {
Err(LuaError::RuntimeError(format!(
"Expected argument #1 to be a Material, got {}",
value.parent.desc.name
)))
}
} else if let Ok((density, friction, elasticity, friction_weight, elasticity_weight)) =
ArgsNumbers::from_lua_multi(args, lua)
{
Ok(PhysicalProperties {
density,
friction,
friction_weight: friction_weight.unwrap_or(1.0),
elasticity,
friction_weight,
elasticity_weight,
)) = ArgsNumbers::from_lua_multi(args, lua)
{
Ok(PhysicalProperties {
density,
friction,
friction_weight: friction_weight.unwrap_or(1.0),
elasticity,
elasticity_weight: elasticity_weight.unwrap_or(1.0),
})
} else {
// FUTURE: Better error message here using given arg types
Err(LuaError::RuntimeError(
"Invalid arguments to constructor".to_string(),
))
}
})?,
)
elasticity_weight: elasticity_weight.unwrap_or(1.0),
})
} else {
// FUTURE: Better error message here using given arg types
Err(LuaError::RuntimeError(
"Invalid arguments to constructor".to_string(),
))
}
};
TableBuilder::new(lua)?
.with_function("new", physical_properties_new)?
.build_readonly()
}
}

View file

@ -4,6 +4,8 @@ use glam::Vec3;
use mlua::prelude::*;
use rbx_dom_weak::types::Ray as DomRay;
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable};
use super::{super::*, Vector3};
/**
@ -26,19 +28,23 @@ impl Ray {
let dot_product = lhs.dot(norm).max(0.0);
self.origin + norm * dot_product
}
}
pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> {
datatype_table.set(
"new",
lua.create_function(
|_, (origin, direction): (LuaUserDataRef<Vector3>, LuaUserDataRef<Vector3>)| {
Ok(Ray {
origin: origin.0,
direction: direction.0,
})
},
)?,
)
impl LuaExportsTable<'_> for Ray {
const EXPORT_NAME: &'static str = "Ray";
fn create_exports_table(lua: &Lua) -> LuaResult<LuaTable> {
let ray_new =
|_, (origin, direction): (LuaUserDataRef<Vector3>, LuaUserDataRef<Vector3>)| {
Ok(Ray {
origin: origin.0,
direction: direction.0,
})
};
TableBuilder::new(lua)?
.with_function("new", ray_new)?
.build_readonly()
}
}

View file

@ -5,6 +5,8 @@ use glam::Vec2;
use mlua::prelude::*;
use rbx_dom_weak::types::Rect as DomRect;
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable};
use super::{super::*, Vector2};
/**
@ -28,33 +30,37 @@ impl Rect {
}
}
impl Rect {
pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> {
impl LuaExportsTable<'_> for Rect {
const EXPORT_NAME: &'static str = "Rect";
fn create_exports_table(lua: &Lua) -> LuaResult<LuaTable> {
type ArgsVector2s<'lua> = (
Option<LuaUserDataRef<'lua, Vector2>>,
Option<LuaUserDataRef<'lua, Vector2>>,
);
type ArgsNums = (Option<f32>, Option<f32>, Option<f32>, Option<f32>);
datatype_table.set(
"new",
lua.create_function(|lua, args: LuaMultiValue| {
if let Ok((min, max)) = ArgsVector2s::from_lua_multi(args.clone(), lua) {
Ok(Rect::new(
min.map(|m| *m).unwrap_or_default().0,
max.map(|m| *m).unwrap_or_default().0,
))
} else if let Ok((x0, y0, x1, y1)) = ArgsNums::from_lua_multi(args, lua) {
let min = Vec2::new(x0.unwrap_or_default(), y0.unwrap_or_default());
let max = Vec2::new(x1.unwrap_or_default(), y1.unwrap_or_default());
Ok(Rect::new(min, max))
} else {
// FUTURE: Better error message here using given arg types
Err(LuaError::RuntimeError(
"Invalid arguments to constructor".to_string(),
))
}
})?,
)
let rect_new = |lua, args: LuaMultiValue| {
if let Ok((min, max)) = ArgsVector2s::from_lua_multi(args.clone(), lua) {
Ok(Rect::new(
min.map(|m| *m).unwrap_or_default().0,
max.map(|m| *m).unwrap_or_default().0,
))
} else if let Ok((x0, y0, x1, y1)) = ArgsNums::from_lua_multi(args, lua) {
let min = Vec2::new(x0.unwrap_or_default(), y0.unwrap_or_default());
let max = Vec2::new(x1.unwrap_or_default(), y1.unwrap_or_default());
Ok(Rect::new(min, max))
} else {
// FUTURE: Better error message here using given arg types
Err(LuaError::RuntimeError(
"Invalid arguments to constructor".to_string(),
))
}
};
TableBuilder::new(lua)?
.with_function("new", rect_new)?
.build_readonly()
}
}

View file

@ -4,6 +4,8 @@ use glam::{Mat4, Vec3};
use mlua::prelude::*;
use rbx_dom_weak::types::Region3 as DomRegion3;
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable};
use super::{super::*, CFrame, Vector3};
/**
@ -18,19 +20,20 @@ pub struct Region3 {
pub(crate) max: Vec3,
}
impl Region3 {
pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> {
datatype_table.set(
"new",
lua.create_function(
|_, (min, max): (LuaUserDataRef<Vector3>, LuaUserDataRef<Vector3>)| {
Ok(Region3 {
min: min.0,
max: max.0,
})
},
)?,
)
impl LuaExportsTable<'_> for Region3 {
const EXPORT_NAME: &'static str = "Region3";
fn create_exports_table(lua: &Lua) -> LuaResult<LuaTable> {
let region3_new = |_, (min, max): (LuaUserDataRef<Vector3>, LuaUserDataRef<Vector3>)| {
Ok(Region3 {
min: min.0,
max: max.0,
})
};
TableBuilder::new(lua)?
.with_function("new", region3_new)?
.build_readonly()
}
}

View file

@ -4,6 +4,8 @@ use glam::IVec3;
use mlua::prelude::*;
use rbx_dom_weak::types::Region3int16 as DomRegion3int16;
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable};
use super::{super::*, Vector3int16};
/**
@ -18,19 +20,21 @@ pub struct Region3int16 {
pub(crate) max: IVec3,
}
impl Region3int16 {
pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> {
datatype_table.set(
"new",
lua.create_function(
|_, (min, max): (LuaUserDataRef<Vector3int16>, LuaUserDataRef<Vector3int16>)| {
Ok(Region3int16 {
min: min.0,
max: max.0,
})
},
)?,
)
impl LuaExportsTable<'_> for Region3int16 {
const EXPORT_NAME: &'static str = "Region3int16";
fn create_exports_table(lua: &Lua) -> LuaResult<LuaTable> {
let region3int16_new =
|_, (min, max): (LuaUserDataRef<Vector3int16>, LuaUserDataRef<Vector3int16>)| {
Ok(Region3int16 {
min: min.0,
max: max.0,
})
};
TableBuilder::new(lua)?
.with_function("new", region3int16_new)?
.build_readonly()
}
}

View file

@ -4,6 +4,8 @@ use std::ops;
use mlua::prelude::*;
use rbx_dom_weak::types::UDim as DomUDim;
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable};
use super::super::*;
/**
@ -21,17 +23,22 @@ impl UDim {
pub(super) fn new(scale: f32, offset: i32) -> Self {
Self { scale, offset }
}
}
pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> {
datatype_table.set(
"new",
lua.create_function(|_, (scale, offset): (Option<f32>, Option<i32>)| {
Ok(UDim {
scale: scale.unwrap_or_default(),
offset: offset.unwrap_or_default(),
})
})?,
)
impl LuaExportsTable<'_> for UDim {
const EXPORT_NAME: &'static str = "UDim";
fn create_exports_table(lua: &Lua) -> LuaResult<LuaTable> {
let udim_new = |_, (scale, offset): (Option<f32>, Option<i32>)| {
Ok(UDim {
scale: scale.unwrap_or_default(),
offset: offset.unwrap_or_default(),
})
};
TableBuilder::new(lua)?
.with_function("new", udim_new)?
.build_readonly()
}
}

View file

@ -5,6 +5,8 @@ use glam::Vec2;
use mlua::prelude::*;
use rbx_dom_weak::types::UDim2 as DomUDim2;
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable};
use super::{super::*, UDim};
/**
@ -18,52 +20,53 @@ pub struct UDim2 {
pub(crate) y: UDim,
}
impl UDim2 {
pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> {
datatype_table.set(
"fromScale",
lua.create_function(|_, (x, y): (Option<f32>, Option<f32>)| {
Ok(UDim2 {
x: UDim::new(x.unwrap_or_default(), 0),
y: UDim::new(y.unwrap_or_default(), 0),
})
})?,
)?;
datatype_table.set(
"fromOffset",
lua.create_function(|_, (x, y): (Option<i32>, Option<i32>)| {
Ok(UDim2 {
x: UDim::new(0f32, x.unwrap_or_default()),
y: UDim::new(0f32, y.unwrap_or_default()),
})
})?,
)?;
impl LuaExportsTable<'_> for UDim2 {
const EXPORT_NAME: &'static str = "UDim2";
fn create_exports_table(lua: &Lua) -> LuaResult<LuaTable> {
let udim2_from_offset = |_, (x, y): (Option<i32>, Option<i32>)| {
Ok(UDim2 {
x: UDim::new(0f32, x.unwrap_or_default()),
y: UDim::new(0f32, y.unwrap_or_default()),
})
};
let udim2_from_scale = |_, (x, y): (Option<f32>, Option<f32>)| {
Ok(UDim2 {
x: UDim::new(x.unwrap_or_default(), 0),
y: UDim::new(y.unwrap_or_default(), 0),
})
};
type ArgsUDims<'lua> = (
Option<LuaUserDataRef<'lua, UDim>>,
Option<LuaUserDataRef<'lua, UDim>>,
);
type ArgsNums = (Option<f32>, Option<i32>, Option<f32>, Option<i32>);
datatype_table.set(
"new",
lua.create_function(|lua, args: LuaMultiValue| {
if let Ok((x, y)) = ArgsUDims::from_lua_multi(args.clone(), lua) {
Ok(UDim2 {
x: x.map(|x| *x).unwrap_or_default(),
y: y.map(|y| *y).unwrap_or_default(),
})
} else if let Ok((sx, ox, sy, oy)) = ArgsNums::from_lua_multi(args, lua) {
Ok(UDim2 {
x: UDim::new(sx.unwrap_or_default(), ox.unwrap_or_default()),
y: UDim::new(sy.unwrap_or_default(), oy.unwrap_or_default()),
})
} else {
// FUTURE: Better error message here using given arg types
Err(LuaError::RuntimeError(
"Invalid arguments to constructor".to_string(),
))
}
})?,
)
let udim2_new = |lua, args: LuaMultiValue| {
if let Ok((x, y)) = ArgsUDims::from_lua_multi(args.clone(), lua) {
Ok(UDim2 {
x: x.map(|x| *x).unwrap_or_default(),
y: y.map(|y| *y).unwrap_or_default(),
})
} else if let Ok((sx, ox, sy, oy)) = ArgsNums::from_lua_multi(args, lua) {
Ok(UDim2 {
x: UDim::new(sx.unwrap_or_default(), ox.unwrap_or_default()),
y: UDim::new(sy.unwrap_or_default(), oy.unwrap_or_default()),
})
} else {
// FUTURE: Better error message here using given arg types
Err(LuaError::RuntimeError(
"Invalid arguments to constructor".to_string(),
))
}
};
TableBuilder::new(lua)?
.with_function("fromOffset", udim2_from_offset)?
.with_function("fromScale", udim2_from_scale)?
.with_function("new", udim2_new)?
.build_readonly()
}
}

View file

@ -5,6 +5,8 @@ use glam::{Vec2, Vec3};
use mlua::prelude::*;
use rbx_dom_weak::types::Vector2 as DomVector2;
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable};
use super::super::*;
/**
@ -17,23 +19,24 @@ use super::super::*;
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct Vector2(pub Vec2);
impl Vector2 {
pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> {
// Constants
datatype_table.set("xAxis", Vector2(Vec2::X))?;
datatype_table.set("yAxis", Vector2(Vec2::Y))?;
datatype_table.set("zero", Vector2(Vec2::ZERO))?;
datatype_table.set("one", Vector2(Vec2::ONE))?;
// Constructors
datatype_table.set(
"new",
lua.create_function(|_, (x, y): (Option<f32>, Option<f32>)| {
Ok(Vector2(Vec2 {
x: x.unwrap_or_default(),
y: y.unwrap_or_default(),
}))
})?,
)
impl LuaExportsTable<'_> for Vector2 {
const EXPORT_NAME: &'static str = "Vector2";
fn create_exports_table(lua: &Lua) -> LuaResult<LuaTable> {
let vector2_new = |_, (x, y): (Option<f32>, Option<f32>)| {
Ok(Vector2(Vec2 {
x: x.unwrap_or_default(),
y: y.unwrap_or_default(),
}))
};
TableBuilder::new(lua)?
.with_value("xAxis", Vector2(Vec2::X))?
.with_value("yAxis", Vector2(Vec2::Y))?
.with_value("zero", Vector2(Vec2::ZERO))?
.with_value("one", Vector2(Vec2::ONE))?
.with_function("new", vector2_new)?
.build_readonly()
}
}

View file

@ -5,6 +5,8 @@ use glam::IVec2;
use mlua::prelude::*;
use rbx_dom_weak::types::Vector2int16 as DomVector2int16;
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable};
use super::super::*;
/**
@ -17,17 +19,20 @@ use super::super::*;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Vector2int16(pub IVec2);
impl Vector2int16 {
pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> {
datatype_table.set(
"new",
lua.create_function(|_, (x, y): (Option<i16>, Option<i16>)| {
Ok(Vector2int16(IVec2 {
x: x.unwrap_or_default() as i32,
y: y.unwrap_or_default() as i32,
}))
})?,
)
impl LuaExportsTable<'_> for Vector2int16 {
const EXPORT_NAME: &'static str = "Vector2int16";
fn create_exports_table(lua: &Lua) -> LuaResult<LuaTable> {
let vector2int16_new = |_, (x, y): (Option<i16>, Option<i16>)| {
Ok(Vector2int16(IVec2 {
x: x.unwrap_or_default() as i32,
y: y.unwrap_or_default() as i32,
}))
};
TableBuilder::new(lua)?
.with_function("new", vector2int16_new)?
.build_readonly()
}
}

View file

@ -5,6 +5,8 @@ use glam::Vec3;
use mlua::prelude::*;
use rbx_dom_weak::types::Vector3 as DomVector3;
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable};
use super::{super::*, EnumItem};
/**
@ -20,74 +22,73 @@ use super::{super::*, EnumItem};
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Vector3(pub Vec3);
impl Vector3 {
pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> {
// Constants
datatype_table.set("xAxis", Vector3(Vec3::X))?;
datatype_table.set("yAxis", Vector3(Vec3::Y))?;
datatype_table.set("zAxis", Vector3(Vec3::Z))?;
datatype_table.set("zero", Vector3(Vec3::ZERO))?;
datatype_table.set("one", Vector3(Vec3::ONE))?;
// Constructors
datatype_table.set(
"fromAxis",
lua.create_function(|_, normal_id: LuaUserDataRef<EnumItem>| {
if normal_id.parent.desc.name == "Axis" {
Ok(match normal_id.name.as_str() {
"X" => Vector3(Vec3::X),
"Y" => Vector3(Vec3::Y),
"Z" => Vector3(Vec3::Z),
name => {
return Err(LuaError::RuntimeError(format!(
"Axis '{}' is not known",
name
)))
}
})
} else {
Err(LuaError::RuntimeError(format!(
"EnumItem must be a Axis, got {}",
normal_id.parent.desc.name
)))
}
})?,
)?;
datatype_table.set(
"fromNormalId",
lua.create_function(|_, normal_id: LuaUserDataRef<EnumItem>| {
if normal_id.parent.desc.name == "NormalId" {
Ok(match normal_id.name.as_str() {
"Left" => Vector3(Vec3::X),
"Top" => Vector3(Vec3::Y),
"Front" => Vector3(-Vec3::Z),
"Right" => Vector3(-Vec3::X),
"Bottom" => Vector3(-Vec3::Y),
"Back" => Vector3(Vec3::Z),
name => {
return Err(LuaError::RuntimeError(format!(
"NormalId '{}' is not known",
name
)))
}
})
} else {
Err(LuaError::RuntimeError(format!(
"EnumItem must be a NormalId, got {}",
normal_id.parent.desc.name
)))
}
})?,
)?;
datatype_table.set(
"new",
lua.create_function(|_, (x, y, z): (Option<f32>, Option<f32>, Option<f32>)| {
Ok(Vector3(Vec3 {
x: x.unwrap_or_default(),
y: y.unwrap_or_default(),
z: z.unwrap_or_default(),
}))
})?,
)
impl LuaExportsTable<'_> for Vector3 {
const EXPORT_NAME: &'static str = "Vector3";
fn create_exports_table(lua: &Lua) -> LuaResult<LuaTable> {
let vector3_from_axis = |_, normal_id: LuaUserDataRef<EnumItem>| {
if normal_id.parent.desc.name == "Axis" {
Ok(match normal_id.name.as_str() {
"X" => Vector3(Vec3::X),
"Y" => Vector3(Vec3::Y),
"Z" => Vector3(Vec3::Z),
name => {
return Err(LuaError::RuntimeError(format!(
"Axis '{}' is not known",
name
)))
}
})
} else {
Err(LuaError::RuntimeError(format!(
"EnumItem must be a Axis, got {}",
normal_id.parent.desc.name
)))
}
};
let vector3_from_normal_id = |_, normal_id: LuaUserDataRef<EnumItem>| {
if normal_id.parent.desc.name == "NormalId" {
Ok(match normal_id.name.as_str() {
"Left" => Vector3(Vec3::X),
"Top" => Vector3(Vec3::Y),
"Front" => Vector3(-Vec3::Z),
"Right" => Vector3(-Vec3::X),
"Bottom" => Vector3(-Vec3::Y),
"Back" => Vector3(Vec3::Z),
name => {
return Err(LuaError::RuntimeError(format!(
"NormalId '{}' is not known",
name
)))
}
})
} else {
Err(LuaError::RuntimeError(format!(
"EnumItem must be a NormalId, got {}",
normal_id.parent.desc.name
)))
}
};
let vector3_new = |_, (x, y, z): (Option<f32>, Option<f32>, Option<f32>)| {
Ok(Vector3(Vec3 {
x: x.unwrap_or_default(),
y: y.unwrap_or_default(),
z: z.unwrap_or_default(),
}))
};
TableBuilder::new(lua)?
.with_value("xAxis", Vector3(Vec3::X))?
.with_value("yAxis", Vector3(Vec3::Y))?
.with_value("zAxis", Vector3(Vec3::Z))?
.with_value("zero", Vector3(Vec3::ZERO))?
.with_value("one", Vector3(Vec3::ONE))?
.with_function("fromAxis", vector3_from_axis)?
.with_function("fromNormalId", vector3_from_normal_id)?
.with_function("new", vector3_new)?
.build_readonly()
}
}

View file

@ -5,6 +5,8 @@ use glam::IVec3;
use mlua::prelude::*;
use rbx_dom_weak::types::Vector3int16 as DomVector3int16;
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable};
use super::super::*;
/**
@ -17,18 +19,21 @@ use super::super::*;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Vector3int16(pub IVec3);
impl Vector3int16 {
pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> {
datatype_table.set(
"new",
lua.create_function(|_, (x, y, z): (Option<i16>, Option<i16>, Option<i16>)| {
Ok(Vector3int16(IVec3 {
x: x.unwrap_or_default() as i32,
y: y.unwrap_or_default() as i32,
z: z.unwrap_or_default() as i32,
}))
})?,
)
impl LuaExportsTable<'_> for Vector3int16 {
const EXPORT_NAME: &'static str = "Vector3int16";
fn create_exports_table(lua: &Lua) -> LuaResult<LuaTable> {
let vector3int16_new = |_, (x, y, z): (Option<i16>, Option<i16>, Option<i16>)| {
Ok(Vector3int16(IVec3 {
x: x.unwrap_or_default() as i32,
y: y.unwrap_or_default() as i32,
z: z.unwrap_or_default() as i32,
}))
};
TableBuilder::new(lua)?
.with_function("new", vector3int16_new)?
.build_readonly()
}
}

68
src/roblox/exports.rs Normal file
View file

@ -0,0 +1,68 @@
use mlua::prelude::*;
/**
Trait for any item that should be exported as part of the `roblox` built-in library.
This may be an enum or a struct that should export constants and/or constructs.
### Example usage
```rs
use mlua::prelude::*;
struct MyType(usize);
impl MyType {
pub fn new(n: usize) -> Self {
Self(n)
}
}
impl LuaExportsTable<'_> for MyType {
const EXPORT_NAME: &'static str = "MyType";
fn create_exports_table(lua: &Lua) -> LuaResult<LuaTable> {
let my_type_new = |lua, n: Option<usize>| {
Self::new(n.unwrap_or_default())
};
TableBuilder::new(lua)?
.with_function("new", my_type_new)?
.build_readonly()
}
}
impl LuaUserData for MyType {
// ...
}
```
*/
pub trait LuaExportsTable<'lua> {
const EXPORT_NAME: &'static str;
fn create_exports_table(lua: &'lua Lua) -> LuaResult<LuaTable<'lua>>;
}
/**
Exports a single item that implements the [`LuaExportsTable`] trait.
Returns the name of the export, as well as the export table.
### Example usage
```rs
let lua: mlua::Lua::new();
let (name1, table1) = export::<Type1>(lua)?;
let (name2, table2) = export::<Type2>(lua)?;
```
*/
pub fn export<'lua, T>(lua: &'lua Lua) -> LuaResult<(&'static str, LuaValue<'lua>)>
where
T: LuaExportsTable<'lua>,
{
Ok((
T::EXPORT_NAME,
<T as LuaExportsTable>::create_exports_table(lua)?.into_lua(lua)?,
))
}

View file

@ -12,7 +12,11 @@ use rbx_dom_weak::{
Instance as DomInstance, InstanceBuilder as DomInstanceBuilder, WeakDom,
};
use crate::roblox::shared::instance::{class_exists, class_is_a};
use crate::{
lune::util::TableBuilder,
roblox::exports::LuaExportsTable,
roblox::shared::instance::{class_exists, class_is_a},
};
pub(crate) mod base;
pub(crate) mod data_model;
@ -686,21 +690,24 @@ impl Instance {
}
}
impl Instance {
pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> {
datatype_table.set(
"new",
lua.create_function(|lua, class_name: String| {
if class_exists(&class_name) {
Instance::new_orphaned(class_name).into_lua(lua)
} else {
Err(LuaError::RuntimeError(format!(
"Failed to create Instance - '{}' is not a valid class name",
class_name
)))
}
})?,
)
impl LuaExportsTable<'_> for Instance {
const EXPORT_NAME: &'static str = "Instance";
fn create_exports_table(lua: &Lua) -> LuaResult<LuaTable> {
let instance_new = |lua, class_name: String| {
if class_exists(&class_name) {
Instance::new_orphaned(class_name).into_lua(lua)
} else {
Err(LuaError::RuntimeError(format!(
"Failed to create Instance - '{}' is not a valid class name",
class_name
)))
}
};
TableBuilder::new(lua)?
.with_function("new", instance_new)?
.build_readonly()
}
}

View file

@ -1,63 +1,59 @@
use mlua::prelude::*;
use crate::roblox::instance::Instance;
use crate::lune::util::TableBuilder;
pub mod datatypes;
pub mod document;
pub mod instance;
pub mod reflection;
pub(crate) mod exports;
pub(crate) mod shared;
fn make<F>(lua: &Lua, f: F) -> LuaResult<LuaValue>
where
F: Fn(&Lua, &LuaTable) -> LuaResult<()>,
{
let tab = lua.create_table()?;
f(lua, &tab)?;
tab.set_readonly(true);
Ok(LuaValue::Table(tab))
}
use exports::export;
#[rustfmt::skip]
fn make_all_datatypes(lua: &Lua) -> LuaResult<Vec<(&'static str, LuaValue)>> {
use datatypes::types::*;
fn create_all_exports(lua: &Lua) -> LuaResult<Vec<(&'static str, LuaValue)>> {
use datatypes::types::*;
use instance::Instance;
Ok(vec![
// Datatypes
("Axes", make(lua, Axes::make_table)?),
("BrickColor", make(lua, BrickColor::make_table)?),
("CFrame", make(lua, CFrame::make_table)?),
("Color3", make(lua, Color3::make_table)?),
("ColorSequence", make(lua, ColorSequence::make_table)?),
("ColorSequenceKeypoint", make(lua, ColorSequenceKeypoint::make_table)?),
("Faces", make(lua, Faces::make_table)?),
("Font", make(lua, Font::make_table)?),
("NumberRange", make(lua, NumberRange::make_table)?),
("NumberSequence", make(lua, NumberSequence::make_table)?),
("NumberSequenceKeypoint", make(lua, NumberSequenceKeypoint::make_table)?),
("PhysicalProperties", make(lua, PhysicalProperties::make_table)?),
("Ray", make(lua, Ray::make_table)?),
("Rect", make(lua, Rect::make_table)?),
("UDim", make(lua, UDim::make_table)?),
("UDim2", make(lua, UDim2::make_table)?),
("Region3", make(lua, Region3::make_table)?),
("Region3int16", make(lua, Region3int16::make_table)?),
("Vector2", make(lua, Vector2::make_table)?),
("Vector2int16", make(lua, Vector2int16::make_table)?),
("Vector3", make(lua, Vector3::make_table)?),
("Vector3int16", make(lua, Vector3int16::make_table)?),
// Classes
("Instance", make(lua, Instance::make_table)?),
// Singletons
// Datatypes
export::<Axes>(lua)?,
export::<BrickColor>(lua)?,
export::<CFrame>(lua)?,
export::<Color3>(lua)?,
export::<ColorSequence>(lua)?,
export::<ColorSequenceKeypoint>(lua)?,
export::<Faces>(lua)?,
export::<Font>(lua)?,
export::<NumberRange>(lua)?,
export::<NumberSequence>(lua)?,
export::<NumberSequenceKeypoint>(lua)?,
export::<PhysicalProperties>(lua)?,
export::<Ray>(lua)?,
export::<Rect>(lua)?,
export::<UDim>(lua)?,
export::<UDim2>(lua)?,
export::<Region3>(lua)?,
export::<Region3int16>(lua)?,
export::<Vector2>(lua)?,
export::<Vector2int16>(lua)?,
export::<Vector3>(lua)?,
export::<Vector3int16>(lua)?,
// Classes
export::<Instance>(lua)?,
// Singletons
("Enum", Enums.into_lua(lua)?),
])
}
pub fn module(lua: &Lua) -> LuaResult<LuaTable> {
let exports = lua.create_table()?;
for (name, tab) in make_all_datatypes(lua)? {
exports.set(name, tab)?;
}
exports.set_readonly(true);
Ok(exports)
// FUTURE: We can probably create these lazily as users
// index the main exports (this return value) table and
// save some memory and startup time. The full exports
// table is quite big and probably won't get any smaller
// since we impl all roblox constructors for each datatype.
let exports = create_all_exports(lua)?;
TableBuilder::new(lua)?
.with_values(exports)?
.build_readonly()
}