Implement number range/sequence roblox datatypes

This commit is contained in:
Filip Tibell 2023-03-17 08:54:57 +01:00
parent da8e86d743
commit e3bcf79f09
No known key found for this signature in database
11 changed files with 404 additions and 33 deletions

View file

@ -121,8 +121,6 @@ impl<'lua> RbxVariantToLua<'lua> for LuaAnyUserData<'lua> {
// check `EnumItem::from_instance_property` for specifics // check `EnumItem::from_instance_property` for specifics
Ok(match variant.clone() { Ok(match variant.clone() {
// Not yet implemented datatypes // Not yet implemented datatypes
// Rbx::NumberRange(_) => todo!(),
// Rbx::NumberSequence(_) => todo!(),
// Rbx::OptionalCFrame(_) => todo!(), // Rbx::OptionalCFrame(_) => todo!(),
// Rbx::PhysicalProperties(_) => todo!(), // Rbx::PhysicalProperties(_) => todo!(),
// Rbx::Ray(_) => todo!(), // Rbx::Ray(_) => todo!(),
@ -139,6 +137,9 @@ impl<'lua> RbxVariantToLua<'lua> for LuaAnyUserData<'lua> {
Rbx::Color3uint8(value) => lua.create_userdata(Color3::from(value))?, Rbx::Color3uint8(value) => lua.create_userdata(Color3::from(value))?,
Rbx::ColorSequence(value) => lua.create_userdata(ColorSequence::from(value))?, Rbx::ColorSequence(value) => lua.create_userdata(ColorSequence::from(value))?,
Rbx::NumberRange(value) => lua.create_userdata(NumberRange::from(value))?,
Rbx::NumberSequence(value) => lua.create_userdata(NumberSequence::from(value))?,
Rbx::Rect(value) => lua.create_userdata(Rect::from(value))?, Rbx::Rect(value) => lua.create_userdata(Rect::from(value))?,
Rbx::UDim(value) => lua.create_userdata(UDim::from(value))?, Rbx::UDim(value) => lua.create_userdata(UDim::from(value))?,
Rbx::UDim2(value) => lua.create_userdata(UDim2::from(value))?, Rbx::UDim2(value) => lua.create_userdata(UDim2::from(value))?,
@ -186,6 +187,9 @@ impl<'lua> LuaToRbxVariant<'lua> for LuaAnyUserData<'lua> {
RbxVariantType::UDim => convert::<UDim, rbx::UDim>, RbxVariantType::UDim => convert::<UDim, rbx::UDim>,
RbxVariantType::UDim2 => convert::<UDim2, rbx::UDim2>, RbxVariantType::UDim2 => convert::<UDim2, rbx::UDim2>,
RbxVariantType::NumberRange => convert::<NumberRange, rbx::NumberRange>,
RbxVariantType::NumberSequence => convert::<NumberSequence, rbx::NumberSequence>,
RbxVariantType::Vector2 => convert::<Vector2, rbx::Vector2>, RbxVariantType::Vector2 => convert::<Vector2, rbx::Vector2>,
RbxVariantType::Vector2int16 => convert::<Vector2int16, rbx::Vector2int16>, RbxVariantType::Vector2int16 => convert::<Vector2int16, rbx::Vector2int16>,
RbxVariantType::Vector3 => convert::<Vector3, rbx::Vector3>, RbxVariantType::Vector3 => convert::<Vector3, rbx::Vector3>,

View file

@ -8,6 +8,9 @@ mod r#enum;
mod r#enum_item; mod r#enum_item;
mod r#enums; mod r#enums;
mod faces; mod faces;
mod number_range;
mod number_sequence;
mod number_sequence_keypoint;
mod rect; mod rect;
mod udim; mod udim;
mod udim2; mod udim2;
@ -23,6 +26,9 @@ pub use color3::Color3;
pub use color_sequence::ColorSequence; pub use color_sequence::ColorSequence;
pub use color_sequence_keypoint::ColorSequenceKeypoint; pub use color_sequence_keypoint::ColorSequenceKeypoint;
pub use faces::Faces; pub use faces::Faces;
pub use number_range::NumberRange;
pub use number_sequence::NumberSequence;
pub use number_sequence_keypoint::NumberSequenceKeypoint;
pub use r#enum::Enum; pub use r#enum::Enum;
pub use r#enum_item::EnumItem; pub use r#enum_item::EnumItem;
pub use r#enums::Enums; pub use r#enums::Enums;
@ -76,20 +82,23 @@ mod tests {
} }
create_tests! { create_tests! {
axes: "datatypes/Axes", axes: "datatypes/Axes",
brick_color: "datatypes/BrickColor", brick_color: "datatypes/BrickColor",
cframe: "datatypes/CFrame", cframe: "datatypes/CFrame",
color3: "datatypes/Color3", color3: "datatypes/Color3",
color_sequence: "datatypes/ColorSequence", color_sequence: "datatypes/ColorSequence",
color_sequence_keypoint: "datatypes/ColorSequenceKeypoint", color_sequence_keypoint: "datatypes/ColorSequenceKeypoint",
r#enum: "datatypes/Enum", r#enum: "datatypes/Enum",
faces: "datatypes/Faces", faces: "datatypes/Faces",
rect: "datatypes/Rect", number_range: "datatypes/NumberRange",
udim: "datatypes/UDim", number_sequence: "datatypes/NumberSequence",
udim2: "datatypes/UDim2", number_sequence_keypoint: "datatypes/NumberSequenceKeypoint",
vector2: "datatypes/Vector2", rect: "datatypes/Rect",
vector2int16: "datatypes/Vector2int16", udim: "datatypes/UDim",
vector3: "datatypes/Vector3", udim2: "datatypes/UDim2",
vector3int16: "datatypes/Vector3int16", vector2: "datatypes/Vector2",
vector2int16: "datatypes/Vector2int16",
vector3: "datatypes/Vector3",
vector3int16: "datatypes/Vector3int16",
} }
} }

View file

@ -0,0 +1,70 @@
use core::fmt;
use mlua::prelude::*;
use rbx_dom_weak::types::NumberRange as RbxNumberRange;
use super::super::*;
/**
An implementation of the [NumberRange](https://create.roblox.com/docs/reference/engine/datatypes/NumberRange) Roblox datatype.
This implements all documented properties, methods & constructors of the NumberRange class as of March 2023.
*/
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct NumberRange {
pub(crate) min: f32,
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 LuaUserData for NumberRange {
fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
fields.add_field_method_get("Min", |_, this| Ok(this.min));
fields.add_field_method_get("Max", |_, this| Ok(this.max));
}
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_meta_method(LuaMetaMethod::Eq, userdata_impl_eq);
methods.add_meta_method(LuaMetaMethod::ToString, userdata_impl_to_string);
}
}
impl fmt::Display for NumberRange {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}, {}", self.min, self.max)
}
}
impl From<RbxNumberRange> for NumberRange {
fn from(v: RbxNumberRange) -> Self {
Self {
min: v.min,
max: v.max,
}
}
}
impl From<NumberRange> for RbxNumberRange {
fn from(v: NumberRange) -> Self {
Self {
min: v.min,
max: v.max,
}
}
}

View file

@ -0,0 +1,119 @@
use core::fmt;
use mlua::prelude::*;
use rbx_dom_weak::types::{
NumberSequence as RbxNumberSequence, NumberSequenceKeypoint as RbxNumberSequenceKeypoint,
};
use super::{super::*, NumberSequenceKeypoint};
/**
An implementation of the [NumberSequence](https://create.roblox.com/docs/reference/engine/datatypes/NumberSequence) Roblox datatype.
This implements all documented properties, methods & constructors of the NumberSequence class as of March 2023.
*/
#[derive(Debug, Clone, PartialEq)]
pub struct NumberSequence {
pub(crate) keypoints: Vec<NumberSequenceKeypoint>,
}
impl NumberSequence {
pub(crate) fn make_table(lua: &Lua, datatype_table: &LuaTable) -> LuaResult<()> {
type ArgsColor = f32;
type ArgsColors = (f32, f32);
type ArgsKeypoints = Vec<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 })
} else {
// FUTURE: Better error message here using given arg types
Err(LuaError::RuntimeError(
"Invalid arguments to constructor".to_string(),
))
}
})?,
)
}
}
impl LuaUserData for NumberSequence {
fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
fields.add_field_method_get("Keypoints", |_, this| Ok(this.keypoints.clone()));
}
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_meta_method(LuaMetaMethod::Eq, userdata_impl_eq);
methods.add_meta_method(LuaMetaMethod::ToString, userdata_impl_to_string);
}
}
impl fmt::Display for NumberSequence {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for (index, keypoint) in self.keypoints.iter().enumerate() {
if index < self.keypoints.len() - 1 {
write!(f, "{}, ", keypoint)?;
} else {
write!(f, "{}", keypoint)?;
}
}
Ok(())
}
}
impl From<RbxNumberSequence> for NumberSequence {
fn from(v: RbxNumberSequence) -> Self {
Self {
keypoints: v
.keypoints
.iter()
.cloned()
.map(NumberSequenceKeypoint::from)
.collect(),
}
}
}
impl From<NumberSequence> for RbxNumberSequence {
fn from(v: NumberSequence) -> Self {
Self {
keypoints: v
.keypoints
.iter()
.cloned()
.map(RbxNumberSequenceKeypoint::from)
.collect(),
}
}
}

View file

@ -0,0 +1,73 @@
use core::fmt;
use mlua::prelude::*;
use rbx_dom_weak::types::NumberSequenceKeypoint as RbxNumberSequenceKeypoint;
use super::super::*;
/**
An implementation of the [NumberSequenceKeypoint](https://create.roblox.com/docs/reference/engine/datatypes/NumberSequenceKeypoint) Roblox datatype.
This implements all documented properties, methods & constructors of the NumberSequenceKeypoint class as of March 2023.
*/
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct NumberSequenceKeypoint {
pub(crate) time: f32,
pub(crate) value: f32,
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 LuaUserData for NumberSequenceKeypoint {
fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
fields.add_field_method_get("Time", |_, this| Ok(this.time));
fields.add_field_method_get("Value", |_, this| Ok(this.value));
fields.add_field_method_get("Envelope", |_, this| Ok(this.envelope));
}
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_meta_method(LuaMetaMethod::Eq, userdata_impl_eq);
methods.add_meta_method(LuaMetaMethod::ToString, userdata_impl_to_string);
}
}
impl fmt::Display for NumberSequenceKeypoint {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} > {}", self.time, self.value)
}
}
impl From<RbxNumberSequenceKeypoint> for NumberSequenceKeypoint {
fn from(v: RbxNumberSequenceKeypoint) -> Self {
Self {
time: v.time,
value: v.value,
envelope: v.envelope,
}
}
}
impl From<NumberSequenceKeypoint> for RbxNumberSequenceKeypoint {
fn from(v: NumberSequenceKeypoint) -> Self {
Self {
time: v.time,
value: v.value,
envelope: v.envelope,
}
}
}

View file

@ -76,7 +76,7 @@ impl LuaUserData for UDim2 {
// Methods // Methods
methods.add_method("Lerp", |_, this, (goal, alpha): (UDim2, f32)| { methods.add_method("Lerp", |_, this, (goal, alpha): (UDim2, f32)| {
let this_x = Vec2::new(this.x.scale, this.x.offset as f32); let this_x = Vec2::new(this.x.scale, this.x.offset as f32);
let goal_x = Vec2::new(goal.x.scale, this.x.offset as f32); let goal_x = Vec2::new(goal.x.scale, goal.x.offset as f32);
let this_y = Vec2::new(this.y.scale, this.y.offset as f32); let this_y = Vec2::new(this.y.scale, this.y.offset as f32);
let goal_y = Vec2::new(goal.y.scale, goal.y.offset as f32); let goal_y = Vec2::new(goal.y.scale, goal.y.offset as f32);

View file

@ -19,20 +19,23 @@ fn make_all_datatypes(lua: &Lua) -> LuaResult<Vec<(&'static str, LuaValue)>> {
use datatypes::types::*; use datatypes::types::*;
Ok(vec![ Ok(vec![
// Classes // Classes
("Axes", make_dt(lua, Axes::make_table)?), ("Axes", make_dt(lua, Axes::make_table)?),
("BrickColor", make_dt(lua, BrickColor::make_table)?), ("BrickColor", make_dt(lua, BrickColor::make_table)?),
("CFrame", make_dt(lua, CFrame::make_table)?), ("CFrame", make_dt(lua, CFrame::make_table)?),
("Color3", make_dt(lua, Color3::make_table)?), ("Color3", make_dt(lua, Color3::make_table)?),
("ColorSequence", make_dt(lua, ColorSequence::make_table)?), ("ColorSequence", make_dt(lua, ColorSequence::make_table)?),
("ColorSequenceKeypoint", make_dt(lua, ColorSequenceKeypoint::make_table)?), ("ColorSequenceKeypoint", make_dt(lua, ColorSequenceKeypoint::make_table)?),
("Faces", make_dt(lua, Faces::make_table)?), ("Faces", make_dt(lua, Faces::make_table)?),
("Rect", make_dt(lua, Rect::make_table)?), ("NumberRange", make_dt(lua, NumberRange::make_table)?),
("UDim", make_dt(lua, UDim::make_table)?), ("NumberSequence", make_dt(lua, NumberSequence::make_table)?),
("UDim2", make_dt(lua, UDim2::make_table)?), ("NumberSequenceKeypoint", make_dt(lua, NumberSequenceKeypoint::make_table)?),
("Vector2", make_dt(lua, Vector2::make_table)?), ("Rect", make_dt(lua, Rect::make_table)?),
("Vector2int16", make_dt(lua, Vector2int16::make_table)?), ("UDim", make_dt(lua, UDim::make_table)?),
("Vector3", make_dt(lua, Vector3::make_table)?), ("UDim2", make_dt(lua, UDim2::make_table)?),
("Vector3int16", make_dt(lua, Vector3int16::make_table)?), ("Vector2", make_dt(lua, Vector2::make_table)?),
("Vector2int16", make_dt(lua, Vector2int16::make_table)?),
("Vector3", make_dt(lua, Vector3::make_table)?),
("Vector3int16", make_dt(lua, Vector3int16::make_table)?),
// Singletons // Singletons
("Enum", LuaValue::UserData(Enums::make_singleton(lua)?)), ("Enum", LuaValue::UserData(Enums::make_singleton(lua)?)),
]) ])

View file

@ -0,0 +1,32 @@
-- HACK: Make luau happy, with the mlua rust
-- crate all globals are also present in _G
local NumberRange = _G.NumberRange
-- Constructors & properties
NumberRange.new(0)
NumberRange.new(0, 0)
assert(not pcall(function()
return NumberRange.new()
end))
assert(not pcall(function()
return NumberRange.new(false)
end))
assert(not pcall(function()
return NumberRange.new("", "")
end))
assert(not pcall(function()
return NumberRange.new(newproxy(true))
end))
assert(NumberRange.new(0, 1).Min == 0)
assert(NumberRange.new(1, 1).Min == 1)
assert(NumberRange.new(0, 0).Max == 0)
assert(NumberRange.new(0, 1).Max == 1)
-- Swapped args should still set proper min/max
assert(NumberRange.new(1, 0).Min == 0)
assert(NumberRange.new(1, 0).Max == 1)

View file

@ -0,0 +1,32 @@
-- HACK: Make luau happy, with the mlua rust
-- crate all globals are also present in _G
local NumberSequence = _G.NumberSequence
local NumberSequenceKeypoint = _G.NumberSequenceKeypoint
-- Constructors & properties
NumberSequence.new(0)
NumberSequence.new(0, 0)
local sequence = NumberSequence.new({
NumberSequenceKeypoint.new(0, 1),
NumberSequenceKeypoint.new(0.5, 0.5),
NumberSequenceKeypoint.new(1, 0),
})
assert(not pcall(function()
return NumberSequence.new()
end))
assert(not pcall(function()
return NumberSequence.new(false)
end))
assert(not pcall(function()
return NumberSequence.new("", "")
end))
assert(not pcall(function()
return NumberSequence.new(newproxy(true))
end))
assert(#sequence.Keypoints == 3)
assert(sequence.Keypoints[1] == NumberSequenceKeypoint.new(0, 1))
assert(sequence.Keypoints[2] == NumberSequenceKeypoint.new(0.5, 0.5))
assert(sequence.Keypoints[3] == NumberSequenceKeypoint.new(1, 0))

View file

@ -0,0 +1,29 @@
-- HACK: Make luau happy, with the mlua rust
-- crate all globals are also present in _G
local NumberSequenceKeypoint = _G.NumberSequenceKeypoint
-- Constructors & properties
NumberSequenceKeypoint.new(0, 0)
NumberSequenceKeypoint.new(1, 0)
NumberSequenceKeypoint.new(0.5, 0.5, 0.5)
assert(not pcall(function()
return NumberSequenceKeypoint.new()
end))
assert(not pcall(function()
return NumberSequenceKeypoint.new(false)
end))
assert(not pcall(function()
return NumberSequenceKeypoint.new("", "")
end))
assert(not pcall(function()
return NumberSequenceKeypoint.new(newproxy(true))
end))
assert(NumberSequenceKeypoint.new(0, 0, 0).Time == 0)
assert(NumberSequenceKeypoint.new(1, 0, 1).Time == 1)
assert(NumberSequenceKeypoint.new(0, 0, 0).Value == 0)
assert(NumberSequenceKeypoint.new(1, 1, 1).Value == 1)
assert(NumberSequenceKeypoint.new(0, 0, 0).Envelope == 0)
assert(NumberSequenceKeypoint.new(1, 1, 1).Envelope == 1)

View file

@ -47,6 +47,6 @@ assert(UDim2.new(2, 4, 6, 8) - UDim2.new(1, 1, 1, 1) == UDim2.new(1, 3, 5, 7))
-- Methods -- Methods
assert(UDim2.new(2, 4, 6, 8):Lerp(UDim2.new(1, 2, 3, 4), 0.0) == UDim2.new(2, 3, 6, 8)) assert(UDim2.new(2, 4, 6, 8):Lerp(UDim2.new(1, 2, 3, 4), 0.0) == UDim2.new(2, 4, 6, 8))
assert(UDim2.new(2, 4, 6, 8):Lerp(UDim2.new(1, 2, 3, 4), 0.5) == UDim2.new(1.5, 3, 4.5, 6)) assert(UDim2.new(2, 4, 6, 8):Lerp(UDim2.new(1, 2, 3, 4), 0.5) == UDim2.new(1.5, 3, 4.5, 6))
assert(UDim2.new(2, 4, 6, 8):Lerp(UDim2.new(1, 2, 3, 4), 1.0) == UDim2.new(1, 2, 3, 4)) assert(UDim2.new(2, 4, 6, 8):Lerp(UDim2.new(1, 2, 3, 4), 1.0) == UDim2.new(1, 2, 3, 4))