mirror of
https://github.com/lune-org/lune.git
synced 2025-01-19 09:18:06 +00:00
Implement roblox instance tags & CollectionService
This commit is contained in:
parent
dcec0e6ad3
commit
5e898858ae
4 changed files with 184 additions and 17 deletions
72
packages/lib-roblox/src/instance/collection_service.rs
Normal file
72
packages/lib-roblox/src/instance/collection_service.rs
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
use mlua::prelude::*;
|
||||||
|
|
||||||
|
use crate::shared::classes::add_class_restricted_method;
|
||||||
|
|
||||||
|
use super::Instance;
|
||||||
|
|
||||||
|
pub const CLASS_NAME: &str = "CollectionService";
|
||||||
|
|
||||||
|
pub fn add_methods<'lua, M: LuaUserDataMethods<'lua, Instance>>(m: &mut M) {
|
||||||
|
add_class_restricted_method(m, CLASS_NAME, "AddTag", collection_service_add_tag);
|
||||||
|
add_class_restricted_method(m, CLASS_NAME, "GetTags", collection_service_get_tags);
|
||||||
|
add_class_restricted_method(m, CLASS_NAME, "HasTag", collection_service_has_tag);
|
||||||
|
add_class_restricted_method(m, CLASS_NAME, "RemoveTag", collection_service_remove_tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Adds a tag to the instance.
|
||||||
|
|
||||||
|
### See Also
|
||||||
|
* [`AddTag`](https://create.roblox.com/docs/reference/engine/classes/CollectionService#AddTag)
|
||||||
|
on the Roblox Developer Hub
|
||||||
|
*/
|
||||||
|
fn collection_service_add_tag(
|
||||||
|
_: &Lua,
|
||||||
|
_: &Instance,
|
||||||
|
(object, tag_name): (Instance, String),
|
||||||
|
) -> LuaResult<()> {
|
||||||
|
object.add_tag(tag_name);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Gets all current tags for the instance.
|
||||||
|
|
||||||
|
### See Also
|
||||||
|
* [`GetTags`](https://create.roblox.com/docs/reference/engine/classes/CollectionService#GetTags)
|
||||||
|
on the Roblox Developer Hub
|
||||||
|
*/
|
||||||
|
fn collection_service_get_tags(_: &Lua, _: &Instance, object: Instance) -> LuaResult<Vec<String>> {
|
||||||
|
Ok(object.get_tags())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Checks if the instance has a specific tag.
|
||||||
|
|
||||||
|
### See Also
|
||||||
|
* [`HasTag`](https://create.roblox.com/docs/reference/engine/classes/CollectionService#HasTag)
|
||||||
|
on the Roblox Developer Hub
|
||||||
|
*/
|
||||||
|
fn collection_service_has_tag(
|
||||||
|
_: &Lua,
|
||||||
|
_: &Instance,
|
||||||
|
(object, tag_name): (Instance, String),
|
||||||
|
) -> LuaResult<bool> {
|
||||||
|
Ok(object.has_tag(tag_name))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Removes a tag from the instance.
|
||||||
|
|
||||||
|
### See Also
|
||||||
|
* [`RemoveTag`](https://create.roblox.com/docs/reference/engine/classes/CollectionService#RemoveTag)
|
||||||
|
on the Roblox Developer Hub
|
||||||
|
*/
|
||||||
|
fn collection_service_remove_tag(
|
||||||
|
_: &Lua,
|
||||||
|
_: &Instance,
|
||||||
|
(object, tag_name): (Instance, String),
|
||||||
|
) -> LuaResult<()> {
|
||||||
|
object.remove_tag(tag_name);
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -6,9 +6,9 @@ use super::Instance;
|
||||||
|
|
||||||
pub const CLASS_NAME: &str = "DataModel";
|
pub const CLASS_NAME: &str = "DataModel";
|
||||||
|
|
||||||
pub fn add_methods<'lua, M: LuaUserDataMethods<'lua, Instance>>(methods: &mut M) {
|
pub fn add_methods<'lua, M: LuaUserDataMethods<'lua, Instance>>(m: &mut M) {
|
||||||
add_class_restricted_method(methods, CLASS_NAME, "GetService", data_model_get_service);
|
add_class_restricted_method(m, CLASS_NAME, "GetService", data_model_get_service);
|
||||||
add_class_restricted_method(methods, CLASS_NAME, "FindService", data_model_find_service);
|
add_class_restricted_method(m, CLASS_NAME, "FindService", data_model_find_service);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -21,6 +21,7 @@ use crate::{
|
||||||
shared::instance::{class_exists, class_is_a, find_property_info},
|
shared::instance::{class_exists, class_is_a, find_property_info},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub(crate) mod collection_service;
|
||||||
pub(crate) mod data_model;
|
pub(crate) mod data_model;
|
||||||
|
|
||||||
static INTERNAL_DOM: Lazy<RwLock<WeakDom>> =
|
static INTERNAL_DOM: Lazy<RwLock<WeakDom>> =
|
||||||
|
@ -478,7 +479,7 @@ impl Instance {
|
||||||
pub fn set_attribute(&self, name: impl AsRef<str>, value: DomValue) {
|
pub fn set_attribute(&self, name: impl AsRef<str>, value: DomValue) {
|
||||||
let mut dom = INTERNAL_DOM
|
let mut dom = INTERNAL_DOM
|
||||||
.try_write()
|
.try_write()
|
||||||
.expect("Failed to get read access to document");
|
.expect("Failed to get write access to document");
|
||||||
let inst = dom
|
let inst = dom
|
||||||
.get_by_ref_mut(self.dom_ref)
|
.get_by_ref_mut(self.dom_ref)
|
||||||
.expect("Failed to find instance in document");
|
.expect("Failed to find instance in document");
|
||||||
|
@ -487,6 +488,96 @@ impl Instance {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Adds a tag to the instance.
|
||||||
|
|
||||||
|
### See Also
|
||||||
|
* [`AddTag`](https://create.roblox.com/docs/reference/engine/classes/CollectionService#AddTag)
|
||||||
|
on the Roblox Developer Hub
|
||||||
|
*/
|
||||||
|
pub fn add_tag(&self, name: impl AsRef<str>) {
|
||||||
|
let mut dom = INTERNAL_DOM
|
||||||
|
.try_write()
|
||||||
|
.expect("Failed to get write access to document");
|
||||||
|
let inst = dom
|
||||||
|
.get_by_ref_mut(self.dom_ref)
|
||||||
|
.expect("Failed to find instance in document");
|
||||||
|
if let Some(DomValue::Tags(tags)) = inst.properties.get_mut("Tags") {
|
||||||
|
tags.push(name.as_ref());
|
||||||
|
} else {
|
||||||
|
inst.properties.insert(
|
||||||
|
"Tags".to_string(),
|
||||||
|
DomValue::Tags(vec![name.as_ref().to_string()].into()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Gets all current tags for the instance.
|
||||||
|
|
||||||
|
### See Also
|
||||||
|
* [`GetTags`](https://create.roblox.com/docs/reference/engine/classes/CollectionService#GetTags)
|
||||||
|
on the Roblox Developer Hub
|
||||||
|
*/
|
||||||
|
pub fn get_tags(&self) -> Vec<String> {
|
||||||
|
let dom = INTERNAL_DOM
|
||||||
|
.try_read()
|
||||||
|
.expect("Failed to get read access to document");
|
||||||
|
let inst = dom
|
||||||
|
.get_by_ref(self.dom_ref)
|
||||||
|
.expect("Failed to find instance in document");
|
||||||
|
if let Some(DomValue::Tags(tags)) = inst.properties.get("Tags") {
|
||||||
|
tags.iter().map(ToString::to_string).collect()
|
||||||
|
} else {
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Checks if the instance has a specific tag.
|
||||||
|
|
||||||
|
### See Also
|
||||||
|
* [`HasTag`](https://create.roblox.com/docs/reference/engine/classes/CollectionService#HasTag)
|
||||||
|
on the Roblox Developer Hub
|
||||||
|
*/
|
||||||
|
pub fn has_tag(&self, name: impl AsRef<str>) -> bool {
|
||||||
|
let dom = INTERNAL_DOM
|
||||||
|
.try_read()
|
||||||
|
.expect("Failed to get read access to document");
|
||||||
|
let inst = dom
|
||||||
|
.get_by_ref(self.dom_ref)
|
||||||
|
.expect("Failed to find instance in document");
|
||||||
|
if let Some(DomValue::Tags(tags)) = inst.properties.get("Tags") {
|
||||||
|
let name = name.as_ref();
|
||||||
|
tags.iter().any(|tag| tag == name)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Removes a tag from the instance.
|
||||||
|
|
||||||
|
### See Also
|
||||||
|
* [`RemoveTag`](https://create.roblox.com/docs/reference/engine/classes/CollectionService#RemoveTag)
|
||||||
|
on the Roblox Developer Hub
|
||||||
|
*/
|
||||||
|
pub fn remove_tag(&self, name: impl AsRef<str>) {
|
||||||
|
let mut dom = INTERNAL_DOM
|
||||||
|
.try_write()
|
||||||
|
.expect("Failed to get write access to document");
|
||||||
|
let inst = dom
|
||||||
|
.get_by_ref_mut(self.dom_ref)
|
||||||
|
.expect("Failed to find instance in document");
|
||||||
|
if let Some(DomValue::Tags(tags)) = inst.properties.get_mut("Tags") {
|
||||||
|
let name = name.as_ref();
|
||||||
|
let mut new_tags = tags.iter().map(ToString::to_string).collect::<Vec<_>>();
|
||||||
|
new_tags.retain(|tag| tag != name);
|
||||||
|
inst.properties
|
||||||
|
.insert("Tags".to_string(), DomValue::Tags(new_tags.into()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Gets all of the current children of this `Instance`.
|
Gets all of the current children of this `Instance`.
|
||||||
|
|
||||||
|
@ -728,11 +819,6 @@ impl LuaUserData for Instance {
|
||||||
"Parent" => {
|
"Parent" => {
|
||||||
return this.get_parent().to_lua(lua);
|
return this.get_parent().to_lua(lua);
|
||||||
}
|
}
|
||||||
// These are stored as properties in Rojo but are actually not, so we block them
|
|
||||||
"Attributes" | "Tags" => return Err(LuaError::RuntimeError(format!(
|
|
||||||
"{} is not a valid member of {}",
|
|
||||||
prop_name, this
|
|
||||||
))),
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -828,13 +914,6 @@ impl LuaUserData for Instance {
|
||||||
this.set_parent(parent);
|
this.set_parent(parent);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
// These are stored as properties in Rojo but are actually not, so we block them
|
|
||||||
"Attributes" | "Tags" => {
|
|
||||||
return Err(LuaError::RuntimeError(format!(
|
|
||||||
"{} is not a valid member of {}",
|
|
||||||
prop_name, this
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -995,12 +1074,21 @@ impl LuaUserData for Instance {
|
||||||
// Here we add inheritance-like behavior for instances by creating
|
// Here we add inheritance-like behavior for instances by creating
|
||||||
// methods that are restricted to specific classnames / base classes
|
// methods that are restricted to specific classnames / base classes
|
||||||
data_model::add_methods(methods);
|
data_model::add_methods(methods);
|
||||||
|
collection_service::add_methods(methods);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Instance {
|
impl fmt::Display for Instance {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "{}", self.get_name())
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
if self.is_destroyed() {
|
||||||
|
"<<DESTROYED>>".to_string()
|
||||||
|
} else {
|
||||||
|
self.get_name()
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,13 @@ pub(crate) fn find_property_info(
|
||||||
let instance_class = instance_class.as_ref();
|
let instance_class = instance_class.as_ref();
|
||||||
let property_name = property_name.as_ref();
|
let property_name = property_name.as_ref();
|
||||||
|
|
||||||
|
// Attributes and tags are *technically* properties but we don't
|
||||||
|
// want to treat them as such when looking up property info, any
|
||||||
|
// reading or modification of these should always be explicit
|
||||||
|
if matches!(property_name, "Attributes" | "Tags") {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
// FUTURE: We can probably cache the result of calling this
|
// FUTURE: We can probably cache the result of calling this
|
||||||
// function, if property access is being used in a tight loop
|
// function, if property access is being used in a tight loop
|
||||||
// in a build step or something similar then it would be beneficial
|
// in a build step or something similar then it would be beneficial
|
||||||
|
|
Loading…
Reference in a new issue