mirror of
https://github.com/lune-org/lune.git
synced 2025-05-04 10:43:57 +01:00
merge: origin/main -> feature/datetime
This commit is contained in:
commit
eee7a671a1
18 changed files with 272 additions and 74 deletions
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -8,6 +8,16 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Added
|
||||
|
||||
- Added [Terrain:GetMaterialColor](https://create.roblox.com/docs/reference/engine/classes/Terrain#GetMaterialColor) and [Terrain:SetMaterialColor](https://create.roblox.com/docs/reference/engine/classes/Terrain#SetMaterialColor) ([#93])
|
||||
- Added support for a variable number of arguments for CFrame methods ([#85])
|
||||
|
||||
[#93]: https://github.com/filiptibell/lune/pull/93
|
||||
[#85]: https://github.com/filiptibell/lune/pull/85
|
||||
|
||||
## `0.7.7` - August 23rd, 2023
|
||||
|
||||
### Added
|
||||
|
|
29
Cargo.lock
generated
29
Cargo.lock
generated
|
@ -58,16 +58,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.3.2"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
|
||||
checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-query",
|
||||
"anstyle-wincon",
|
||||
"colorchoice",
|
||||
"is-terminal",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
|
@ -97,9 +96,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "1.0.2"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c677ab05e09154296dd37acecd46420c17b9713e8366facafa8fc0885167cf4c"
|
||||
checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys 0.48.0",
|
||||
|
@ -340,9 +339,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.3.24"
|
||||
version = "4.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb690e81c7840c0d7aade59f242ea3b41b9bc27bcd5997890e7702ae4b32e487"
|
||||
checksum = "1d5f1946157a96594eb2d2c10eb7ad9a2b27518cb3000209dec700c35df9197d"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
|
@ -351,9 +350,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.3.24"
|
||||
version = "4.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ed2e96bc16d8d740f6f48d663eddf4b8a0983e79210fd55479b7bcd0a69860e"
|
||||
checksum = "78116e32a042dd73c2901f0dc30790d20ff3447f3e3472fad359e8c3d282bcd6"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
|
@ -363,9 +362,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.3.12"
|
||||
version = "4.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050"
|
||||
checksum = "c9fd1a5729c4548118d7d70ff234a44868d00489a4b6597b0b020918a0e91a1a"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
|
@ -375,9 +374,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.5.0"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
|
||||
checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961"
|
||||
|
||||
[[package]]
|
||||
name = "clipboard-win"
|
||||
|
@ -1414,9 +1413,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.12"
|
||||
version = "0.2.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05"
|
||||
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
[tools]
|
||||
luau-lsp = "JohnnyMorganz/luau-lsp@1.20.0"
|
||||
selene = "Kampfkarren/selene@0.24.0"
|
||||
stylua = "JohnnyMorganz/StyLua@0.17.0"
|
||||
luau-lsp = "JohnnyMorganz/luau-lsp@1.23.0"
|
||||
selene = "Kampfkarren/selene@0.25.0"
|
||||
stylua = "JohnnyMorganz/StyLua@0.18.1"
|
||||
|
|
|
@ -39,7 +39,7 @@ impl<'lua> FromLua<'lua> for LuauCompileOptions {
|
|||
|
||||
let get_and_check = |name: &'static str| -> LuaResult<Option<u8>> {
|
||||
match t.get(name)? {
|
||||
Some(n @ (0 | 1 | 2)) => Ok(Some(n)),
|
||||
Some(n @ (0..=2)) => Ok(Some(n)),
|
||||
Some(n) => Err(LuaError::runtime(format!(
|
||||
"'{name}' must be one of: 0, 1, or 2 - got {n}"
|
||||
))),
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::{cell::Cell, sync::Arc};
|
||||
use std::sync::Arc;
|
||||
|
||||
use hyper::upgrade::Upgraded;
|
||||
use mlua::prelude::*;
|
||||
|
@ -46,7 +46,7 @@ return freeze(setmetatable({
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct NetWebSocket<T> {
|
||||
close_code: Arc<Cell<Option<u16>>>,
|
||||
close_code: Arc<AsyncMutex<Option<u16>>>,
|
||||
read_stream: Arc<AsyncMutex<SplitStream<WebSocketStream<T>>>>,
|
||||
write_stream: Arc<AsyncMutex<SplitSink<WebSocketStream<T>, WsMessage>>>,
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ where
|
|||
let (write, read) = value.split();
|
||||
|
||||
Self {
|
||||
close_code: Arc::new(Cell::new(None)),
|
||||
close_code: Arc::new(AsyncMutex::new(None)),
|
||||
read_stream: Arc::new(AsyncMutex::new(read)),
|
||||
write_stream: Arc::new(AsyncMutex::new(write)),
|
||||
}
|
||||
|
@ -137,10 +137,16 @@ fn close_code<'lua, T>(
|
|||
where
|
||||
T: AsyncRead + AsyncWrite + Unpin,
|
||||
{
|
||||
Ok(match socket.close_code.get() {
|
||||
Some(code) => LuaValue::Number(code as f64),
|
||||
None => LuaValue::Nil,
|
||||
})
|
||||
Ok(
|
||||
match *socket
|
||||
.close_code
|
||||
.try_lock()
|
||||
.expect("Failed to lock close code")
|
||||
{
|
||||
Some(code) => LuaValue::Number(code as f64),
|
||||
None => LuaValue::Nil,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
async fn close<'lua, T>(
|
||||
|
@ -204,7 +210,8 @@ where
|
|||
let msg = match item {
|
||||
Ok(Some(WsMessage::Close(msg))) => {
|
||||
if let Some(msg) = &msg {
|
||||
socket.close_code.replace(Some(msg.code.into()));
|
||||
let mut code = socket.close_code.lock().await;
|
||||
*code = Some(msg.code.into());
|
||||
}
|
||||
Ok(Some(WsMessage::Close(msg)))
|
||||
}
|
||||
|
|
|
@ -57,7 +57,12 @@ impl<'fut> Scheduler<'fut> {
|
|||
if thread.status() != LuaThreadStatus::Resumable {
|
||||
// NOTE: Threads that were spawned to resume
|
||||
// with an error will not have a result sender
|
||||
if let Some(sender) = self.thread_senders.borrow_mut().remove(&thread_id) {
|
||||
if let Some(sender) = self
|
||||
.thread_senders
|
||||
.try_lock()
|
||||
.expect("Failed to get thread senders")
|
||||
.remove(&thread_id)
|
||||
{
|
||||
if sender.receiver_count() > 0 {
|
||||
let stored = match res {
|
||||
Err(e) => Err(e),
|
||||
|
|
|
@ -14,8 +14,8 @@ impl<'fut> Scheduler<'fut> {
|
|||
pub(super) fn has_thread(&self) -> bool {
|
||||
!self
|
||||
.threads
|
||||
.try_borrow()
|
||||
.expect("Failed to borrow threads vec")
|
||||
.try_lock()
|
||||
.expect("Failed to lock threads vec")
|
||||
.is_empty()
|
||||
}
|
||||
|
||||
|
@ -27,9 +27,9 @@ impl<'fut> Scheduler<'fut> {
|
|||
pub(super) fn pop_thread(&self) -> LuaResult<Option<SchedulerThread>> {
|
||||
match self
|
||||
.threads
|
||||
.try_borrow_mut()
|
||||
.try_lock()
|
||||
.into_lua_err()
|
||||
.context("Failed to borrow threads vec")?
|
||||
.context("Failed to lock threads vec")?
|
||||
.pop_front()
|
||||
{
|
||||
Some(thread) => Ok(Some(thread)),
|
||||
|
@ -54,9 +54,9 @@ impl<'fut> Scheduler<'fut> {
|
|||
|
||||
self.state.set_thread_error(thread_id, err);
|
||||
self.threads
|
||||
.try_borrow_mut()
|
||||
.try_lock()
|
||||
.into_lua_err()
|
||||
.context("Failed to borrow threads vec")?
|
||||
.context("Failed to lock threads vec")?
|
||||
.push_front(thread);
|
||||
|
||||
// NOTE: We might be resuming futures, need to signal that a
|
||||
|
@ -83,16 +83,18 @@ impl<'fut> Scheduler<'fut> {
|
|||
let thread_id = thread.id();
|
||||
|
||||
self.threads
|
||||
.try_borrow_mut()
|
||||
.try_lock()
|
||||
.into_lua_err()
|
||||
.context("Failed to borrow threads vec")?
|
||||
.context("Failed to lock threads vec")?
|
||||
.push_front(thread);
|
||||
|
||||
// NOTE: We might be resuming the same thread several times and
|
||||
// pushing it to the scheduler several times before it is done,
|
||||
// and we should only ever create one result sender per thread
|
||||
self.thread_senders
|
||||
.borrow_mut()
|
||||
.try_lock()
|
||||
.into_lua_err()
|
||||
.context("Failed to lock thread senders vec")?
|
||||
.entry(thread_id)
|
||||
.or_insert_with(|| SchedulerThreadSender::new(1));
|
||||
|
||||
|
@ -120,16 +122,18 @@ impl<'fut> Scheduler<'fut> {
|
|||
let thread_id = thread.id();
|
||||
|
||||
self.threads
|
||||
.try_borrow_mut()
|
||||
.try_lock()
|
||||
.into_lua_err()
|
||||
.context("Failed to borrow threads vec")?
|
||||
.context("Failed to lock threads vec")?
|
||||
.push_back(thread);
|
||||
|
||||
// NOTE: We might be resuming the same thread several times and
|
||||
// pushing it to the scheduler several times before it is done,
|
||||
// and we should only ever create one result sender per thread
|
||||
self.thread_senders
|
||||
.borrow_mut()
|
||||
.try_lock()
|
||||
.into_lua_err()
|
||||
.context("Failed to lock thread senders vec")?
|
||||
.entry(thread_id)
|
||||
.or_insert_with(|| SchedulerThreadSender::new(1));
|
||||
|
||||
|
@ -149,7 +153,7 @@ impl<'fut> Scheduler<'fut> {
|
|||
thread_id: SchedulerThreadId,
|
||||
) -> LuaResult<LuaMultiValue<'a>> {
|
||||
let mut recv = {
|
||||
let senders = self.thread_senders.borrow();
|
||||
let senders = self.thread_senders.lock().await;
|
||||
let sender = senders
|
||||
.get(&thread_id)
|
||||
.expect("Tried to wait for thread that is not queued");
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use std::{
|
||||
cell::RefCell,
|
||||
collections::{HashMap, VecDeque},
|
||||
pin::Pin,
|
||||
sync::Arc,
|
||||
|
@ -37,8 +36,8 @@ type SchedulerFuture<'fut> = Pin<Box<dyn Future<Output = ()> + 'fut>>;
|
|||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct Scheduler<'fut> {
|
||||
state: Arc<SchedulerState>,
|
||||
threads: Arc<RefCell<VecDeque<SchedulerThread>>>,
|
||||
thread_senders: Arc<RefCell<HashMap<SchedulerThreadId, SchedulerThreadSender>>>,
|
||||
threads: Arc<AsyncMutex<VecDeque<SchedulerThread>>>,
|
||||
thread_senders: Arc<AsyncMutex<HashMap<SchedulerThreadId, SchedulerThreadSender>>>,
|
||||
/*
|
||||
FUTURE: Get rid of these, let the tokio runtime handle running
|
||||
and resumption of futures completely, just use our scheduler
|
||||
|
@ -64,11 +63,12 @@ impl<'fut> Scheduler<'fut> {
|
|||
/**
|
||||
Creates a new scheduler.
|
||||
*/
|
||||
#[allow(clippy::arc_with_non_send_sync)] // FIXME: Clippy lints our tokio mutexes that are definitely Send + Sync
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
state: Arc::new(SchedulerState::new()),
|
||||
threads: Arc::new(RefCell::new(VecDeque::new())),
|
||||
thread_senders: Arc::new(RefCell::new(HashMap::new())),
|
||||
threads: Arc::new(AsyncMutex::new(VecDeque::new())),
|
||||
thread_senders: Arc::new(AsyncMutex::new(HashMap::new())),
|
||||
futures_lua: Arc::new(AsyncMutex::new(FuturesUnordered::new())),
|
||||
futures_background: Arc::new(AsyncMutex::new(FuturesUnordered::new())),
|
||||
}
|
||||
|
|
|
@ -118,8 +118,8 @@ pub fn pretty_format_value(
|
|||
COLOR_GREEN.apply_to(
|
||||
s.to_string_lossy()
|
||||
.replace('"', r#"\""#)
|
||||
.replace('\r', r#"\r"#)
|
||||
.replace('\n', r#"\n"#)
|
||||
.replace('\r', r"\r")
|
||||
.replace('\n', r"\n")
|
||||
)
|
||||
)?,
|
||||
LuaValue::Table(ref tab) => {
|
||||
|
|
|
@ -2,7 +2,7 @@ use core::fmt;
|
|||
use std::ops;
|
||||
|
||||
use glam::{EulerRot, Mat4, Quat, Vec3};
|
||||
use mlua::prelude::*;
|
||||
use mlua::{prelude::*, Variadic};
|
||||
use rbx_dom_weak::types::{CFrame as DomCFrame, Matrix3 as DomMatrix3, Vector3 as DomVector3};
|
||||
|
||||
use crate::{lune::util::TableBuilder, roblox::exports::LuaExportsTable};
|
||||
|
@ -205,38 +205,46 @@ impl LuaUserData for CFrame {
|
|||
translation,
|
||||
)))
|
||||
});
|
||||
methods.add_method("ToWorldSpace", |_, this, rhs: LuaUserDataRef<CFrame>| {
|
||||
Ok(*this * *rhs)
|
||||
});
|
||||
methods.add_method("ToObjectSpace", |_, this, rhs: LuaUserDataRef<CFrame>| {
|
||||
let inverse = this.inverse();
|
||||
Ok(inverse * *rhs)
|
||||
});
|
||||
methods.add_method(
|
||||
"ToWorldSpace",
|
||||
|_, this, rhs: Variadic<LuaUserDataRef<CFrame>>| {
|
||||
Ok(Variadic::from_iter(rhs.into_iter().map(|cf| *this * *cf)))
|
||||
},
|
||||
);
|
||||
methods.add_method(
|
||||
"ToObjectSpace",
|
||||
|_, this, rhs: Variadic<LuaUserDataRef<CFrame>>| {
|
||||
let inverse = this.inverse();
|
||||
Ok(Variadic::from_iter(rhs.into_iter().map(|cf| inverse * *cf)))
|
||||
},
|
||||
);
|
||||
methods.add_method(
|
||||
"PointToWorldSpace",
|
||||
|_, this, rhs: LuaUserDataRef<Vector3>| Ok(*this * *rhs),
|
||||
|_, this, rhs: Variadic<LuaUserDataRef<Vector3>>| {
|
||||
Ok(Variadic::from_iter(rhs.into_iter().map(|v3| *this * *v3)))
|
||||
},
|
||||
);
|
||||
methods.add_method(
|
||||
"PointToObjectSpace",
|
||||
|_, this, rhs: LuaUserDataRef<Vector3>| {
|
||||
|_, this, rhs: Variadic<LuaUserDataRef<Vector3>>| {
|
||||
let inverse = this.inverse();
|
||||
Ok(inverse * *rhs)
|
||||
Ok(Variadic::from_iter(rhs.into_iter().map(|v3| inverse * *v3)))
|
||||
},
|
||||
);
|
||||
methods.add_method(
|
||||
"VectorToWorldSpace",
|
||||
|_, this, rhs: LuaUserDataRef<Vector3>| {
|
||||
|_, this, rhs: Variadic<LuaUserDataRef<Vector3>>| {
|
||||
let result = *this - Vector3(this.position());
|
||||
Ok(result * *rhs)
|
||||
Ok(Variadic::from_iter(rhs.into_iter().map(|v3| result * *v3)))
|
||||
},
|
||||
);
|
||||
methods.add_method(
|
||||
"VectorToObjectSpace",
|
||||
|_, this, rhs: LuaUserDataRef<Vector3>| {
|
||||
|_, this, rhs: Variadic<LuaUserDataRef<Vector3>>| {
|
||||
let inverse = this.inverse();
|
||||
let result = inverse - Vector3(inverse.position());
|
||||
|
||||
Ok(result * *rhs)
|
||||
Ok(Variadic::from_iter(rhs.into_iter().map(|v3| result * *v3)))
|
||||
},
|
||||
);
|
||||
#[rustfmt::skip]
|
||||
|
|
|
@ -108,6 +108,20 @@ impl LuaExportsTable<'_> for Color3 {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for Color3 {
|
||||
fn from_lua(value: LuaValue<'lua>, _: &'lua Lua) -> LuaResult<Self> {
|
||||
if let LuaValue::UserData(ud) = value {
|
||||
Ok(*ud.borrow::<Color3>()?)
|
||||
} else {
|
||||
Err(LuaError::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "Color3",
|
||||
message: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaUserData for Color3 {
|
||||
fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field_method_get("R", |_, this| Ok(this.r));
|
||||
|
@ -287,20 +301,12 @@ impl From<Color3> for DomColor3 {
|
|||
|
||||
impl From<DomColor3uint8> for Color3 {
|
||||
fn from(v: DomColor3uint8) -> Self {
|
||||
Self {
|
||||
r: (v.r as f32) / 255f32,
|
||||
g: (v.g as f32) / 255f32,
|
||||
b: (v.b as f32) / 255f32,
|
||||
}
|
||||
Color3::from(DomColor3::from(v))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Color3> for DomColor3uint8 {
|
||||
fn from(v: Color3) -> Self {
|
||||
Self {
|
||||
r: v.r.clamp(u8::MIN as f32, u8::MAX as f32) as u8,
|
||||
g: v.g.clamp(u8::MIN as f32, u8::MAX as f32) as u8,
|
||||
b: v.b.clamp(u8::MIN as f32, u8::MAX as f32) as u8,
|
||||
}
|
||||
DomColor3uint8::from(DomColor3::from(v))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,6 +74,20 @@ impl LuaUserData for EnumItem {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'lua> FromLua<'lua> for EnumItem {
|
||||
fn from_lua(value: LuaValue<'lua>, _: &'lua Lua) -> LuaResult<Self> {
|
||||
if let LuaValue::UserData(ud) = value {
|
||||
Ok(ud.borrow::<EnumItem>()?.to_owned())
|
||||
} else {
|
||||
Err(LuaError::FromLuaConversionError {
|
||||
from: value.type_name(),
|
||||
to: "EnumItem",
|
||||
message: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for EnumItem {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}.{}", self.parent, self.name)
|
||||
|
|
|
@ -20,6 +20,7 @@ use crate::{
|
|||
|
||||
pub(crate) mod base;
|
||||
pub(crate) mod data_model;
|
||||
pub(crate) mod terrain;
|
||||
pub(crate) mod workspace;
|
||||
|
||||
const PROPERTY_NAME_ATTRIBUTES: &str = "Attributes";
|
||||
|
@ -729,6 +730,7 @@ impl LuaUserData for Instance {
|
|||
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
base::add_methods(methods);
|
||||
data_model::add_methods(methods);
|
||||
terrain::add_methods(methods);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
127
src/roblox/instance/terrain.rs
Normal file
127
src/roblox/instance/terrain.rs
Normal file
|
@ -0,0 +1,127 @@
|
|||
use mlua::prelude::*;
|
||||
use rbx_dom_weak::types::{MaterialColors, TerrainMaterials, Variant};
|
||||
|
||||
use crate::roblox::{
|
||||
datatypes::types::{Color3, EnumItem},
|
||||
shared::classes::{add_class_restricted_method, add_class_restricted_method_mut},
|
||||
};
|
||||
|
||||
use super::Instance;
|
||||
|
||||
pub const CLASS_NAME: &str = "Terrain";
|
||||
|
||||
fn material_from_name(material_name: &str) -> Option<TerrainMaterials> {
|
||||
match material_name {
|
||||
"Grass" => Some(TerrainMaterials::Grass),
|
||||
"Slate" => Some(TerrainMaterials::Slate),
|
||||
"Concrete" => Some(TerrainMaterials::Concrete),
|
||||
"Brick" => Some(TerrainMaterials::Brick),
|
||||
"Sand" => Some(TerrainMaterials::Sand),
|
||||
"WoodPlanks" => Some(TerrainMaterials::WoodPlanks),
|
||||
"Rock" => Some(TerrainMaterials::Rock),
|
||||
"Glacier" => Some(TerrainMaterials::Glacier),
|
||||
"Snow" => Some(TerrainMaterials::Snow),
|
||||
"Sandstone" => Some(TerrainMaterials::Sandstone),
|
||||
"Mud" => Some(TerrainMaterials::Mud),
|
||||
"Basalt" => Some(TerrainMaterials::Basalt),
|
||||
"Ground" => Some(TerrainMaterials::Ground),
|
||||
"CrackedLava" => Some(TerrainMaterials::CrackedLava),
|
||||
"Asphalt" => Some(TerrainMaterials::Asphalt),
|
||||
"Cobblestone" => Some(TerrainMaterials::Cobblestone),
|
||||
"Ice" => Some(TerrainMaterials::Ice),
|
||||
"LeafyGrass" => Some(TerrainMaterials::LeafyGrass),
|
||||
"Salt" => Some(TerrainMaterials::Salt),
|
||||
"Limestone" => Some(TerrainMaterials::Limestone),
|
||||
"Pavement" => Some(TerrainMaterials::Pavement),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_methods<'lua, M: LuaUserDataMethods<'lua, Instance>>(methods: &mut M) {
|
||||
add_class_restricted_method(
|
||||
methods,
|
||||
CLASS_NAME,
|
||||
"GetMaterialColor",
|
||||
terrain_get_material_color,
|
||||
);
|
||||
|
||||
add_class_restricted_method_mut(
|
||||
methods,
|
||||
CLASS_NAME,
|
||||
"SetMaterialColor",
|
||||
terrain_set_material_color,
|
||||
)
|
||||
}
|
||||
|
||||
fn get_or_create_material_colors(instance: &Instance) -> MaterialColors {
|
||||
if let Some(Variant::MaterialColors(material_colors)) = instance.get_property("MaterialColors")
|
||||
{
|
||||
material_colors
|
||||
} else {
|
||||
MaterialColors::default()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the color of the given terrain material.
|
||||
|
||||
### See Also
|
||||
* [`GetMaterialColor`](https://create.roblox.com/docs/reference/engine/classes/Terrain#GetMaterialColor)
|
||||
on the Roblox Developer Hub
|
||||
*/
|
||||
fn terrain_get_material_color(_: &Lua, this: &Instance, material: EnumItem) -> LuaResult<Color3> {
|
||||
let material_colors = get_or_create_material_colors(this);
|
||||
|
||||
if &material.parent.desc.name != "Material" {
|
||||
return Err(LuaError::RuntimeError(format!(
|
||||
"Expected Enum.Material, got Enum.{}",
|
||||
&material.parent.desc.name
|
||||
)));
|
||||
}
|
||||
|
||||
if let Some(terrain_material) = material_from_name(&material.name) {
|
||||
Ok(material_colors.get_color(terrain_material).into())
|
||||
} else {
|
||||
Err(LuaError::RuntimeError(format!(
|
||||
"{} is not a valid Terrain material",
|
||||
&material.name
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Sets the color of the given terrain material.
|
||||
|
||||
### See Also
|
||||
* [`SetMaterialColor`](https://create.roblox.com/docs/reference/engine/classes/Terrain#SetMaterialColor)
|
||||
on the Roblox Developer Hub
|
||||
*/
|
||||
fn terrain_set_material_color(
|
||||
_: &Lua,
|
||||
this: &mut Instance,
|
||||
args: (EnumItem, Color3),
|
||||
) -> LuaResult<()> {
|
||||
let mut material_colors = get_or_create_material_colors(this);
|
||||
let material = args.0;
|
||||
let color = args.1;
|
||||
|
||||
if &material.parent.desc.name != "Material" {
|
||||
return Err(LuaError::RuntimeError(format!(
|
||||
"Expected Enum.Material, got Enum.{}",
|
||||
&material.parent.desc.name
|
||||
)));
|
||||
}
|
||||
|
||||
let terrain_material = if let Some(terrain_material) = material_from_name(&material.name) {
|
||||
terrain_material
|
||||
} else {
|
||||
return Err(LuaError::RuntimeError(format!(
|
||||
"{} is not a valid Terrain material",
|
||||
&material.name
|
||||
)));
|
||||
};
|
||||
|
||||
material_colors.set_color(terrain_material, color.into());
|
||||
this.set_property("MaterialColors", Variant::MaterialColors(material_colors));
|
||||
Ok(())
|
||||
}
|
|
@ -71,7 +71,6 @@ pub(crate) fn add_class_restricted_method<'lua, M: LuaUserDataMethods<'lua, Inst
|
|||
});
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn add_class_restricted_method_mut<
|
||||
'lua,
|
||||
M: LuaUserDataMethods<'lua, Instance>,
|
||||
|
|
|
@ -146,6 +146,7 @@ create_tests! {
|
|||
|
||||
roblox_instance_classes_data_model: "roblox/instance/classes/DataModel",
|
||||
roblox_instance_classes_workspace: "roblox/instance/classes/Workspace",
|
||||
roblox_instance_classes_terrain: "roblox/instance/classes/Terrain",
|
||||
|
||||
roblox_instance_methods_clear_all_children: "roblox/instance/methods/ClearAllChildren",
|
||||
roblox_instance_methods_clone: "roblox/instance/methods/Clone",
|
||||
|
|
|
@ -103,6 +103,9 @@ local offset = CFrame.new(0, 0, -5)
|
|||
assert(offset:ToWorldSpace(offset).Z == offset.Z * 2)
|
||||
assert(offset:ToObjectSpace(offset).Z == 0)
|
||||
|
||||
assert(select("#", offset:ToWorldSpace(offset, offset, offset)) == 3)
|
||||
assert(select("#", offset:ToObjectSpace(offset, offset, offset)) == 3)
|
||||
|
||||
local world = CFrame.fromOrientation(0, math.rad(90), 0) * CFrame.new(0, 0, -5)
|
||||
local world2 = CFrame.fromOrientation(0, -math.rad(90), 0) * CFrame.new(0, 0, -5)
|
||||
assertEq(CFrame.identity:ToObjectSpace(world), world)
|
||||
|
|
13
tests/roblox/instance/classes/Terrain.luau
Normal file
13
tests/roblox/instance/classes/Terrain.luau
Normal file
|
@ -0,0 +1,13 @@
|
|||
local roblox = require("@lune/roblox") :: any
|
||||
local Instance = roblox.Instance
|
||||
local Color3 = roblox.Color3
|
||||
local Enum = roblox.Enum
|
||||
|
||||
local game = Instance.new("DataModel")
|
||||
local workspace = game:GetService("Workspace")
|
||||
local terrain = (workspace :: any).Terrain
|
||||
|
||||
assert(terrain:GetMaterialColor(Enum.Material.Grass) == Color3.fromRGB(106, 127, 63))
|
||||
|
||||
terrain:SetMaterialColor(Enum.Material.Sand, Color3.new(1, 1, 1))
|
||||
assert(terrain:GetMaterialColor(Enum.Material.Sand) == Color3.new(1, 1, 1))
|
Loading…
Add table
Reference in a new issue