From 7860f2717f2aba20dcad4bfcdb87e7cc28c833af Mon Sep 17 00:00:00 2001 From: morosanu Date: Tue, 22 Jul 2025 18:12:16 -0400 Subject: [PATCH 01/22] Fix auto connect for play mode (#1066) --- CHANGELOG.md | 3 ++- plugin/src/App/init.lua | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d426619d..3f98d844 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,10 @@ # Rojo Changelog ## Unreleased - +* Fixed `Auto Connect Playtest Server` no longer functioning due to Roblox change ([#1066]) * Added an update indicator to the version header when a new version of the plugin is available. ([#1069]) +[#1049]: https://github.com/rojo-rbx/rojo/pull/1066 [#1069]: https://github.com/rojo-rbx/rojo/pull/1069 ## 7.5.1 - April 25th, 2025 diff --git a/plugin/src/App/init.lua b/plugin/src/App/init.lua index 828f7299..74235656 100644 --- a/plugin/src/App/init.lua +++ b/plugin/src/App/init.lua @@ -386,7 +386,8 @@ function App:releaseSyncLock() end function App:isAutoConnectPlaytestServerAvailable() - return RunService:IsRunMode() + return RunService:IsRunning() + and RunService:IsStudio() and RunService:IsServer() and Settings:get("autoConnectPlaytestServer") and workspace:GetAttribute("__Rojo_ConnectionUrl") From 6dfdfbe514b888c9d132bcb94f1324aac3d6e7c6 Mon Sep 17 00:00:00 2001 From: Cameron Campbell Date: Wed, 23 Jul 2025 00:13:23 +0100 Subject: [PATCH 02/22] Cache Rust Dependencies in release.yml. (#1079) --- .github/workflows/ci.yml | 56 +++++++++++++++++++++++++++++++---- .github/workflows/release.yml | 26 ++++++++++++---- 2 files changed, 70 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8f7b8a02..6f68febe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,8 +26,14 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable - - name: Rust cache - uses: Swatinem/rust-cache@v2 + - name: Restore Rust Cache + uses: actions/cache/restore@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ matrix.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - name: Setup Aftman uses: ok-nick/setup-aftman@v0.4.2 @@ -40,6 +46,15 @@ jobs: - name: Test run: cargo test --locked --verbose + - name: Save Rust Cache + uses: actions/cache/save@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ matrix.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + msrv: name: Check MSRV runs-on: ubuntu-latest @@ -52,8 +67,14 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@1.70.0 - - name: Rust cache - uses: Swatinem/rust-cache@v2 + - name: Restore Rust Cache + uses: actions/cache/restore@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - name: Setup Aftman uses: ok-nick/setup-aftman@v0.4.2 @@ -63,6 +84,15 @@ jobs: - name: Build run: cargo build --locked --verbose + - name: Save Rust Cache + uses: actions/cache/save@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + lint: name: Rustfmt, Clippy, Stylua, & Selene runs-on: ubuntu-latest @@ -77,8 +107,14 @@ jobs: with: components: rustfmt, clippy - - name: Rust cache - uses: Swatinem/rust-cache@v2 + - name: Restore Rust Cache + uses: actions/cache/restore@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - name: Setup Aftman uses: ok-nick/setup-aftman@v0.4.2 @@ -97,3 +133,11 @@ jobs: - name: Clippy run: cargo clippy + - name: Save Rust Cache + uses: actions/cache/save@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6c299878..4ce868a6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -86,6 +86,15 @@ jobs: with: targets: ${{ matrix.target }} + - name: Restore Rust Cache + uses: actions/cache/restore@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ matrix.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + - name: Setup Aftman uses: ok-nick/setup-aftman@v0.4.2 with: @@ -93,10 +102,15 @@ jobs: - name: Build Release run: cargo build --release --locked --verbose --target ${{ matrix.target }} - env: - # Build into a known directory so we can find our build artifact more - # easily. - CARGO_TARGET_DIR: output + + - name: Save Rust Cache + uses: actions/cache/save@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ matrix.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - name: Generate Artifact Name shell: bash @@ -113,11 +127,11 @@ jobs: mkdir staging if [ "${{ matrix.host }}" = "windows" ]; then - cp "output/${{ matrix.target }}/release/$BIN.exe" staging/ + cp "target/${{ matrix.target }}/release/$BIN.exe" staging/ cd staging 7z a ../$ARTIFACT_NAME * else - cp "output/${{ matrix.target }}/release/$BIN" staging/ + cp "target/${{ matrix.target }}/release/$BIN" staging/ cd staging zip ../$ARTIFACT_NAME * fi From e092a7301fb5522c8c4916e71de151aff789bbf0 Mon Sep 17 00:00:00 2001 From: Micah Date: Fri, 25 Jul 2025 15:04:42 -0700 Subject: [PATCH 03/22] Change background color of web UI to gray (#1080) --- CHANGELOG.md | 2 ++ assets/index.css | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f98d844..60b7c05b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,11 @@ # Rojo Changelog ## Unreleased +* Changed the background of the server's in-browser UI to be gray instead of white ([#1080]) * Fixed `Auto Connect Playtest Server` no longer functioning due to Roblox change ([#1066]) * Added an update indicator to the version header when a new version of the plugin is available. ([#1069]) +[#1080]: https://github.com/rojo-rbx/rojo/pull/1080 [#1049]: https://github.com/rojo-rbx/rojo/pull/1066 [#1069]: https://github.com/rojo-rbx/rojo/pull/1069 diff --git a/assets/index.css b/assets/index.css index bbec79cb..2eada350 100644 --- a/assets/index.css +++ b/assets/index.css @@ -17,6 +17,10 @@ html { line-height: 1.4; } +body { + background-color: #e7e7e7 +} + img { max-width:100%; max-height:100%; From 7f68d9887bd4bd75f0f4be301cb84134c3c31513 Mon Sep 17 00:00:00 2001 From: Sebastian Stachowicz <50264212+Sebastian2852@users.noreply.github.com> Date: Sat, 26 Jul 2025 00:27:11 +0200 Subject: [PATCH 04/22] Fixed nil -> nil props showing up as failing in patch visualizer (plugin) (#1081) --- CHANGELOG.md | 2 ++ plugin/src/Reconciler/diff.lua | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60b7c05b..de1c097a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,12 @@ # Rojo Changelog ## Unreleased +* Fixed nil -> nil properties showing up as failing to sync in plugin's patch visualizer ([#1081]) * Changed the background of the server's in-browser UI to be gray instead of white ([#1080]) * Fixed `Auto Connect Playtest Server` no longer functioning due to Roblox change ([#1066]) * Added an update indicator to the version header when a new version of the plugin is available. ([#1069]) +[#1081]: https://github.com/rojo-rbx/rojo/pull/1081 [#1080]: https://github.com/rojo-rbx/rojo/pull/1080 [#1049]: https://github.com/rojo-rbx/rojo/pull/1066 [#1069]: https://github.com/rojo-rbx/rojo/pull/1069 diff --git a/plugin/src/Reconciler/diff.lua b/plugin/src/Reconciler/diff.lua index 97760829..4a1d9361 100644 --- a/plugin/src/Reconciler/diff.lua +++ b/plugin/src/Reconciler/diff.lua @@ -25,6 +25,14 @@ local function trueEquals(a, b): boolean return true end + -- Treat nil and { Ref = "000...0" } as equal + if + (a == nil and type(b) == "table" and b.Ref == "00000000000000000000000000000000") + or (b == nil and type(a) == "table" and a.Ref == "00000000000000000000000000000000") + then + return true + end + local typeA, typeB = typeof(a), typeof(b) -- For tables, try recursive deep equality From 9598553e5d4952ccc98fe9dbde53cfa526c541d9 Mon Sep 17 00:00:00 2001 From: Micah Date: Thu, 31 Jul 2025 09:19:57 -0700 Subject: [PATCH 05/22] Normalize paths in sourcemap generation (#1085) --- CHANGELOG.md | 2 ++ src/cli/sourcemap.rs | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de1c097a..ab5843f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,13 @@ # Rojo Changelog ## Unreleased +* Fixed `sourcemap` command outputting paths with OS-specific path separators ([#1085]) * Fixed nil -> nil properties showing up as failing to sync in plugin's patch visualizer ([#1081]) * Changed the background of the server's in-browser UI to be gray instead of white ([#1080]) * Fixed `Auto Connect Playtest Server` no longer functioning due to Roblox change ([#1066]) * Added an update indicator to the version header when a new version of the plugin is available. ([#1069]) +[#1085]: https://github.com/rojo-rbx/rojo/pull/1085 [#1081]: https://github.com/rojo-rbx/rojo/pull/1081 [#1080]: https://github.com/rojo-rbx/rojo/pull/1080 [#1049]: https://github.com/rojo-rbx/rojo/pull/1066 diff --git a/src/cli/sourcemap.rs b/src/cli/sourcemap.rs index 4c759f90..268a9724 100644 --- a/src/cli/sourcemap.rs +++ b/src/cli/sourcemap.rs @@ -28,7 +28,10 @@ struct SourcemapNode<'a> { name: &'a str, class_name: Ustr, - #[serde(skip_serializing_if = "Vec::is_empty")] + #[serde( + skip_serializing_if = "Vec::is_empty", + serialize_with = "crate::path_serializer::serialize_vec_absolute" + )] file_paths: Vec, #[serde(skip_serializing_if = "Vec::is_empty")] From 3002d250a1a42a38d61bb47f5664e8bfa025c149 Mon Sep 17 00:00:00 2001 From: Sebastian Stachowicz <50264212+Sebastian2852@users.noreply.github.com> Date: Fri, 1 Aug 2025 02:36:03 +0000 Subject: [PATCH 06/22] Fix Table diff colors (#1084) --- CHANGELOG.md | 5 +++- .../Components/PatchVisualizer/DomLabel.lua | 2 +- .../Components/StringDiffVisualizer/init.lua | 4 +-- .../Components/TableDiffVisualizer/Array.lua | 2 +- .../TableDiffVisualizer/Dictionary.lua | 10 +++----- plugin/src/App/Theme.lua | 25 ++++++++++++++++--- 6 files changed, 33 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab5843f2..abafc706 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,15 @@ # Rojo Changelog ## Unreleased + +* Fixed colors of Table diff ([#1084]) * Fixed `sourcemap` command outputting paths with OS-specific path separators ([#1085]) * Fixed nil -> nil properties showing up as failing to sync in plugin's patch visualizer ([#1081]) -* Changed the background of the server's in-browser UI to be gray instead of white ([#1080]) +* Changed the background of the server's in-browser UI to be gray instead of white ([#1080]) * Fixed `Auto Connect Playtest Server` no longer functioning due to Roblox change ([#1066]) * Added an update indicator to the version header when a new version of the plugin is available. ([#1069]) +[#1084]: https://github.com/rojo-rbx/rojo/pull/1084 [#1085]: https://github.com/rojo-rbx/rojo/pull/1085 [#1081]: https://github.com/rojo-rbx/rojo/pull/1081 [#1080]: https://github.com/rojo-rbx/rojo/pull/1080 diff --git a/plugin/src/App/Components/PatchVisualizer/DomLabel.lua b/plugin/src/App/Components/PatchVisualizer/DomLabel.lua index 689f8143..bc9b110e 100644 --- a/plugin/src/App/Components/PatchVisualizer/DomLabel.lua +++ b/plugin/src/App/Components/PatchVisualizer/DomLabel.lua @@ -95,7 +95,7 @@ function DomLabel:render() return Theme.with(function(theme) local color = if props.isWarning then theme.Diff.Warning - elseif props.patchType then theme.Diff[props.patchType] + elseif props.patchType then theme.Diff.Background[props.patchType] else theme.TextColor local indent = (depth - 1) * 12 + 15 diff --git a/plugin/src/App/Components/StringDiffVisualizer/init.lua b/plugin/src/App/Components/StringDiffVisualizer/init.lua index a826c649..e8e0d024 100644 --- a/plugin/src/App/Components/StringDiffVisualizer/init.lua +++ b/plugin/src/App/Components/StringDiffVisualizer/init.lua @@ -178,7 +178,7 @@ function StringDiffVisualizer:render() size = UDim2.new(1, 0, 1, 0), position = UDim2.new(0, 0, 0, 0), text = oldString, - lineBackground = theme.Diff.Remove, + lineBackground = theme.Diff.Background.Remove, markedLines = self.state.remove, }), }), @@ -193,7 +193,7 @@ function StringDiffVisualizer:render() size = UDim2.new(1, 0, 1, 0), position = UDim2.new(0, 0, 0, 0), text = newString, - lineBackground = theme.Diff.Add, + lineBackground = theme.Diff.Background.Add, markedLines = self.state.add, }), }), diff --git a/plugin/src/App/Components/TableDiffVisualizer/Array.lua b/plugin/src/App/Components/TableDiffVisualizer/Array.lua index 4e9a3daf..1ca002d4 100644 --- a/plugin/src/App/Components/TableDiffVisualizer/Array.lua +++ b/plugin/src/App/Components/TableDiffVisualizer/Array.lua @@ -93,7 +93,7 @@ function Array:render() e("Frame", { Size = UDim2.new(1, 0, 0, 25), BackgroundTransparency = if patchType == "Remain" then 1 else self.props.transparency, - BackgroundColor3 = if patchType == "Remain" then theme.Diff.Row else theme.Diff[patchType], + BackgroundColor3 = theme.Diff.Background[patchType], BorderSizePixel = 0, LayoutOrder = i, }, { diff --git a/plugin/src/App/Components/TableDiffVisualizer/Dictionary.lua b/plugin/src/App/Components/TableDiffVisualizer/Dictionary.lua index db3697bd..f5ac319c 100644 --- a/plugin/src/App/Components/TableDiffVisualizer/Dictionary.lua +++ b/plugin/src/App/Components/TableDiffVisualizer/Dictionary.lua @@ -91,9 +91,7 @@ function Dictionary:render() LayoutOrder = order, BorderSizePixel = 0, BackgroundTransparency = if line.patchType == "Remain" then 1 else self.props.transparency, - BackgroundColor3 = if line.patchType == "Remain" - then theme.Diff.Row - else theme.Diff[line.patchType], + BackgroundColor3 = theme.Diff.Background[line.patchType], }, { DiffIcon = if line.patchType ~= "Remain" then e("ImageLabel", { @@ -114,7 +112,7 @@ function Dictionary:render() TextXAlignment = Enum.TextXAlignment.Left, FontFace = theme.Font.Main, TextSize = theme.TextSize.Body, - TextColor3 = theme.Settings.Setting.DescriptionColor, + TextColor3 = theme.Diff.Text[line.patchType], TextTruncate = Enum.TextTruncate.AtEnd, }), OldValue = e("Frame", { @@ -125,7 +123,7 @@ function Dictionary:render() e(DisplayValue, { value = oldValue, transparency = self.props.transparency, - textColor = theme.Settings.Setting.DescriptionColor, + textColor = theme.Diff.Text[line.patchType], }), }), NewValue = e("Frame", { @@ -136,7 +134,7 @@ function Dictionary:render() e(DisplayValue, { value = newValue, transparency = self.props.transparency, - textColor = theme.Settings.Setting.DescriptionColor, + textColor = theme.Diff.Text[line.patchType], }), }), }) diff --git a/plugin/src/App/Theme.lua b/plugin/src/App/Theme.lua index 6e644aca..09e3bd57 100644 --- a/plugin/src/App/Theme.lua +++ b/plugin/src/App/Theme.lua @@ -165,12 +165,29 @@ function StudioProvider:updateTheme() BackgroundColor = studioTheme:GetColor(Enum.StudioStyleGuideColor.InputFieldBackground), }, Diff = { - -- Studio doesn't have good colors since their diffs use backgrounds, not text - Add = if isDark then Color3.fromRGB(143, 227, 154) else Color3.fromRGB(41, 164, 45), - Remove = if isDark then Color3.fromRGB(242, 125, 125) else Color3.fromRGB(150, 29, 29), - Edit = if isDark then Color3.fromRGB(120, 154, 248) else Color3.fromRGB(0, 70, 160), + -- Very bright different colors in case some places were not updated to use + -- the new background diff colors. + Add = Color3.fromRGB(255, 0, 255), + Remove = Color3.fromRGB(255, 0, 255), + Edit = Color3.fromRGB(255, 0, 255), + Row = studioTheme:GetColor(Enum.StudioStyleGuideColor.BrightText), Warning = studioTheme:GetColor(Enum.StudioStyleGuideColor.WarningText), + + Background = { + -- Studio doesn't have good colors since their diffs use backgrounds, not text + Add = if isDark then Color3.fromRGB(143, 227, 154) else Color3.fromRGB(41, 164, 45), + Remove = if isDark then Color3.fromRGB(242, 125, 125) else Color3.fromRGB(150, 29, 29), + Edit = if isDark then Color3.fromRGB(120, 154, 248) else Color3.fromRGB(0, 70, 160), + Remain = studioTheme:GetColor(Enum.StudioStyleGuideColor.BrightText), + }, + + Text = { + Add = if isDark then Color3.new(0, 0, 0) else Color3.new(1, 1, 1), + Remove = if isDark then Color3.new(0, 0, 0) else Color3.new(1, 1, 1), + Edit = if isDark then Color3.new(0, 0, 0) else Color3.new(1, 1, 1), + Remain = studioTheme:GetColor(Enum.StudioStyleGuideColor.MainText), + }, }, ConnectionDetails = { ProjectNameColor = studioTheme:GetColor(Enum.StudioStyleGuideColor.BrightText), From a4eb65ca3fb1a695ee28c00793ea0d4cc763fe69 Mon Sep 17 00:00:00 2001 From: Micah Date: Sat, 2 Aug 2025 20:58:13 -0700 Subject: [PATCH 07/22] Add YAML middleware that behaves like TOML and JSON (#1093) --- CHANGELOG.md | 3 +- Cargo.lock | 42 ++++ Cargo.toml | 1 + src/snapshot_middleware/mod.rs | 5 + ...ware__yaml__test__instance_from_vfs-2.snap | 27 ++ ...leware__yaml__test__instance_from_vfs.snap | 21 ++ src/snapshot_middleware/yaml.rs | 234 ++++++++++++++++++ 7 files changed, 332 insertions(+), 1 deletion(-) create mode 100644 src/snapshot_middleware/snapshots/librojo__snapshot_middleware__yaml__test__instance_from_vfs-2.snap create mode 100644 src/snapshot_middleware/snapshots/librojo__snapshot_middleware__yaml__test__instance_from_vfs.snap create mode 100644 src/snapshot_middleware/yaml.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index abafc706..e0e8442d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # Rojo Changelog ## Unreleased - +* Add support for syncing `yml` and `yaml` files (behaves similar to JSON and TOML) ([#1093]) * Fixed colors of Table diff ([#1084]) * Fixed `sourcemap` command outputting paths with OS-specific path separators ([#1085]) * Fixed nil -> nil properties showing up as failing to sync in plugin's patch visualizer ([#1081]) @@ -9,6 +9,7 @@ * Fixed `Auto Connect Playtest Server` no longer functioning due to Roblox change ([#1066]) * Added an update indicator to the version header when a new version of the plugin is available. ([#1069]) +[#1093]: https://github.com/rojo-rbx/rojo/pull/1093 [#1084]: https://github.com/rojo-rbx/rojo/pull/1084 [#1085]: https://github.com/rojo-rbx/rojo/pull/1085 [#1081]: https://github.com/rojo-rbx/rojo/pull/1081 diff --git a/Cargo.lock b/Cargo.lock index 79cfe134..4b291c64 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -45,6 +45,12 @@ version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" +[[package]] +name = "arraydeque" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" + [[package]] name = "arrayref" version = "0.3.7" @@ -519,6 +525,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -751,6 +763,24 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +[[package]] +name = "hashbrown" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.4", +] + [[package]] name = "heck" version = "0.4.1" @@ -1879,6 +1909,7 @@ dependencies = [ "uuid", "walkdir", "winreg 0.10.1", + "yaml-rust2", ] [[package]] @@ -2877,6 +2908,17 @@ dependencies = [ "linked-hash-map", ] +[[package]] +name = "yaml-rust2" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ce2a4ff45552406d02501cea6c18d8a7e50228e7736a872951fe2fe75c91be7" +dependencies = [ + "arraydeque", + "encoding_rs", + "hashlink", +] + [[package]] name = "yansi" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index f62a0cca..286255dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -93,6 +93,7 @@ tokio = { version = "1.36.0", features = ["rt", "rt-multi-thread"] } uuid = { version = "1.7.0", features = ["v4", "serde"] } clap = { version = "3.2.25", features = ["derive"] } profiling = "1.0.15" +yaml-rust2 = "0.10.3" [target.'cfg(windows)'.dependencies] winreg = "0.10.1" diff --git a/src/snapshot_middleware/mod.rs b/src/snapshot_middleware/mod.rs index 957adf6b..3d2fbde5 100644 --- a/src/snapshot_middleware/mod.rs +++ b/src/snapshot_middleware/mod.rs @@ -17,6 +17,7 @@ mod rbxmx; mod toml; mod txt; mod util; +mod yaml; use std::{ path::{Path, PathBuf}, @@ -41,6 +42,7 @@ use self::{ rbxmx::snapshot_rbxmx, toml::snapshot_toml, txt::snapshot_txt, + yaml::snapshot_yaml, }; pub use self::{project::snapshot_project_node, util::emit_legacy_scripts_default}; @@ -212,6 +214,7 @@ pub enum Middleware { Rbxmx, Toml, Text, + Yaml, Ignore, } @@ -250,6 +253,7 @@ impl Middleware { Self::Rbxmx => snapshot_rbxmx(context, vfs, path, name), Self::Toml => snapshot_toml(context, vfs, path, name), Self::Text => snapshot_txt(context, vfs, path, name), + Self::Yaml => snapshot_yaml(context, vfs, path, name), Self::Ignore => Ok(None), } } @@ -315,6 +319,7 @@ pub fn default_sync_rules() -> &'static [SyncRule] { sync_rule!("*.txt", Text), sync_rule!("*.rbxmx", Rbxmx), sync_rule!("*.rbxm", Rbxm), + sync_rule!("*.{yml,yaml}", Yaml), ] }) } diff --git a/src/snapshot_middleware/snapshots/librojo__snapshot_middleware__yaml__test__instance_from_vfs-2.snap b/src/snapshot_middleware/snapshots/librojo__snapshot_middleware__yaml__test__instance_from_vfs-2.snap new file mode 100644 index 00000000..f38291b6 --- /dev/null +++ b/src/snapshot_middleware/snapshots/librojo__snapshot_middleware__yaml__test__instance_from_vfs-2.snap @@ -0,0 +1,27 @@ +--- +source: src/snapshot_middleware/yaml.rs +expression: source +--- +return { + string = "this is a string", + boolean = true, + integer = 1337, + float = 123456789.5, + ["value-with-hypen"] = "it sure is", + sequence = {"wow", 8675309}, + map = { + key = "value", + key2 = "value 2", + key3 = "value 3", + }, + ["nested-map"] = {{ + key = "value", + }, { + key2 = "value 2", + }, { + key3 = "value 3", + }}, + whatever_this_is = {"i imagine", "it's", "a", "sequence?"}, + null1 = nil, + null2 = nil, +} diff --git a/src/snapshot_middleware/snapshots/librojo__snapshot_middleware__yaml__test__instance_from_vfs.snap b/src/snapshot_middleware/snapshots/librojo__snapshot_middleware__yaml__test__instance_from_vfs.snap new file mode 100644 index 00000000..86e8a375 --- /dev/null +++ b/src/snapshot_middleware/snapshots/librojo__snapshot_middleware__yaml__test__instance_from_vfs.snap @@ -0,0 +1,21 @@ +--- +source: src/snapshot_middleware/yaml.rs +expression: instance_snapshot +--- +snapshot_id: "00000000000000000000000000000000" +metadata: + ignore_unknown_instances: false + instigating_source: + Path: /foo.yaml + relevant_paths: + - /foo.yaml + - /foo.meta.json + context: + emit_legacy_scripts: true + specified_id: ~ +name: foo +class_name: ModuleScript +properties: + Source: + String: "return {\n\tstring = \"this is a string\",\n\tboolean = true,\n\tinteger = 1337,\n\tfloat = 123456789.5,\n\t[\"value-with-hypen\"] = \"it sure is\",\n\tsequence = {\"wow\", 8675309},\n\tmap = {\n\t\tkey = \"value\",\n\t\tkey2 = \"value 2\",\n\t\tkey3 = \"value 3\",\n\t},\n\t[\"nested-map\"] = {{\n\t\tkey = \"value\",\n\t}, {\n\t\tkey2 = \"value 2\",\n\t}, {\n\t\tkey3 = \"value 3\",\n\t}},\n\twhatever_this_is = {\"i imagine\", \"it's\", \"a\", \"sequence?\"},\n\tnull1 = nil,\n\tnull2 = nil,\n}" +children: [] diff --git a/src/snapshot_middleware/yaml.rs b/src/snapshot_middleware/yaml.rs new file mode 100644 index 00000000..346e4386 --- /dev/null +++ b/src/snapshot_middleware/yaml.rs @@ -0,0 +1,234 @@ +use std::path::Path; + +use anyhow::Context as _; +use memofs::{IoResultExt, Vfs}; +use rbx_dom_weak::ustr; +use yaml_rust2::{Yaml, YamlLoader}; + +use crate::{ + lua_ast::{Expression, Statement}, + snapshot::{InstanceContext, InstanceMetadata, InstanceSnapshot}, +}; + +use super::meta_file::AdjacentMetadata; + +pub fn snapshot_yaml( + context: &InstanceContext, + vfs: &Vfs, + path: &Path, + name: &str, +) -> anyhow::Result> { + let contents = vfs.read_to_string(path)?; + + let mut values = YamlLoader::load_from_str(&contents)?; + let value = values + .pop() + .context("all YAML documents must contain a document")?; + if !values.is_empty() { + anyhow::bail!("Rojo does not currently support multiple documents in a YAML file") + } + + let as_lua = Statement::Return(yaml_to_luau(value)?); + + let meta_path = path.with_file_name(format!("{}.meta.json", name)); + + let mut snapshot = InstanceSnapshot::new() + .name(name) + .class_name("ModuleScript") + .property(ustr("Source"), as_lua.to_string()) + .metadata( + InstanceMetadata::new() + .instigating_source(path) + .relevant_paths(vec![path.to_path_buf(), meta_path.clone()]) + .context(context), + ); + + if let Some(meta_contents) = vfs.read(&meta_path).with_not_found()? { + let mut metadata = AdjacentMetadata::from_slice(&meta_contents, meta_path)?; + metadata.apply_all(&mut snapshot)?; + } + + Ok(Some(snapshot)) +} + +fn yaml_to_luau(value: Yaml) -> anyhow::Result { + const MAX_FLOAT_INT: i64 = 1 << 53; + + Ok(match value { + Yaml::String(str) => Expression::String(str), + Yaml::Boolean(bool) => Expression::Bool(bool), + Yaml::Integer(int) => { + if int <= MAX_FLOAT_INT { + Expression::Number(int as f64) + } else { + anyhow::bail!( + "the integer '{int}' cannot be losslessly converted into a Luau number" + ) + } + } + Yaml::Real(_) => { + let value = value.as_f64().expect("value should be a valid f64"); + Expression::Number(value) + } + Yaml::Null => Expression::Nil, + Yaml::Array(values) => { + let new_values: anyhow::Result> = + values.into_iter().map(yaml_to_luau).collect(); + Expression::Array(new_values?) + } + Yaml::Hash(map) => { + let new_values: anyhow::Result> = map + .into_iter() + .map(|(k, v)| { + let k = yaml_to_luau(k)?; + let v = yaml_to_luau(v)?; + Ok((k, v)) + }) + .collect(); + Expression::table(new_values?) + } + Yaml::Alias(_) => { + anyhow::bail!("Rojo cannot convert YAML aliases to Luau") + } + Yaml::BadValue => { + anyhow::bail!("Rojo cannot convert YAML to Luau because of a parsing error") + } + }) +} + +#[cfg(test)] +mod test { + use super::*; + + use memofs::{InMemoryFs, VfsSnapshot}; + use rbx_dom_weak::types::Variant; + + #[test] + fn instance_from_vfs() { + let mut imfs = InMemoryFs::new(); + imfs.load_snapshot( + "/foo.yaml", + VfsSnapshot::file( + r#" +--- +string: this is a string +boolean: true +integer: 1337 +float: 123456789.5 +value-with-hypen: it sure is +sequence: + - wow + - 8675309 +map: + key: value + key2: "value 2" + key3: 'value 3' +nested-map: + - key: value + - key2: "value 2" + - key3: 'value 3' +whatever_this_is: [i imagine, it's, a, sequence?] +null1: ~ +null2: null"#, + ), + ) + .unwrap(); + + let vfs = Vfs::new(imfs.clone()); + + let instance_snapshot = snapshot_yaml( + &InstanceContext::default(), + &vfs, + Path::new("/foo.yaml"), + "foo", + ) + .unwrap() + .unwrap(); + + insta::assert_yaml_snapshot!(instance_snapshot); + + let source = instance_snapshot + .properties + .get(&ustr("Source")) + .expect("the result from snapshot_yaml should have a Source property"); + if let Variant::String(source) = source { + insta::assert_snapshot!(source) + } else { + panic!("the Source property from snapshot_yaml was not a String") + } + } + + #[test] + #[should_panic(expected = "multiple documents")] + fn multiple_documents() { + let mut imfs = InMemoryFs::new(); + imfs.load_snapshot( + "/foo.yaml", + VfsSnapshot::file( + r#" +--- +document-1: this is a document +--- +document-2: this is also a document"#, + ), + ) + .unwrap(); + + let vfs = Vfs::new(imfs.clone()); + + snapshot_yaml( + &InstanceContext::default(), + &vfs, + Path::new("/foo.yaml"), + "foo", + ) + .unwrap() + .unwrap(); + } + + #[test] + #[should_panic = "cannot be losslessly converted into a Luau number"] + fn integer_border() { + let mut imfs = InMemoryFs::new(); + imfs.load_snapshot( + "/allowed.yaml", + VfsSnapshot::file( + r#" +value: 9007199254740992 +"#, + ), + ) + .unwrap(); + imfs.load_snapshot( + "/not-allowed.yaml", + VfsSnapshot::file( + r#" +value: 9007199254740993 +"#, + ), + ) + .unwrap(); + + let vfs = Vfs::new(imfs.clone()); + + assert!( + snapshot_yaml( + &InstanceContext::default(), + &vfs, + Path::new("/allowed.yaml"), + "allowed", + ) + .is_ok(), + "snapshot_yaml failed to snapshot document with integer '9007199254740992' in it" + ); + + snapshot_yaml( + &InstanceContext::default(), + &vfs, + Path::new("/not-allowed.yaml"), + "not-allowed", + ) + .unwrap() + .unwrap(); + } +} From 2ddb21ec5fb8567fd4a0dfec4c951207785108ce Mon Sep 17 00:00:00 2001 From: ffrostfall <80861876+ffrostfall@users.noreply.github.com> Date: Mon, 4 Aug 2025 14:33:35 -0400 Subject: [PATCH 08/22] Add option for emitting absolute paths to `rojo sourcemap` (#1092) Co-authored-by: Micah --- .github/workflows/ci.yml | 2 +- CHANGELOG.md | 2 ++ Cargo.toml | 2 +- src/cli/sourcemap.rs | 52 ++++++++++++++++++++++++++++++++-------- 4 files changed, 46 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6f68febe..2cc6a265 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,7 +65,7 @@ jobs: submodules: true - name: Install Rust - uses: dtolnay/rust-toolchain@1.70.0 + uses: dtolnay/rust-toolchain@1.79.0 - name: Restore Rust Cache uses: actions/cache/restore@v4 diff --git a/CHANGELOG.md b/CHANGELOG.md index e0e8442d..f09a084f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ * Changed the background of the server's in-browser UI to be gray instead of white ([#1080]) * Fixed `Auto Connect Playtest Server` no longer functioning due to Roblox change ([#1066]) * Added an update indicator to the version header when a new version of the plugin is available. ([#1069]) +* Added `--absolute` flag to the sourcemap subcommand, which will emit absolute paths instead of relative paths. ([#1092]) [#1093]: https://github.com/rojo-rbx/rojo/pull/1093 [#1084]: https://github.com/rojo-rbx/rojo/pull/1084 @@ -16,6 +17,7 @@ [#1080]: https://github.com/rojo-rbx/rojo/pull/1080 [#1049]: https://github.com/rojo-rbx/rojo/pull/1066 [#1069]: https://github.com/rojo-rbx/rojo/pull/1069 +[#1092]: https://github.com/rojo-rbx/rojo/pull/1092 ## 7.5.1 - April 25th, 2025 * Fixed output spam related to `Instance.Capabilities` in the plugin diff --git a/Cargo.toml b/Cargo.toml index 286255dd..7b34dbce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rojo" version = "7.5.1" -rust-version = "1.70.0" +rust-version = "1.79.0" authors = [ "Lucien Greathouse ", "Micah Reid ", diff --git a/src/cli/sourcemap.rs b/src/cli/sourcemap.rs index 268a9724..95fe6631 100644 --- a/src/cli/sourcemap.rs +++ b/src/cli/sourcemap.rs @@ -1,7 +1,8 @@ use std::{ + borrow::Cow, io::{BufWriter, Write}, mem::forget, - path::{Path, PathBuf}, + path::{self, Path, PathBuf}, }; use clap::Parser; @@ -20,6 +21,7 @@ use crate::{ use super::resolve_path; const PATH_STRIP_FAILED_ERR: &str = "Failed to create relative paths for project file!"; +const ABSOLUTE_PATH_FAILED_ERR: &str = "Failed to turn relative path into absolute path!"; /// Representation of a node in the generated sourcemap tree. #[derive(Serialize)] @@ -32,7 +34,7 @@ struct SourcemapNode<'a> { skip_serializing_if = "Vec::is_empty", serialize_with = "crate::path_serializer::serialize_vec_absolute" )] - file_paths: Vec, + file_paths: Vec>, #[serde(skip_serializing_if = "Vec::is_empty")] children: Vec>, @@ -60,6 +62,10 @@ pub struct SourcemapCommand { /// Whether to automatically recreate a snapshot when any input files change. #[clap(long)] pub watch: bool, + + /// Whether the sourcemap should use absolute paths instead of relative paths. + #[clap(long)] + pub absolute: bool, } impl SourcemapCommand { @@ -86,7 +92,7 @@ impl SourcemapCommand { .build_global() .unwrap(); - write_sourcemap(&session, self.output.as_deref(), filter)?; + write_sourcemap(&session, self.output.as_deref(), filter, self.absolute)?; if self.watch { let rt = Runtime::new().unwrap(); @@ -97,7 +103,7 @@ impl SourcemapCommand { cursor = new_cursor; if patch_set_affects_sourcemap(&session, &patch_set, filter) { - write_sourcemap(&session, self.output.as_deref(), filter)?; + write_sourcemap(&session, self.output.as_deref(), filter, self.absolute)?; } } } @@ -163,13 +169,16 @@ fn recurse_create_node<'a>( referent: Ref, project_dir: &Path, filter: fn(&InstanceWithMeta) -> bool, + use_absolute_paths: bool, ) -> Option> { let instance = tree.get_instance(referent).expect("instance did not exist"); let children: Vec<_> = instance .children() .par_iter() - .filter_map(|&child_id| recurse_create_node(tree, child_id, project_dir, filter)) + .filter_map(|&child_id| { + recurse_create_node(tree, child_id, project_dir, filter, use_absolute_paths) + }) .collect(); // If this object has no children and doesn't pass the filter, it doesn't @@ -184,14 +193,30 @@ fn recurse_create_node<'a>( .iter() // Not all paths listed as relevant are guaranteed to exist. .filter(|path| path.is_file()) - .map(|path| path.strip_prefix(project_dir).expect(PATH_STRIP_FAILED_ERR)) - .map(|path| path.to_path_buf()) - .collect(); + .map(|path| path.as_path()); + + let mut output_file_paths: Vec> = + Vec::with_capacity(instance.metadata().relevant_paths.len()); + + if use_absolute_paths { + // It's somewhat important to note here that `path::absolute` takes in a Path and returns a PathBuf + for val in file_paths { + output_file_paths.push(Cow::Owned( + path::absolute(val).expect(ABSOLUTE_PATH_FAILED_ERR), + )); + } + } else { + for val in file_paths { + output_file_paths.push(Cow::from( + val.strip_prefix(project_dir).expect(PATH_STRIP_FAILED_ERR), + )); + } + }; Some(SourcemapNode { name: instance.name(), class_name: instance.class_name(), - file_paths, + file_paths: output_file_paths, children, }) } @@ -200,10 +225,17 @@ fn write_sourcemap( session: &ServeSession, output: Option<&Path>, filter: fn(&InstanceWithMeta) -> bool, + use_absolute_paths: bool, ) -> anyhow::Result<()> { let tree = session.tree(); - let root_node = recurse_create_node(&tree, tree.get_root_id(), session.root_dir(), filter); + let root_node = recurse_create_node( + &tree, + tree.get_root_id(), + session.root_dir(), + filter, + use_absolute_paths, + ); if let Some(output_path) = output { let mut file = BufWriter::new(File::create(output_path)?); From 0056849b5199d4ac5cc465d7865b95bae6e5bd72 Mon Sep 17 00:00:00 2001 From: Micah Date: Wed, 13 Aug 2025 15:46:08 -0700 Subject: [PATCH 09/22] Put Rojo version in crash message (#1101) --- src/main.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 4e0fb507..357c27cb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,7 +20,10 @@ fn main() { }, }; - log::error!("Rojo crashed!"); + log::error!( + "Rojo crashed! You are running Rojo {}.", + env!("CARGO_PKG_VERSION") + ); log::error!("This is probably a Rojo bug."); log::error!(""); log::error!( From 4e9e6233ff96e3de0921fc3d40cb68e4edcd1d4a Mon Sep 17 00:00:00 2001 From: wad <77159113+wad4444@users.noreply.github.com> Date: Sat, 16 Aug 2025 04:12:36 +0300 Subject: [PATCH 10/22] fix: apply `gameId` and `placeId` only after initial sync (#1104) --- CHANGELOG.md | 2 ++ plugin/src/ServeSession.lua | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f09a084f..eb44c444 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ * Fixed `Auto Connect Playtest Server` no longer functioning due to Roblox change ([#1066]) * Added an update indicator to the version header when a new version of the plugin is available. ([#1069]) * Added `--absolute` flag to the sourcemap subcommand, which will emit absolute paths instead of relative paths. ([#1092]) +* Fixed applying `gameId` and `placeId` before initial sync was accepted ([#1104]) [#1093]: https://github.com/rojo-rbx/rojo/pull/1093 [#1084]: https://github.com/rojo-rbx/rojo/pull/1084 @@ -18,6 +19,7 @@ [#1049]: https://github.com/rojo-rbx/rojo/pull/1066 [#1069]: https://github.com/rojo-rbx/rojo/pull/1069 [#1092]: https://github.com/rojo-rbx/rojo/pull/1092 +[#1104]: https://github.com/rojo-rbx/rojo/pull/1104 ## 7.5.1 - April 25th, 2025 * Fixed output spam related to `Instance.Capabilities` in the plugin diff --git a/plugin/src/ServeSession.lua b/plugin/src/ServeSession.lua index bb844d06..62a5c76a 100644 --- a/plugin/src/ServeSession.lua +++ b/plugin/src/ServeSession.lua @@ -139,10 +139,9 @@ function ServeSession:start() self.__apiContext :connect() :andThen(function(serverInfo) - self:__applyGameAndPlaceId(serverInfo) - return self:__initialSync(serverInfo):andThen(function() self:__setStatus(Status.Connected, serverInfo.projectName) + self:__applyGameAndPlaceId(serverInfo) return self:__mainSyncLoop() end) From 54e0ff230bdd2a164ef8ec255e2d62aed041d55a Mon Sep 17 00:00:00 2001 From: boatbomber Date: Thu, 28 Aug 2025 17:15:34 -0700 Subject: [PATCH 11/22] Improvements to sync reminder UX (#1096) --- CHANGELOG.md | 5 +- .../Notifications/FullscreenNotification.lua | 151 ++++++++ .../Notifications/Notification.lua} | 47 +-- .../src/App/Components/Notifications/init.lua | 66 ++++ plugin/src/App/StatusPages/Settings/init.lua | 30 +- plugin/src/App/init.lua | 335 +++++++++++++----- plugin/src/Settings.lua | 16 +- 7 files changed, 513 insertions(+), 137 deletions(-) create mode 100644 plugin/src/App/Components/Notifications/FullscreenNotification.lua rename plugin/src/App/{Notifications.lua => Components/Notifications/Notification.lua} (84%) create mode 100644 plugin/src/App/Components/Notifications/init.lua diff --git a/CHANGELOG.md b/CHANGELOG.md index eb44c444..a71d618e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Rojo Changelog ## Unreleased + +* Add auto-reconnect and improve UX for sync reminders ([#1096]) * Add support for syncing `yml` and `yaml` files (behaves similar to JSON and TOML) ([#1093]) * Fixed colors of Table diff ([#1084]) * Fixed `sourcemap` command outputting paths with OS-specific path separators ([#1085]) @@ -11,12 +13,13 @@ * Added `--absolute` flag to the sourcemap subcommand, which will emit absolute paths instead of relative paths. ([#1092]) * Fixed applying `gameId` and `placeId` before initial sync was accepted ([#1104]) +[#1096]: https://github.com/rojo-rbx/rojo/pull/1096 [#1093]: https://github.com/rojo-rbx/rojo/pull/1093 [#1084]: https://github.com/rojo-rbx/rojo/pull/1084 [#1085]: https://github.com/rojo-rbx/rojo/pull/1085 [#1081]: https://github.com/rojo-rbx/rojo/pull/1081 [#1080]: https://github.com/rojo-rbx/rojo/pull/1080 -[#1049]: https://github.com/rojo-rbx/rojo/pull/1066 +[#1066]: https://github.com/rojo-rbx/rojo/pull/1066 [#1069]: https://github.com/rojo-rbx/rojo/pull/1069 [#1092]: https://github.com/rojo-rbx/rojo/pull/1092 [#1104]: https://github.com/rojo-rbx/rojo/pull/1104 diff --git a/plugin/src/App/Components/Notifications/FullscreenNotification.lua b/plugin/src/App/Components/Notifications/FullscreenNotification.lua new file mode 100644 index 00000000..215398b2 --- /dev/null +++ b/plugin/src/App/Components/Notifications/FullscreenNotification.lua @@ -0,0 +1,151 @@ +local StudioService = game:GetService("StudioService") + +local Rojo = script:FindFirstAncestor("Rojo") +local Plugin = Rojo.Plugin +local Packages = Rojo.Packages + +local Roact = require(Packages.Roact) +local Log = require(Packages.Log) + +local Theme = require(Plugin.App.Theme) +local Assets = require(Plugin.Assets) + +local TextButton = require(Plugin.App.Components.TextButton) + +local e = Roact.createElement + +local FullscreenNotification = Roact.Component:extend("FullscreeFullscreenNotificationnNotification") + +function FullscreenNotification:init() + self.transparency, self.setTransparency = Roact.createBinding(0) + self.lifetime = self.props.timeout +end + +function FullscreenNotification:dismiss() + if self.props.onClose then + self.props.onClose() + end +end + +function FullscreenNotification:didMount() + self.props.soundPlayer:play(Assets.Sounds.Notification) + + self.timeout = task.spawn(function() + local clock = os.clock() + local seen = false + while task.wait(1 / 10) do + local now = os.clock() + local dt = now - clock + clock = now + + if not seen then + seen = StudioService.ActiveScript == nil + end + + if not seen then + -- Don't run down timer before being viewed + continue + end + + self.lifetime -= dt + if self.lifetime <= 0 then + self:dismiss() + break + end + end + + self.timeout = nil + end) +end + +function FullscreenNotification:willUnmount() + if self.timeout and coroutine.status(self.timeout) ~= "dead" then + task.cancel(self.timeout) + end +end + +function FullscreenNotification:render() + return Theme.with(function(theme) + local actionButtons = {} + if self.props.actions then + for key, action in self.props.actions do + actionButtons[key] = e(TextButton, { + text = action.text, + style = action.style, + onClick = function() + self:dismiss() + if action.onClick then + local success, err = pcall(action.onClick, self) + if not success then + Log.warn("Error in notification action: " .. tostring(err)) + end + end + end, + layoutOrder = -action.layoutOrder, + transparency = self.transparency, + }) + end + end + + return e("Frame", { + BackgroundColor3 = theme.BackgroundColor, + Size = UDim2.fromScale(1, 1), + ZIndex = self.props.layoutOrder, + }, { + Padding = e("UIPadding", { + PaddingLeft = UDim.new(0, 17), + PaddingRight = UDim.new(0, 15), + PaddingTop = UDim.new(0, 10), + PaddingBottom = UDim.new(0, 10), + }), + Layout = e("UIListLayout", { + SortOrder = Enum.SortOrder.LayoutOrder, + FillDirection = Enum.FillDirection.Vertical, + HorizontalAlignment = Enum.HorizontalAlignment.Center, + VerticalAlignment = Enum.VerticalAlignment.Center, + Padding = UDim.new(0, 10), + }), + Logo = e("ImageLabel", { + ImageTransparency = self.transparency, + Image = Assets.Images.Logo, + ImageColor3 = theme.Header.LogoColor, + BackgroundTransparency = 1, + Size = UDim2.fromOffset(60, 27), + LayoutOrder = 1, + }), + Info = e("TextLabel", { + Text = self.props.text, + FontFace = theme.Font.Main, + TextSize = theme.TextSize.Body, + TextColor3 = theme.Notification.InfoColor, + TextTransparency = self.transparency, + TextXAlignment = Enum.TextXAlignment.Center, + TextYAlignment = Enum.TextYAlignment.Center, + TextWrapped = true, + BackgroundTransparency = 1, + + AutomaticSize = Enum.AutomaticSize.Y, + Size = UDim2.fromScale(0.4, 0), + LayoutOrder = 2, + }), + Actions = if self.props.actions + then e("Frame", { + Size = UDim2.new(1, -40, 0, 37), + BackgroundTransparency = 1, + LayoutOrder = 3, + }, { + Layout = e("UIListLayout", { + FillDirection = Enum.FillDirection.Horizontal, + HorizontalAlignment = Enum.HorizontalAlignment.Center, + VerticalAlignment = Enum.VerticalAlignment.Center, + SortOrder = Enum.SortOrder.LayoutOrder, + Padding = UDim.new(0, 5), + }), + Buttons = Roact.createFragment(actionButtons), + }) + else nil, + }) + end) +end + +return FullscreenNotification diff --git a/plugin/src/App/Notifications.lua b/plugin/src/App/Components/Notifications/Notification.lua similarity index 84% rename from plugin/src/App/Notifications.lua rename to plugin/src/App/Components/Notifications/Notification.lua index 0d2129a4..47fb0d08 100644 --- a/plugin/src/App/Notifications.lua +++ b/plugin/src/App/Components/Notifications/Notification.lua @@ -16,8 +16,6 @@ local getTextBoundsAsync = require(Plugin.App.getTextBoundsAsync) local BorderedContainer = require(Plugin.App.Components.BorderedContainer) local TextButton = require(Plugin.App.Components.TextButton) -local baseClock = DateTime.now().UnixTimestampMillis - local e = Roact.createElement local Notification = Roact.Component:extend("Notification") @@ -77,7 +75,9 @@ function Notification:didMount() end function Notification:willUnmount() - task.cancel(self.timeout) + if self.timeout and coroutine.status(self.timeout) ~= "dead" then + task.cancel(self.timeout) + end end function Notification:render() @@ -95,9 +95,12 @@ function Notification:render() text = action.text, style = action.style, onClick = function() - local success, err = pcall(action.onClick, self) - if not success then - Log.warn("Error in notification action: " .. tostring(err)) + self:dismiss() + if action.onClick then + local success, err = pcall(action.onClick, self) + if not success then + Log.warn("Error in notification action: " .. tostring(err)) + end end end, layoutOrder = -action.layoutOrder, @@ -138,17 +141,17 @@ function Notification:render() }, { e(BorderedContainer, { transparency = transparency, - size = UDim2.new(1, 0, 1, 0), + size = UDim2.fromScale(1, 1), }, { Contents = e("Frame", { - Size = UDim2.new(1, 0, 1, 0), + Size = UDim2.fromScale(1, 1), BackgroundTransparency = 1, }, { Logo = e("ImageLabel", { ImageTransparency = transparency, Image = Assets.Images.PluginButton, BackgroundTransparency = 1, - Size = UDim2.new(0, logoSize, 0, logoSize), + Size = UDim2.fromOffset(logoSize, logoSize), Position = UDim2.new(0, 0, 0, 0), AnchorPoint = Vector2.new(0, 0), }), @@ -171,7 +174,7 @@ function Notification:render() Actions = if self.props.actions then e("Frame", { Size = UDim2.new(1, -40, 0, actionsY), - Position = UDim2.new(1, 0, 1, 0), + Position = UDim2.fromScale(1, 1), AnchorPoint = Vector2.new(1, 1), BackgroundTransparency = 1, }, { @@ -198,26 +201,4 @@ function Notification:render() end) end -local Notifications = Roact.Component:extend("Notifications") - -function Notifications:render() - local notifs = {} - - for id, notif in self.props.notifications do - notifs["NotifID_" .. id] = e(Notification, { - soundPlayer = self.props.soundPlayer, - text = notif.text, - timestamp = notif.timestamp, - timeout = notif.timeout, - actions = notif.actions, - layoutOrder = (notif.timestamp - baseClock), - onClose = function() - self.props.onClose(id) - end, - }) - end - - return Roact.createFragment(notifs) -end - -return Notifications +return Notification diff --git a/plugin/src/App/Components/Notifications/init.lua b/plugin/src/App/Components/Notifications/init.lua new file mode 100644 index 00000000..1cc544d5 --- /dev/null +++ b/plugin/src/App/Components/Notifications/init.lua @@ -0,0 +1,66 @@ +local Rojo = script:FindFirstAncestor("Rojo") + +local Packages = Rojo.Packages +local Roact = require(Packages.Roact) + +local e = Roact.createElement + +local Notification = require(script.Notification) +local FullscreenNotification = require(script.FullscreenNotification) + +local Notifications = Roact.Component:extend("Notifications") + +function Notifications:render() + local popupNotifs = {} + local fullscreenNotifs = {} + + for id, notif in self.props.notifications do + local targetTable = if notif.isFullscreen then fullscreenNotifs else popupNotifs + local targetComponent = if notif.isFullscreen then FullscreenNotification else Notification + targetTable["NotifID_" .. id] = e(targetComponent, { + soundPlayer = self.props.soundPlayer, + text = notif.text, + timeout = notif.timeout, + actions = notif.actions, + layoutOrder = id, + onClose = function() + if notif.onClose then + notif.onClose() + end + self.props.onClose(id) + end, + }) + end + + return e("Frame", { + Size = UDim2.fromScale(1, 1), + BackgroundTransparency = 1, + }, { + Fullscreen = e("Frame", { + Size = UDim2.fromScale(1, 1), + BackgroundTransparency = 1, + }, { + notifs = Roact.createFragment(fullscreenNotifs), + }), + Popups = e("Frame", { + Size = UDim2.fromScale(1, 1), + BackgroundTransparency = 1, + }, { + Layout = e("UIListLayout", { + SortOrder = Enum.SortOrder.LayoutOrder, + HorizontalAlignment = Enum.HorizontalAlignment.Right, + VerticalAlignment = Enum.VerticalAlignment.Bottom, + Padding = UDim.new(0, 5), + }), + Padding = e("UIPadding", { + PaddingTop = UDim.new(0, 5), + PaddingBottom = UDim.new(0, 5), + PaddingLeft = UDim.new(0, 5), + PaddingRight = UDim.new(0, 5), + }), + notifs = Roact.createFragment(popupNotifs), + }), + }) +end + +return Notifications diff --git a/plugin/src/App/StatusPages/Settings/init.lua b/plugin/src/App/StatusPages/Settings/init.lua index 1f9af0c0..7d05fd63 100644 --- a/plugin/src/App/StatusPages/Settings/init.lua +++ b/plugin/src/App/StatusPages/Settings/init.lua @@ -27,6 +27,7 @@ end local invertedLevels = invertTbl(Log.Level) local confirmationBehaviors = { "Initial", "Always", "Large Changes", "Unlisted PlaceId", "Never" } +local syncReminderModes = { "None", "Notify", "Fullscreen" } local function Navbar(props) return Theme.with(function(theme) @@ -93,6 +94,14 @@ function SettingsPage:render() contentSize = self.contentSize, transparency = self.props.transparency, }, { + AutoReconnect = e(Setting, { + id = "autoReconnect", + name = "Auto Reconnect", + description = "Reconnect to server on place open if the served project matches the last sync to the place", + transparency = self.props.transparency, + layoutOrder = layoutIncrement(), + }), + ShowNotifications = e(Setting, { id = "showNotifications", name = "Show Notifications", @@ -101,13 +110,26 @@ function SettingsPage:render() layoutOrder = layoutIncrement(), }), - SyncReminder = e(Setting, { - id = "syncReminder", + SyncReminderMode = e(Setting, { + id = "syncReminderMode", name = "Sync Reminder", - description = "Notify to sync when opening a place that has previously been synced", + description = "What type of reminders you receive for syncing your project", transparency = self.props.transparency, - visible = Settings:getBinding("showNotifications"), layoutOrder = layoutIncrement(), + visible = Settings:getBinding("showNotifications"), + + options = syncReminderModes, + }), + + SyncReminderPolling = e(Setting, { + id = "syncReminderPolling", + name = "Sync Reminder Polling", + description = "Look for available sync servers periodically", + transparency = self.props.transparency, + layoutOrder = layoutIncrement(), + visible = Settings:getBindings("syncReminderMode", "showNotifications"):map(function(values) + return values.syncReminderMode ~= "None" and values.showNotifications + end), }), ConfirmationBehavior = e(Setting, { diff --git a/plugin/src/App/init.lua b/plugin/src/App/init.lua index 74235656..7ac84f60 100644 --- a/plugin/src/App/init.lua +++ b/plugin/src/App/init.lua @@ -9,6 +9,7 @@ local Packages = Rojo.Packages local Roact = require(Packages.Roact) local Log = require(Packages.Log) +local Promise = require(Packages.Promise) local Assets = require(Plugin.Assets) local Version = require(Plugin.Version) @@ -27,7 +28,7 @@ local timeUtil = require(Plugin.timeUtil) local Theme = require(script.Theme) local Page = require(script.Page) -local Notifications = require(script.Notifications) +local Notifications = require(script.Components.Notifications) local Tooltip = require(script.Components.Tooltip) local StudioPluginAction = require(script.Components.Studio.StudioPluginAction) local StudioToolbar = require(script.Components.Studio.StudioToolbar) @@ -78,17 +79,18 @@ function App:init() action ) ) - local dismissNotif = self:addNotification( - string.format("You've undone '%s'.\nIf this was not intended, please restore.", action), - 10, - { + local dismissNotif = self:addNotification({ + text = string.format("You've undone '%s'.\nIf this was not intended, please restore.", action), + timeout = 10, + onClose = function() + cleanup() + end, + actions = { Restore = { text = "Restore", style = "Solid", layoutOrder = 1, - onClick = function(notification) - cleanup() - notification:dismiss() + onClick = function() ChangeHistoryService:Redo() end, }, @@ -96,13 +98,9 @@ function App:init() text = "Dismiss", style = "Bordered", layoutOrder = 2, - onClick = function(notification) - cleanup() - notification:dismiss() - end, }, - } - ) + }, + }) undoConnection = ChangeHistoryService.OnUndo:Once(function() -- Our notif is now out of date- redoing will not restore the patch @@ -142,42 +140,20 @@ function App:init() if RunService:IsEdit() then self:checkForUpdates() - if - Settings:get("syncReminder") - and self.serveSession == nil - and self:getPriorSyncInfo().timestamp ~= nil - and (self:isSyncLockAvailable()) - then - local syncInfo = self:getPriorSyncInfo() - local timeSinceSync = timeUtil.elapsedToText(os.time() - syncInfo.timestamp) - local syncDetail = if syncInfo.projectName - then `project '{syncInfo.projectName}'` - else `{syncInfo.host or Config.defaultHost}:{syncInfo.port or Config.defaultPort}` + self:startSyncReminderPolling() + self.disconnectSyncReminderPollingChanged = Settings:onChanged("syncReminderPolling", function(enabled) + if enabled then + self:startSyncReminderPolling() + else + self:stopSyncReminderPolling() + end + end) - self:addNotification( - `You synced {syncDetail} to this place {timeSinceSync}. Would you like to reconnect?`, - 300, - { - Connect = { - text = "Connect", - style = "Solid", - layoutOrder = 1, - onClick = function(notification) - notification:dismiss() - self:startSession() - end, - }, - Dismiss = { - text = "Dismiss", - style = "Bordered", - layoutOrder = 2, - onClick = function(notification) - notification:dismiss() - end, - }, - } - ) - end + self:tryAutoReconnect():andThen(function(didReconnect) + if not didReconnect then + self:checkSyncReminder() + end + end) end if self:isAutoConnectPlaytestServerAvailable() then @@ -203,16 +179,23 @@ function App:willUnmount() self.disconnectUpdatesCheckChanged() self.disconnectPrereleasesCheckChanged() + if self.disconnectSyncReminderPollingChanged then + self.disconnectSyncReminderPollingChanged() + end + + self:stopSyncReminderPolling() self.autoConnectPlaytestServerListener() self:clearRunningConnectionInfo() end -function App:addNotification( +function App:addNotification(notif: { text: string, + isFullscreen: boolean?, timeout: number?, - actions: { [string]: { text: string, style: string, layoutOrder: number, onClick: (any) -> () } }? -) + actions: { [string]: { text: string, style: string, layoutOrder: number, onClick: (any) -> ()? } }?, + onClose: (any) -> ()?, +}) if not Settings:get("showNotifications") then return end @@ -220,17 +203,17 @@ function App:addNotification( self.notifId += 1 local id = self.notifId - local notifications = table.clone(self.state.notifications) - notifications[id] = { - text = text, - timestamp = DateTime.now().UnixTimestampMillis, - timeout = timeout or 3, - actions = actions, - } + self:setState(function(prevState) + local notifications = table.clone(prevState.notifications) + notifications[id] = Dictionary.merge({ + timeout = notif.timeout or 5, + isFullscreen = notif.isFullscreen or false, + }, notif) - self:setState({ - notifications = notifications, - }) + return { + notifications = notifications, + } + end) return function() self:closeNotification(id) @@ -242,26 +225,29 @@ function App:closeNotification(id: number) return end - local notifications = table.clone(self.state.notifications) - notifications[id] = nil + self:setState(function(prevState) + local notifications = table.clone(prevState.notifications) + notifications[id] = nil - self:setState({ - notifications = notifications, - }) + return { + notifications = notifications, + } + end) end function App:checkForUpdates() local updateMessage = Version.getUpdateMessage() if updateMessage then - self:addNotification(updateMessage, 500, { - Dismiss = { - text = "Dismiss", - style = "Bordered", - layoutOrder = 2, - onClick = function(notification) - notification:dismiss() - end, + self:addNotification({ + text = updateMessage, + timeout = 500, + actions = { + Dismiss = { + text = "Dismiss", + style = "Bordered", + layoutOrder = 2, + }, }, }) end @@ -385,6 +371,155 @@ function App:releaseSyncLock() Log.trace("Could not relase sync lock because it is owned by {}", lock.Value) end +function App:findActiveServer() + local host, port = self:getHostAndPort() + local baseUrl = if string.find(host, "^https?://") + then string.format("%s:%s", host, port) + else string.format("http://%s:%s", host, port) + + Log.trace("Checking for active sync server at {}", baseUrl) + + local apiContext = ApiContext.new(baseUrl) + return apiContext:connect():andThen(function(serverInfo) + apiContext:disconnect() + return serverInfo, host, port + end) +end + +function App:tryAutoReconnect() + if not Settings:get("autoReconnect") then + return Promise.resolve(false) + end + + local priorSyncInfo = self:getPriorSyncInfo() + if not priorSyncInfo.projectName then + Log.trace("No prior sync info found, skipping auto-reconnect") + return Promise.resolve(false) + end + + return self:findActiveServer() + :andThen(function(serverInfo) + -- change + if serverInfo.projectName == priorSyncInfo.projectName then + Log.trace("Auto-reconnect found matching server, reconnecting...") + self:addNotification({ + text = `Auto-reconnect discovered project '{serverInfo.projectName}'...`, + }) + self:startSession() + return true + end + Log.trace("Auto-reconnect found different server, not reconnecting") + return false + end) + :catch(function() + Log.trace("Auto-reconnect did not find a server, not reconnecting") + return false + end) +end + +function App:checkSyncReminder() + local syncReminderMode = Settings:get("syncReminderMode") + if syncReminderMode == "None" then + return + end + + if self.serveSession ~= nil or not self:isSyncLockAvailable() then + -- Already syncing or cannot sync, no reason to remind + return + end + + local priorSyncInfo = self:getPriorSyncInfo() + + self:findActiveServer() + :andThen(function(serverInfo, host, port) + self:sendSyncReminder( + `Project '{serverInfo.projectName}' is serving at {host}:{port}.\nWould you like to connect?` + ) + end) + :catch(function() + if priorSyncInfo.timestamp and priorSyncInfo.projectName then + -- We didn't find an active server, + -- but this place has a prior sync + -- so we should remind the user to serve + + local timeSinceSync = timeUtil.elapsedToText(os.time() - priorSyncInfo.timestamp) + self:sendSyncReminder( + `You synced project '{priorSyncInfo.projectName}' to this place {timeSinceSync}.\nDid you mean to run 'rojo serve' and then connect?` + ) + end + end) +end + +function App:startSyncReminderPolling() + if + self.syncReminderPollingThread ~= nil + or Settings:get("syncReminderMode") == "None" + or not Settings:get("syncReminderPolling") + then + return + end + + Log.trace("Starting sync reminder polling thread") + self.syncReminderPollingThread = task.spawn(function() + while task.wait(30) do + if self.syncReminderPollingThread == nil then + -- The polling thread was stopped, so exit + return + end + if self.dismissSyncReminder then + -- There is already a sync reminder being shown + task.wait(5) + continue + end + self:checkSyncReminder() + end + end) +end + +function App:stopSyncReminderPolling() + if self.syncReminderPollingThread then + Log.trace("Stopping sync reminder polling thread") + task.cancel(self.syncReminderPollingThread) + self.syncReminderPollingThread = nil + end +end + +function App:sendSyncReminder(message: string) + local syncReminderMode = Settings:get("syncReminderMode") + if syncReminderMode == "None" then + return + end + + self.dismissSyncReminder = self:addNotification({ + text = message, + timeout = 120, + isFullscreen = Settings:get("syncReminderMode") == "Fullscreen", + onClose = function() + self.dismissSyncReminder = nil + end, + actions = { + Connect = { + text = "Connect", + style = "Solid", + layoutOrder = 1, + onClick = function() + self:startSession() + end, + }, + Dismiss = { + text = "Dismiss", + style = "Bordered", + layoutOrder = 2, + onClick = function() + -- If the user dismisses the reminder, + -- then we don't need to remind them again + self:stopSyncReminderPolling() + end, + }, + }, + }) +end + function App:isAutoConnectPlaytestServerAvailable() return RunService:IsRunning() and RunService:IsStudio() @@ -435,7 +570,10 @@ function App:startSession() local msg = string.format("Could not sync because user '%s' is already syncing", tostring(priorOwner)) Log.warn(msg) - self:addNotification(msg, 10) + self:addNotification({ + text = msg, + timeout = 10, + }) self:setState({ appStatus = AppStatus.Error, errorMessage = msg, @@ -506,11 +644,18 @@ function App:startSession() serveSession:onStatusChanged(function(status, details) if status == ServeSession.Status.Connecting then + if self.dismissSyncReminder then + self.dismissSyncReminder() + self.dismissSyncReminder = nil + end + self:setState({ appStatus = AppStatus.Connecting, toolbarIcon = Assets.Images.PluginButton, }) - self:addNotification("Connecting to session...") + self:addNotification({ + text = "Connecting to session...", + }) elseif status == ServeSession.Status.Connected then self.knownProjects[details] = true self:setPriorSyncInfo(host, port, details) @@ -523,7 +668,9 @@ function App:startSession() address = address, toolbarIcon = Assets.Images.PluginButtonConnected, }) - self:addNotification(string.format("Connected to session '%s' at %s.", details, address), 5) + self:addNotification({ + text = string.format("Connected to session '%s' at %s.", details, address), + }) elseif status == ServeSession.Status.Disconnected then self.serveSession = nil self:releaseSyncLock() @@ -546,13 +693,19 @@ function App:startSession() errorMessage = tostring(details), toolbarIcon = Assets.Images.PluginButtonWarning, }) - self:addNotification(tostring(details), 10) + self:addNotification({ + text = tostring(details), + timeout = 10, + }) else self:setState({ appStatus = AppStatus.NotConnected, toolbarIcon = Assets.Images.PluginButton, }) - self:addNotification("Disconnected from session.") + self:addNotification({ + text = "Disconnected from session.", + timeout = 10, + }) end end end) @@ -630,13 +783,13 @@ function App:startSession() toolbarIcon = Assets.Images.PluginButton, }) - self:addNotification( - string.format( + self:addNotification({ + text = string.format( "Please accept%sor abort the initializing sync session.", Settings:get("twoWaySync") and ", reject, " or " " ), - 7 - ) + timeout = 7, + }) return self.confirmationEvent:Wait() end) @@ -797,19 +950,7 @@ function App:render() ResetOnSpawn = false, DisplayOrder = 100, }, { - layout = e("UIListLayout", { - SortOrder = Enum.SortOrder.LayoutOrder, - HorizontalAlignment = Enum.HorizontalAlignment.Right, - VerticalAlignment = Enum.VerticalAlignment.Bottom, - Padding = UDim.new(0, 5), - }), - padding = e("UIPadding", { - PaddingTop = UDim.new(0, 5), - PaddingBottom = UDim.new(0, 5), - PaddingLeft = UDim.new(0, 5), - PaddingRight = UDim.new(0, 5), - }), - notifs = e(Notifications, { + Notifications = e(Notifications, { soundPlayer = self.props.soundPlayer, notifications = self.state.notifications, onClose = function(id) diff --git a/plugin/src/Settings.lua b/plugin/src/Settings.lua index 7810635b..08f7e081 100644 --- a/plugin/src/Settings.lua +++ b/plugin/src/Settings.lua @@ -12,12 +12,14 @@ local Roact = require(Packages.Roact) local defaultSettings = { openScriptsExternally = false, twoWaySync = false, + autoReconnect = false, showNotifications = true, - syncReminder = true, + syncReminderMode = "Notify" :: "None" | "Notify" | "Fullscreen", + syncReminderPolling = true, checkForUpdates = true, checkForPrereleases = false, autoConnectPlaytestServer = false, - confirmationBehavior = "Initial", + confirmationBehavior = "Initial" :: "Never" | "Initial" | "Large Changes" | "Unlisted PlaceId", largeChangesConfirmationThreshold = 5, playSounds = true, typecheckingEnabled = false, @@ -108,4 +110,14 @@ function Settings:getBinding(name) return bind end +function Settings:getBindings(...: string) + local bindings = {} + for i = 1, select("#", ...) do + local source = select(i, ...) + bindings[source] = self:getBinding(source) + end + + return Roact.joinBindings(bindings) +end + return Settings From 5f33435f3ca39e265573277d2176beacf63574f0 Mon Sep 17 00:00:00 2001 From: Micah Date: Fri, 29 Aug 2025 18:45:15 -0700 Subject: [PATCH 12/22] Move to using Rokit, update tools, and don't install unnecessary tools (#1109) --- .github/workflows/ci.yml | 16 +++------------- .github/workflows/release.yml | 11 +++-------- aftman.toml | 5 ----- rokit.toml | 5 +++++ selene.toml | 3 +++ 5 files changed, 14 insertions(+), 26 deletions(-) delete mode 100644 aftman.toml create mode 100644 rokit.toml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2cc6a265..6a69653c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,11 +35,6 @@ jobs: target key: ${{ matrix.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - - name: Setup Aftman - uses: ok-nick/setup-aftman@v0.4.2 - with: - version: 'v0.3.0' - - name: Build run: cargo build --locked --verbose @@ -76,11 +71,6 @@ jobs: target key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - - name: Setup Aftman - uses: ok-nick/setup-aftman@v0.4.2 - with: - version: 'v0.3.0' - - name: Build run: cargo build --locked --verbose @@ -116,10 +106,10 @@ jobs: target key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - - name: Setup Aftman - uses: ok-nick/setup-aftman@v0.4.2 + - name: Setup Rokit + uses: CompeyDev/setup-rokit@v0.1.2 with: - version: 'v0.3.0' + version: 'v1.1.0' - name: Stylua run: stylua --check plugin/src diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4ce868a6..60388221 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,10 +25,10 @@ jobs: with: submodules: true - - name: Setup Aftman - uses: ok-nick/setup-aftman@v0.4.2 + - name: Setup Rokit + uses: CompeyDev/setup-rokit@v0.1.2 with: - version: 'v0.3.0' + version: 'v1.1.0' - name: Build Plugin run: rojo build plugin.project.json --output Rojo.rbxm @@ -95,11 +95,6 @@ jobs: target key: ${{ matrix.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - - name: Setup Aftman - uses: ok-nick/setup-aftman@v0.4.2 - with: - version: 'v0.3.0' - - name: Build Release run: cargo build --release --locked --verbose --target ${{ matrix.target }} diff --git a/aftman.toml b/aftman.toml deleted file mode 100644 index 8de6c658..00000000 --- a/aftman.toml +++ /dev/null @@ -1,5 +0,0 @@ -[tools] -rojo = "rojo-rbx/rojo@7.4.1" -selene = "Kampfkarren/selene@0.27.1" -stylua = "JohnnyMorganz/stylua@0.20.0" -run-in-roblox = "rojo-rbx/run-in-roblox@0.3.0" diff --git a/rokit.toml b/rokit.toml new file mode 100644 index 00000000..6fbd8746 --- /dev/null +++ b/rokit.toml @@ -0,0 +1,5 @@ +[tools] +rojo = "rojo-rbx/rojo@7.5.1" +selene = "Kampfkarren/selene@0.29.0" +stylua = "JohnnyMorganz/stylua@2.1.0" +run-in-roblox = "rojo-rbx/run-in-roblox@0.3.0" diff --git a/selene.toml b/selene.toml index 16c0939f..857227a4 100644 --- a/selene.toml +++ b/selene.toml @@ -2,3 +2,6 @@ std = "roblox+testez" [config] unused_variable = { allow_unused_self = true } + +[lints] +roblox_manual_fromscale_or_fromoffset = "allow" From e2633126ee486446330ca82d74c65581b36e4bb0 Mon Sep 17 00:00:00 2001 From: Micah Date: Sat, 30 Aug 2025 13:42:52 -0700 Subject: [PATCH 13/22] Change Foreman to Rokit in CONTRIBUTING.md (#1110) --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7eccb0ee..d5398bbb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,7 +15,7 @@ You'll want these tools to work on Rojo: * Latest stable Rust compiler * Latest stable [Rojo](https://github.com/rojo-rbx/rojo) -* [Foreman](https://github.com/Roblox/foreman) +* [Rokit](https://github.com/rojo-rbx/rokit) * [Luau Language Server](https://github.com/JohnnyMorganz/luau-lsp) (Only needed if working on the Studio plugin.) When working on the Studio plugin, we recommend using this command to automatically rebuild the plugin when you save a change: From c1326ba06ec3939dde18e50b6bbe836864c10dfb Mon Sep 17 00:00:00 2001 From: Micah Date: Sat, 30 Aug 2025 14:10:59 -0700 Subject: [PATCH 14/22] Add Arm64 builds to CI/Releases + build on ubuntu 22.04 (#1098) --- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6a69653c..3efb622d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: strategy: matrix: - os: [ubuntu-latest, windows-latest, macos-latest] + os: [ubuntu-22.04, windows-latest, macos-latest, windows-11-arm, ubuntu-22.04-arm] steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 60388221..581be871 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -53,15 +53,25 @@ jobs: # https://doc.rust-lang.org/rustc/platform-support.html include: - host: linux - os: ubuntu-latest + os: ubuntu-22.04 target: x86_64-unknown-linux-gnu label: linux-x86_64 + - host: linux + os: ubuntu-22.04-arm + target: aarch64-unknown-linux-gnu + label: linux-aarch64 + - host: windows os: windows-latest target: x86_64-pc-windows-msvc label: windows-x86_64 + - host: windows + os: windows-11-arm + target: aarch64-pc-windows-msvc + label: windows-aarch64 + - host: macos os: macos-latest target: x86_64-apple-darwin From 59e36491a50618f81e2c312cd3284db39d24611b Mon Sep 17 00:00:00 2001 From: KAS Date: Tue, 16 Sep 2025 23:30:34 +0530 Subject: [PATCH 15/22] Fix a grammar error and a typo (#1113) --- CHANGELOG.md | 2 +- CONTRIBUTING.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a71d618e..0c8cdd24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -720,7 +720,7 @@ This is a general maintenance release for the Rojo 0.5.x release series. ## [0.5.0 Alpha 11](https://github.com/rojo-rbx/rojo/releases/tag/v0.5.0-alpha.11) (May 29, 2019) * Added support for implicit property values in JSON model files ([#154](https://github.com/rojo-rbx/rojo/pull/154)) -* `Content` propertyes can now be specified in projects and model files as regular string literals. +* `Content` properties can now be specified in projects and model files as regular string literals. * Added support for `BrickColor` properties. * Added support for properties added in client release 384, like `Lighting.Technology` being set to `"ShadowMap"`. * Improved performance when working with XML models and places diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d5398bbb..c050a223 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -37,7 +37,7 @@ bash scripts/unit-test-plugin.sh ## Documentation Documentation impacts way more people than the individual lines of code we write. -If you find any problems in documentation, including typos, bad grammar, misleading phrasing, or missing content, feel free to file issues and pull requests to fix them. +If you find any problems in the documentation, including typos, bad grammar, misleading phrasing, or missing content, feel free to file issues and pull requests to fix them. ## Bug Reports and Feature Requests Most of the tools around Rojo try to be clear when an issue is a bug. Even if they aren't, sometimes things don't work quite right. From 80a381dbb1aee4db4dc8906a0b5dd190e5e09a44 Mon Sep 17 00:00:00 2001 From: Micah Date: Sun, 21 Sep 2025 15:09:20 -0700 Subject: [PATCH 16/22] Use SerializationService as a fallback for when patch application fails (#1030) --- CHANGELOG.md | 7 + Cargo.lock | 7 + Cargo.toml | 1 + crates/rojo-insta-ext/src/redaction_map.rs | 6 + plugin/Packages/t | 2 +- plugin/src/ApiContext.lua | 30 +++ plugin/src/App/StatusPages/Settings/init.lua | 8 + plugin/src/App/init.lua | 4 +- plugin/src/PatchSet.lua | 16 ++ plugin/src/Reconciler/applyPatch.lua | 16 -- plugin/src/Reconciler/init.lua | 64 ----- plugin/src/ServeSession.lua | 226 ++++++++++++++++-- plugin/src/Settings.lua | 1 + plugin/src/Types.lua | 12 + ..._end__tests__serve__forced_parent_all.snap | 16 ++ ...end__tests__serve__forced_parent_info.snap | 13 + ..._serve__forced_parent_serialize_model.snap | 27 +++ ...d__tests__serve__meshpart_with_id_all.snap | 58 +++++ ...__tests__serve__meshpart_with_id_info.snap | 13 + ...rve__meshpart_with_id_serialize_model.snap | 43 ++++ .../forced_parent/default.project.json | 6 + .../meshpart_with_id/default.project.json | 22 ++ src/web/api.rs | 156 +++++++++++- src/web/interface.rs | 38 +++ tests/rojo_test/serve_util.rs | 39 ++- tests/tests/serve.rs | 70 +++++- 26 files changed, 793 insertions(+), 108 deletions(-) create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__serve__forced_parent_all.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__serve__forced_parent_info.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__serve__forced_parent_serialize_model.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__serve__meshpart_with_id_all.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__serve__meshpart_with_id_info.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__serve__meshpart_with_id_serialize_model.snap create mode 100644 rojo-test/serve-tests/forced_parent/default.project.json create mode 100644 rojo-test/serve-tests/meshpart_with_id/default.project.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c8cdd24..288e059a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## Unreleased +* Added fallback method for when an Instance can't be synced through normal means ([#1030]) + This should make it possible to sync `MeshParts` and `Unions`! + + The fallback involves deleting and recreating Instances. This will break + properties that reference them that Rojo does not know about, so be weary. + * Add auto-reconnect and improve UX for sync reminders ([#1096]) * Add support for syncing `yml` and `yaml` files (behaves similar to JSON and TOML) ([#1093]) * Fixed colors of Table diff ([#1084]) @@ -13,6 +19,7 @@ * Added `--absolute` flag to the sourcemap subcommand, which will emit absolute paths instead of relative paths. ([#1092]) * Fixed applying `gameId` and `placeId` before initial sync was accepted ([#1104]) +[#1030]: https://github.com/rojo-rbx/rojo/pull/1030 [#1096]: https://github.com/rojo-rbx/rojo/pull/1096 [#1093]: https://github.com/rojo-rbx/rojo/pull/1093 [#1084]: https://github.com/rojo-rbx/rojo/pull/1084 diff --git a/Cargo.lock b/Cargo.lock index 4b291c64..58a8649e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -401,6 +401,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "data-encoding" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" + [[package]] name = "diff" version = "0.1.13" @@ -1870,6 +1876,7 @@ dependencies = [ "criterion", "crossbeam-channel", "csv", + "data-encoding", "embed-resource", "env_logger", "fs-err", diff --git a/Cargo.toml b/Cargo.toml index 7b34dbce..e54ade8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -94,6 +94,7 @@ uuid = { version = "1.7.0", features = ["v4", "serde"] } clap = { version = "3.2.25", features = ["derive"] } profiling = "1.0.15" yaml-rust2 = "0.10.3" +data-encoding = "2.8.0" [target.'cfg(windows)'.dependencies] winreg = "0.10.1" diff --git a/crates/rojo-insta-ext/src/redaction_map.rs b/crates/rojo-insta-ext/src/redaction_map.rs index fab06bf9..7f51775e 100644 --- a/crates/rojo-insta-ext/src/redaction_map.rs +++ b/crates/rojo-insta-ext/src/redaction_map.rs @@ -22,6 +22,12 @@ impl RedactionMap { } } + /// Returns the numeric ID that was assigned to the provided value, + /// if one exists. + pub fn get_id_for_value(&self, value: impl ToString) -> Option { + self.ids.get(&value.to_string()).cloned() + } + pub fn intern(&mut self, id: impl ToString) { let last_id = &mut self.last_id; diff --git a/plugin/Packages/t b/plugin/Packages/t index 1f975425..1dbfccc1 160000 --- a/plugin/Packages/t +++ b/plugin/Packages/t @@ -1 +1 @@ -Subproject commit 1f9754254b17bff5a0b7527620083f8ea85f579a +Subproject commit 1dbfccc182d5870cca5266dd6f3d81618f69fc29 diff --git a/plugin/src/ApiContext.lua b/plugin/src/ApiContext.lua index 3eb59003..02d4964d 100644 --- a/plugin/src/ApiContext.lua +++ b/plugin/src/ApiContext.lua @@ -10,6 +10,8 @@ local Version = require(script.Parent.Version) local validateApiInfo = Types.ifEnabled(Types.ApiInfoResponse) local validateApiRead = Types.ifEnabled(Types.ApiReadResponse) local validateApiSubscribe = Types.ifEnabled(Types.ApiSubscribeResponse) +local validateApiSerialize = Types.ifEnabled(Types.ApiSerializeResponse) +local validateApiRefPatch = Types.ifEnabled(Types.ApiRefPatchResponse) local function rejectFailedRequests(response) if response.code >= 400 then @@ -252,4 +254,32 @@ function ApiContext:open(id) end) end +function ApiContext:serialize(ids: { string }) + local url = ("%s/api/serialize/%s"):format(self.__baseUrl, table.concat(ids, ",")) + + return Http.get(url):andThen(rejectFailedRequests):andThen(Http.Response.json):andThen(function(body) + if body.sessionId ~= self.__sessionId then + return Promise.reject("Server changed ID") + end + + assert(validateApiSerialize(body)) + + return body + end) +end + +function ApiContext:refPatch(ids: { string }) + local url = ("%s/api/ref-patch/%s"):format(self.__baseUrl, table.concat(ids, ",")) + + return Http.get(url):andThen(rejectFailedRequests):andThen(Http.Response.json):andThen(function(body) + if body.sessionId ~= self.__sessionId then + return Promise.reject("Server changed ID") + end + + assert(validateApiRefPatch(body)) + + return body + end) +end + return ApiContext diff --git a/plugin/src/App/StatusPages/Settings/init.lua b/plugin/src/App/StatusPages/Settings/init.lua index 7d05fd63..b63d30e5 100644 --- a/plugin/src/App/StatusPages/Settings/init.lua +++ b/plugin/src/App/StatusPages/Settings/init.lua @@ -181,6 +181,14 @@ function SettingsPage:render() layoutOrder = layoutIncrement(), }), + EnableSyncFallback = e(Setting, { + id = "enableSyncFallback", + name = "Enable Sync Fallback", + description = "Whether Instances that fail to sync are remade as a fallback. If this is enabled, Instances may be destroyed and remade when syncing.", + transparency = self.props.transparency, + layoutOrder = layoutIncrement(), + }), + CheckForUpdates = e(Setting, { id = "checkForUpdates", name = "Check For Updates", diff --git a/plugin/src/App/init.lua b/plugin/src/App/init.lua index 7ac84f60..ae03a6f1 100644 --- a/plugin/src/App/init.lua +++ b/plugin/src/App/init.lua @@ -595,13 +595,13 @@ function App:startSession() twoWaySync = Settings:get("twoWaySync"), }) - self.cleanupPrecommit = serveSession.__reconciler:hookPrecommit(function(patch, instanceMap) + self.cleanupPrecommit = serveSession:hookPrecommit(function(patch, instanceMap) -- Build new tree for patch self:setState({ patchTree = PatchTree.build(patch, instanceMap, { "Property", "Old", "New" }), }) end) - self.cleanupPostcommit = serveSession.__reconciler:hookPostcommit(function(patch, instanceMap, unappliedPatch) + self.cleanupPostcommit = serveSession:hookPostcommit(function(patch, instanceMap, unappliedPatch) -- Update tree with unapplied metadata self:setState(function(prevState) return { diff --git a/plugin/src/PatchSet.lua b/plugin/src/PatchSet.lua index 064def36..3f6ece3c 100644 --- a/plugin/src/PatchSet.lua +++ b/plugin/src/PatchSet.lua @@ -282,6 +282,22 @@ function PatchSet.assign(target, ...) return target end +function PatchSet.addedIdList(patchSet): { string } + local idList = table.create(#patchSet.added) + for id in patchSet.added do + table.insert(idList, id) + end + return table.freeze(idList) +end + +function PatchSet.updatedIdList(patchSet): { string } + local idList = table.create(#patchSet.updated) + for _, item in patchSet.updated do + table.insert(idList, item.id) + end + return table.freeze(idList) +end + --[[ Create a list of human-readable statements summarizing the contents of this patch, intended to be displayed to users. diff --git a/plugin/src/Reconciler/applyPatch.lua b/plugin/src/Reconciler/applyPatch.lua index 6c475f2f..8da223d1 100644 --- a/plugin/src/Reconciler/applyPatch.lua +++ b/plugin/src/Reconciler/applyPatch.lua @@ -5,8 +5,6 @@ Patches can come from the server or be generated by the client. ]] -local ChangeHistoryService = game:GetService("ChangeHistoryService") - local Packages = script.Parent.Parent.Parent.Packages local Log = require(Packages.Log) @@ -20,13 +18,6 @@ local reifyInstance, applyDeferredRefs = reify.reifyInstance, reify.applyDeferre local setProperty = require(script.Parent.setProperty) local function applyPatch(instanceMap, patch) - local patchTimestamp = DateTime.now():FormatLocalTime("LTS", "en-us") - local historyRecording = ChangeHistoryService:TryBeginRecording("Rojo: Patch " .. patchTimestamp) - if not historyRecording then - -- There can only be one recording at a time - Log.debug("Failed to begin history recording for " .. patchTimestamp .. ". Another recording is in progress.") - end - -- Tracks any portions of the patch that could not be applied to the DOM. local unappliedPatch = PatchSet.newEmpty() @@ -73,9 +64,6 @@ local function applyPatch(instanceMap, patch) if parentInstance == nil then -- This would be peculiar. If you create an instance with no -- parent, were you supposed to create it at all? - if historyRecording then - ChangeHistoryService:FinishRecording(historyRecording, Enum.FinishRecordingOperation.Commit) - end invariant( "Cannot add an instance from a patch that has no parent.\nInstance {} with parent {}.\nState: {:#?}", id, @@ -244,10 +232,6 @@ local function applyPatch(instanceMap, patch) end end - if historyRecording then - ChangeHistoryService:FinishRecording(historyRecording, Enum.FinishRecordingOperation.Commit) - end - applyDeferredRefs(instanceMap, deferredRefs, unappliedPatch) return unappliedPatch diff --git a/plugin/src/Reconciler/init.lua b/plugin/src/Reconciler/init.lua index df7cd92f..4ed46503 100644 --- a/plugin/src/Reconciler/init.lua +++ b/plugin/src/Reconciler/init.lua @@ -5,9 +5,6 @@ local Rojo = script:FindFirstAncestor("Rojo") local Plugin = Rojo.Plugin -local Packages = Rojo.Packages - -local Log = require(Packages.Log) local Timer = require(Plugin.Timer) @@ -22,78 +19,17 @@ function Reconciler.new(instanceMap) local self = { -- Tracks all of the instances known by the reconciler by ID. __instanceMap = instanceMap, - __precommitCallbacks = {}, - __postcommitCallbacks = {}, } return setmetatable(self, Reconciler) end -function Reconciler:hookPrecommit(callback: (patch: any, instanceMap: any) -> ()): () -> () - table.insert(self.__precommitCallbacks, callback) - Log.trace("Added precommit callback: {}", callback) - - return function() - -- Remove the callback from the list - for i, cb in self.__precommitCallbacks do - if cb == callback then - table.remove(self.__precommitCallbacks, i) - Log.trace("Removed precommit callback: {}", callback) - break - end - end - end -end - -function Reconciler:hookPostcommit(callback: (patch: any, instanceMap: any, unappliedPatch: any) -> ()): () -> () - table.insert(self.__postcommitCallbacks, callback) - Log.trace("Added postcommit callback: {}", callback) - - return function() - -- Remove the callback from the list - for i, cb in self.__postcommitCallbacks do - if cb == callback then - table.remove(self.__postcommitCallbacks, i) - Log.trace("Removed postcommit callback: {}", callback) - break - end - end - end -end - function Reconciler:applyPatch(patch) Timer.start("Reconciler:applyPatch") - Timer.start("precommitCallbacks") - -- Precommit callbacks must be serial in order to obey the contract that - -- they execute before commit - for _, callback in self.__precommitCallbacks do - local success, err = pcall(callback, patch, self.__instanceMap) - if not success then - Log.warn("Precommit hook errored: {}", err) - end - end - Timer.stop() - - Timer.start("apply") local unappliedPatch = applyPatch(self.__instanceMap, patch) - Timer.stop() - - Timer.start("postcommitCallbacks") - -- Postcommit callbacks can be called with spawn since regardless of firing order, they are - -- guaranteed to be called after the commit - for _, callback in self.__postcommitCallbacks do - task.spawn(function() - local success, err = pcall(callback, patch, self.__instanceMap, unappliedPatch) - if not success then - Log.warn("Postcommit hook errored: {}", err) - end - end) - end - Timer.stop() Timer.stop() - return unappliedPatch end diff --git a/plugin/src/ServeSession.lua b/plugin/src/ServeSession.lua index 62a5c76a..9994be10 100644 --- a/plugin/src/ServeSession.lua +++ b/plugin/src/ServeSession.lua @@ -1,11 +1,15 @@ local StudioService = game:GetService("StudioService") local RunService = game:GetService("RunService") +local ChangeHistoryService = game:GetService("ChangeHistoryService") +local SerializationService = game:GetService("SerializationService") +local Selection = game:GetService("Selection") local Packages = script.Parent.Parent.Packages local Log = require(Packages.Log) local Fmt = require(Packages.Fmt) local t = require(Packages.t) local Promise = require(Packages.Promise) +local Timer = require(script.Parent.Timer) local ChangeBatcher = require(script.Parent.ChangeBatcher) local encodePatchUpdate = require(script.Parent.ChangeBatcher.encodePatchUpdate) @@ -95,6 +99,8 @@ function ServeSession.new(options) __changeBatcher = changeBatcher, __statusChangedCallback = nil, __connections = connections, + __precommitCallbacks = {}, + __postcommitCallbacks = {}, } setmetatable(self, ServeSession) @@ -125,12 +131,46 @@ function ServeSession:setConfirmCallback(callback) self.__userConfirmCallback = callback end +--[=[ + Hooks a function to run before patch application. + The provided function is called with the incoming patch and an InstanceMap + as parameters. +]=] function ServeSession:hookPrecommit(callback) - return self.__reconciler:hookPrecommit(callback) + table.insert(self.__precommitCallbacks, callback) + Log.trace("Added precommit callback: {}", callback) + + return function() + -- Remove the callback from the list + for i, cb in self.__precommitCallbacks do + if cb == callback then + table.remove(self.__precommitCallbacks, i) + Log.trace("Removed precommit callback: {}", callback) + break + end + end + end end +--[=[ + Hooks a function to run after patch application. + The provided function is called with the applied patch, the current + InstanceMap, and a PatchSet containing any unapplied changes. +]=] function ServeSession:hookPostcommit(callback) - return self.__reconciler:hookPostcommit(callback) + table.insert(self.__postcommitCallbacks, callback) + Log.trace("Added postcommit callback: {}", callback) + + return function() + -- Remove the callback from the list + for i, cb in self.__postcommitCallbacks do + if cb == callback then + table.remove(self.__postcommitCallbacks, i) + Log.trace("Removed postcommit callback: {}", callback) + break + end + end + end end function ServeSession:start() @@ -206,6 +246,169 @@ function ServeSession:__onActiveScriptChanged(activeScript) self.__apiContext:open(scriptId) end +function ServeSession:__replaceInstances(idList) + if #idList == 0 then + return true, PatchSet.newEmpty() + end + -- It would be annoying if selection went away, so we try to preserve it. + local selection = Selection:Get() + local selectionMap = {} + for i, instance in selection do + selectionMap[instance] = i + end + + -- TODO: Should we do this in multiple requests so we can more granularly mark failures? + local modelSuccess, replacements = self.__apiContext + :serialize(idList) + :andThen(function(response) + Log.debug("Deserializing results from serialize endpoint") + local objects = SerializationService:DeserializeInstancesAsync(response.modelContents) + if not objects[1] then + return Promise.reject("Serialize endpoint did not deserialize into any Instances") + end + if #objects[1]:GetChildren() ~= #idList then + return Promise.reject("Serialize endpoint did not return the correct number of Instances") + end + + local instanceMap = {} + for _, item in objects[1]:GetChildren() do + instanceMap[item.Name] = item.Value + end + return instanceMap + end) + :await() + + local refSuccess, refPatch = self.__apiContext + :refPatch(idList) + :andThen(function(response) + return response.patch + end) + :await() + + if not (modelSuccess and refSuccess) then + return false + end + + for id, replacement in replacements do + local oldInstance = self.__instanceMap.fromIds[id] + self.__instanceMap:insert(id, replacement) + Log.trace("Swapping Instance {} out via api/models/ endpoint", id) + local oldParent = oldInstance.Parent + for _, child in oldInstance:GetChildren() do + child.Parent = replacement + end + + replacement.Parent = oldParent + -- ChangeHistoryService doesn't like it if an Instance has been + -- Destroyed. So, we have to accept the potential memory hit and + -- just set the parent to `nil`. + oldInstance.Parent = nil + + if selectionMap[oldInstance] then + -- This is a bit funky, but it saves the order of Selection + -- which might matter for some use cases. + selection[selectionMap[oldInstance]] = replacement + end + end + + local patchApplySuccess, unappliedPatch = pcall(self.__reconciler.applyPatch, self.__reconciler, refPatch) + if patchApplySuccess then + Selection:Set(selection) + return true, unappliedPatch + else + error(unappliedPatch) + end +end + +function ServeSession:__applyPatch(patch) + local patchTimestamp = DateTime.now():FormatLocalTime("LTS", "en-us") + local historyRecording = ChangeHistoryService:TryBeginRecording("Rojo: Patch " .. patchTimestamp) + if not historyRecording then + -- There can only be one recording at a time + Log.debug("Failed to begin history recording for " .. patchTimestamp .. ". Another recording is in progress.") + end + + Timer.start("precommitCallbacks") + -- Precommit callbacks must be serial in order to obey the contract that + -- they execute before commit + for _, callback in self.__precommitCallbacks do + local success, err = pcall(callback, patch, self.__instanceMap) + if not success then + Log.warn("Precommit hook errored: {}", err) + end + end + Timer.stop() + + local patchApplySuccess, unappliedPatch = pcall(self.__reconciler.applyPatch, self.__reconciler, patch) + if not patchApplySuccess then + if historyRecording then + ChangeHistoryService:FinishRecording(historyRecording, Enum.FinishRecordingOperation.Commit) + end + -- This might make a weird stack trace but the only way applyPatch can + -- fail is if a bug occurs so it's probably fine. + error(unappliedPatch) + end + + if PatchSet.isEmpty(unappliedPatch) then + if historyRecording then + ChangeHistoryService:FinishRecording(historyRecording, Enum.FinishRecordingOperation.Commit) + end + return + end + + local addedIdList = PatchSet.addedIdList(unappliedPatch) + local updatedIdList = PatchSet.updatedIdList(unappliedPatch) + + local actualUnappliedPatches = PatchSet.newEmpty() + if Settings:get("enableSyncFallback") then + Log.debug("ServeSession:__replaceInstances(unappliedPatch.added)") + Timer.start("ServeSession:__replaceInstances(unappliedPatch.added)") + local addSuccess, unappliedAddedRefs = self:__replaceInstances(addedIdList) + Timer.stop() + + Log.debug("ServeSession:__replaceInstances(unappliedPatch.updated)") + Timer.start("ServeSession:__replaceInstances(unappliedPatch.updated)") + local updateSuccess, unappliedUpdateRefs = self:__replaceInstances(updatedIdList) + Timer.stop() + + if addSuccess then + table.clear(unappliedPatch.added) + PatchSet.assign(actualUnappliedPatches, unappliedAddedRefs) + end + if updateSuccess then + table.clear(unappliedPatch.updated) + PatchSet.assign(actualUnappliedPatches, unappliedUpdateRefs) + end + else + Log.debug("Skipping ServeSession:__replaceInstances because of setting") + end + PatchSet.assign(actualUnappliedPatches, unappliedPatch) + + if not PatchSet.isEmpty(actualUnappliedPatches) then + Log.debug( + "Could not apply all changes requested by the Rojo server:\n{}", + PatchSet.humanSummary(self.__instanceMap, unappliedPatch) + ) + end + + Timer.start("postcommitCallbacks") + -- Postcommit callbacks can be called with spawn since regardless of firing order, they are + -- guaranteed to be called after the commit + for _, callback in self.__postcommitCallbacks do + task.spawn(function() + local success, err = pcall(callback, patch, self.__instanceMap, actualUnappliedPatches) + if not success then + Log.warn("Postcommit hook errored: {}", err) + end + end) + end + Timer.stop() + + if historyRecording then + ChangeHistoryService:FinishRecording(historyRecording, Enum.FinishRecordingOperation.Commit) + end +end + function ServeSession:__initialSync(serverInfo) return self.__apiContext:read({ serverInfo.rootInstanceId }):andThen(function(readResponseBody) -- Tell the API Context that we're up-to-date with the version of @@ -280,15 +483,7 @@ function ServeSession:__initialSync(serverInfo) return self.__apiContext:write(inversePatch) elseif userDecision == "Accept" then - local unappliedPatch = self.__reconciler:applyPatch(catchUpPatch) - - if not PatchSet.isEmpty(unappliedPatch) then - Log.debug( - "Could not apply all changes requested by the Rojo server:\n{}", - PatchSet.humanSummary(self.__instanceMap, unappliedPatch) - ) - end - + self:__applyPatch(catchUpPatch) return Promise.resolve() else return Promise.reject("Invalid user decision: " .. userDecision) @@ -311,14 +506,7 @@ function ServeSession:__mainSyncLoop() Log.trace("Serve session {} retrieved {} messages", tostring(self), #messages) for _, message in messages do - local unappliedPatch = self.__reconciler:applyPatch(message) - - if not PatchSet.isEmpty(unappliedPatch) then - Log.debug( - "Could not apply all changes requested by the Rojo server:\n{}", - PatchSet.humanSummary(self.__instanceMap, unappliedPatch) - ) - end + self:__applyPatch(message) end end) :await() diff --git a/plugin/src/Settings.lua b/plugin/src/Settings.lua index 08f7e081..64f32241 100644 --- a/plugin/src/Settings.lua +++ b/plugin/src/Settings.lua @@ -14,6 +14,7 @@ local defaultSettings = { twoWaySync = false, autoReconnect = false, showNotifications = true, + enableSyncFallback = true, syncReminderMode = "Notify" :: "None" | "Notify" | "Fullscreen", syncReminderPolling = true, checkForUpdates = true, diff --git a/plugin/src/Types.lua b/plugin/src/Types.lua index 12e1dbba..d11c9822 100644 --- a/plugin/src/Types.lua +++ b/plugin/src/Types.lua @@ -55,6 +55,16 @@ local ApiSubscribeResponse = t.interface({ messages = t.array(ApiSubscribeMessage), }) +local ApiSerializeResponse = t.interface({ + sessionId = t.string, + modelContents = t.buffer, +}) + +local ApiRefPatchResponse = t.interface({ + sessionId = t.string, + patch = ApiSubscribeMessage, +}) + local ApiError = t.interface({ kind = t.union(t.literal("NotFound"), t.literal("BadRequest"), t.literal("InternalError")), details = t.string, @@ -82,6 +92,8 @@ return strict("Types", { ApiInstanceUpdate = ApiInstanceUpdate, ApiInstanceMetadata = ApiInstanceMetadata, ApiSubscribeMessage = ApiSubscribeMessage, + ApiSerializeResponse = ApiSerializeResponse, + ApiRefPatchResponse = ApiRefPatchResponse, ApiValue = ApiValue, RbxId = RbxId, diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__serve__forced_parent_all.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__serve__forced_parent_all.snap new file mode 100644 index 00000000..1f5d934b --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__serve__forced_parent_all.snap @@ -0,0 +1,16 @@ +--- +source: tests/tests/serve.rs +expression: "read_response.intern_and_redact(&mut redactions, root_id)" +--- +instances: + id-2: + Children: [] + ClassName: Attachment + Id: id-2 + Metadata: + ignoreUnknownInstances: true + Name: forced_parent + Parent: "00000000000000000000000000000000" + Properties: {} +messageCursor: 0 +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__serve__forced_parent_info.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__serve__forced_parent_info.snap new file mode 100644 index 00000000..67b95d51 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__serve__forced_parent_info.snap @@ -0,0 +1,13 @@ +--- +source: tests/tests/serve.rs +expression: redactions.redacted_yaml(&info) +--- +expectedPlaceIds: ~ +gameId: ~ +placeId: ~ +projectName: forced_parent +protocolVersion: 4 +rootInstanceId: id-2 +serverVersion: "[server-version]" +sessionId: id-1 +unexpectedPlaceIds: ~ diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__serve__forced_parent_serialize_model.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__serve__forced_parent_serialize_model.snap new file mode 100644 index 00000000..a0a17963 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__serve__forced_parent_serialize_model.snap @@ -0,0 +1,27 @@ +--- +source: tests/tests/serve.rs +expression: model +--- + + + + Folder + + + + id-2 + 2 + + + + Part + + + + forced_parent + + + + + + diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__serve__meshpart_with_id_all.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__serve__meshpart_with_id_all.snap new file mode 100644 index 00000000..b264882c --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__serve__meshpart_with_id_all.snap @@ -0,0 +1,58 @@ +--- +source: tests/tests/serve.rs +expression: "read_response.intern_and_redact(&mut redactions, root_id)" +--- +instances: + id-2: + Children: + - id-3 + ClassName: DataModel + Id: id-2 + Metadata: + ignoreUnknownInstances: true + Name: meshpart + Parent: "00000000000000000000000000000000" + Properties: {} + id-3: + Children: + - id-4 + - id-5 + ClassName: Workspace + Id: id-3 + Metadata: + ignoreUnknownInstances: true + Name: Workspace + Parent: id-2 + Properties: + NeedsPivotMigration: + Bool: false + id-4: + Children: [] + ClassName: ObjectValue + Id: id-4 + Metadata: + ignoreUnknownInstances: true + Name: ObjectValue + Parent: id-3 + Properties: + Attributes: + Attributes: + Rojo_Target_Value: + String: sword + Value: + Ref: id-5 + id-5: + Children: [] + ClassName: MeshPart + Id: id-5 + Metadata: + ignoreUnknownInstances: true + Name: Sword + Parent: id-3 + Properties: + MeshId: + ContentId: "rbxasset://fonts/sword.mesh" + TextureID: + ContentId: "rbxasset://textures/SwordTexture.png" +messageCursor: 0 +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__serve__meshpart_with_id_info.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__serve__meshpart_with_id_info.snap new file mode 100644 index 00000000..0220db0a --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__serve__meshpart_with_id_info.snap @@ -0,0 +1,13 @@ +--- +source: tests/tests/serve.rs +expression: redactions.redacted_yaml(&info) +--- +expectedPlaceIds: ~ +gameId: ~ +placeId: ~ +projectName: meshpart +protocolVersion: 4 +rootInstanceId: id-2 +serverVersion: "[server-version]" +sessionId: id-1 +unexpectedPlaceIds: ~ diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__serve__meshpart_with_id_serialize_model.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__serve__meshpart_with_id_serialize_model.snap new file mode 100644 index 00000000..dae16506 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__serve__meshpart_with_id_serialize_model.snap @@ -0,0 +1,43 @@ +--- +source: tests/tests/serve.rs +expression: model +--- + + + + Folder + + + + id-5 + + 2 + + + + Sword + + rbxasset://fonts/sword.mesh + + + rbxasset://textures/SwordTexture.png + + + + + + + id-4 + + 4 + + + + ObjectValue + AQAAABEAAABSb2pvX1RhcmdldF9WYWx1ZQIFAAAAc3dvcmQ= + null + + + + + diff --git a/rojo-test/serve-tests/forced_parent/default.project.json b/rojo-test/serve-tests/forced_parent/default.project.json new file mode 100644 index 00000000..bcca1bde --- /dev/null +++ b/rojo-test/serve-tests/forced_parent/default.project.json @@ -0,0 +1,6 @@ +{ + "name": "forced_parent", + "tree": { + "$className": "Attachment" + } +} \ No newline at end of file diff --git a/rojo-test/serve-tests/meshpart_with_id/default.project.json b/rojo-test/serve-tests/meshpart_with_id/default.project.json new file mode 100644 index 00000000..4ec9f170 --- /dev/null +++ b/rojo-test/serve-tests/meshpart_with_id/default.project.json @@ -0,0 +1,22 @@ +{ + "name": "meshpart", + "tree": { + "$className": "DataModel", + "Workspace": { + "Sword": { + "$id": "sword", + "$className": "MeshPart", + "$properties": { + "MeshId": "rbxasset://fonts/sword.mesh", + "TextureID": "rbxasset://textures/SwordTexture.png" + } + }, + "ObjectValue": { + "$className": "ObjectValue", + "$attributes": { + "Rojo_Target_Value": "sword" + } + } + } + } +} \ No newline at end of file diff --git a/src/web/api.rs b/src/web/api.rs index ca39eccc..b1de79e8 100644 --- a/src/web/api.rs +++ b/src/web/api.rs @@ -1,11 +1,20 @@ //! Defines Rojo's HTTP API, all under /api. These endpoints generally return //! JSON. -use std::{collections::HashMap, fs, path::PathBuf, str::FromStr, sync::Arc}; +use std::{ + collections::{HashMap, HashSet}, + fs, + path::PathBuf, + str::FromStr, + sync::Arc, +}; use hyper::{body, Body, Method, Request, Response, StatusCode}; use opener::OpenError; -use rbx_dom_weak::types::Ref; +use rbx_dom_weak::{ + types::{Ref, Variant}, + InstanceBuilder, UstrMap, WeakDom, +}; use crate::{ serve_session::ServeSession, @@ -18,6 +27,7 @@ use crate::{ }, util::{json, json_ok}, }, + web_api::{BufferEncode, InstanceUpdate, RefPatchResponse, SerializeResponse}, }; pub async fn call(serve_session: Arc, request: Request) -> Response { @@ -31,10 +41,16 @@ pub async fn call(serve_session: Arc, request: Request) -> R (&Method::GET, path) if path.starts_with("/api/subscribe/") => { service.handle_api_subscribe(request).await } + (&Method::GET, path) if path.starts_with("/api/serialize/") => { + service.handle_api_serialize(request).await + } + (&Method::GET, path) if path.starts_with("/api/ref-patch/") => { + service.handle_api_ref_patch(request).await + } + (&Method::POST, path) if path.starts_with("/api/open/") => { service.handle_api_open(request).await } - (&Method::POST, "/api/write") => service.handle_api_write(request).await, (_method, path) => json( @@ -201,6 +217,126 @@ impl ApiService { }) } + /// Accepts a list of IDs and returns them serialized as a binary model. + /// The model is sent in a schema that causes Roblox to deserialize it as + /// a Luau `buffer`. + /// + /// The returned model is a folder that contains ObjectValues with names + /// that correspond to the requested Instances. These values have their + /// `Value` property set to point to the requested Instance. + async fn handle_api_serialize(&self, request: Request) -> Response { + let argument = &request.uri().path()["/api/serialize/".len()..]; + let requested_ids: Result, _> = argument.split(',').map(Ref::from_str).collect(); + + let requested_ids = match requested_ids { + Ok(ids) => ids, + Err(_) => { + return json( + ErrorResponse::bad_request("Malformed ID list"), + StatusCode::BAD_REQUEST, + ); + } + }; + let mut response_dom = WeakDom::new(InstanceBuilder::new("Folder")); + + let tree = self.serve_session.tree(); + for id in &requested_ids { + if let Some(instance) = tree.get_instance(*id) { + let clone = response_dom.insert( + Ref::none(), + InstanceBuilder::new(instance.class_name()) + .with_name(instance.name()) + .with_properties(instance.properties().clone()), + ); + let object_value = response_dom.insert( + response_dom.root_ref(), + InstanceBuilder::new("ObjectValue") + .with_name(id.to_string()) + .with_property("Value", clone), + ); + + let mut child_ref = clone; + if let Some(parent_class) = parent_requirements(&instance.class_name()) { + child_ref = + response_dom.insert(object_value, InstanceBuilder::new(parent_class)); + response_dom.transfer_within(clone, child_ref); + } + + response_dom.transfer_within(child_ref, object_value); + } else { + json( + ErrorResponse::bad_request(format!("provided id {id} is not in the tree")), + StatusCode::BAD_REQUEST, + ); + } + } + drop(tree); + + let mut source = Vec::new(); + rbx_binary::to_writer(&mut source, &response_dom, &[response_dom.root_ref()]).unwrap(); + + json_ok(SerializeResponse { + session_id: self.serve_session.session_id(), + model_contents: BufferEncode::new(source), + }) + } + + /// Returns a list of all referent properties that point towards the + /// provided IDs. Used because the plugin does not store a RojoTree, + /// and referent properties need to be updated after the serialize + /// endpoint is used. + async fn handle_api_ref_patch(self, request: Request) -> Response { + let argument = &request.uri().path()["/api/ref-patch/".len()..]; + let requested_ids: Result, _> = + argument.split(',').map(Ref::from_str).collect(); + + let requested_ids = match requested_ids { + Ok(ids) => ids, + Err(_) => { + return json( + ErrorResponse::bad_request("Malformed ID list"), + StatusCode::BAD_REQUEST, + ); + } + }; + + let mut instance_updates: HashMap = HashMap::new(); + + let tree = self.serve_session.tree(); + for instance in tree.descendants(tree.get_root_id()) { + for (prop_name, prop_value) in instance.properties() { + let Variant::Ref(prop_value) = prop_value else { + continue; + }; + if let Some(target_id) = requested_ids.get(prop_value) { + let instance_id = instance.id(); + let update = + instance_updates + .entry(instance_id) + .or_insert_with(|| InstanceUpdate { + id: instance_id, + changed_class_name: None, + changed_name: None, + changed_metadata: None, + changed_properties: UstrMap::default(), + }); + update + .changed_properties + .insert(*prop_name, Some(Variant::Ref(*target_id))); + } + } + } + + json_ok(RefPatchResponse { + session_id: self.serve_session.session_id(), + patch: SubscribeMessage { + added: HashMap::new(), + removed: Vec::new(), + updated: instance_updates.into_values().collect(), + }, + }) + } + /// Open a script with the given ID in the user's default text editor. async fn handle_api_open(&self, request: Request) -> Response { let argument = &request.uri().path()["/api/open/".len()..]; @@ -306,3 +442,17 @@ fn pick_script_path(instance: InstanceWithMeta<'_>) -> Option { }) .map(|path| path.to_owned()) } + +/// Certain Instances MUST be a child of specific classes. This function +/// tracks that information for the Serialize endpoint. +/// +/// If a parent requirement exists, it will be returned. +/// Otherwise returns `None`. +fn parent_requirements(class: &str) -> Option<&str> { + Some(match class { + "Attachment" | "Bone" => "Part", + "Animator" => "Humanoid", + "BaseWrap" | "WrapLayer" | "WrapTarget" | "WrapDeformer" => "MeshPart", + _ => return None, + }) +} diff --git a/src/web/interface.rs b/src/web/interface.rs index 4ac80999..fffa6654 100644 --- a/src/web/interface.rs +++ b/src/web/interface.rs @@ -208,6 +208,44 @@ pub struct OpenResponse { pub session_id: SessionId, } +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct SerializeResponse { + pub session_id: SessionId, + pub model_contents: BufferEncode, +} + +/// Using this struct we can force Roblox to JSONDecode this as a buffer. +/// This is what Roblox's serde APIs use, so it saves a step in the plugin. +#[derive(Debug, Serialize, Deserialize)] +pub struct BufferEncode { + m: (), + t: Cow<'static, str>, + base64: String, +} + +impl BufferEncode { + pub fn new(content: Vec) -> Self { + let base64 = data_encoding::BASE64.encode(&content); + Self { + m: (), + t: Cow::Borrowed("buffer"), + base64, + } + } + + pub fn model(&self) -> &str { + &self.base64 + } +} + +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct RefPatchResponse<'a> { + pub session_id: SessionId, + pub patch: SubscribeMessage<'a>, +} + /// General response type returned from all Rojo routes #[derive(Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] diff --git a/tests/rojo_test/serve_util.rs b/tests/rojo_test/serve_util.rs index 56061935..71d07778 100644 --- a/tests/rojo_test/serve_util.rs +++ b/tests/rojo_test/serve_util.rs @@ -1,4 +1,5 @@ use std::{ + fmt::Write as _, fs, path::{Path, PathBuf}, process::Command, @@ -11,7 +12,7 @@ use rbx_dom_weak::types::Ref; use tempfile::{tempdir, TempDir}; -use librojo::web_api::{ReadResponse, ServerInfoResponse, SubscribeResponse}; +use librojo::web_api::{ReadResponse, SerializeResponse, ServerInfoResponse, SubscribeResponse}; use rojo_insta_ext::RedactionMap; use crate::rojo_test::io_util::{ @@ -174,6 +175,18 @@ impl TestServeSession { reqwest::blocking::get(url)?.json() } + + pub fn get_api_serialize(&self, ids: &[Ref]) -> Result { + let mut id_list = String::with_capacity(ids.len() * 33); + for id in ids { + write!(id_list, "{id},").unwrap(); + } + id_list.pop(); + + let url = format!("http://localhost:{}/api/serialize/{}", self.port, id_list); + + reqwest::blocking::get(url)?.json() + } } /// Probably-okay way to generate random enough port numbers for running the @@ -187,3 +200,27 @@ fn get_port_number() -> usize { NEXT_PORT_NUMBER.fetch_add(1, Ordering::SeqCst) } + +/// Takes a SerializeResponse and creates an XML model out of the response. +/// +/// Since the provided structure intentionally includes unredacted referents, +/// some post-processing is done to ensure they don't show up in the model. +pub fn serialize_to_xml_model(response: &SerializeResponse, redactions: &RedactionMap) -> String { + let model_content = data_encoding::BASE64 + .decode(response.model_contents.model().as_bytes()) + .unwrap(); + + let mut dom = rbx_binary::from_reader(model_content.as_slice()).unwrap(); + // This makes me realize that maybe we need a `descendants_mut` iter. + let ref_list: Vec = dom.descendants().map(|inst| inst.referent()).collect(); + for referent in ref_list { + let inst = dom.get_by_ref_mut(referent).unwrap(); + if let Some(id) = redactions.get_id_for_value(&inst.name) { + inst.name = format!("id-{id}"); + } + } + + let mut data = Vec::new(); + rbx_xml::to_writer_default(&mut data, &dom, dom.root().children()).unwrap(); + String::from_utf8(data).expect("rbx_xml should never produce invalid utf-8") +} diff --git a/tests/tests/serve.rs b/tests/tests/serve.rs index 94dec37e..e3dae2d7 100644 --- a/tests/tests/serve.rs +++ b/tests/tests/serve.rs @@ -1,9 +1,12 @@ use std::fs; -use insta::{assert_yaml_snapshot, with_settings}; +use insta::{assert_snapshot, assert_yaml_snapshot, with_settings}; use tempfile::tempdir; -use crate::rojo_test::{internable::InternAndRedact, serve_util::run_serve_test}; +use crate::rojo_test::{ + internable::InternAndRedact, + serve_util::{run_serve_test, serialize_to_xml_model}, +}; #[test] fn empty() { @@ -591,3 +594,66 @@ fn model_pivot_migration() { ); }); } + +#[test] +fn meshpart_with_id() { + run_serve_test("meshpart_with_id", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + assert_yaml_snapshot!("meshpart_with_id_info", redactions.redacted_yaml(&info)); + + let read_response = session.get_api_read(root_id).unwrap(); + assert_yaml_snapshot!( + "meshpart_with_id_all", + read_response.intern_and_redact(&mut redactions, root_id) + ); + + // This is a bit awkward, but it's fine. + let (meshpart, _) = read_response + .instances + .iter() + .find(|(_, inst)| inst.class_name == "MeshPart") + .unwrap(); + let (objectvalue, _) = read_response + .instances + .iter() + .find(|(_, inst)| inst.class_name == "ObjectValue") + .unwrap(); + + let serialize_response = session + .get_api_serialize(&[*meshpart, *objectvalue]) + .unwrap(); + + // We don't assert a snapshot on the SerializeResponse because the model includes the + // Refs from the DOM as names, which means it will obviously be different every time + // this code runs. Still, we ensure that the SessionId is right at least. + assert_eq!(serialize_response.session_id, info.session_id); + + let model = serialize_to_xml_model(&serialize_response, &redactions); + assert_snapshot!("meshpart_with_id_serialize_model", model); + }); +} + +#[test] +fn forced_parent() { + run_serve_test("forced_parent", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + assert_yaml_snapshot!("forced_parent_info", redactions.redacted_yaml(&info)); + + let read_response = session.get_api_read(root_id).unwrap(); + assert_yaml_snapshot!( + "forced_parent_all", + read_response.intern_and_redact(&mut redactions, root_id) + ); + + let serialize_response = session.get_api_serialize(&[root_id]).unwrap(); + + assert_eq!(serialize_response.session_id, info.session_id); + + let model = serialize_to_xml_model(&serialize_response, &redactions); + assert_snapshot!("forced_parent_serialize_model", model); + }); +} From 6ea95d487c86a6688c3a3090f446ab0c9dce1d76 Mon Sep 17 00:00:00 2001 From: Micah Date: Tue, 30 Sep 2025 14:38:38 -0700 Subject: [PATCH 17/22] Refactor `init` command (#1117) --- .../model}/README.md | 0 .../model}/default.project.json | 0 .../model}/gitignore.txt | 0 .../model/src/init.luau} | 2 +- .../place}/README.md | 0 .../place}/default.project.json | 0 .../place}/gitignore.txt | 0 .../place/src/client/init.client.luau | 1 + .../place/src/server/init.server.luau | 1 + .../place/src/shared/Hello.luau | 3 + .../plugin}/README.md | 0 .../plugin}/default.project.json | 0 .../plugin}/gitignore.txt | 0 .../plugin/src/init.server.luau | 1 + build.rs | 12 +- src/cli/init.rs | 233 +++++++----------- 16 files changed, 99 insertions(+), 154 deletions(-) rename assets/{default-model-project => project-templates/model}/README.md (100%) rename assets/{default-model-project => project-templates/model}/default.project.json (100%) rename assets/{default-model-project => project-templates/model}/gitignore.txt (100%) rename assets/{default-model-project/src-init.luau => project-templates/model/src/init.luau} (97%) rename assets/{default-place-project => project-templates/place}/README.md (100%) rename assets/{default-place-project => project-templates/place}/default.project.json (100%) rename assets/{default-place-project => project-templates/place}/gitignore.txt (100%) create mode 100644 assets/project-templates/place/src/client/init.client.luau create mode 100644 assets/project-templates/place/src/server/init.server.luau create mode 100644 assets/project-templates/place/src/shared/Hello.luau rename assets/{default-plugin-project => project-templates/plugin}/README.md (100%) rename assets/{default-plugin-project => project-templates/plugin}/default.project.json (100%) rename assets/{default-plugin-project => project-templates/plugin}/gitignore.txt (100%) create mode 100644 assets/project-templates/plugin/src/init.server.luau diff --git a/assets/default-model-project/README.md b/assets/project-templates/model/README.md similarity index 100% rename from assets/default-model-project/README.md rename to assets/project-templates/model/README.md diff --git a/assets/default-model-project/default.project.json b/assets/project-templates/model/default.project.json similarity index 100% rename from assets/default-model-project/default.project.json rename to assets/project-templates/model/default.project.json diff --git a/assets/default-model-project/gitignore.txt b/assets/project-templates/model/gitignore.txt similarity index 100% rename from assets/default-model-project/gitignore.txt rename to assets/project-templates/model/gitignore.txt diff --git a/assets/default-model-project/src-init.luau b/assets/project-templates/model/src/init.luau similarity index 97% rename from assets/default-model-project/src-init.luau rename to assets/project-templates/model/src/init.luau index 41faf217..aacfcce9 100644 --- a/assets/default-model-project/src-init.luau +++ b/assets/project-templates/model/src/init.luau @@ -2,4 +2,4 @@ return { hello = function() print("Hello world, from {project_name}!") end, -} \ No newline at end of file +} diff --git a/assets/default-place-project/README.md b/assets/project-templates/place/README.md similarity index 100% rename from assets/default-place-project/README.md rename to assets/project-templates/place/README.md diff --git a/assets/default-place-project/default.project.json b/assets/project-templates/place/default.project.json similarity index 100% rename from assets/default-place-project/default.project.json rename to assets/project-templates/place/default.project.json diff --git a/assets/default-place-project/gitignore.txt b/assets/project-templates/place/gitignore.txt similarity index 100% rename from assets/default-place-project/gitignore.txt rename to assets/project-templates/place/gitignore.txt diff --git a/assets/project-templates/place/src/client/init.client.luau b/assets/project-templates/place/src/client/init.client.luau new file mode 100644 index 00000000..5b0486bf --- /dev/null +++ b/assets/project-templates/place/src/client/init.client.luau @@ -0,0 +1 @@ +print("Hello world, from client!") diff --git a/assets/project-templates/place/src/server/init.server.luau b/assets/project-templates/place/src/server/init.server.luau new file mode 100644 index 00000000..ec610e9c --- /dev/null +++ b/assets/project-templates/place/src/server/init.server.luau @@ -0,0 +1 @@ +print("Hello world, from server!") diff --git a/assets/project-templates/place/src/shared/Hello.luau b/assets/project-templates/place/src/shared/Hello.luau new file mode 100644 index 00000000..56ec1f24 --- /dev/null +++ b/assets/project-templates/place/src/shared/Hello.luau @@ -0,0 +1,3 @@ +return function() + print("Hello, world!") +end diff --git a/assets/default-plugin-project/README.md b/assets/project-templates/plugin/README.md similarity index 100% rename from assets/default-plugin-project/README.md rename to assets/project-templates/plugin/README.md diff --git a/assets/default-plugin-project/default.project.json b/assets/project-templates/plugin/default.project.json similarity index 100% rename from assets/default-plugin-project/default.project.json rename to assets/project-templates/plugin/default.project.json diff --git a/assets/default-plugin-project/gitignore.txt b/assets/project-templates/plugin/gitignore.txt similarity index 100% rename from assets/default-plugin-project/gitignore.txt rename to assets/project-templates/plugin/gitignore.txt diff --git a/assets/project-templates/plugin/src/init.server.luau b/assets/project-templates/plugin/src/init.server.luau new file mode 100644 index 00000000..931c6bfc --- /dev/null +++ b/assets/project-templates/plugin/src/init.server.luau @@ -0,0 +1 @@ +print("Hello world, from plugin!") diff --git a/build.rs b/build.rs index 64b5bd94..6bfd8e55 100644 --- a/build.rs +++ b/build.rs @@ -47,6 +47,7 @@ fn main() -> Result<(), anyhow::Error> { let root_dir = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()); let plugin_dir = root_dir.join("plugin"); + let templates_dir = root_dir.join("assets").join("project-templates"); let our_version = Version::parse(env::var_os("CARGO_PKG_VERSION").unwrap().to_str().unwrap())?; let plugin_version = @@ -57,7 +58,9 @@ fn main() -> Result<(), anyhow::Error> { "plugin version does not match Cargo version" ); - let snapshot = VfsSnapshot::dir(hashmap! { + let template_snapshot = snapshot_from_fs_path(&templates_dir)?; + + let plugin_snapshot = VfsSnapshot::dir(hashmap! { "default.project.json" => snapshot_from_fs_path(&root_dir.join("plugin.project.json"))?, "plugin" => VfsSnapshot::dir(hashmap! { "fmt" => snapshot_from_fs_path(&plugin_dir.join("fmt"))?, @@ -70,10 +73,11 @@ fn main() -> Result<(), anyhow::Error> { }), }); - let out_path = Path::new(&out_dir).join("plugin.bincode"); - let out_file = File::create(out_path)?; + let template_file = File::create(Path::new(&out_dir).join("templates.bincode"))?; + let plugin_file = File::create(Path::new(&out_dir).join("plugin.bincode"))?; - bincode::serialize_into(out_file, &snapshot)?; + bincode::serialize_into(plugin_file, &plugin_snapshot)?; + bincode::serialize_into(template_file, &template_snapshot)?; println!("cargo:rerun-if-changed=build/windows/rojo-manifest.rc"); println!("cargo:rerun-if-changed=build/windows/rojo.manifest"); diff --git a/src/cli/init.rs b/src/cli/init.rs index 5f861470..01e78260 100644 --- a/src/cli/init.rs +++ b/src/cli/init.rs @@ -1,30 +1,25 @@ -use std::io::{self, Write}; -use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; use std::str::FromStr; +use std::{ + collections::VecDeque, + path::{Path, PathBuf}, +}; +use std::{ + ffi::OsStr, + io::{self, Write}, +}; use anyhow::{bail, format_err}; use clap::Parser; use fs_err as fs; use fs_err::OpenOptions; +use memofs::{InMemoryFs, Vfs, VfsSnapshot}; use super::resolve_path; -static MODEL_PROJECT: &str = - include_str!("../../assets/default-model-project/default.project.json"); -static MODEL_README: &str = include_str!("../../assets/default-model-project/README.md"); -static MODEL_INIT: &str = include_str!("../../assets/default-model-project/src-init.luau"); -static MODEL_GIT_IGNORE: &str = include_str!("../../assets/default-model-project/gitignore.txt"); +const GIT_IGNORE_PLACEHOLDER: &str = "gitignore.txt"; -static PLACE_PROJECT: &str = - include_str!("../../assets/default-place-project/default.project.json"); -static PLACE_README: &str = include_str!("../../assets/default-place-project/README.md"); -static PLACE_GIT_IGNORE: &str = include_str!("../../assets/default-place-project/gitignore.txt"); - -static PLUGIN_PROJECT: &str = - include_str!("../../assets/default-plugin-project/default.project.json"); -static PLUGIN_README: &str = include_str!("../../assets/default-plugin-project/README.md"); -static PLUGIN_GIT_IGNORE: &str = include_str!("../../assets/default-plugin-project/gitignore.txt"); +static TEMPLATE_BINCODE: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/templates.bincode")); /// Initializes a new Rojo project. #[derive(Debug, Parser)] @@ -40,6 +35,8 @@ pub struct InitCommand { impl InitCommand { pub fn run(self) -> anyhow::Result<()> { + let template = self.kind.template(); + let base_path = resolve_path(&self.path); fs::create_dir_all(&base_path)?; @@ -53,10 +50,51 @@ impl InitCommand { name: project_name.to_owned(), }; - match self.kind { - InitKind::Place => init_place(&base_path, project_params)?, - InitKind::Model => init_model(&base_path, project_params)?, - InitKind::Plugin => init_plugin(&base_path, project_params)?, + println!( + "Creating new {:?} project '{}'", + self.kind, project_params.name + ); + + let vfs = Vfs::new(template); + vfs.set_watch_enabled(false); + + let mut queue = VecDeque::with_capacity(8); + for entry in vfs.read_dir("")? { + queue.push_back(entry?.path().to_path_buf()) + } + + while let Some(mut path) = queue.pop_front() { + let metadata = vfs.metadata(&path)?; + if metadata.is_dir() { + fs_err::create_dir(base_path.join(&path))?; + for entry in vfs.read_dir(&path)? { + queue.push_back(entry?.path().to_path_buf()); + } + } else { + let content = vfs.read_to_string_lf_normalized(&path)?; + if let Some(file_stem) = path.file_name().and_then(OsStr::to_str) { + if file_stem == GIT_IGNORE_PLACEHOLDER { + path.set_file_name(".gitignore"); + } + } + write_if_not_exists( + &base_path.join(&path), + &project_params.render_template(&content), + )?; + } + } + + if should_git_init(&base_path) { + log::debug!("Initializing Git repository..."); + + let status = Command::new("git") + .arg("init") + .current_dir(&base_path) + .status()?; + + if !status.success() { + bail!("git init failed: status code {:?}", status.code()); + } } println!("Created project successfully."); @@ -78,6 +116,32 @@ pub enum InitKind { Plugin, } +impl InitKind { + fn template(&self) -> InMemoryFs { + let template_path = match self { + Self::Place => "place", + Self::Model => "model", + Self::Plugin => "plugin", + }; + + let snapshot: VfsSnapshot = bincode::deserialize(TEMPLATE_BINCODE) + .expect("Rojo's templates were not properly packed into Rojo's binary"); + + if let VfsSnapshot::Dir { mut children } = snapshot { + if let Some(template) = children.remove(template_path) { + let mut fs = InMemoryFs::new(); + fs.load_snapshot("", template) + .expect("loading a template in memory should never fail"); + fs + } else { + panic!("template for project type {:?} is missing", self) + } + } else { + panic!("Rojo's templates were packed as a file instead of a directory") + } + } +} + impl FromStr for InitKind { type Err = anyhow::Error; @@ -94,92 +158,6 @@ impl FromStr for InitKind { } } -fn init_place(base_path: &Path, project_params: ProjectParams) -> anyhow::Result<()> { - println!("Creating new place project '{}'", project_params.name); - - let project_file = project_params.render_template(PLACE_PROJECT); - try_create_project(base_path, &project_file)?; - - let readme = project_params.render_template(PLACE_README); - write_if_not_exists(&base_path.join("README.md"), &readme)?; - - let src = base_path.join("src"); - fs::create_dir_all(&src)?; - - let src_shared = src.join("shared"); - fs::create_dir_all(src.join(&src_shared))?; - - let src_server = src.join("server"); - fs::create_dir_all(src.join(&src_server))?; - - let src_client = src.join("client"); - fs::create_dir_all(src.join(&src_client))?; - - write_if_not_exists( - &src_shared.join("Hello.luau"), - "return function()\n\tprint(\"Hello, world!\")\nend", - )?; - - write_if_not_exists( - &src_server.join("init.server.luau"), - "print(\"Hello world, from server!\")", - )?; - - write_if_not_exists( - &src_client.join("init.client.luau"), - "print(\"Hello world, from client!\")", - )?; - - let git_ignore = project_params.render_template(PLACE_GIT_IGNORE); - try_git_init(base_path, &git_ignore)?; - - Ok(()) -} - -fn init_model(base_path: &Path, project_params: ProjectParams) -> anyhow::Result<()> { - println!("Creating new model project '{}'", project_params.name); - - let project_file = project_params.render_template(MODEL_PROJECT); - try_create_project(base_path, &project_file)?; - - let readme = project_params.render_template(MODEL_README); - write_if_not_exists(&base_path.join("README.md"), &readme)?; - - let src = base_path.join("src"); - fs::create_dir_all(&src)?; - - let init = project_params.render_template(MODEL_INIT); - write_if_not_exists(&src.join("init.luau"), &init)?; - - let git_ignore = project_params.render_template(MODEL_GIT_IGNORE); - try_git_init(base_path, &git_ignore)?; - - Ok(()) -} - -fn init_plugin(base_path: &Path, project_params: ProjectParams) -> anyhow::Result<()> { - println!("Creating new plugin project '{}'", project_params.name); - - let project_file = project_params.render_template(PLUGIN_PROJECT); - try_create_project(base_path, &project_file)?; - - let readme = project_params.render_template(PLUGIN_README); - write_if_not_exists(&base_path.join("README.md"), &readme)?; - - let src = base_path.join("src"); - fs::create_dir_all(&src)?; - - write_if_not_exists( - &src.join("init.server.luau"), - "print(\"Hello world, from plugin!\")\n", - )?; - - let git_ignore = project_params.render_template(PLUGIN_GIT_IGNORE); - try_git_init(base_path, &git_ignore)?; - - Ok(()) -} - /// Contains parameters used in templates to create a project. struct ProjectParams { name: String, @@ -194,23 +172,6 @@ impl ProjectParams { } } -/// Attempt to initialize a Git repository if necessary, and create .gitignore. -fn try_git_init(path: &Path, git_ignore: &str) -> Result<(), anyhow::Error> { - if should_git_init(path) { - log::debug!("Initializing Git repository..."); - - let status = Command::new("git").arg("init").current_dir(path).status()?; - - if !status.success() { - bail!("git init failed: status code {:?}", status.code()); - } - } - - write_if_not_exists(&path.join(".gitignore"), git_ignore)?; - - Ok(()) -} - /// Tells whether we should initialize a Git repository inside the given path. /// /// Will return false if the user doesn't have Git installed or if the path is @@ -251,29 +212,3 @@ fn write_if_not_exists(path: &Path, contents: &str) -> Result<(), anyhow::Error> Ok(()) } - -/// Try to create a project file and fail if it already exists. -fn try_create_project(base_path: &Path, contents: &str) -> Result<(), anyhow::Error> { - let project_path = base_path.join("default.project.json"); - - let file_res = OpenOptions::new() - .write(true) - .create_new(true) - .open(&project_path); - - let mut file = match file_res { - Ok(file) => file, - Err(err) => { - return match err.kind() { - io::ErrorKind::AlreadyExists => { - bail!("Project file already exists: {}", project_path.display()) - } - _ => Err(err.into()), - } - } - }; - - file.write_all(contents.as_bytes())?; - - Ok(()) -} From beb497878be4134c2e170f6a006a4d49e4c2ee84 Mon Sep 17 00:00:00 2001 From: Micah Date: Tue, 7 Oct 2025 17:12:22 -0700 Subject: [PATCH 18/22] Add flag for skipping git initialization to `init` command (#1122) --- CHANGELOG.md | 2 ++ src/cli/init.rs | 13 ++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 288e059a..8a923798 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +* Added flag to `rojo init` to skip initializing a git repository ([#1122]) * Added fallback method for when an Instance can't be synced through normal means ([#1030]) This should make it possible to sync `MeshParts` and `Unions`! @@ -19,6 +20,7 @@ * Added `--absolute` flag to the sourcemap subcommand, which will emit absolute paths instead of relative paths. ([#1092]) * Fixed applying `gameId` and `placeId` before initial sync was accepted ([#1104]) +[#1122]: https://github.com/rojo-rbx/rojo/pull/1122 [#1030]: https://github.com/rojo-rbx/rojo/pull/1030 [#1096]: https://github.com/rojo-rbx/rojo/pull/1096 [#1093]: https://github.com/rojo-rbx/rojo/pull/1093 diff --git a/src/cli/init.rs b/src/cli/init.rs index 01e78260..03a4bc36 100644 --- a/src/cli/init.rs +++ b/src/cli/init.rs @@ -22,15 +22,22 @@ const GIT_IGNORE_PLACEHOLDER: &str = "gitignore.txt"; static TEMPLATE_BINCODE: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/templates.bincode")); /// Initializes a new Rojo project. +/// +/// By default, this will attempt to initialize a 'git' repository in the +/// project directory if `git` is installed. To avoid this, pass `--skip-git`. #[derive(Debug, Parser)] pub struct InitCommand { /// Path to the place to create the project. Defaults to the current directory. #[clap(default_value = "")] pub path: PathBuf, - /// The kind of project to create, 'place', 'plugin', or 'model'. Defaults to place. + /// The kind of project to create, 'place', 'plugin', or 'model'. #[clap(long, default_value = "place")] pub kind: InitKind, + + /// Skips the initialization of a git repository. + #[clap(long)] + pub skip_git: bool, } impl InitCommand { @@ -73,7 +80,7 @@ impl InitCommand { } else { let content = vfs.read_to_string_lf_normalized(&path)?; if let Some(file_stem) = path.file_name().and_then(OsStr::to_str) { - if file_stem == GIT_IGNORE_PLACEHOLDER { + if file_stem == GIT_IGNORE_PLACEHOLDER && !self.skip_git { path.set_file_name(".gitignore"); } } @@ -84,7 +91,7 @@ impl InitCommand { } } - if should_git_init(&base_path) { + if !self.skip_git && should_git_init(&base_path) { log::debug!("Initializing Git repository..."); let status = Command::new("git") From f3c423d77d61ae2baa5b9de0806a1e937de90517 Mon Sep 17 00:00:00 2001 From: Micah Date: Fri, 10 Oct 2025 13:00:56 -0700 Subject: [PATCH 19/22] Fix the various lints (#1124) --- crates/memofs/src/in_memory_fs.rs | 22 ++++++---------- crates/memofs/src/noop_backend.rs | 40 ++++++------------------------ crates/memofs/src/std_backend.rs | 6 ++--- src/cli/upload.rs | 29 +--------------------- src/snapshot/metadata.rs | 2 +- src/snapshot/tree.rs | 4 +-- src/snapshot_middleware/project.rs | 2 +- tests/rojo_test/serve_util.rs | 2 +- 8 files changed, 24 insertions(+), 83 deletions(-) diff --git a/crates/memofs/src/in_memory_fs.rs b/crates/memofs/src/in_memory_fs.rs index fc8cf2ef..7bc654af 100644 --- a/crates/memofs/src/in_memory_fs.rs +++ b/crates/memofs/src/in_memory_fs.rs @@ -228,23 +228,17 @@ impl VfsBackend for InMemoryFs { } fn must_be_file(path: &Path) -> io::Result { - Err(io::Error::new( - io::ErrorKind::Other, - format!( - "path {} was a directory, but must be a file", - path.display() - ), - )) + Err(io::Error::other(format!( + "path {} was a directory, but must be a file", + path.display() + ))) } fn must_be_dir(path: &Path) -> io::Result { - Err(io::Error::new( - io::ErrorKind::Other, - format!( - "path {} was a file, but must be a directory", - path.display() - ), - )) + Err(io::Error::other(format!( + "path {} was a file, but must be a directory", + path.display() + ))) } fn not_found(path: &Path) -> io::Result { diff --git a/crates/memofs/src/noop_backend.rs b/crates/memofs/src/noop_backend.rs index efc8fd4a..31a2cca9 100644 --- a/crates/memofs/src/noop_backend.rs +++ b/crates/memofs/src/noop_backend.rs @@ -15,45 +15,27 @@ impl NoopBackend { impl VfsBackend for NoopBackend { fn read(&mut self, _path: &Path) -> io::Result> { - Err(io::Error::new( - io::ErrorKind::Other, - "NoopBackend doesn't do anything", - )) + Err(io::Error::other("NoopBackend doesn't do anything")) } fn write(&mut self, _path: &Path, _data: &[u8]) -> io::Result<()> { - Err(io::Error::new( - io::ErrorKind::Other, - "NoopBackend doesn't do anything", - )) + Err(io::Error::other("NoopBackend doesn't do anything")) } fn read_dir(&mut self, _path: &Path) -> io::Result { - Err(io::Error::new( - io::ErrorKind::Other, - "NoopBackend doesn't do anything", - )) + Err(io::Error::other("NoopBackend doesn't do anything")) } fn remove_file(&mut self, _path: &Path) -> io::Result<()> { - Err(io::Error::new( - io::ErrorKind::Other, - "NoopBackend doesn't do anything", - )) + Err(io::Error::other("NoopBackend doesn't do anything")) } fn remove_dir_all(&mut self, _path: &Path) -> io::Result<()> { - Err(io::Error::new( - io::ErrorKind::Other, - "NoopBackend doesn't do anything", - )) + Err(io::Error::other("NoopBackend doesn't do anything")) } fn metadata(&mut self, _path: &Path) -> io::Result { - Err(io::Error::new( - io::ErrorKind::Other, - "NoopBackend doesn't do anything", - )) + Err(io::Error::other("NoopBackend doesn't do anything")) } fn event_receiver(&self) -> crossbeam_channel::Receiver { @@ -61,17 +43,11 @@ impl VfsBackend for NoopBackend { } fn watch(&mut self, _path: &Path) -> io::Result<()> { - Err(io::Error::new( - io::ErrorKind::Other, - "NoopBackend doesn't do anything", - )) + Err(io::Error::other("NoopBackend doesn't do anything")) } fn unwatch(&mut self, _path: &Path) -> io::Result<()> { - Err(io::Error::new( - io::ErrorKind::Other, - "NoopBackend doesn't do anything", - )) + Err(io::Error::other("NoopBackend doesn't do anything")) } } diff --git a/crates/memofs/src/std_backend.rs b/crates/memofs/src/std_backend.rs index 0a8fb3b7..54c142dc 100644 --- a/crates/memofs/src/std_backend.rs +++ b/crates/memofs/src/std_backend.rs @@ -109,15 +109,13 @@ impl VfsBackend for StdBackend { self.watches.insert(path.to_path_buf()); self.watcher .watch(path, RecursiveMode::Recursive) - .map_err(|inner| io::Error::new(io::ErrorKind::Other, inner)) + .map_err(io::Error::other) } } fn unwatch(&mut self, path: &Path) -> io::Result<()> { self.watches.remove(path); - self.watcher - .unwatch(path) - .map_err(|inner| io::Error::new(io::ErrorKind::Other, inner)) + self.watcher.unwatch(path).map_err(io::Error::other) } } diff --git a/src/cli/upload.rs b/src/cli/upload.rs index ce7a2b5d..3c3610fd 100644 --- a/src/cli/upload.rs +++ b/src/cli/upload.rs @@ -1,7 +1,6 @@ use std::path::PathBuf; -use std::str::FromStr; -use anyhow::{bail, format_err, Context}; +use anyhow::{bail, Context}; use clap::Parser; use memofs::Vfs; use reqwest::{ @@ -91,32 +90,6 @@ impl UploadCommand { } } -/// The kind of asset to upload to the website. Affects what endpoints Rojo uses -/// and changes how the asset is built. -#[derive(Debug, Clone, Copy)] -enum UploadKind { - /// Upload to a place. - Place, - - /// Upload to a model-like asset, like a Model, Plugin, or Package. - Model, -} - -impl FromStr for UploadKind { - type Err = anyhow::Error; - - fn from_str(source: &str) -> Result { - match source { - "place" => Ok(UploadKind::Place), - "model" => Ok(UploadKind::Model), - attempted => Err(format_err!( - "Invalid upload kind '{}'. Valid kinds are: place, model", - attempted - )), - } - } -} - fn do_upload(buffer: Vec, asset_id: u64, cookie: &str) -> anyhow::Result<()> { let url = format!( "https://data.roblox.com/Data/Upload.ashx?assetid={}", diff --git a/src/snapshot/metadata.rs b/src/snapshot/metadata.rs index abe66df2..20505f29 100644 --- a/src/snapshot/metadata.rs +++ b/src/snapshot/metadata.rs @@ -221,7 +221,7 @@ pub enum InstigatingSource { ProjectNode( #[serde(serialize_with = "path_serializer::serialize_absolute")] PathBuf, String, - ProjectNode, + Box, Option, ), } diff --git a/src/snapshot/tree.rs b/src/snapshot/tree.rs index c2cabac5..76bcdc07 100644 --- a/src/snapshot/tree.rs +++ b/src/snapshot/tree.rs @@ -73,7 +73,7 @@ impl RojoTree { self.inner.root_ref() } - pub fn get_instance(&self, id: Ref) -> Option { + pub fn get_instance(&self, id: Ref) -> Option> { if let Some(instance) = self.inner.get_by_ref(id) { let metadata = self.metadata_map.get(&id).unwrap(); @@ -83,7 +83,7 @@ impl RojoTree { } } - pub fn get_instance_mut(&mut self, id: Ref) -> Option { + pub fn get_instance_mut(&mut self, id: Ref) -> Option> { if let Some(instance) = self.inner.get_by_ref_mut(id) { let metadata = self.metadata_map.get_mut(&id).unwrap(); diff --git a/src/snapshot_middleware/project.rs b/src/snapshot_middleware/project.rs index a9816a73..819e57b3 100644 --- a/src/snapshot_middleware/project.rs +++ b/src/snapshot_middleware/project.rs @@ -289,7 +289,7 @@ pub fn snapshot_project_node( metadata.instigating_source = Some(InstigatingSource::ProjectNode( project_path.to_path_buf(), instance_name.to_string(), - node.clone(), + Box::new(node.clone()), parent_class.map(|name| name.to_owned()), )); diff --git a/tests/rojo_test/serve_util.rs b/tests/rojo_test/serve_util.rs index 71d07778..22cc630f 100644 --- a/tests/rojo_test/serve_util.rs +++ b/tests/rojo_test/serve_util.rs @@ -160,7 +160,7 @@ impl TestServeSession { Ok(serde_json::from_str(&body).expect("Server returned malformed response")) } - pub fn get_api_read(&self, id: Ref) -> Result { + pub fn get_api_read(&self, id: Ref) -> Result, reqwest::Error> { let url = format!("http://localhost:{}/api/read/{}", self.port, id); let body = reqwest::blocking::get(url)?.text()?; From 441c469966ded2c7b4a5f7b9aa18a4c8a27499b3 Mon Sep 17 00:00:00 2001 From: Micah Date: Fri, 10 Oct 2025 19:17:55 -0700 Subject: [PATCH 20/22] Release Rojo v7.6.0 (#1125) --- .github/workflows/ci.yml | 2 +- CHANGELOG.md | 3 +- Cargo.lock | 87 +- Cargo.toml | 14 +- README.md | 2 +- plugin/Version.txt | 2 +- plugin/rbx_dom_lua/EncodedValue.lua | 28 +- plugin/rbx_dom_lua/allValues.json | 3 +- plugin/rbx_dom_lua/database.json | 7981 ++++++++++++++++++++++++--- src/resolution.rs | 4 +- src/snapshot_middleware/lua.rs | 1 + src/snapshot_middleware/project.rs | 2 +- 12 files changed, 7426 insertions(+), 703 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3efb622d..868600e1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -60,7 +60,7 @@ jobs: submodules: true - name: Install Rust - uses: dtolnay/rust-toolchain@1.79.0 + uses: dtolnay/rust-toolchain@1.83.0 - name: Restore Rust Cache uses: actions/cache/restore@v4 diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a923798..4422d9be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,6 @@ # Rojo Changelog -## Unreleased - +## 7.6.0 - October 10th, 2025 * Added flag to `rojo init` to skip initializing a git repository ([#1122]) * Added fallback method for when an Instance can't be synced through normal means ([#1030]) This should make it possible to sync `MeshParts` and `Unions`! diff --git a/Cargo.lock b/Cargo.lock index 58a8649e..3e745ac5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -430,7 +430,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" dependencies = [ "cfg-if 0.1.10", - "dirs-sys", + "dirs-sys 0.3.7", +] + +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys 0.4.1", ] [[package]] @@ -444,6 +453,18 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "either" version = "1.10.0" @@ -1093,23 +1114,12 @@ dependencies = [ ] [[package]] -name = "lz4" -version = "1.24.0" +name = "lz4_flex" +version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e9e2dd86df36ce760a60f6ff6ad526f7ba1f14ba0356f8254fb6905e6494df1" +checksum = "08ab2867e3eeeca90e844d1940eab391c9dc5228783db2ed999acbc0a9ed375a" dependencies = [ - "libc", - "lz4-sys", -] - -[[package]] -name = "lz4-sys" -version = "1.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900" -dependencies = [ - "cc", - "libc", + "twox-hash", ] [[package]] @@ -1301,6 +1311,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "os_str_bytes" version = "6.6.1" @@ -1599,13 +1615,13 @@ dependencies = [ [[package]] name = "rbx_binary" -version = "1.0.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9573fee5e073d7b303f475c285197fdc8179468de66ca60ee115a58fbac99296" +checksum = "0d419f67c8012bf83569086e1208c541478b3b8e4f523deaa0b80d723fb5ef22" dependencies = [ "ahash", "log", - "lz4", + "lz4_flex", "profiling", "rbx_dom_weak", "rbx_reflection", @@ -1616,9 +1632,9 @@ dependencies = [ [[package]] name = "rbx_dom_weak" -version = "3.0.0" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04425cf6e9376e5486f4fb35906c120d1b1b45618a490318cf563fab1fa230a9" +checksum = "bc74878a4a801afc8014b14ede4b38015a13de5d29ab0095d5ed284a744253f6" dependencies = [ "ahash", "rbx_types", @@ -1628,9 +1644,9 @@ dependencies = [ [[package]] name = "rbx_reflection" -version = "5.0.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b6d0d62baa613556b058a5f94a53b01cf0ccde0ea327ce03056e335b982e77e" +checksum = "565dd3430991f35443fa6d23cc239fade2110c5089deb6bae5de77c400df4fd2" dependencies = [ "rbx_types", "serde", @@ -1639,11 +1655,12 @@ dependencies = [ [[package]] name = "rbx_reflection_database" -version = "1.0.3+roblox-670" +version = "2.0.0+roblox-694" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22c05ef92528c0fb0cc580592a65ca178d3ea9beb07a1d9ca0a2503c4f3721c" +checksum = "844ceb61f23bad59b06d7299b69ff276579316eafa9857981da3012a6223f663" dependencies = [ - "lazy_static", + "dirs 5.0.1", + "log", "rbx_reflection", "rmp-serde", "serde", @@ -1651,9 +1668,9 @@ dependencies = [ [[package]] name = "rbx_types" -version = "2.0.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78e4fdde46493def107e5f923d82e813dec9b3eef52c2f75fbad3a716023eda2" +checksum = "03220ffce2bd06ad04f77a003cb807f2e5b2a18e97623066a5ac735a978398af" dependencies = [ "base64 0.13.1", "bitflags 1.3.2", @@ -1666,9 +1683,9 @@ dependencies = [ [[package]] name = "rbx_xml" -version = "1.0.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb623833c31cc43bbdaeb32f5e91db8ecd63fc46e438d0d268baf9e61539cf1c" +checksum = "be6c302cefe9c92ed09bcbb075cd24379271de135b0af331409a64c2ea3646ee" dependencies = [ "ahash", "base64 0.13.1", @@ -1860,14 +1877,14 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "743bb8c693a387f1ae8d2026d82d8b0c175cc4777b97c1f7b12fdb3be595bb13" dependencies = [ - "dirs", + "dirs 2.0.2", "thiserror", "winreg 0.6.2", ] [[package]] name = "rojo" -version = "7.5.1" +version = "7.6.0" dependencies = [ "anyhow", "backtrace", @@ -2454,6 +2471,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "twox-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c" + [[package]] name = "typenum" version = "1.17.0" diff --git a/Cargo.toml b/Cargo.toml index e54ade8c..a3da7dc5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rojo" -version = "7.5.1" -rust-version = "1.79.0" +version = "7.6.0" +rust-version = "1.83" authors = [ "Lucien Greathouse ", "Micah Reid ", @@ -55,11 +55,11 @@ memofs = { version = "0.3.0", path = "crates/memofs" } # rbx_reflection_database = { path = "../rbx-dom/rbx_reflection_database" } # rbx_xml = { path = "../rbx-dom/rbx_xml" } -rbx_binary = "1.0.0" -rbx_dom_weak = "3.0.0" -rbx_reflection = "5.0.0" -rbx_reflection_database = "1.0.3" -rbx_xml = "1.0.0" +rbx_binary = "2.0.0" +rbx_dom_weak = "4.0.0" +rbx_reflection = "6.0.0" +rbx_reflection_database = "2.0.0" +rbx_xml = "2.0.0" anyhow = "1.0.80" backtrace = "0.3.69" diff --git a/README.md b/README.md index bb4effcf..3ddcdcd5 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ Check out our [contribution guide](CONTRIBUTING.md) for detailed instructions fo Pull requests are welcome! -Rojo supports Rust 1.70.0 and newer. The minimum supported version of Rust is based on the latest versions of the dependencies that Rojo has. +Rojo supports Rust 1.83 and newer. The minimum supported version of Rust is based on the latest versions of the dependencies that Rojo has. ## License Rojo is available under the terms of the Mozilla Public License, Version 2.0. See [LICENSE.txt](LICENSE.txt) for details. diff --git a/plugin/Version.txt b/plugin/Version.txt index 7501d508..8df288f2 100644 --- a/plugin/Version.txt +++ b/plugin/Version.txt @@ -1 +1 @@ -7.5.1 \ No newline at end of file +7.6.0 \ No newline at end of file diff --git a/plugin/rbx_dom_lua/EncodedValue.lua b/plugin/rbx_dom_lua/EncodedValue.lua index cfb10262..cddff6a3 100644 --- a/plugin/rbx_dom_lua/EncodedValue.lua +++ b/plugin/rbx_dom_lua/EncodedValue.lua @@ -378,13 +378,26 @@ types = { if pod == "Default" then return nil else - return PhysicalProperties.new( - pod.density, - pod.friction, - pod.elasticity, - pod.frictionWeight, - pod.elasticityWeight - ) + -- Passing `nil` instead of not passing anything gives + -- different results, so we have to branch here. + if pod.acousticAbsorption then + return (PhysicalProperties.new :: any)( + pod.density, + pod.friction, + pod.elasticity, + pod.frictionWeight, + pod.elasticityWeight, + pod.acousticAbsorption + ) + else + return PhysicalProperties.new( + pod.density, + pod.friction, + pod.elasticity, + pod.frictionWeight, + pod.elasticityWeight + ) + end end end, @@ -398,6 +411,7 @@ types = { elasticity = roblox.Elasticity, frictionWeight = roblox.FrictionWeight, elasticityWeight = roblox.ElasticityWeight, + acousticAbsorption = roblox.AcousticAbsorption, } end end, diff --git a/plugin/rbx_dom_lua/allValues.json b/plugin/rbx_dom_lua/allValues.json index cdf25acc..6c74956d 100644 --- a/plugin/rbx_dom_lua/allValues.json +++ b/plugin/rbx_dom_lua/allValues.json @@ -441,7 +441,8 @@ "friction": 1.0, "elasticity": 0.0, "frictionWeight": 50.0, - "elasticityWeight": 25.0 + "elasticityWeight": 25.0, + "acousticAbsorption": 0.15625 } }, "ty": "PhysicalProperties" diff --git a/plugin/rbx_dom_lua/database.json b/plugin/rbx_dom_lua/database.json index c07313d9..adae3c1c 100644 --- a/plugin/rbx_dom_lua/database.json +++ b/plugin/rbx_dom_lua/database.json @@ -1,9 +1,9 @@ { "Version": [ 0, - 670, + 694, 0, - 6700713 + 6940982 ], "Classes": { "Accessory": { @@ -1389,25 +1389,6 @@ } } }, - "AnalysticsSettings": { - "Name": "AnalysticsSettings", - "Tags": [ - "NotCreatable" - ], - "Superclass": "GenericSettings", - "Properties": {}, - "DefaultProperties": { - "Archivable": { - "Bool": true - }, - "HistoryId": { - "UniqueId": "00000000000000000000000000000000" - }, - "UniqueId": { - "UniqueId": "00000000000000000000000000000000" - } - } - }, "AnalyticsService": { "Name": "AnalyticsService", "Tags": [ @@ -2452,7 +2433,6 @@ "Value": "Bool" }, "Tags": [ - "NotBrowsable", "NotReplicated", "ReadOnly" ], @@ -2582,8 +2562,7 @@ "Value": "String" }, "Tags": [ - "Hidden", - "NotScriptable" + "Hidden" ], "Kind": { "Canonical": { @@ -2740,8 +2719,7 @@ "Enum": "AnnotationRequestStatus" }, "Tags": [ - "Hidden", - "NotReplicated" + "Hidden" ], "Kind": { "Canonical": { @@ -2804,8 +2782,7 @@ "Enum": "AnnotationRequestStatus" }, "Tags": [ - "Hidden", - "NotReplicated" + "Hidden" ], "Kind": { "Canonical": { @@ -2863,6 +2840,27 @@ } } }, + "AppRatingPromptService": { + "Name": "AppRatingPromptService", + "Tags": [ + "NotCreatable", + "NotReplicated", + "Service" + ], + "Superclass": "Instance", + "Properties": {}, + "DefaultProperties": { + "Archivable": { + "Bool": true + }, + "HistoryId": { + "UniqueId": "00000000000000000000000000000000" + }, + "UniqueId": { + "UniqueId": "00000000000000000000000000000000" + } + } + }, "AppStorageService": { "Name": "AppStorageService", "Tags": [ @@ -3262,8 +3260,25 @@ "Service" ], "Superclass": "Instance", - "Properties": {}, + "Properties": { + "AllowInsertFreeAssets": { + "Name": "AllowInsertFreeAssets", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + } + }, "DefaultProperties": { + "AllowInsertFreeAssets": { + "Bool": false + }, "Archivable": { "Bool": true }, @@ -4614,6 +4629,19 @@ "Tags": [], "Superclass": "Instance", "Properties": { + "AcousticSimulationEnabled": { + "Name": "AcousticSimulationEnabled", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "AngleAttenuation": { "Name": "AngleAttenuation", "Scriptability": "None", @@ -4653,13 +4681,28 @@ } } }, + "PositionOverride": { + "Name": "PositionOverride", + "Scriptability": "None", + "DataType": { + "Value": "Ref" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "SimulationFidelity": { "Name": "SimulationFidelity", "Scriptability": "ReadWrite", "DataType": { "Enum": "AudioSimulationFidelity" }, - "Tags": [], + "Tags": [ + "Deprecated" + ], "Kind": { "Canonical": { "Serialization": "Serializes" @@ -4668,6 +4711,9 @@ } }, "DefaultProperties": { + "AcousticSimulationEnabled": { + "Bool": true + }, "AngleAttenuation": { "BinaryString": "AA==" }, @@ -5148,6 +5194,106 @@ } } }, + "AudioGate": { + "Name": "AudioGate", + "Tags": [], + "Superclass": "Instance", + "Properties": { + "Attack": { + "Name": "Attack", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Float32" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Bypass": { + "Name": "Bypass", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Release": { + "Name": "Release", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Float32" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Threshold": { + "Name": "Threshold", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "NumberRange" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + } + }, + "DefaultProperties": { + "Archivable": { + "Bool": true + }, + "Attack": { + "Float32": 0.01 + }, + "Attributes": { + "Attributes": {} + }, + "Bypass": { + "Bool": false + }, + "Capabilities": { + "SecurityCapabilities": 0 + }, + "HistoryId": { + "UniqueId": "00000000000000000000000000000000" + }, + "Release": { + "Float32": 0.1 + }, + "Sandboxed": { + "Bool": false + }, + "SourceAssetId": { + "Int64": -1 + }, + "Tags": { + "Tags": [] + }, + "Threshold": { + "NumberRange": [ + -36.0, + -24.0 + ] + }, + "UniqueId": { + "UniqueId": "00000000000000000000000000000000" + } + } + }, "AudioLimiter": { "Name": "AudioLimiter", "Tags": [], @@ -5249,6 +5395,19 @@ "Tags": [], "Superclass": "Instance", "Properties": { + "AcousticSimulationEnabled": { + "Name": "AcousticSimulationEnabled", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "AngleAttenuation": { "Name": "AngleAttenuation", "Scriptability": "None", @@ -5288,13 +5447,28 @@ } } }, + "PositionOverride": { + "Name": "PositionOverride", + "Scriptability": "None", + "DataType": { + "Value": "Ref" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "SimulationFidelity": { "Name": "SimulationFidelity", "Scriptability": "ReadWrite", "DataType": { "Enum": "AudioSimulationFidelity" }, - "Tags": [], + "Tags": [ + "Deprecated" + ], "Kind": { "Canonical": { "Serialization": "Serializes" @@ -5303,6 +5477,9 @@ } }, "DefaultProperties": { + "AcousticSimulationEnabled": { + "Bool": true + }, "AngleAttenuation": { "BinaryString": "AA==" }, @@ -5456,7 +5633,12 @@ "Tags": [], "Kind": { "Canonical": { - "Serialization": "Serializes" + "Serialization": { + "Migrate": { + "To": "AudioContent", + "Migration": "ContentIdToContent" + } + } } } }, @@ -5477,6 +5659,19 @@ } } }, + "AudioContent": { + "Name": "AudioContent", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Content" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "AutoLoad": { "Name": "AutoLoad", "Scriptability": "ReadWrite", @@ -5490,6 +5685,19 @@ } } }, + "IsMutedForCapture": { + "Name": "IsMutedForCapture", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "IsPlaying": { "Name": "IsPlaying", "Scriptability": "Read", @@ -5618,12 +5826,12 @@ "Archivable": { "Bool": true }, - "Asset": { - "ContentId": "" - }, "Attributes": { "Attributes": {} }, + "AudioContent": { + "Content": "None" + }, "AutoLoad": { "Bool": true }, @@ -5633,6 +5841,9 @@ "HistoryId": { "UniqueId": "00000000000000000000000000000000" }, + "IsMutedForCapture": { + "Bool": false + }, "LoopRegion": { "NumberRange": [ 0.0, @@ -5671,6 +5882,73 @@ } } }, + "AudioRecorder": { + "Name": "AudioRecorder", + "Tags": [ + "NotBrowsable" + ], + "Superclass": "Instance", + "Properties": { + "IsRecording": { + "Name": "IsRecording", + "Scriptability": "Read", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "TimeLength": { + "Name": "TimeLength", + "Scriptability": "Read", + "DataType": { + "Value": "Float64" + }, + "Tags": [ + "NotReplicated", + "ReadOnly" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + } + }, + "DefaultProperties": { + "Archivable": { + "Bool": true + }, + "Attributes": { + "Attributes": {} + }, + "Capabilities": { + "SecurityCapabilities": 0 + }, + "HistoryId": { + "UniqueId": "00000000000000000000000000000000" + }, + "IsRecording": { + "Bool": false + }, + "Sandboxed": { + "Bool": false + }, + "SourceAssetId": { + "Int64": -1 + }, + "Tags": { + "Tags": [] + }, + "UniqueId": { + "UniqueId": "00000000000000000000000000000000" + } + } + }, "AudioReverb": { "Name": "AudioReverb", "Tags": [], @@ -6052,12 +6330,93 @@ } } }, - "AudioTextToSpeech": { - "Name": "AudioTextToSpeech", + "AudioSpeechToText": { + "Name": "AudioSpeechToText", "Tags": [ "NotBrowsable" ], "Superclass": "Instance", + "Properties": { + "Enabled": { + "Name": "Enabled", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Text": { + "Name": "Text", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "String" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "VoiceDetected": { + "Name": "VoiceDetected", + "Scriptability": "Read", + "DataType": { + "Value": "Bool" + }, + "Tags": [ + "NotReplicated", + "ReadOnly" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + } + }, + "DefaultProperties": { + "Archivable": { + "Bool": true + }, + "Attributes": { + "Attributes": {} + }, + "Capabilities": { + "SecurityCapabilities": 0 + }, + "Enabled": { + "Bool": false + }, + "HistoryId": { + "UniqueId": "00000000000000000000000000000000" + }, + "Sandboxed": { + "Bool": false + }, + "SourceAssetId": { + "Int64": -1 + }, + "Tags": { + "Tags": [] + }, + "Text": { + "String": "" + }, + "UniqueId": { + "UniqueId": "00000000000000000000000000000000" + } + } + }, + "AudioTextToSpeech": { + "Name": "AudioTextToSpeech", + "Tags": [], + "Superclass": "Instance", "Properties": { "IsLoaded": { "Name": "IsLoaded", @@ -6260,6 +6619,151 @@ } } }, + "AudioTremolo": { + "Name": "AudioTremolo", + "Tags": [], + "Superclass": "Instance", + "Properties": { + "Bypass": { + "Name": "Bypass", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Depth": { + "Name": "Depth", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Float32" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Duty": { + "Name": "Duty", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Float32" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Frequency": { + "Name": "Frequency", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Float32" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Shape": { + "Name": "Shape", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Float32" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Skew": { + "Name": "Skew", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Float32" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Square": { + "Name": "Square", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Float32" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + } + }, + "DefaultProperties": { + "Archivable": { + "Bool": true + }, + "Attributes": { + "Attributes": {} + }, + "Bypass": { + "Bool": false + }, + "Capabilities": { + "SecurityCapabilities": 0 + }, + "Depth": { + "Float32": 1.0 + }, + "Duty": { + "Float32": 0.5 + }, + "Frequency": { + "Float32": 5.0 + }, + "HistoryId": { + "UniqueId": "00000000000000000000000000000000" + }, + "Sandboxed": { + "Bool": false + }, + "Shape": { + "Float32": 0.0 + }, + "Skew": { + "Float32": 0.0 + }, + "SourceAssetId": { + "Int64": -1 + }, + "Square": { + "Float32": 0.0 + }, + "Tags": { + "Tags": [] + }, + "UniqueId": { + "UniqueId": "00000000000000000000000000000000" + } + } + }, "AuroraScript": { "Name": "AuroraScript", "Tags": [ @@ -6267,6 +6771,23 @@ ], "Superclass": "LuaSourceContainer", "Properties": { + "AuroraScriptBindingsSerialize": { + "Name": "AuroraScriptBindingsSerialize", + "Scriptability": "None", + "DataType": { + "Value": "BinaryString" + }, + "Tags": [ + "Hidden", + "NotReplicated", + "NotScriptable" + ], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "EnableCulling": { "Name": "EnableCulling", "Scriptability": "ReadWrite", @@ -6331,19 +6852,6 @@ "Serialization": "Serializes" } } - }, - "Tag": { - "Name": "Tag", - "Scriptability": "ReadWrite", - "DataType": { - "Value": "String" - }, - "Tags": [], - "Kind": { - "Canonical": { - "Serialization": "Serializes" - } - } } }, "DefaultProperties": { @@ -6361,17 +6869,84 @@ } } }, - "AuroraScriptService": { - "Name": "AuroraScriptService", + "AuroraScriptObject": { + "Name": "AuroraScriptObject", "Tags": [ "Deprecated", - "NotCreatable", - "Service" + "NotCreatable" ], "Superclass": "Instance", "Properties": { - "BufferSize": { - "Name": "BufferSize", + "BehaviorWeak": { + "Name": "BehaviorWeak", + "Scriptability": "None", + "DataType": { + "Value": "Ref" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "BoundInstanceWeak": { + "Name": "BoundInstanceWeak", + "Scriptability": "None", + "DataType": { + "Value": "Ref" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "FrameId": { + "Name": "FrameId", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Int32" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "LODLevel": { + "Name": "LODLevel", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Int32" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "MaxFrequency": { + "Name": "MaxFrequency", + "Scriptability": "None", + "DataType": { + "Value": "Int32" + }, + "Tags": [ + "Hidden", + "NotScriptable" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, + "PriorFrameInvoked": { + "Name": "PriorFrameInvoked", "Scriptability": "ReadWrite", "DataType": { "Value": "Int32" @@ -6396,8 +6971,8 @@ } } }, - "AuroraService": { - "Name": "AuroraService", + "AuroraScriptService": { + "Name": "AuroraScriptService", "Tags": [ "Deprecated", "NotCreatable", @@ -6417,6 +6992,1497 @@ } } }, + "AuroraService": { + "Name": "AuroraService", + "Tags": [ + "Deprecated", + "NotCreatable", + "Service" + ], + "Superclass": "Instance", + "Properties": { + "BufferFullInputCount": { + "Name": "BufferFullInputCount", + "Scriptability": "None", + "DataType": { + "Value": "Int32" + }, + "Tags": [ + "Hidden", + "NotScriptable" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, + "HashRoundingPoint": { + "Name": "HashRoundingPoint", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Float64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "IgnoreRotation": { + "Name": "IgnoreRotation", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "InputDropRate": { + "Name": "InputDropRate", + "Scriptability": "None", + "DataType": { + "Value": "Float32" + }, + "Tags": [ + "Hidden", + "NotScriptable" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, + "LockStepIdOffset": { + "Name": "LockStepIdOffset", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "OutOfOrderInputCount": { + "Name": "OutOfOrderInputCount", + "Scriptability": "None", + "DataType": { + "Value": "Int32" + }, + "Tags": [ + "Hidden", + "NotScriptable" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, + "RCCHeartbeatFPS": { + "Name": "RCCHeartbeatFPS", + "Scriptability": "None", + "DataType": { + "Value": "Float64" + }, + "Tags": [ + "Hidden", + "NotScriptable" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, + "RollbackOffset": { + "Name": "RollbackOffset", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Int32" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "TooOldInputCount": { + "Name": "TooOldInputCount", + "Scriptability": "None", + "DataType": { + "Value": "Int32" + }, + "Tags": [ + "Hidden", + "NotScriptable" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + } + }, + "DefaultProperties": { + "Archivable": { + "Bool": true + }, + "HistoryId": { + "UniqueId": "00000000000000000000000000000000" + }, + "UniqueId": { + "UniqueId": "00000000000000000000000000000000" + } + } + }, + "AvatarAccessoryRules": { + "Name": "AvatarAccessoryRules", + "Tags": [], + "Superclass": "Instance", + "Properties": { + "AccessoryMode": { + "Name": "AccessoryMode", + "Scriptability": "None", + "DataType": { + "Enum": "AvatarSettingsAccessoryMode" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomAccessoryMode": { + "Name": "CustomAccessoryMode", + "Scriptability": "None", + "DataType": { + "Enum": "AvatarSettingsCustomAccessoryMode" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomBackAccessoryEnabled": { + "Name": "CustomBackAccessoryEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomBackAccessoryId": { + "Name": "CustomBackAccessoryId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomFaceAccessoryEnabled": { + "Name": "CustomFaceAccessoryEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomFaceAccessoryId": { + "Name": "CustomFaceAccessoryId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomFrontAccessoryEnabled": { + "Name": "CustomFrontAccessoryEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomFrontAccessoryId": { + "Name": "CustomFrontAccessoryId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomHairAccessoryEnabled": { + "Name": "CustomHairAccessoryEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomHairAccessoryId": { + "Name": "CustomHairAccessoryId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomHeadAccessoryEnabled": { + "Name": "CustomHeadAccessoryEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomHeadAccessoryId": { + "Name": "CustomHeadAccessoryId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomNeckAccessoryEnabled": { + "Name": "CustomNeckAccessoryEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomNeckAccessoryId": { + "Name": "CustomNeckAccessoryId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomShoulderAccessoryEnabled": { + "Name": "CustomShoulderAccessoryEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomShoulderAccessoryId": { + "Name": "CustomShoulderAccessoryId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomWaistAccessoryEnabled": { + "Name": "CustomWaistAccessoryEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomWaistAccessoryId": { + "Name": "CustomWaistAccessoryId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "EnableSound": { + "Name": "EnableSound", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "EnableVFX": { + "Name": "EnableVFX", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "LimitBounds": { + "Name": "LimitBounds", + "Scriptability": "None", + "DataType": { + "Value": "Vector3" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "LimitMethod": { + "Name": "LimitMethod", + "Scriptability": "None", + "DataType": { + "Enum": "AvatarSettingsAccessoryLimitMethod" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + } + }, + "DefaultProperties": { + "AccessoryMode": { + "Enum": 0 + }, + "Archivable": { + "Bool": true + }, + "Attributes": { + "Attributes": {} + }, + "Capabilities": { + "SecurityCapabilities": 0 + }, + "CustomAccessoryMode": { + "Enum": 0 + }, + "CustomBackAccessoryEnabled": { + "Bool": false + }, + "CustomBackAccessoryId": { + "Int64": 0 + }, + "CustomFaceAccessoryEnabled": { + "Bool": false + }, + "CustomFaceAccessoryId": { + "Int64": 0 + }, + "CustomFrontAccessoryEnabled": { + "Bool": false + }, + "CustomFrontAccessoryId": { + "Int64": 0 + }, + "CustomHairAccessoryEnabled": { + "Bool": false + }, + "CustomHairAccessoryId": { + "Int64": 0 + }, + "CustomHeadAccessoryEnabled": { + "Bool": false + }, + "CustomHeadAccessoryId": { + "Int64": 0 + }, + "CustomNeckAccessoryEnabled": { + "Bool": false + }, + "CustomNeckAccessoryId": { + "Int64": 0 + }, + "CustomShoulderAccessoryEnabled": { + "Bool": false + }, + "CustomShoulderAccessoryId": { + "Int64": 0 + }, + "CustomWaistAccessoryEnabled": { + "Bool": false + }, + "CustomWaistAccessoryId": { + "Int64": 0 + }, + "EnableSound": { + "Bool": true + }, + "EnableVFX": { + "Bool": true + }, + "HistoryId": { + "UniqueId": "00000000000000000000000000000000" + }, + "LimitBounds": { + "Vector3": [ + 0.0, + 0.0, + 0.0 + ] + }, + "LimitMethod": { + "Enum": 1 + }, + "Sandboxed": { + "Bool": false + }, + "SourceAssetId": { + "Int64": -1 + }, + "Tags": { + "Tags": [] + }, + "UniqueId": { + "UniqueId": "00000000000000000000000000000000" + } + } + }, + "AvatarAnimationRules": { + "Name": "AvatarAnimationRules", + "Tags": [], + "Superclass": "Instance", + "Properties": { + "AnimationClipsMode": { + "Name": "AnimationClipsMode", + "Scriptability": "None", + "DataType": { + "Enum": "AvatarSettingsAnimationClipsMode" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "AnimationPacksMode": { + "Name": "AnimationPacksMode", + "Scriptability": "None", + "DataType": { + "Enum": "AvatarSettingsAnimationPacksMode" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomClimbAnimationEnabled": { + "Name": "CustomClimbAnimationEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomClimbAnimationId": { + "Name": "CustomClimbAnimationId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomFallAnimationEnabled": { + "Name": "CustomFallAnimationEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomFallAnimationId": { + "Name": "CustomFallAnimationId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomIdleAlt1AnimationEnabled": { + "Name": "CustomIdleAlt1AnimationEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomIdleAlt1AnimationId": { + "Name": "CustomIdleAlt1AnimationId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomIdleAlt2AnimationEnabled": { + "Name": "CustomIdleAlt2AnimationEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomIdleAlt2AnimationId": { + "Name": "CustomIdleAlt2AnimationId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomIdleAnimationEnabled": { + "Name": "CustomIdleAnimationEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomIdleAnimationId": { + "Name": "CustomIdleAnimationId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomJumpAnimationEnabled": { + "Name": "CustomJumpAnimationEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomJumpAnimationId": { + "Name": "CustomJumpAnimationId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomRunAnimationEnabled": { + "Name": "CustomRunAnimationEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomRunAnimationId": { + "Name": "CustomRunAnimationId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomSwimAnimationEnabled": { + "Name": "CustomSwimAnimationEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomSwimAnimationId": { + "Name": "CustomSwimAnimationId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomSwimIdleAnimationEnabled": { + "Name": "CustomSwimIdleAnimationEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomSwimIdleAnimationId": { + "Name": "CustomSwimIdleAnimationId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomWalkAnimationEnabled": { + "Name": "CustomWalkAnimationEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomWalkAnimationId": { + "Name": "CustomWalkAnimationId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + } + }, + "DefaultProperties": { + "AnimationClipsMode": { + "Enum": 0 + }, + "AnimationPacksMode": { + "Enum": 0 + }, + "Archivable": { + "Bool": true + }, + "Attributes": { + "Attributes": {} + }, + "Capabilities": { + "SecurityCapabilities": 0 + }, + "CustomClimbAnimationEnabled": { + "Bool": false + }, + "CustomClimbAnimationId": { + "Int64": 0 + }, + "CustomFallAnimationEnabled": { + "Bool": false + }, + "CustomFallAnimationId": { + "Int64": 0 + }, + "CustomIdleAlt1AnimationEnabled": { + "Bool": false + }, + "CustomIdleAlt1AnimationId": { + "Int64": 0 + }, + "CustomIdleAlt2AnimationEnabled": { + "Bool": false + }, + "CustomIdleAlt2AnimationId": { + "Int64": 0 + }, + "CustomIdleAnimationEnabled": { + "Bool": false + }, + "CustomIdleAnimationId": { + "Int64": 0 + }, + "CustomJumpAnimationEnabled": { + "Bool": false + }, + "CustomJumpAnimationId": { + "Int64": 0 + }, + "CustomRunAnimationEnabled": { + "Bool": false + }, + "CustomRunAnimationId": { + "Int64": 0 + }, + "CustomSwimAnimationEnabled": { + "Bool": false + }, + "CustomSwimAnimationId": { + "Int64": 0 + }, + "CustomSwimIdleAnimationEnabled": { + "Bool": false + }, + "CustomSwimIdleAnimationId": { + "Int64": 0 + }, + "CustomWalkAnimationEnabled": { + "Bool": false + }, + "CustomWalkAnimationId": { + "Int64": 0 + }, + "HistoryId": { + "UniqueId": "00000000000000000000000000000000" + }, + "Sandboxed": { + "Bool": false + }, + "SourceAssetId": { + "Int64": -1 + }, + "Tags": { + "Tags": [] + }, + "UniqueId": { + "UniqueId": "00000000000000000000000000000000" + } + } + }, + "AvatarBodyRules": { + "Name": "AvatarBodyRules", + "Tags": [], + "Superclass": "Instance", + "Properties": { + "AppearanceMode": { + "Name": "AppearanceMode", + "Scriptability": "None", + "DataType": { + "Enum": "AvatarSettingsAppearanceMode" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "BuildMode": { + "Name": "BuildMode", + "Scriptability": "None", + "DataType": { + "Enum": "AvatarSettingsBuildMode" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomBodyBundleId": { + "Name": "CustomBodyBundleId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomBodyType": { + "Name": "CustomBodyType", + "Scriptability": "None", + "DataType": { + "Enum": "AvatarSettingsCustomBodyType" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomBodyTypeScale": { + "Name": "CustomBodyTypeScale", + "Scriptability": "None", + "DataType": { + "Value": "NumberRange" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomEyebrowEnabled": { + "Name": "CustomEyebrowEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomEyebrowId": { + "Name": "CustomEyebrowId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomEyelashEnabled": { + "Name": "CustomEyelashEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomEyelashId": { + "Name": "CustomEyelashId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomFaceEnabled": { + "Name": "CustomFaceEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomFaceId": { + "Name": "CustomFaceId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomHeadEnabled": { + "Name": "CustomHeadEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomHeadId": { + "Name": "CustomHeadId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomHeadScale": { + "Name": "CustomHeadScale", + "Scriptability": "None", + "DataType": { + "Value": "NumberRange" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomHeight": { + "Name": "CustomHeight", + "Scriptability": "None", + "DataType": { + "Value": "NumberRange" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomHeightScale": { + "Name": "CustomHeightScale", + "Scriptability": "None", + "DataType": { + "Value": "NumberRange" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomLeftArmEnabled": { + "Name": "CustomLeftArmEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomLeftArmId": { + "Name": "CustomLeftArmId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomLeftLegEnabled": { + "Name": "CustomLeftLegEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomLeftLegId": { + "Name": "CustomLeftLegId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomMoodEnabled": { + "Name": "CustomMoodEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomMoodId": { + "Name": "CustomMoodId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomProportionsScale": { + "Name": "CustomProportionsScale", + "Scriptability": "None", + "DataType": { + "Value": "NumberRange" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomRightArmEnabled": { + "Name": "CustomRightArmEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomRightArmId": { + "Name": "CustomRightArmId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomRightLegEnabled": { + "Name": "CustomRightLegEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomRightLegId": { + "Name": "CustomRightLegId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomTorsoEnabled": { + "Name": "CustomTorsoEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomTorsoId": { + "Name": "CustomTorsoId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomWidthScale": { + "Name": "CustomWidthScale", + "Scriptability": "None", + "DataType": { + "Value": "NumberRange" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "KeepPlayerHead": { + "Name": "KeepPlayerHead", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "ScaleMode": { + "Name": "ScaleMode", + "Scriptability": "None", + "DataType": { + "Enum": "AvatarSettingsScaleMode" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + } + }, + "DefaultProperties": { + "AppearanceMode": { + "Enum": 0 + }, + "Archivable": { + "Bool": true + }, + "Attributes": { + "Attributes": {} + }, + "BuildMode": { + "Enum": 0 + }, + "Capabilities": { + "SecurityCapabilities": 0 + }, + "CustomBodyBundleId": { + "Int64": 0 + }, + "CustomBodyType": { + "Enum": 0 + }, + "CustomBodyTypeScale": { + "NumberRange": [ + 0.0, + 1.0 + ] + }, + "CustomEyebrowEnabled": { + "Bool": false + }, + "CustomEyebrowId": { + "Int64": 0 + }, + "CustomEyelashEnabled": { + "Bool": false + }, + "CustomEyelashId": { + "Int64": 0 + }, + "CustomFaceEnabled": { + "Bool": false + }, + "CustomFaceId": { + "Int64": 0 + }, + "CustomHeadEnabled": { + "Bool": false + }, + "CustomHeadId": { + "Int64": 0 + }, + "CustomHeadScale": { + "NumberRange": [ + 0.95, + 1.0 + ] + }, + "CustomHeight": { + "NumberRange": [ + 5.5, + 5.5 + ] + }, + "CustomHeightScale": { + "NumberRange": [ + 0.9, + 1.05 + ] + }, + "CustomLeftArmEnabled": { + "Bool": false + }, + "CustomLeftArmId": { + "Int64": 0 + }, + "CustomLeftLegEnabled": { + "Bool": false + }, + "CustomLeftLegId": { + "Int64": 0 + }, + "CustomMoodEnabled": { + "Bool": false + }, + "CustomMoodId": { + "Int64": 0 + }, + "CustomProportionsScale": { + "NumberRange": [ + 0.0, + 1.0 + ] + }, + "CustomRightArmEnabled": { + "Bool": false + }, + "CustomRightArmId": { + "Int64": 0 + }, + "CustomRightLegEnabled": { + "Bool": false + }, + "CustomRightLegId": { + "Int64": 0 + }, + "CustomTorsoEnabled": { + "Bool": false + }, + "CustomTorsoId": { + "Int64": 0 + }, + "CustomWidthScale": { + "NumberRange": [ + 0.7, + 1.0 + ] + }, + "HistoryId": { + "UniqueId": "00000000000000000000000000000000" + }, + "KeepPlayerHead": { + "Bool": true + }, + "Sandboxed": { + "Bool": false + }, + "ScaleMode": { + "Enum": 0 + }, + "SourceAssetId": { + "Int64": -1 + }, + "Tags": { + "Tags": [] + }, + "UniqueId": { + "UniqueId": "00000000000000000000000000000000" + } + } + }, "AvatarChatService": { "Name": "AvatarChatService", "Tags": [ @@ -6487,6 +8553,576 @@ } } }, + "AvatarClothingRules": { + "Name": "AvatarClothingRules", + "Tags": [], + "Superclass": "Instance", + "Properties": { + "ClothingMode": { + "Name": "ClothingMode", + "Scriptability": "None", + "DataType": { + "Enum": "AvatarSettingsClothingMode" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomClassicPantsAccessoryEnabled": { + "Name": "CustomClassicPantsAccessoryEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomClassicPantsAccessoryId": { + "Name": "CustomClassicPantsAccessoryId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomClassicShirtsAccessoryEnabled": { + "Name": "CustomClassicShirtsAccessoryEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomClassicShirtsAccessoryId": { + "Name": "CustomClassicShirtsAccessoryId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomClassicTShirtsAccessoryEnabled": { + "Name": "CustomClassicTShirtsAccessoryEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomClassicTShirtsAccessoryId": { + "Name": "CustomClassicTShirtsAccessoryId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomClothingMode": { + "Name": "CustomClothingMode", + "Scriptability": "None", + "DataType": { + "Enum": "AvatarSettingsCustomClothingMode" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomDressSkirtAccessoryEnabled": { + "Name": "CustomDressSkirtAccessoryEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomDressSkirtAccessoryId": { + "Name": "CustomDressSkirtAccessoryId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomJacketAccessoryEnabled": { + "Name": "CustomJacketAccessoryEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomJacketAccessoryId": { + "Name": "CustomJacketAccessoryId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomLeftShoesAccessoryEnabled": { + "Name": "CustomLeftShoesAccessoryEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomLeftShoesAccessoryId": { + "Name": "CustomLeftShoesAccessoryId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomPantsAccessoryEnabled": { + "Name": "CustomPantsAccessoryEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomPantsAccessoryId": { + "Name": "CustomPantsAccessoryId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomRightShoesAccessoryEnabled": { + "Name": "CustomRightShoesAccessoryEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomRightShoesAccessoryId": { + "Name": "CustomRightShoesAccessoryId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomShirtAccessoryEnabled": { + "Name": "CustomShirtAccessoryEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomShirtAccessoryId": { + "Name": "CustomShirtAccessoryId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomShortsAccessoryEnabled": { + "Name": "CustomShortsAccessoryEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomShortsAccessoryId": { + "Name": "CustomShortsAccessoryId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomSweaterAccessoryEnabled": { + "Name": "CustomSweaterAccessoryEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomSweaterAccessoryId": { + "Name": "CustomSweaterAccessoryId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomTShirtAccessoryEnabled": { + "Name": "CustomTShirtAccessoryEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "CustomTShirtAccessoryId": { + "Name": "CustomTShirtAccessoryId", + "Scriptability": "None", + "DataType": { + "Value": "Int64" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "LimitBounds": { + "Name": "LimitBounds", + "Scriptability": "None", + "DataType": { + "Value": "Vector3" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + } + }, + "DefaultProperties": { + "Archivable": { + "Bool": true + }, + "Attributes": { + "Attributes": {} + }, + "Capabilities": { + "SecurityCapabilities": 0 + }, + "ClothingMode": { + "Enum": 0 + }, + "CustomClassicPantsAccessoryEnabled": { + "Bool": false + }, + "CustomClassicPantsAccessoryId": { + "Int64": 0 + }, + "CustomClassicShirtsAccessoryEnabled": { + "Bool": false + }, + "CustomClassicShirtsAccessoryId": { + "Int64": 0 + }, + "CustomClassicTShirtsAccessoryEnabled": { + "Bool": false + }, + "CustomClassicTShirtsAccessoryId": { + "Int64": 0 + }, + "CustomClothingMode": { + "Enum": 0 + }, + "CustomDressSkirtAccessoryEnabled": { + "Bool": false + }, + "CustomDressSkirtAccessoryId": { + "Int64": 0 + }, + "CustomJacketAccessoryEnabled": { + "Bool": false + }, + "CustomJacketAccessoryId": { + "Int64": 0 + }, + "CustomLeftShoesAccessoryEnabled": { + "Bool": false + }, + "CustomLeftShoesAccessoryId": { + "Int64": 0 + }, + "CustomPantsAccessoryEnabled": { + "Bool": false + }, + "CustomPantsAccessoryId": { + "Int64": 0 + }, + "CustomRightShoesAccessoryEnabled": { + "Bool": false + }, + "CustomRightShoesAccessoryId": { + "Int64": 0 + }, + "CustomShirtAccessoryEnabled": { + "Bool": false + }, + "CustomShirtAccessoryId": { + "Int64": 0 + }, + "CustomShortsAccessoryEnabled": { + "Bool": false + }, + "CustomShortsAccessoryId": { + "Int64": 0 + }, + "CustomSweaterAccessoryEnabled": { + "Bool": false + }, + "CustomSweaterAccessoryId": { + "Int64": 0 + }, + "CustomTShirtAccessoryEnabled": { + "Bool": false + }, + "CustomTShirtAccessoryId": { + "Int64": 0 + }, + "HistoryId": { + "UniqueId": "00000000000000000000000000000000" + }, + "LimitBounds": { + "Vector3": [ + 0.0, + 0.0, + 0.0 + ] + }, + "Sandboxed": { + "Bool": false + }, + "SourceAssetId": { + "Int64": -1 + }, + "Tags": { + "Tags": [] + }, + "UniqueId": { + "UniqueId": "00000000000000000000000000000000" + } + } + }, + "AvatarCollisionRules": { + "Name": "AvatarCollisionRules", + "Tags": [], + "Superclass": "Instance", + "Properties": { + "CollisionMode": { + "Name": "CollisionMode", + "Scriptability": "None", + "DataType": { + "Enum": "AvatarSettingsCollisionMode" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "HitAndTouchDetectionMode": { + "Name": "HitAndTouchDetectionMode", + "Scriptability": "None", + "DataType": { + "Enum": "AvatarSettingsHitAndTouchDetectionMode" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "LegacyCollisionMode": { + "Name": "LegacyCollisionMode", + "Scriptability": "None", + "DataType": { + "Enum": "AvatarSettingsLegacyCollisionMode" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "SingleColliderSize": { + "Name": "SingleColliderSize", + "Scriptability": "None", + "DataType": { + "Value": "Vector3" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + } + }, + "DefaultProperties": { + "Archivable": { + "Bool": true + }, + "Attributes": { + "Attributes": {} + }, + "Capabilities": { + "SecurityCapabilities": 0 + }, + "CollisionMode": { + "Enum": 0 + }, + "HistoryId": { + "UniqueId": "00000000000000000000000000000000" + }, + "HitAndTouchDetectionMode": { + "Enum": 0 + }, + "LegacyCollisionMode": { + "Enum": 1 + }, + "Sandboxed": { + "Bool": false + }, + "SingleColliderSize": { + "Vector3": [ + 2.0, + 3.0, + 1.0 + ] + }, + "SourceAssetId": { + "Int64": -1 + }, + "Tags": { + "Tags": [] + }, + "UniqueId": { + "UniqueId": "00000000000000000000000000000000" + } + } + }, "AvatarCreationService": { "Name": "AvatarCreationService", "Tags": [ @@ -6549,22 +9185,99 @@ } } }, - "AvatarPreloader": { - "Name": "AvatarPreloader", - "Tags": [ - "NotCreatable", - "NotReplicated", - "Service" - ], + "AvatarRules": { + "Name": "AvatarRules", + "Tags": [], "Superclass": "Instance", - "Properties": {}, + "Properties": { + "AvatarType": { + "Name": "AvatarType", + "Scriptability": "None", + "DataType": { + "Enum": "GameAvatarType" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + } + }, "DefaultProperties": { "Archivable": { "Bool": true }, + "Attributes": { + "Attributes": {} + }, + "AvatarType": { + "Enum": 1 + }, + "Capabilities": { + "SecurityCapabilities": 0 + }, "HistoryId": { "UniqueId": "00000000000000000000000000000000" }, + "Sandboxed": { + "Bool": false + }, + "SourceAssetId": { + "Int64": -1 + }, + "Tags": { + "Tags": [] + }, + "UniqueId": { + "UniqueId": "00000000000000000000000000000000" + } + } + }, + "AvatarSettings": { + "Name": "AvatarSettings", + "Tags": [ + "NotCreatable", + "Service" + ], + "Superclass": "Instance", + "Properties": { + "Loaded": { + "Name": "Loaded", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + } + }, + "DefaultProperties": { + "Archivable": { + "Bool": true + }, + "Attributes": { + "Attributes": {} + }, + "Capabilities": { + "SecurityCapabilities": 0 + }, + "HistoryId": { + "UniqueId": "00000000000000000000000000000000" + }, + "Sandboxed": { + "Bool": false + }, + "SourceAssetId": { + "Int64": -1 + }, + "Tags": { + "Tags": [] + }, "UniqueId": { "UniqueId": "00000000000000000000000000000000" } @@ -6609,6 +9322,19 @@ ], "Superclass": "Model", "Properties": { + "TextureContent": { + "Name": "TextureContent", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Content" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "TextureId": { "Name": "TextureId", "Scriptability": "ReadWrite", @@ -6618,7 +9344,12 @@ "Tags": [], "Kind": { "Canonical": { - "Serialization": "Serializes" + "Serialization": { + "Migrate": { + "To": "TextureContent", + "Migration": "ContentIdToContent" + } + } } } } @@ -8351,9 +11082,7 @@ "DataType": { "Value": "ContentId" }, - "Tags": [ - "NotScriptable" - ], + "Tags": [], "Kind": { "Canonical": { "Serialization": "Serializes" @@ -9007,7 +11736,9 @@ "DataType": { "Value": "Float32" }, - "Tags": [], + "Tags": [ + "Deprecated" + ], "Kind": { "Canonical": { "Serialization": "Serializes" @@ -9020,10 +11751,12 @@ "DataType": { "Value": "Float32" }, - "Tags": [], + "Tags": [ + "NotReplicated" + ], "Kind": { "Canonical": { - "Serialization": "Serializes" + "Serialization": "DoesNotSerialize" } } }, @@ -9033,7 +11766,9 @@ "DataType": { "Value": "Float32" }, - "Tags": [], + "Tags": [ + "Deprecated" + ], "Kind": { "Canonical": { "Serialization": "Serializes" @@ -9186,9 +11921,6 @@ "DistanceLowerLimit": { "Float32": 0.0 }, - "DistanceStep": { - "Float32": 0.0 - }, "DistanceUpperLimit": { "Float32": -1.0 }, @@ -10833,6 +13565,19 @@ "Tags": [], "Superclass": "HandleAdornment", "Properties": { + "Shading": { + "Name": "Shading", + "Scriptability": "ReadWrite", + "DataType": { + "Enum": "AdornShading" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "Size": { "Name": "Size", "Scriptability": "ReadWrite", @@ -10902,6 +13647,9 @@ "Sandboxed": { "Bool": false }, + "Shading": { + "Enum": 0 + }, "Size": { "Vector3": [ 1.0, @@ -12403,7 +15151,88 @@ "NotReplicated" ], "Superclass": "Object", - "Properties": {}, + "Properties": { + "CaptureType": { + "Name": "CaptureType", + "Scriptability": "Read", + "DataType": { + "Enum": "CaptureType" + }, + "Tags": [ + "NotReplicated", + "ReadOnly" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, + "FilePathString": { + "Name": "FilePathString", + "Scriptability": "None", + "DataType": { + "Value": "String" + }, + "Tags": [ + "NotReplicated", + "ReadOnly" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, + "LocalId": { + "Name": "LocalId", + "Scriptability": "Read", + "DataType": { + "Value": "String" + }, + "Tags": [ + "NotReplicated", + "ReadOnly" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, + "SourcePlaceId": { + "Name": "SourcePlaceId", + "Scriptability": "Read", + "DataType": { + "Value": "Int64" + }, + "Tags": [ + "NotReplicated", + "ReadOnly" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, + "SourceUniverseId": { + "Name": "SourceUniverseId", + "Scriptability": "Read", + "DataType": { + "Value": "Int64" + }, + "Tags": [ + "NotReplicated", + "ReadOnly" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + } + }, "DefaultProperties": {} }, "CaptureService": { @@ -12426,6 +15255,26 @@ } } }, + "CapturesPages": { + "Name": "CapturesPages", + "Tags": [ + "NotCreatable", + "NotReplicated" + ], + "Superclass": "Pages", + "Properties": {}, + "DefaultProperties": { + "Archivable": { + "Bool": true + }, + "HistoryId": { + "UniqueId": "00000000000000000000000000000000" + }, + "UniqueId": { + "UniqueId": "00000000000000000000000000000000" + } + } + }, "CatalogPages": { "Name": "CatalogPages", "Tags": [ @@ -12466,6 +15315,26 @@ } } }, + "ChangeHistoryStreamingService": { + "Name": "ChangeHistoryStreamingService", + "Tags": [ + "NotCreatable", + "Service" + ], + "Superclass": "Instance", + "Properties": {}, + "DefaultProperties": { + "Archivable": { + "Bool": true + }, + "HistoryId": { + "UniqueId": "00000000000000000000000000000000" + }, + "UniqueId": { + "UniqueId": "00000000000000000000000000000000" + } + } + }, "ChannelSelectorSoundEffect": { "Name": "ChannelSelectorSoundEffect", "Tags": [ @@ -13854,6 +16723,24 @@ "Value": "ContentId" }, "Tags": [], + "Kind": { + "Canonical": { + "Serialization": { + "Migrate": { + "To": "CursorIconContent", + "Migration": "ContentIdToContent" + } + } + } + } + }, + "CursorIconContent": { + "Name": "CursorIconContent", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Content" + }, + "Tags": [], "Kind": { "Canonical": { "Serialization": "Serializes" @@ -13884,8 +16771,8 @@ "Capabilities": { "SecurityCapabilities": 0 }, - "CursorIcon": { - "ContentId": "" + "CursorIconContent": { + "Content": "None" }, "HistoryId": { "UniqueId": "00000000000000000000000000000000" @@ -14680,187 +17567,6 @@ } } }, - "CommandInstance": { - "Name": "CommandInstance", - "Tags": [ - "NotCreatable", - "NotReplicated" - ], - "Superclass": "Instance", - "Properties": { - "AllowGUIAccessPoints": { - "Name": "AllowGUIAccessPoints", - "Scriptability": "Read", - "DataType": { - "Value": "Bool" - }, - "Tags": [ - "NotReplicated", - "ReadOnly" - ], - "Kind": { - "Canonical": { - "Serialization": "DoesNotSerialize" - } - } - }, - "Checked": { - "Name": "Checked", - "Scriptability": "None", - "DataType": { - "Value": "Bool" - }, - "Tags": [ - "NotReplicated" - ], - "Kind": { - "Canonical": { - "Serialization": "DoesNotSerialize" - } - } - }, - "DefaultShortcut": { - "Name": "DefaultShortcut", - "Scriptability": "None", - "DataType": { - "Value": "String" - }, - "Tags": [ - "NotReplicated", - "ReadOnly" - ], - "Kind": { - "Canonical": { - "Serialization": "DoesNotSerialize" - } - } - }, - "DisplayName": { - "Name": "DisplayName", - "Scriptability": "ReadWrite", - "DataType": { - "Value": "String" - }, - "Tags": [ - "NotReplicated" - ], - "Kind": { - "Canonical": { - "Serialization": "DoesNotSerialize" - } - } - }, - "Enabled": { - "Name": "Enabled", - "Scriptability": "None", - "DataType": { - "Value": "Bool" - }, - "Tags": [ - "NotReplicated" - ], - "Kind": { - "Canonical": { - "Serialization": "DoesNotSerialize" - } - } - }, - "Icon": { - "Name": "Icon", - "Scriptability": "None", - "DataType": { - "Value": "String" - }, - "Tags": [ - "NotReplicated" - ], - "Kind": { - "Canonical": { - "Serialization": "DoesNotSerialize" - } - } - }, - "Name": { - "Name": "Name", - "Scriptability": "Read", - "DataType": { - "Value": "String" - }, - "Tags": [ - "NotReplicated", - "ReadOnly" - ], - "Kind": { - "Canonical": { - "Serialization": "DoesNotSerialize" - } - } - }, - "Permission": { - "Name": "Permission", - "Scriptability": "None", - "DataType": { - "Enum": "CommandPermission" - }, - "Tags": [ - "NotReplicated", - "ReadOnly" - ], - "Kind": { - "Canonical": { - "Serialization": "DoesNotSerialize" - } - } - }, - "StatusTip": { - "Name": "StatusTip", - "Scriptability": "None", - "DataType": { - "Value": "String" - }, - "Tags": [ - "NotReplicated" - ], - "Kind": { - "Canonical": { - "Serialization": "DoesNotSerialize" - } - } - } - }, - "DefaultProperties": { - "Archivable": { - "Bool": true - }, - "HistoryId": { - "UniqueId": "00000000000000000000000000000000" - }, - "UniqueId": { - "UniqueId": "00000000000000000000000000000000" - } - } - }, - "CommandService": { - "Name": "CommandService", - "Tags": [ - "NotCreatable", - "NotReplicated", - "Service" - ], - "Superclass": "Instance", - "Properties": {}, - "DefaultProperties": { - "Archivable": { - "Bool": true - }, - "HistoryId": { - "UniqueId": "00000000000000000000000000000000" - }, - "UniqueId": { - "UniqueId": "00000000000000000000000000000000" - } - } - }, "CommerceService": { "Name": "CommerceService", "Tags": [ @@ -15031,6 +17737,19 @@ } } }, + "Hollow": { + "Name": "Hollow", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "Radius": { "Name": "Radius", "Scriptability": "ReadWrite", @@ -15043,6 +17762,19 @@ "Serialization": "Serializes" } } + }, + "Shading": { + "Name": "Shading", + "Scriptability": "ReadWrite", + "DataType": { + "Enum": "AdornShading" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } } }, "DefaultProperties": { @@ -15100,12 +17832,18 @@ "HistoryId": { "UniqueId": "00000000000000000000000000000000" }, + "Hollow": { + "Bool": false + }, "Radius": { "Float32": 0.5 }, "Sandboxed": { "Bool": false }, + "Shading": { + "Enum": 0 + }, "SizeRelativeOffset": { "Vector3": [ 0.0, @@ -16641,6 +19379,19 @@ "Serialization": "Serializes" } } + }, + "Shading": { + "Name": "Shading", + "Scriptability": "ReadWrite", + "DataType": { + "Enum": "AdornShading" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } } }, "DefaultProperties": { @@ -16710,6 +19461,9 @@ "Sandboxed": { "Bool": false }, + "Shading": { + "Enum": 0 + }, "SizeRelativeOffset": { "Vector3": [ 0.0, @@ -19014,6 +21768,41 @@ } } }, + "ColorMap": { + "Name": "ColorMap", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "ContentId" + }, + "Tags": [ + "NotReplicated" + ], + "Kind": { + "Canonical": { + "Serialization": { + "Migrate": { + "To": "ColorMapContent", + "Migration": "ContentIdToContent" + } + } + } + } + }, + "ColorMapContent": { + "Name": "ColorMapContent", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Content" + }, + "Tags": [ + "NotReplicated" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, "LocalTransparencyModifier": { "Name": "LocalTransparencyModifier", "Scriptability": "ReadWrite", @@ -19030,6 +21819,105 @@ } } }, + "MetalnessMap": { + "Name": "MetalnessMap", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "ContentId" + }, + "Tags": [ + "Hidden" + ], + "Kind": { + "Canonical": { + "Serialization": { + "Migrate": { + "To": "MetalnessMapContent", + "Migration": "ContentIdToContent" + } + } + } + } + }, + "MetalnessMapContent": { + "Name": "MetalnessMapContent", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Content" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "NormalMap": { + "Name": "NormalMap", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "ContentId" + }, + "Tags": [ + "Hidden" + ], + "Kind": { + "Canonical": { + "Serialization": { + "Migrate": { + "To": "NormalMapContent", + "Migration": "ContentIdToContent" + } + } + } + } + }, + "NormalMapContent": { + "Name": "NormalMapContent", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Content" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "RoughnessMap": { + "Name": "RoughnessMap", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "ContentId" + }, + "Tags": [ + "Hidden" + ], + "Kind": { + "Canonical": { + "Serialization": { + "Migrate": { + "To": "RoughnessMapContent", + "Migration": "ContentIdToContent" + } + } + } + } + }, + "RoughnessMapContent": { + "Name": "RoughnessMapContent", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Content" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "Shiny": { "Name": "Shiny", "Scriptability": "ReadWrite", @@ -19093,6 +21981,35 @@ } } }, + "TexturePack": { + "Name": "TexturePack", + "Scriptability": "None", + "DataType": { + "Value": "ContentId" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "TexturePackMetadata": { + "Name": "TexturePackMetadata", + "Scriptability": "None", + "DataType": { + "Value": "String" + }, + "Tags": [ + "Hidden", + "NotScriptable" + ], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "Transparency": { "Name": "Transparency", "Scriptability": "ReadWrite", @@ -19106,6 +22023,32 @@ } } }, + "UVOffset": { + "Name": "UVOffset", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Vector2" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "UVScale": { + "Name": "UVScale", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Vector2" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "ZIndex": { "Name": "ZIndex", "Scriptability": "ReadWrite", @@ -19143,6 +22086,15 @@ "HistoryId": { "UniqueId": "00000000000000000000000000000000" }, + "MetalnessMapContent": { + "Content": "None" + }, + "NormalMapContent": { + "Content": "None" + }, + "RoughnessMapContent": { + "Content": "None" + }, "Sandboxed": { "Bool": false }, @@ -19155,9 +22107,27 @@ "TextureContent": { "Content": "None" }, + "TexturePack": { + "ContentId": "" + }, + "TexturePackMetadata": { + "String": "" + }, "Transparency": { "Float32": 0.0 }, + "UVOffset": { + "Vector2": [ + 0.0, + 0.0 + ] + }, + "UVScale": { + "Vector2": [ + 1.0, + 1.0 + ] + }, "UniqueId": { "UniqueId": "00000000000000000000000000000000" }, @@ -19820,6 +22790,24 @@ "Value": "ContentId" }, "Tags": [], + "Kind": { + "Canonical": { + "Serialization": { + "Migrate": { + "To": "ActivatedCursorIconContent", + "Migration": "ContentIdToContent" + } + } + } + } + }, + "ActivatedCursorIconContent": { + "Name": "ActivatedCursorIconContent", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Content" + }, + "Tags": [], "Kind": { "Canonical": { "Serialization": "Serializes" @@ -20225,8 +23213,8 @@ } }, "DefaultProperties": { - "ActivatedCursorIcon": { - "ContentId": "" + "ActivatedCursorIconContent": { + "Content": "None" }, "ApplyAtCenterOfMass": { "Bool": false @@ -20240,8 +23228,8 @@ "Capabilities": { "SecurityCapabilities": 0 }, - "CursorIcon": { - "ContentId": "" + "CursorIconContent": { + "Content": "None" }, "DragFrame": { "CFrame": { @@ -20383,13 +23371,12 @@ "Properties": { "AlignDraggedObjects": { "Name": "AlignDraggedObjects", - "Scriptability": "Read", + "Scriptability": "ReadWrite", "DataType": { "Value": "Bool" }, "Tags": [ - "NotReplicated", - "ReadOnly" + "NotReplicated" ], "Kind": { "Canonical": { @@ -20399,13 +23386,12 @@ }, "AngleSnapEnabled": { "Name": "AngleSnapEnabled", - "Scriptability": "Read", + "Scriptability": "ReadWrite", "DataType": { "Value": "Bool" }, "Tags": [ - "NotReplicated", - "ReadOnly" + "NotReplicated" ], "Kind": { "Canonical": { @@ -20415,13 +23401,12 @@ }, "AngleSnapIncrement": { "Name": "AngleSnapIncrement", - "Scriptability": "Read", + "Scriptability": "ReadWrite", "DataType": { "Value": "Float32" }, "Tags": [ - "NotReplicated", - "ReadOnly" + "NotReplicated" ], "Kind": { "Canonical": { @@ -20431,13 +23416,12 @@ }, "AnimateHover": { "Name": "AnimateHover", - "Scriptability": "Read", + "Scriptability": "ReadWrite", "DataType": { "Value": "Bool" }, "Tags": [ - "NotReplicated", - "ReadOnly" + "NotReplicated" ], "Kind": { "Canonical": { @@ -20447,13 +23431,12 @@ }, "CollisionsEnabled": { "Name": "CollisionsEnabled", - "Scriptability": "Read", + "Scriptability": "ReadWrite", "DataType": { "Value": "Bool" }, "Tags": [ - "NotReplicated", - "ReadOnly" + "NotReplicated" ], "Kind": { "Canonical": { @@ -20463,13 +23446,12 @@ }, "DraggerCoordinateSpace": { "Name": "DraggerCoordinateSpace", - "Scriptability": "Read", + "Scriptability": "ReadWrite", "DataType": { "Enum": "DraggerCoordinateSpace" }, "Tags": [ - "NotReplicated", - "ReadOnly" + "NotReplicated" ], "Kind": { "Canonical": { @@ -20479,13 +23461,12 @@ }, "DraggerMovementMode": { "Name": "DraggerMovementMode", - "Scriptability": "Read", + "Scriptability": "ReadWrite", "DataType": { "Enum": "DraggerMovementMode" }, "Tags": [ - "NotReplicated", - "ReadOnly" + "NotReplicated" ], "Kind": { "Canonical": { @@ -20495,13 +23476,12 @@ }, "GeometrySnapColor": { "Name": "GeometrySnapColor", - "Scriptability": "Read", + "Scriptability": "ReadWrite", "DataType": { "Value": "Color3" }, "Tags": [ - "NotReplicated", - "ReadOnly" + "NotReplicated" ], "Kind": { "Canonical": { @@ -20511,13 +23491,12 @@ }, "HoverAnimateFrequency": { "Name": "HoverAnimateFrequency", - "Scriptability": "Read", + "Scriptability": "ReadWrite", "DataType": { "Value": "Float32" }, "Tags": [ - "NotReplicated", - "ReadOnly" + "NotReplicated" ], "Kind": { "Canonical": { @@ -20532,9 +23511,7 @@ "Value": "Int32" }, "Tags": [ - "Hidden", - "NotReplicated", - "ReadOnly" + "Hidden" ], "Kind": { "Canonical": { @@ -20544,13 +23521,12 @@ }, "HoverThickness": { "Name": "HoverThickness", - "Scriptability": "Read", + "Scriptability": "ReadWrite", "DataType": { "Value": "Float32" }, "Tags": [ - "NotReplicated", - "ReadOnly" + "NotReplicated" ], "Kind": { "Canonical": { @@ -20560,13 +23536,12 @@ }, "JointsEnabled": { "Name": "JointsEnabled", - "Scriptability": "Read", + "Scriptability": "ReadWrite", "DataType": { "Value": "Bool" }, "Tags": [ - "NotReplicated", - "ReadOnly" + "NotReplicated" ], "Kind": { "Canonical": { @@ -20576,13 +23551,12 @@ }, "LinearSnapEnabled": { "Name": "LinearSnapEnabled", - "Scriptability": "Read", + "Scriptability": "ReadWrite", "DataType": { "Value": "Bool" }, "Tags": [ - "NotReplicated", - "ReadOnly" + "NotReplicated" ], "Kind": { "Canonical": { @@ -20592,13 +23566,12 @@ }, "LinearSnapIncrement": { "Name": "LinearSnapIncrement", - "Scriptability": "Read", + "Scriptability": "ReadWrite", "DataType": { "Value": "Float32" }, "Tags": [ - "NotReplicated", - "ReadOnly" + "NotReplicated" ], "Kind": { "Canonical": { @@ -20613,8 +23586,7 @@ "Value": "Bool" }, "Tags": [ - "NotReplicated", - "ReadOnly" + "NotReplicated" ], "Kind": { "Canonical": { @@ -20629,8 +23601,7 @@ "Value": "Bool" }, "Tags": [ - "NotReplicated", - "ReadOnly" + "NotReplicated" ], "Kind": { "Canonical": { @@ -20640,13 +23611,12 @@ }, "ShowHover": { "Name": "ShowHover", - "Scriptability": "Read", + "Scriptability": "ReadWrite", "DataType": { "Value": "Bool" }, "Tags": [ - "NotReplicated", - "ReadOnly" + "NotReplicated" ], "Kind": { "Canonical": { @@ -20833,6 +23803,7 @@ }, "Tags": [ "Hidden", + "NotReplicated", "NotScriptable" ], "Kind": { @@ -20939,7 +23910,9 @@ "DataType": { "Value": "Bool" }, - "Tags": [], + "Tags": [ + "Deprecated" + ], "Kind": { "Canonical": { "Serialization": "Serializes" @@ -21183,6 +24156,37 @@ } } }, + "ExampleV2Service": { + "Name": "ExampleV2Service", + "Tags": [ + "NotCreatable", + "NotReplicated", + "Service" + ], + "Superclass": "Instance", + "Properties": {}, + "DefaultProperties": { + "Archivable": { + "Bool": true + }, + "HistoryId": { + "UniqueId": "00000000000000000000000000000000" + }, + "UniqueId": { + "UniqueId": "00000000000000000000000000000000" + } + } + }, + "ExecutedRemoteCommand": { + "Name": "ExecutedRemoteCommand", + "Tags": [ + "NotCreatable", + "NotReplicated" + ], + "Superclass": "Object", + "Properties": {}, + "DefaultProperties": {} + }, "ExperienceAuthService": { "Name": "ExperienceAuthService", "Tags": [ @@ -21404,6 +24408,27 @@ } } }, + "ExperienceStateRecordingService": { + "Name": "ExperienceStateRecordingService", + "Tags": [ + "NotCreatable", + "NotReplicated", + "Service" + ], + "Superclass": "Instance", + "Properties": {}, + "DefaultProperties": { + "Archivable": { + "Bool": true + }, + "HistoryId": { + "UniqueId": "00000000000000000000000000000000" + }, + "UniqueId": { + "UniqueId": "00000000000000000000000000000000" + } + } + }, "ExplorerFilter": { "Name": "ExplorerFilter", "Tags": [ @@ -22812,46 +25837,6 @@ } } }, - "FeedPages": { - "Name": "FeedPages", - "Tags": [ - "NotCreatable", - "NotReplicated" - ], - "Superclass": "Pages", - "Properties": {}, - "DefaultProperties": { - "Archivable": { - "Bool": true - }, - "HistoryId": { - "UniqueId": "00000000000000000000000000000000" - }, - "UniqueId": { - "UniqueId": "00000000000000000000000000000000" - } - } - }, - "FeedService": { - "Name": "FeedService", - "Tags": [ - "NotCreatable", - "Service" - ], - "Superclass": "Instance", - "Properties": {}, - "DefaultProperties": { - "Archivable": { - "Bool": true - }, - "HistoryId": { - "UniqueId": "00000000000000000000000000000000" - }, - "UniqueId": { - "UniqueId": "00000000000000000000000000000000" - } - } - }, "File": { "Name": "File", "Tags": [ @@ -23317,8 +26302,8 @@ "TeamColor": { "BrickColor": 194 }, - "TextureId": { - "ContentId": "" + "TextureContent": { + "Content": "None" }, "ToolTip": { "String": "" @@ -23762,7 +26747,7 @@ "UniqueId": "00000000000000000000000000000000" }, "ValuesAndTimes": { - "BinaryString": "AQAAAAAAAAABAAAAAAAAAA==" + "BinaryString": "AgAAAAAAAAABAAAAAAAAAA==" } } }, @@ -25075,44 +28060,6 @@ } } }, - "GoogleAnalyticsConfiguration": { - "Name": "GoogleAnalyticsConfiguration", - "Tags": [ - "NotCreatable", - "Service" - ], - "Superclass": "Instance", - "Properties": { - "gaID": { - "Name": "gaID", - "Scriptability": "None", - "DataType": { - "Value": "String" - }, - "Tags": [ - "Hidden", - "NotReplicated", - "NotScriptable" - ], - "Kind": { - "Canonical": { - "Serialization": "Serializes" - } - } - } - }, - "DefaultProperties": { - "Archivable": { - "Bool": true - }, - "HistoryId": { - "UniqueId": "00000000000000000000000000000000" - }, - "UniqueId": { - "UniqueId": "00000000000000000000000000000000" - } - } - }, "GroundController": { "Name": "GroundController", "Tags": [], @@ -25484,6 +28431,23 @@ } } }, + "ActiveQueryNames": { + "Name": "ActiveQueryNames", + "Scriptability": "None", + "DataType": { + "Value": "String" + }, + "Tags": [ + "Hidden", + "NotReplicated", + "NotScriptable" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, "AutoLocalize": { "Name": "AutoLocalize", "Scriptability": "ReadWrite", @@ -26835,6 +29799,38 @@ "Serialization": "DoesNotSerialize" } } + }, + "ViewportDisplaySize": { + "Name": "ViewportDisplaySize", + "Scriptability": "Read", + "DataType": { + "Enum": "DisplaySize" + }, + "Tags": [ + "NotReplicated", + "ReadOnly" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, + "ViewportSizeInMM": { + "Name": "ViewportSizeInMM", + "Scriptability": "None", + "DataType": { + "Value": "Vector2" + }, + "Tags": [ + "NotReplicated", + "ReadOnly" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } } }, "DefaultProperties": { @@ -26890,6 +29886,1017 @@ } } }, + "HandRigDescription": { + "Name": "HandRigDescription", + "Tags": [ + "NotBrowsable" + ], + "Superclass": "Instance", + "Properties": { + "Index1": { + "Name": "Index1", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Ref" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Index1TposeAdjustment": { + "Name": "Index1TposeAdjustment", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "CFrame" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Index2": { + "Name": "Index2", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Ref" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Index2TposeAdjustment": { + "Name": "Index2TposeAdjustment", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "CFrame" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Index3": { + "Name": "Index3", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Ref" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Index3TposeAdjustment": { + "Name": "Index3TposeAdjustment", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "CFrame" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "IndexRange": { + "Name": "IndexRange", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Vector3" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "IndexSize": { + "Name": "IndexSize", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Float32" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Middle1": { + "Name": "Middle1", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Ref" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Middle1TposeAdjustment": { + "Name": "Middle1TposeAdjustment", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "CFrame" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Middle2": { + "Name": "Middle2", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Ref" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Middle2TposeAdjustment": { + "Name": "Middle2TposeAdjustment", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "CFrame" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Middle3": { + "Name": "Middle3", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Ref" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Middle3TposeAdjustment": { + "Name": "Middle3TposeAdjustment", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "CFrame" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "MiddleRange": { + "Name": "MiddleRange", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Vector3" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "MiddleSize": { + "Name": "MiddleSize", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Float32" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Pinky1": { + "Name": "Pinky1", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Ref" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Pinky1TposeAdjustment": { + "Name": "Pinky1TposeAdjustment", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "CFrame" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Pinky2": { + "Name": "Pinky2", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Ref" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Pinky2TposeAdjustment": { + "Name": "Pinky2TposeAdjustment", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "CFrame" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Pinky3": { + "Name": "Pinky3", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Ref" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Pinky3TposeAdjustment": { + "Name": "Pinky3TposeAdjustment", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "CFrame" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "PinkyRange": { + "Name": "PinkyRange", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Vector3" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "PinkySize": { + "Name": "PinkySize", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Float32" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Ring1": { + "Name": "Ring1", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Ref" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Ring1TposeAdjustment": { + "Name": "Ring1TposeAdjustment", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "CFrame" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Ring2": { + "Name": "Ring2", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Ref" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Ring2TposeAdjustment": { + "Name": "Ring2TposeAdjustment", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "CFrame" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Ring3": { + "Name": "Ring3", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Ref" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Ring3TposeAdjustment": { + "Name": "Ring3TposeAdjustment", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "CFrame" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "RingRange": { + "Name": "RingRange", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Vector3" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "RingSize": { + "Name": "RingSize", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Float32" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Side": { + "Name": "Side", + "Scriptability": "ReadWrite", + "DataType": { + "Enum": "HandRigDescriptionSide" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Thumb1": { + "Name": "Thumb1", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Ref" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Thumb1TposeAdjustment": { + "Name": "Thumb1TposeAdjustment", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "CFrame" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Thumb2": { + "Name": "Thumb2", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Ref" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Thumb2TposeAdjustment": { + "Name": "Thumb2TposeAdjustment", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "CFrame" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Thumb3": { + "Name": "Thumb3", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Ref" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Thumb3TposeAdjustment": { + "Name": "Thumb3TposeAdjustment", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "CFrame" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "ThumbRange": { + "Name": "ThumbRange", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Vector3" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "ThumbSize": { + "Name": "ThumbSize", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Float32" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + } + }, + "DefaultProperties": { + "Archivable": { + "Bool": true + }, + "Attributes": { + "Attributes": {} + }, + "Capabilities": { + "SecurityCapabilities": 0 + }, + "HistoryId": { + "UniqueId": "00000000000000000000000000000000" + }, + "Index1TposeAdjustment": { + "CFrame": { + "position": [ + 0.0, + 0.0, + 0.0 + ], + "orientation": [ + [ + 1.0, + 0.0, + 0.0 + ], + [ + 0.0, + 1.0, + 0.0 + ], + [ + 0.0, + 0.0, + 1.0 + ] + ] + } + }, + "Index2TposeAdjustment": { + "CFrame": { + "position": [ + 0.0, + 0.0, + 0.0 + ], + "orientation": [ + [ + 1.0, + 0.0, + 0.0 + ], + [ + 0.0, + 1.0, + 0.0 + ], + [ + 0.0, + 0.0, + 1.0 + ] + ] + } + }, + "Index3TposeAdjustment": { + "CFrame": { + "position": [ + 0.0, + 0.0, + 0.0 + ], + "orientation": [ + [ + 1.0, + 0.0, + 0.0 + ], + [ + 0.0, + 1.0, + 0.0 + ], + [ + 0.0, + 0.0, + 1.0 + ] + ] + } + }, + "IndexRange": { + "Vector3": [ + 0.0, + 0.0, + 0.0 + ] + }, + "IndexSize": { + "Float32": 0.0 + }, + "Middle1TposeAdjustment": { + "CFrame": { + "position": [ + 0.0, + 0.0, + 0.0 + ], + "orientation": [ + [ + 1.0, + 0.0, + 0.0 + ], + [ + 0.0, + 1.0, + 0.0 + ], + [ + 0.0, + 0.0, + 1.0 + ] + ] + } + }, + "Middle2TposeAdjustment": { + "CFrame": { + "position": [ + 0.0, + 0.0, + 0.0 + ], + "orientation": [ + [ + 1.0, + 0.0, + 0.0 + ], + [ + 0.0, + 1.0, + 0.0 + ], + [ + 0.0, + 0.0, + 1.0 + ] + ] + } + }, + "Middle3TposeAdjustment": { + "CFrame": { + "position": [ + 0.0, + 0.0, + 0.0 + ], + "orientation": [ + [ + 1.0, + 0.0, + 0.0 + ], + [ + 0.0, + 1.0, + 0.0 + ], + [ + 0.0, + 0.0, + 1.0 + ] + ] + } + }, + "MiddleRange": { + "Vector3": [ + 0.0, + 0.0, + 0.0 + ] + }, + "MiddleSize": { + "Float32": 0.0 + }, + "Pinky1TposeAdjustment": { + "CFrame": { + "position": [ + 0.0, + 0.0, + 0.0 + ], + "orientation": [ + [ + 1.0, + 0.0, + 0.0 + ], + [ + 0.0, + 1.0, + 0.0 + ], + [ + 0.0, + 0.0, + 1.0 + ] + ] + } + }, + "Pinky2TposeAdjustment": { + "CFrame": { + "position": [ + 0.0, + 0.0, + 0.0 + ], + "orientation": [ + [ + 1.0, + 0.0, + 0.0 + ], + [ + 0.0, + 1.0, + 0.0 + ], + [ + 0.0, + 0.0, + 1.0 + ] + ] + } + }, + "Pinky3TposeAdjustment": { + "CFrame": { + "position": [ + 0.0, + 0.0, + 0.0 + ], + "orientation": [ + [ + 1.0, + 0.0, + 0.0 + ], + [ + 0.0, + 1.0, + 0.0 + ], + [ + 0.0, + 0.0, + 1.0 + ] + ] + } + }, + "PinkyRange": { + "Vector3": [ + 0.0, + 0.0, + 0.0 + ] + }, + "PinkySize": { + "Float32": 0.0 + }, + "Ring1TposeAdjustment": { + "CFrame": { + "position": [ + 0.0, + 0.0, + 0.0 + ], + "orientation": [ + [ + 1.0, + 0.0, + 0.0 + ], + [ + 0.0, + 1.0, + 0.0 + ], + [ + 0.0, + 0.0, + 1.0 + ] + ] + } + }, + "Ring2TposeAdjustment": { + "CFrame": { + "position": [ + 0.0, + 0.0, + 0.0 + ], + "orientation": [ + [ + 1.0, + 0.0, + 0.0 + ], + [ + 0.0, + 1.0, + 0.0 + ], + [ + 0.0, + 0.0, + 1.0 + ] + ] + } + }, + "Ring3TposeAdjustment": { + "CFrame": { + "position": [ + 0.0, + 0.0, + 0.0 + ], + "orientation": [ + [ + 1.0, + 0.0, + 0.0 + ], + [ + 0.0, + 1.0, + 0.0 + ], + [ + 0.0, + 0.0, + 1.0 + ] + ] + } + }, + "RingRange": { + "Vector3": [ + 0.0, + 0.0, + 0.0 + ] + }, + "RingSize": { + "Float32": 0.0 + }, + "Sandboxed": { + "Bool": false + }, + "Side": { + "Enum": 0 + }, + "SourceAssetId": { + "Int64": -1 + }, + "Tags": { + "Tags": [] + }, + "Thumb1TposeAdjustment": { + "CFrame": { + "position": [ + 0.0, + 0.0, + 0.0 + ], + "orientation": [ + [ + 1.0, + 0.0, + 0.0 + ], + [ + 0.0, + 1.0, + 0.0 + ], + [ + 0.0, + 0.0, + 1.0 + ] + ] + } + }, + "Thumb2TposeAdjustment": { + "CFrame": { + "position": [ + 0.0, + 0.0, + 0.0 + ], + "orientation": [ + [ + 1.0, + 0.0, + 0.0 + ], + [ + 0.0, + 1.0, + 0.0 + ], + [ + 0.0, + 0.0, + 1.0 + ] + ] + } + }, + "Thumb3TposeAdjustment": { + "CFrame": { + "position": [ + 0.0, + 0.0, + 0.0 + ], + "orientation": [ + [ + 1.0, + 0.0, + 0.0 + ], + [ + 0.0, + 1.0, + 0.0 + ], + [ + 0.0, + 0.0, + 1.0 + ] + ] + } + }, + "ThumbRange": { + "Vector3": [ + 0.0, + 0.0, + 0.0 + ] + }, + "ThumbSize": { + "Float32": 0.0 + }, + "UniqueId": { + "UniqueId": "00000000000000000000000000000000" + } + } + }, "HandleAdornment": { "Name": "HandleAdornment", "Tags": [ @@ -26936,6 +30943,22 @@ } } }, + "GizmoReference": { + "Name": "GizmoReference", + "Scriptability": "None", + "DataType": { + "Value": "Ref" + }, + "Tags": [ + "Hidden", + "NotReplicated" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, "SizeRelativeOffset": { "Name": "SizeRelativeOffset", "Scriptability": "ReadWrite", @@ -27161,9 +31184,7 @@ }, "HapticEffect": { "Name": "HapticEffect", - "Tags": [ - "NotBrowsable" - ], + "Tags": [], "Superclass": "Instance", "Properties": { "Looped": { @@ -27224,7 +31245,25 @@ "DataType": { "Value": "Ref" }, - "Tags": [], + "Tags": [ + "Deprecated" + ], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "WaveformData": { + "Name": "WaveformData", + "Scriptability": "None", + "DataType": { + "Value": "BinaryString" + }, + "Tags": [ + "Hidden", + "NotScriptable" + ], "Kind": { "Canonical": { "Serialization": "Serializes" @@ -27272,6 +31311,9 @@ }, "UniqueId": { "UniqueId": "00000000000000000000000000000000" + }, + "WaveformData": { + "BinaryString": "" } } }, @@ -27296,6 +31338,27 @@ } } }, + "HarmonyService": { + "Name": "HarmonyService", + "Tags": [ + "NotCreatable", + "NotReplicated", + "Service" + ], + "Superclass": "Instance", + "Properties": {}, + "DefaultProperties": { + "Archivable": { + "Bool": true + }, + "HistoryId": { + "UniqueId": "00000000000000000000000000000000" + }, + "UniqueId": { + "UniqueId": "00000000000000000000000000000000" + } + } + }, "Hat": { "Name": "Hat", "Tags": [ @@ -28203,8 +32266,8 @@ "Tags": { "Tags": [] }, - "TextureId": { - "ContentId": "" + "TextureContent": { + "Content": "None" }, "UniqueId": { "UniqueId": "00000000000000000000000000000000" @@ -28803,6 +32866,22 @@ } } }, + "OverrideDefaultCollisions": { + "Name": "OverrideDefaultCollisions", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [ + "Hidden", + "NotScriptable" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, "PlatformStand": { "Name": "PlatformStand", "Scriptability": "ReadWrite", @@ -28875,6 +32954,22 @@ } } }, + "RotationType": { + "Name": "RotationType", + "Scriptability": "None", + "DataType": { + "Enum": "RotationType" + }, + "Tags": [ + "Hidden", + "NotScriptable" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, "SeatPart": { "Name": "SeatPart", "Scriptability": "Read", @@ -29893,7 +33988,9 @@ }, "HumanoidRigDescription": { "Name": "HumanoidRigDescription", - "Tags": [], + "Tags": [ + "NotBrowsable" + ], "Superclass": "Instance", "Properties": { "Chest": { @@ -29961,8 +34058,8 @@ } } }, - "Head": { - "Name": "Head", + "HeadBase": { + "Name": "HeadBase", "Scriptability": "ReadWrite", "DataType": { "Value": "Ref" @@ -29974,8 +34071,8 @@ } } }, - "HeadRangeMax": { - "Name": "HeadRangeMax", + "HeadBaseRangeMax": { + "Name": "HeadBaseRangeMax", "Scriptability": "ReadWrite", "DataType": { "Value": "Vector3" @@ -29987,8 +34084,8 @@ } } }, - "HeadRangeMin": { - "Name": "HeadRangeMin", + "HeadBaseRangeMin": { + "Name": "HeadBaseRangeMin", "Scriptability": "ReadWrite", "DataType": { "Value": "Vector3" @@ -30000,8 +34097,8 @@ } } }, - "HeadSize": { - "Name": "HeadSize", + "HeadBaseSize": { + "Name": "HeadBaseSize", "Scriptability": "ReadWrite", "DataType": { "Value": "Float32" @@ -30013,8 +34110,8 @@ } } }, - "HeadTposeAdjustment": { - "Name": "HeadTposeAdjustment", + "HeadBaseTposeAdjustment": { + "Name": "HeadBaseTposeAdjustment", "Scriptability": "ReadWrite", "DataType": { "Value": "CFrame" @@ -30611,6 +34708,19 @@ } } }, + "OriginOffset": { + "Name": "OriginOffset", + "Scriptability": "None", + "DataType": { + "Value": "CFrame" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "Pelvis": { "Name": "Pelvis", "Scriptability": "ReadWrite", @@ -31380,24 +35490,24 @@ ] } }, - "HeadRangeMax": { + "HeadBaseRangeMax": { "Vector3": [ 0.0, 0.0, 0.0 ] }, - "HeadRangeMin": { + "HeadBaseRangeMin": { "Vector3": [ 0.0, 0.0, 0.0 ] }, - "HeadSize": { + "HeadBaseSize": { "Float32": 0.0 }, - "HeadTposeAdjustment": { + "HeadBaseTposeAdjustment": { "CFrame": { "position": [ 0.0, @@ -31813,6 +35923,32 @@ ] } }, + "OriginOffset": { + "CFrame": { + "position": [ + 0.0, + 0.0, + 0.0 + ], + "orientation": [ + [ + 1.0, + 0.0, + 0.0 + ], + [ + 0.0, + 1.0, + 0.0 + ], + [ + 0.0, + 0.0, + 1.0 + ] + ] + } + }, "PelvisRangeMax": { "Vector3": [ 0.0, @@ -33626,6 +37762,19 @@ } } }, + "UseFileLevelCompressionInsteadOfChunk": { + "Name": "UseFileLevelCompressionInsteadOfChunk", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "ZstdCompression": { "Name": "ZstdCompression", "Scriptability": "ReadWrite", @@ -33654,11 +37803,77 @@ }, "InputAction": { "Name": "InputAction", - "Tags": [ - "NotBrowsable" - ], + "Tags": [], "Superclass": "Instance", "Properties": { + "BoolState": { + "Name": "BoolState", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [ + "NotReplicated", + "NotScriptable", + "ReadOnly" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, + "Direction1DState": { + "Name": "Direction1DState", + "Scriptability": "None", + "DataType": { + "Value": "Float32" + }, + "Tags": [ + "NotReplicated", + "NotScriptable", + "ReadOnly" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, + "Direction2DState": { + "Name": "Direction2DState", + "Scriptability": "None", + "DataType": { + "Value": "Vector2" + }, + "Tags": [ + "NotReplicated", + "NotScriptable", + "ReadOnly" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, + "Direction3DState": { + "Name": "Direction3DState", + "Scriptability": "None", + "DataType": { + "Value": "Vector3" + }, + "Tags": [ + "NotReplicated", + "NotScriptable", + "ReadOnly" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, "Enabled": { "Name": "Enabled", "Scriptability": "ReadWrite", @@ -33721,11 +37936,22 @@ }, "InputBinding": { "Name": "InputBinding", - "Tags": [ - "NotBrowsable" - ], + "Tags": [], "Superclass": "Instance", "Properties": { + "Backward": { + "Name": "Backward", + "Scriptability": "ReadWrite", + "DataType": { + "Enum": "KeyCode" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "Down": { "Name": "Down", "Scriptability": "ReadWrite", @@ -33739,6 +37965,19 @@ } } }, + "Forward": { + "Name": "Forward", + "Scriptability": "ReadWrite", + "DataType": { + "Enum": "KeyCode" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "KeyCode": { "Name": "KeyCode", "Scriptability": "ReadWrite", @@ -33791,6 +38030,19 @@ } } }, + "ResponseCurve": { + "Name": "ResponseCurve", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Float32" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "Right": { "Name": "Right", "Scriptability": "ReadWrite", @@ -33864,12 +38116,18 @@ "Attributes": { "Attributes": {} }, + "Backward": { + "Enum": 0 + }, "Capabilities": { "SecurityCapabilities": 0 }, "Down": { "Enum": 0 }, + "Forward": { + "Enum": 0 + }, "HistoryId": { "UniqueId": "00000000000000000000000000000000" }, @@ -33885,6 +38143,9 @@ "ReleasedThreshold": { "Float32": 0.2 }, + "ResponseCurve": { + "Float32": 1.0 + }, "Right": { "Enum": 0 }, @@ -33916,9 +38177,7 @@ }, "InputContext": { "Name": "InputContext", - "Tags": [ - "NotBrowsable" - ], + "Tags": [], "Superclass": "Instance", "Properties": { "Enabled": { @@ -34412,8 +38671,7 @@ "Value": "UniqueId" }, "Tags": [ - "NotReplicated", - "NotScriptable" + "NotReplicated" ], "Kind": { "Canonical": { @@ -34500,6 +38758,26 @@ } } }, + "InstanceExtensionsService": { + "Name": "InstanceExtensionsService", + "Tags": [ + "NotCreatable", + "Service" + ], + "Superclass": "Instance", + "Properties": {}, + "DefaultProperties": { + "Archivable": { + "Bool": true + }, + "HistoryId": { + "UniqueId": "00000000000000000000000000000000" + }, + "UniqueId": { + "UniqueId": "00000000000000000000000000000000" + } + } + }, "IntConstrainedValue": { "Name": "IntConstrainedValue", "Tags": [ @@ -34875,6 +39153,9 @@ 255 ] }, + "ComponentIndex": { + "Int32": -1 + }, "CustomPhysicalProperties": { "PhysicalProperties": "Default" }, @@ -34902,6 +39183,9 @@ "HistoryId": { "UniqueId": "00000000000000000000000000000000" }, + "InertiaMigrated": { + "Bool": true + }, "InitialSize": { "Vector3": [ 1.0, @@ -34939,6 +39223,9 @@ "MeshData2": { "SharedString": "" }, + "OffCentered": { + "Bool": false + }, "PhysicalConfigData": { "SharedString": "" }, @@ -35038,27 +39325,27 @@ }, "UnscaledCofm": { "Vector3": [ - null, - null, - null + 0.0, + 0.0, + 0.0 ] }, "UnscaledVolInertiaDiags": { "Vector3": [ - null, - null, - null + 0.16666666, + 0.16666666, + 0.16666666 ] }, "UnscaledVolInertiaOffDiags": { "Vector3": [ - null, - null, - null + 0.0, + 0.0, + 0.0 ] }, "UnscaledVolume": { - "Float32": null + "Float32": 1.0 }, "UsePartColor": { "Bool": false @@ -35759,6 +40046,21 @@ } } }, + "ExtendLightRangeTo120": { + "Name": "ExtendLightRangeTo120", + "Scriptability": "None", + "DataType": { + "Enum": "RolloutState" + }, + "Tags": [ + "NotScriptable" + ], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "FogColor": { "Name": "FogColor", "Scriptability": "ReadWrite", @@ -35826,13 +40128,11 @@ }, "LightingStyle": { "Name": "LightingStyle", - "Scriptability": "None", + "Scriptability": "Read", "DataType": { "Enum": "LightingStyle" }, - "Tags": [ - "NotScriptable" - ], + "Tags": [], "Kind": { "Canonical": { "Serialization": "Serializes" @@ -35869,13 +40169,11 @@ }, "PrioritizeLightingQuality": { "Name": "PrioritizeLightingQuality", - "Scriptability": "None", + "Scriptability": "Read", "DataType": { "Value": "Bool" }, - "Tags": [ - "NotScriptable" - ], + "Tags": [], "Kind": { "Canonical": { "Serialization": "Serializes" @@ -35960,6 +40258,9 @@ "RBX_LightingCompatibilityMigrated": { "Bool": true }, + "RBX_LightingTechnologyUnifiedMigration": { + "Bool": true + }, "RBX_OriginalTechnologyOnFileLoad": { "Int32": 2 } @@ -35994,6 +40295,9 @@ "ExposureCompensation": { "Float32": 0.0 }, + "ExtendLightRangeTo120": { + "Enum": 0 + }, "FogColor": { "Color3": [ 0.75, @@ -36017,7 +40321,7 @@ "UniqueId": "00000000000000000000000000000000" }, "LightingStyle": { - "Enum": 0 + "Enum": 1 }, "OutdoorAmbient": { "Color3": [ @@ -36030,7 +40334,7 @@ "Bool": true }, "PrioritizeLightingQuality": { - "Bool": true + "Bool": false }, "Sandboxed": { "Bool": false @@ -36419,6 +40723,19 @@ } } }, + "ReactionForceEnabled": { + "Name": "ReactionForceEnabled", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "RelativeTo": { "Name": "RelativeTo", "Scriptability": "ReadWrite", @@ -36536,6 +40853,9 @@ 0.0 ] }, + "ReactionForceEnabled": { + "Bool": true + }, "RelativeTo": { "Enum": 2 }, @@ -37503,6 +41823,37 @@ } } }, + "MLService": { + "Name": "MLService", + "Tags": [ + "NotCreatable", + "NotReplicated", + "Service" + ], + "Superclass": "Instance", + "Properties": {}, + "DefaultProperties": { + "Archivable": { + "Bool": true + }, + "HistoryId": { + "UniqueId": "00000000000000000000000000000000" + }, + "UniqueId": { + "UniqueId": "00000000000000000000000000000000" + } + } + }, + "MLSession": { + "Name": "MLSession", + "Tags": [ + "NotCreatable", + "NotReplicated" + ], + "Superclass": "Object", + "Properties": {}, + "DefaultProperties": {} + }, "ManualGlue": { "Name": "ManualGlue", "Tags": [ @@ -37765,7 +42116,7 @@ "UniqueId": "00000000000000000000000000000000" }, "ValuesAndTimes": { - "BinaryString": "AQAAAAAAAAABAAAAAAAAAA==" + "BinaryString": "AgAAAAAAAAABAAAAAAAAAA==" } } }, @@ -37831,26 +42182,6 @@ } } }, - "MaterialGenerationSession": { - "Name": "MaterialGenerationSession", - "Tags": [ - "NotCreatable", - "NotReplicated" - ], - "Superclass": "Instance", - "Properties": {}, - "DefaultProperties": { - "Archivable": { - "Bool": true - }, - "HistoryId": { - "UniqueId": "00000000000000000000000000000000" - }, - "UniqueId": { - "UniqueId": "00000000000000000000000000000000" - } - } - }, "MaterialImportData": { "Name": "MaterialImportData", "Tags": [ @@ -38791,6 +43122,24 @@ "Value": "ContentId" }, "Tags": [], + "Kind": { + "Canonical": { + "Serialization": { + "Migrate": { + "To": "ColorMapContent", + "Migration": "ContentIdToContent" + } + } + } + } + }, + "ColorMapContent": { + "Name": "ColorMapContent", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Content" + }, + "Tags": [], "Kind": { "Canonical": { "Serialization": "Serializes" @@ -38810,6 +43159,45 @@ } } }, + "EmissiveMaskContent": { + "Name": "EmissiveMaskContent", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Content" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "EmissiveStrength": { + "Name": "EmissiveStrength", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Float32" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "EmissiveTint": { + "Name": "EmissiveTint", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Color3" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "MaterialPattern": { "Name": "MaterialPattern", "Scriptability": "ReadWrite", @@ -38830,6 +43218,24 @@ "Value": "ContentId" }, "Tags": [], + "Kind": { + "Canonical": { + "Serialization": { + "Migrate": { + "To": "MetalnessMapContent", + "Migration": "ContentIdToContent" + } + } + } + } + }, + "MetalnessMapContent": { + "Name": "MetalnessMapContent", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Content" + }, + "Tags": [], "Kind": { "Canonical": { "Serialization": "Serializes" @@ -38843,6 +43249,24 @@ "Value": "ContentId" }, "Tags": [], + "Kind": { + "Canonical": { + "Serialization": { + "Migrate": { + "To": "NormalMapContent", + "Migration": "ContentIdToContent" + } + } + } + } + }, + "NormalMapContent": { + "Name": "NormalMapContent", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Content" + }, + "Tags": [], "Kind": { "Canonical": { "Serialization": "Serializes" @@ -38856,6 +43280,24 @@ "Value": "ContentId" }, "Tags": [], + "Kind": { + "Canonical": { + "Serialization": { + "Migrate": { + "To": "RoughnessMapContent", + "Migration": "ContentIdToContent" + } + } + } + } + }, + "RoughnessMapContent": { + "Name": "RoughnessMapContent", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Content" + }, + "Tags": [], "Kind": { "Canonical": { "Serialization": "Serializes" @@ -38905,26 +43347,39 @@ "Capabilities": { "SecurityCapabilities": 0 }, - "ColorMap": { - "ContentId": "" + "ColorMapContent": { + "Content": "None" }, "CustomPhysicalProperties": { "PhysicalProperties": "Default" }, + "EmissiveMaskContent": { + "Content": "None" + }, + "EmissiveStrength": { + "Float32": 1.0 + }, + "EmissiveTint": { + "Color3": [ + 1.0, + 1.0, + 1.0 + ] + }, "HistoryId": { "UniqueId": "00000000000000000000000000000000" }, "MaterialPattern": { "Enum": 0 }, - "MetalnessMap": { - "ContentId": "" + "MetalnessMapContent": { + "Content": "None" }, - "NormalMap": { - "ContentId": "" + "NormalMapContent": { + "Content": "None" }, - "RoughnessMap": { - "ContentId": "" + "RoughnessMapContent": { + "Content": "None" }, "Sandboxed": { "Bool": false @@ -39466,23 +43921,6 @@ } } }, - "EditableMeshString": { - "Name": "EditableMeshString", - "Scriptability": "None", - "DataType": { - "Value": "SharedString" - }, - "Tags": [ - "Hidden", - "NotReplicated", - "NotScriptable" - ], - "Kind": { - "Canonical": { - "Serialization": "DoesNotSerialize" - } - } - }, "HasJointOffset": { "Name": "HasJointOffset", "Scriptability": "Read", @@ -39490,6 +43928,7 @@ "Value": "Bool" }, "Tags": [ + "Deprecated", "Hidden" ], "Kind": { @@ -39536,6 +43975,7 @@ "Value": "Vector3" }, "Tags": [ + "Deprecated", "Hidden" ], "Kind": { @@ -39640,6 +44080,22 @@ } } }, + "SolidMeshHolder": { + "Name": "SolidMeshHolder", + "Scriptability": "None", + "DataType": { + "Value": "NetAssetRef" + }, + "Tags": [ + "Hidden", + "NotScriptable" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, "TextureContent": { "Name": "TextureContent", "Scriptability": "ReadWrite", @@ -39815,6 +44271,9 @@ "HistoryId": { "UniqueId": "00000000000000000000000000000000" }, + "InertiaMigrated": { + "Bool": true + }, "InitialSize": { "Vector3": [ 0.0, @@ -39955,27 +44414,27 @@ }, "UnscaledCofm": { "Vector3": [ - null, - null, - null + 0.0, + 0.0, + 0.0 ] }, "UnscaledVolInertiaDiags": { "Vector3": [ - null, - null, - null + 1.6666664e-31, + 1.6666664e-31, + 1.6666664e-31 ] }, "UnscaledVolInertiaOffDiags": { "Vector3": [ - null, - null, - null + 0.0, + 0.0, + 0.0 ] }, "UnscaledVolume": { - "Float32": null + "Float32": 9.999999e-19 }, "Velocity": { "Vector3": [ @@ -40398,6 +44857,41 @@ } } }, + "MicroProfilerService": { + "Name": "MicroProfilerService", + "Tags": [ + "NotCreatable", + "NotReplicated", + "Service" + ], + "Superclass": "Instance", + "Properties": { + "ContextLabel": { + "Name": "ContextLabel", + "Scriptability": "None", + "DataType": { + "Value": "String" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + } + }, + "DefaultProperties": { + "Archivable": { + "Bool": true + }, + "HistoryId": { + "UniqueId": "00000000000000000000000000000000" + }, + "UniqueId": { + "UniqueId": "00000000000000000000000000000000" + } + } + }, "Model": { "Name": "Model", "Tags": [], @@ -40557,6 +45051,22 @@ } } }, + "SlimHash": { + "Name": "SlimHash", + "Scriptability": "None", + "DataType": { + "Value": "BinaryString" + }, + "Tags": [ + "Hidden", + "NotScriptable" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, "WorldPivot": { "Name": "WorldPivot", "Scriptability": "ReadWrite", @@ -40667,6 +45177,27 @@ } } }, + "ModerationService": { + "Name": "ModerationService", + "Tags": [ + "NotCreatable", + "NotReplicated", + "Service" + ], + "Superclass": "Instance", + "Properties": {}, + "DefaultProperties": { + "Archivable": { + "Bool": true + }, + "HistoryId": { + "UniqueId": "00000000000000000000000000000000" + }, + "UniqueId": { + "UniqueId": "00000000000000000000000000000000" + } + } + }, "ModuleScript": { "Name": "ModuleScript", "Tags": [], @@ -41157,6 +45688,24 @@ "Value": "ContentId" }, "Tags": [], + "Kind": { + "Canonical": { + "Serialization": { + "Migrate": { + "To": "IconContent", + "Migration": "ContentIdToContent" + } + } + } + } + }, + "IconContent": { + "Name": "IconContent", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Content" + }, + "Tags": [], "Kind": { "Canonical": { "Serialization": "Serializes" @@ -41415,7 +45964,25 @@ "Name": "NegateOperation", "Tags": [], "Superclass": "PartOperation", - "Properties": {}, + "Properties": { + "PreviousOperation": { + "Name": "PreviousOperation", + "Scriptability": "None", + "DataType": { + "Enum": "NegateOperationHiddenHistory" + }, + "Tags": [ + "Hidden", + "NotReplicated", + "NotScriptable" + ], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + } + }, "DefaultProperties": { "AeroMeshData": { "SharedString": "" @@ -41519,6 +46086,9 @@ 255 ] }, + "ComponentIndex": { + "Int32": -1 + }, "CustomPhysicalProperties": { "PhysicalProperties": "Default" }, @@ -41546,6 +46116,9 @@ "HistoryId": { "UniqueId": "00000000000000000000000000000000" }, + "InertiaMigrated": { + "Bool": true + }, "InitialSize": { "Vector3": [ 1.0, @@ -41583,6 +46156,9 @@ "MeshData2": { "SharedString": "" }, + "OffCentered": { + "Bool": false + }, "PhysicalConfigData": { "SharedString": "" }, @@ -41615,6 +46191,9 @@ ] } }, + "PreviousOperation": { + "Enum": 0 + }, "Reflectance": { "Float32": 0.0 }, @@ -41682,27 +46261,27 @@ }, "UnscaledCofm": { "Vector3": [ - null, - null, - null + 0.0, + 0.0, + 0.0 ] }, "UnscaledVolInertiaDiags": { "Vector3": [ - null, - null, - null + 0.16666666, + 0.16666666, + 0.16666666 ] }, "UnscaledVolInertiaOffDiags": { "Vector3": [ - null, - null, - null + 0.0, + 0.0, + 0.0 ] }, "UnscaledVolume": { - "Float32": null + "Float32": 1.0 }, "UsePartColor": { "Bool": false @@ -43540,23 +48119,6 @@ } } }, - "CSGMesh2KernelMap": { - "Name": "CSGMesh2KernelMap", - "Scriptability": "None", - "DataType": { - "Value": "SharedString" - }, - "Tags": [ - "Hidden", - "NotReplicated", - "NotScriptable" - ], - "Kind": { - "Canonical": { - "Serialization": "DoesNotSerialize" - } - } - }, "ChildData": { "Name": "ChildData", "Scriptability": "None", @@ -43604,7 +48166,7 @@ ], "Kind": { "Canonical": { - "Serialization": "DoesNotSerialize" + "Serialization": "Serializes" } } }, @@ -43640,13 +48202,14 @@ } } }, - "ManifoldMesh": { - "Name": "ManifoldMesh", + "ManifoldMesh_DEPRECATED": { + "Name": "ManifoldMesh_DEPRECATED", "Scriptability": "None", "DataType": { "Value": "SharedString" }, "Tags": [ + "Deprecated", "Hidden", "NotReplicated", "NotScriptable" @@ -43689,6 +48252,23 @@ } } }, + "OffCentered": { + "Name": "OffCentered", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [ + "Hidden", + "NotReplicated", + "NotScriptable" + ], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "PhysicsData": { "Name": "PhysicsData", "Scriptability": "None", @@ -43765,6 +48345,22 @@ } } }, + "SolidMeshHolder": { + "Name": "SolidMeshHolder", + "Scriptability": "None", + "DataType": { + "Value": "NetAssetRef" + }, + "Tags": [ + "Hidden", + "NotScriptable" + ], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "TriangleCount": { "Name": "TriangleCount", "Scriptability": "Read", @@ -43898,6 +48494,9 @@ 255 ] }, + "ComponentIndex": { + "Int32": -1 + }, "CustomPhysicalProperties": { "PhysicalProperties": "Default" }, @@ -43925,6 +48524,9 @@ "HistoryId": { "UniqueId": "00000000000000000000000000000000" }, + "InertiaMigrated": { + "Bool": true + }, "InitialSize": { "Vector3": [ 1.0, @@ -43962,6 +48564,9 @@ "MeshData2": { "SharedString": "" }, + "OffCentered": { + "Bool": false + }, "PhysicalConfigData": { "SharedString": "" }, @@ -44061,27 +48666,27 @@ }, "UnscaledCofm": { "Vector3": [ - null, - null, - null + 0.0, + 0.0, + 0.0 ] }, "UnscaledVolInertiaDiags": { "Vector3": [ - null, - null, - null + 0.16666666, + 0.16666666, + 0.16666666 ] }, "UnscaledVolInertiaOffDiags": { "Vector3": [ - null, - null, - null + 0.0, + 0.0, + 0.0 ] }, "UnscaledVolume": { - "Float32": null + "Float32": 1.0 }, "UsePartColor": { "Bool": false @@ -44302,6 +48907,32 @@ } } }, + "FlipbookSizeX": { + "Name": "FlipbookSizeX", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Int32" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "FlipbookSizeY": { + "Name": "FlipbookSizeY", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Int32" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "FlipbookStartRandom": { "Name": "FlipbookStartRandom", "Scriptability": "ReadWrite", @@ -44700,6 +49331,12 @@ "FlipbookMode": { "Enum": 0 }, + "FlipbookSizeX": { + "Int32": 1 + }, + "FlipbookSizeY": { + "Int32": 1 + }, "FlipbookStartRandom": { "Bool": false }, @@ -44840,6 +49477,27 @@ } } }, + "PartyEmulatorService": { + "Name": "PartyEmulatorService", + "Tags": [ + "NotCreatable", + "NotReplicated", + "Service" + ], + "Superclass": "Instance", + "Properties": {}, + "DefaultProperties": { + "Archivable": { + "Bool": true + }, + "HistoryId": { + "UniqueId": "00000000000000000000000000000000" + }, + "UniqueId": { + "UniqueId": "00000000000000000000000000000000" + } + } + }, "PatchBundlerFileWatch": { "Name": "PatchBundlerFileWatch", "Tags": [ @@ -48016,7 +52674,6 @@ "Value": "Bool" }, "Tags": [ - "NotReplicated", "NotScriptable" ], "Kind": { @@ -49601,6 +54258,19 @@ } } }, + "MaxIndicatorDistance": { + "Name": "MaxIndicatorDistance", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Float32" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "ObjectText": { "Name": "ObjectText", "Scriptability": "ReadWrite", @@ -49707,6 +54377,9 @@ "MaxActivationDistance": { "Float32": 10.0 }, + "MaxIndicatorDistance": { + "Float32": 0.0 + }, "ObjectText": { "String": "" }, @@ -49757,6 +54430,19 @@ } } }, + "MaxIndicatorsVisible": { + "Name": "MaxIndicatorsVisible", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Int32" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "MaxPromptsVisible": { "Name": "MaxPromptsVisible", "Scriptability": "ReadWrite", @@ -49787,6 +54473,9 @@ "HistoryId": { "UniqueId": "00000000000000000000000000000000" }, + "MaxIndicatorsVisible": { + "Int32": 16 + }, "MaxPromptsVisible": { "Int32": 16 }, @@ -50026,6 +54715,46 @@ } } }, + "RecommendationPages": { + "Name": "RecommendationPages", + "Tags": [ + "NotCreatable", + "NotReplicated" + ], + "Superclass": "Pages", + "Properties": {}, + "DefaultProperties": { + "Archivable": { + "Bool": true + }, + "HistoryId": { + "UniqueId": "00000000000000000000000000000000" + }, + "UniqueId": { + "UniqueId": "00000000000000000000000000000000" + } + } + }, + "RecommendationService": { + "Name": "RecommendationService", + "Tags": [ + "NotCreatable", + "Service" + ], + "Superclass": "Instance", + "Properties": {}, + "DefaultProperties": { + "Archivable": { + "Bool": true + }, + "HistoryId": { + "UniqueId": "00000000000000000000000000000000" + }, + "UniqueId": { + "UniqueId": "00000000000000000000000000000000" + } + } + }, "ReflectionMetadata": { "Name": "ReflectionMetadata", "Tags": [], @@ -50954,6 +55683,26 @@ } } }, + "RemoteCommandService": { + "Name": "RemoteCommandService", + "Tags": [ + "NotCreatable", + "Service" + ], + "Superclass": "Instance", + "Properties": {}, + "DefaultProperties": { + "Archivable": { + "Bool": true + }, + "HistoryId": { + "UniqueId": "00000000000000000000000000000000" + }, + "UniqueId": { + "UniqueId": "00000000000000000000000000000000" + } + } + }, "RemoteCursorService": { "Name": "RemoteCursorService", "Tags": [ @@ -53264,6 +58013,22 @@ } } }, + "PredictionState": { + "Name": "PredictionState", + "Scriptability": "Read", + "DataType": { + "Enum": "PredictionState" + }, + "Tags": [ + "NotReplicated", + "ReadOnly" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, "RunState": { "Name": "RunState", "Scriptability": "ReadWrite", @@ -53278,6 +58043,22 @@ "Serialization": "DoesNotSerialize" } } + }, + "ServerFrame": { + "Name": "ServerFrame", + "Scriptability": "Read", + "DataType": { + "Value": "Int64" + }, + "Tags": [ + "NotReplicated", + "ReadOnly" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } } }, "DefaultProperties": { @@ -53349,6 +58130,26 @@ } } }, + "RuntimeContentService": { + "Name": "RuntimeContentService", + "Tags": [ + "NotCreatable", + "Service" + ], + "Superclass": "Instance", + "Properties": {}, + "DefaultProperties": { + "Archivable": { + "Bool": true + }, + "HistoryId": { + "UniqueId": "00000000000000000000000000000000" + }, + "UniqueId": { + "UniqueId": "00000000000000000000000000000000" + } + } + }, "RuntimeScriptService": { "Name": "RuntimeScriptService", "Tags": [ @@ -54245,6 +59046,24 @@ "Value": "ContentId" }, "Tags": [], + "Kind": { + "Canonical": { + "Serialization": { + "Migrate": { + "To": "BottomImageContent", + "Migration": "ContentIdToContent" + } + } + } + } + }, + "BottomImageContent": { + "Name": "BottomImageContent", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Content" + }, + "Tags": [], "Kind": { "Canonical": { "Serialization": "Serializes" @@ -54361,6 +59180,24 @@ "Value": "ContentId" }, "Tags": [], + "Kind": { + "Canonical": { + "Serialization": { + "Migrate": { + "To": "MidImageContent", + "Migration": "ContentIdToContent" + } + } + } + } + }, + "MidImageContent": { + "Name": "MidImageContent", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Content" + }, + "Tags": [], "Kind": { "Canonical": { "Serialization": "Serializes" @@ -54487,6 +59324,24 @@ "Value": "ContentId" }, "Tags": [], + "Kind": { + "Canonical": { + "Serialization": { + "Migrate": { + "To": "TopImageContent", + "Migration": "ContentIdToContent" + } + } + } + } + }, + "TopImageContent": { + "Name": "TopImageContent", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Content" + }, + "Tags": [], "Kind": { "Canonical": { "Serialization": "Serializes" @@ -54585,8 +59440,10 @@ "BorderSizePixel": { "Int32": 1 }, - "BottomImage": { - "ContentId": "rbxasset://textures/ui/Scroll/scroll-bottom.png" + "BottomImageContent": { + "Content": { + "Uri": "rbxasset://textures/ui/Scroll/scroll-bottom.png" + } }, "CanvasPosition": { "Vector2": [ @@ -54630,8 +59487,10 @@ "LayoutOrder": { "Int32": 0 }, - "MidImage": { - "ContentId": "rbxasset://textures/ui/Scroll/scroll-middle.png" + "MidImageContent": { + "Content": { + "Uri": "rbxasset://textures/ui/Scroll/scroll-middle.png" + } }, "Position": { "UDim2": [ @@ -54712,8 +59571,10 @@ "Tags": { "Tags": [] }, - "TopImage": { - "ContentId": "rbxasset://textures/ui/Scroll/scroll-top.png" + "TopImageContent": { + "Content": { + "Uri": "rbxasset://textures/ui/Scroll/scroll-top.png" + } }, "UniqueId": { "UniqueId": "00000000000000000000000000000000" @@ -55824,6 +60685,26 @@ } } }, + "SessionCheckService": { + "Name": "SessionCheckService", + "Tags": [ + "NotCreatable", + "Service" + ], + "Superclass": "Instance", + "Properties": {}, + "DefaultProperties": { + "Archivable": { + "Bool": true + }, + "HistoryId": { + "UniqueId": "00000000000000000000000000000000" + }, + "UniqueId": { + "UniqueId": "00000000000000000000000000000000" + } + } + }, "SessionService": { "Name": "SessionService", "Tags": [ @@ -56542,6 +61423,19 @@ } } }, + "SkyboxOrientation": { + "Name": "SkyboxOrientation", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Vector3" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "SkyboxRt": { "Name": "SkyboxRt", "Scriptability": "ReadWrite", @@ -56645,6 +61539,13 @@ "SkyboxLf": { "ContentId": "rbxasset://textures/sky/sky512_lf.tex" }, + "SkyboxOrientation": { + "Vector3": [ + 0.0, + 0.0, + 0.0 + ] + }, "SkyboxRt": { "ContentId": "rbxasset://textures/sky/sky512_rt.tex" }, @@ -56891,6 +61792,47 @@ } } }, + "SlimContentProvider": { + "Name": "SlimContentProvider", + "Tags": [ + "NotCreatable", + "Service" + ], + "Superclass": "CacheableContentProvider", + "Properties": {}, + "DefaultProperties": { + "Archivable": { + "Bool": true + }, + "HistoryId": { + "UniqueId": "00000000000000000000000000000000" + }, + "UniqueId": { + "UniqueId": "00000000000000000000000000000000" + } + } + }, + "SlimService": { + "Name": "SlimService", + "Tags": [ + "NotCreatable", + "NotReplicated", + "Service" + ], + "Superclass": "Instance", + "Properties": {}, + "DefaultProperties": { + "Archivable": { + "Bool": true + }, + "HistoryId": { + "UniqueId": "00000000000000000000000000000000" + }, + "UniqueId": { + "UniqueId": "00000000000000000000000000000000" + } + } + }, "Smoke": { "Name": "Smoke", "Tags": [], @@ -57278,6 +62220,19 @@ "Tags": [], "Superclass": "Instance", "Properties": { + "AudioContent": { + "Name": "AudioContent", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Content" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "ChannelCount": { "Name": "ChannelCount", "Scriptability": "None", @@ -57326,6 +62281,19 @@ } } }, + "IsMutedForCapture": { + "Name": "IsMutedForCapture", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "IsPaused": { "Name": "IsPaused", "Scriptability": "Read", @@ -57633,7 +62601,12 @@ "Tags": [], "Kind": { "Canonical": { - "Serialization": "Serializes" + "Serialization": { + "Migrate": { + "To": "AudioContent", + "Migration": "ContentIdToContent" + } + } } } }, @@ -57774,12 +62747,18 @@ "Attributes": { "Attributes": {} }, + "AudioContent": { + "Content": "None" + }, "Capabilities": { "SecurityCapabilities": 0 }, "HistoryId": { "UniqueId": "00000000000000000000000000000000" }, + "IsMutedForCapture": { + "Bool": false + }, "LoopRegion": { "NumberRange": [ 0.0, @@ -57819,9 +62798,6 @@ "Sandboxed": { "Bool": false }, - "SoundId": { - "ContentId": "" - }, "SourceAssetId": { "Int64": -1 }, @@ -57942,6 +62918,19 @@ ], "Superclass": "Instance", "Properties": { + "AcousticSimulationEnabled": { + "Name": "AcousticSimulationEnabled", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "AmbientReverb": { "Name": "AmbientReverb", "Scriptability": "ReadWrite", @@ -57970,6 +62959,19 @@ } } }, + "AudioPlayerVolumeFix": { + "Name": "AudioPlayerVolumeFix", + "Scriptability": "None", + "DataType": { + "Enum": "RolloutState" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "CharacterSoundsUseNewApi": { "Name": "CharacterSoundsUseNewApi", "Scriptability": "ReadWrite", @@ -58080,6 +63082,9 @@ } }, "DefaultProperties": { + "AcousticSimulationEnabled": { + "Bool": false + }, "AmbientReverb": { "Enum": 0 }, @@ -58092,6 +63097,9 @@ "AudioApiByDefault": { "Enum": 0 }, + "AudioPlayerVolumeFix": { + "Enum": 0 + }, "Capabilities": { "SecurityCapabilities": 0 }, @@ -58136,6 +63144,27 @@ } } }, + "SoundShimService": { + "Name": "SoundShimService", + "Tags": [ + "NotCreatable", + "NotReplicated", + "Service" + ], + "Superclass": "Instance", + "Properties": {}, + "DefaultProperties": { + "Archivable": { + "Bool": true + }, + "HistoryId": { + "UniqueId": "00000000000000000000000000000000" + }, + "UniqueId": { + "UniqueId": "00000000000000000000000000000000" + } + } + }, "Sparkles": { "Name": "Sparkles", "Tags": [], @@ -58688,6 +63717,19 @@ "Serialization": "Serializes" } } + }, + "Shading": { + "Name": "Shading", + "Scriptability": "ReadWrite", + "DataType": { + "Enum": "AdornShading" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } } }, "DefaultProperties": { @@ -58748,6 +63790,9 @@ "Sandboxed": { "Bool": false }, + "Shading": { + "Enum": 0 + }, "SizeRelativeOffset": { "Vector3": [ 0.0, @@ -59779,6 +64824,21 @@ } } }, + "ClassicDeath": { + "Name": "ClassicDeath", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Bool" + }, + "Tags": [ + "NotBrowsable" + ], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "DevCameraOcclusionMode": { "Name": "DevCameraOcclusionMode", "Scriptability": "ReadWrite", @@ -60301,6 +65361,9 @@ "CharacterWalkSpeed": { "Float32": 16.0 }, + "ClassicDeath": { + "Bool": true + }, "DevCameraOcclusionMode": { "Enum": 0 }, @@ -60602,6 +65665,22 @@ } } }, + "MemoryTrackingEnabled": { + "Name": "MemoryTrackingEnabled", + "Scriptability": "Read", + "DataType": { + "Value": "Bool" + }, + "Tags": [ + "NotReplicated", + "ReadOnly" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, "MovingPrimitivesCount": { "Name": "MovingPrimitivesCount", "Scriptability": "Read", @@ -61289,6 +66368,19 @@ } } }, + "AutoUpdateEnabled": { + "Name": "AutoUpdateEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "AutocompleteAcceptanceBehavior": { "Name": "AutocompleteAcceptanceBehavior", "Scriptability": "None", @@ -61518,6 +66610,19 @@ } } }, + "CameraSpeedLockDefault": { + "Name": "CameraSpeedLockDefault", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "CameraTweenFocus": { "Name": "CameraTweenFocus", "Scriptability": "None", @@ -62026,6 +67131,19 @@ } } }, + "EnableStudioStreaming": { + "Name": "EnableStudioStreaming", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "Error Color": { "Name": "Error Color", "Scriptability": "ReadWrite", @@ -62082,19 +67200,6 @@ } } }, - "FreeCameraSpeedScroll": { - "Name": "FreeCameraSpeedScroll", - "Scriptability": "None", - "DataType": { - "Value": "Bool" - }, - "Tags": [], - "Kind": { - "Canonical": { - "Serialization": "Serializes" - } - } - }, "Function Name Color": { "Name": "Function Name Color", "Scriptability": "ReadWrite", @@ -62313,6 +67418,19 @@ } } }, + "LoadInternalPlugins": { + "Name": "LoadInternalPlugins", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "LoadUserPluginsInRunModes": { "Name": "LoadUserPluginsInRunModes", "Scriptability": "None", @@ -62399,6 +67517,19 @@ } } }, + "MaxFindReplaceAllResults": { + "Name": "MaxFindReplaceAllResults", + "Scriptability": "None", + "DataType": { + "Value": "Int32" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "Maximum Output Lines": { "Name": "Maximum Output Lines", "Scriptability": "ReadWrite", @@ -63786,8 +68917,7 @@ }, "Tags": [ "Hidden", - "NotReplicated", - "ReadOnly" + "NotReplicated" ], "Kind": { "Canonical": { @@ -64132,6 +69262,19 @@ "Hidden", "NotScriptable" ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, + "Priority": { + "Name": "Priority", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Int32" + }, + "Tags": [], "Kind": { "Canonical": { "Serialization": "Serializes" @@ -64165,8 +69308,8 @@ "HistoryId": { "UniqueId": "00000000000000000000000000000000" }, - "Index": { - "Int32": -1 + "Priority": { + "Int32": 0 }, "Sandboxed": { "Bool": false @@ -64228,6 +69371,82 @@ } } }, + "StyleQuery": { + "Name": "StyleQuery", + "Tags": [ + "NotBrowsable", + "NotReplicated" + ], + "Superclass": "Instance", + "Properties": { + "AspectRatioRange": { + "Name": "AspectRatioRange", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "NumberRange" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "IsActive": { + "Name": "IsActive", + "Scriptability": "Read", + "DataType": { + "Value": "Bool" + }, + "Tags": [ + "NotReplicated", + "ReadOnly" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, + "MaxSize": { + "Name": "MaxSize", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Vector2" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "MinSize": { + "Name": "MinSize", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Vector2" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + } + }, + "DefaultProperties": { + "Archivable": { + "Bool": true + }, + "HistoryId": { + "UniqueId": "00000000000000000000000000000000" + }, + "UniqueId": { + "UniqueId": "00000000000000000000000000000000" + } + } + }, "StyleRule": { "Name": "StyleRule", "Tags": [], @@ -64529,6 +69748,45 @@ } } }, + "EmissiveMaskContent": { + "Name": "EmissiveMaskContent", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Content" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "EmissiveStrength": { + "Name": "EmissiveStrength", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Float32" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "EmissiveTint": { + "Name": "EmissiveTint", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Color3" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "MetalnessMap": { "Name": "MetalnessMap", "Scriptability": "ReadWrite", @@ -64665,6 +69923,19 @@ "ColorMapContent": { "Content": "None" }, + "EmissiveMaskContent": { + "Content": "None" + }, + "EmissiveStrength": { + "Float32": 1.0 + }, + "EmissiveTint": { + "Color3": [ + 1.0, + 1.0, + 1.0 + ] + }, "HistoryId": { "UniqueId": "00000000000000000000000000000000" }, @@ -66143,21 +71414,6 @@ } } }, - "ShorelinesUpgraded": { - "Name": "ShorelinesUpgraded", - "Scriptability": "None", - "DataType": { - "Value": "Bool" - }, - "Tags": [ - "Hidden" - ], - "Kind": { - "Canonical": { - "Serialization": "Serializes" - } - } - }, "SmoothGrid": { "Name": "SmoothGrid", "Scriptability": "None", @@ -66567,9 +71823,6 @@ "Sandboxed": { "Bool": false }, - "ShorelinesUpgraded": { - "Bool": false - }, "Size": { "Vector3": [ 2044.0, @@ -66647,6 +71900,63 @@ "Value": "ContentId" }, "Tags": [], + "Kind": { + "Canonical": { + "Serialization": { + "Migrate": { + "To": "ColorMapContent", + "Migration": "ContentIdToContent" + } + } + } + } + }, + "ColorMapContent": { + "Name": "ColorMapContent", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Content" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "EmissiveMaskContent": { + "Name": "EmissiveMaskContent", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Content" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "EmissiveStrength": { + "Name": "EmissiveStrength", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Float32" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "EmissiveTint": { + "Name": "EmissiveTint", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Color3" + }, + "Tags": [], "Kind": { "Canonical": { "Serialization": "Serializes" @@ -66686,6 +71996,24 @@ "Value": "ContentId" }, "Tags": [], + "Kind": { + "Canonical": { + "Serialization": { + "Migrate": { + "To": "MetalnessMapContent", + "Migration": "ContentIdToContent" + } + } + } + } + }, + "MetalnessMapContent": { + "Name": "MetalnessMapContent", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Content" + }, + "Tags": [], "Kind": { "Canonical": { "Serialization": "Serializes" @@ -66699,6 +72027,24 @@ "Value": "ContentId" }, "Tags": [], + "Kind": { + "Canonical": { + "Serialization": { + "Migrate": { + "To": "NormalMapContent", + "Migration": "ContentIdToContent" + } + } + } + } + }, + "NormalMapContent": { + "Name": "NormalMapContent", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Content" + }, + "Tags": [], "Kind": { "Canonical": { "Serialization": "Serializes" @@ -66712,6 +72058,24 @@ "Value": "ContentId" }, "Tags": [], + "Kind": { + "Canonical": { + "Serialization": { + "Migrate": { + "To": "RoughnessMapContent", + "Migration": "ContentIdToContent" + } + } + } + } + }, + "RoughnessMapContent": { + "Name": "RoughnessMapContent", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Content" + }, + "Tags": [], "Kind": { "Canonical": { "Serialization": "Serializes" @@ -66758,8 +72122,21 @@ "Capabilities": { "SecurityCapabilities": 0 }, - "ColorMap": { - "ContentId": "" + "ColorMapContent": { + "Content": "None" + }, + "EmissiveMaskContent": { + "Content": "None" + }, + "EmissiveStrength": { + "Float32": 1.0 + }, + "EmissiveTint": { + "Color3": [ + 1.0, + 1.0, + 1.0 + ] }, "Face": { "Enum": 1 @@ -66770,14 +72147,14 @@ "MaterialPattern": { "Enum": 0 }, - "MetalnessMap": { - "ContentId": "" + "MetalnessMapContent": { + "Content": "None" }, - "NormalMap": { - "ContentId": "" + "NormalMapContent": { + "Content": "None" }, - "RoughnessMap": { - "ContentId": "" + "RoughnessMapContent": { + "Content": "None" }, "Sandboxed": { "Bool": false @@ -66799,6 +72176,36 @@ } } }, + "TerrainIterateOperation": { + "Name": "TerrainIterateOperation", + "Tags": [ + "NotCreatable", + "NotReplicated" + ], + "Superclass": "Object", + "Properties": {}, + "DefaultProperties": {} + }, + "TerrainModifyOperation": { + "Name": "TerrainModifyOperation", + "Tags": [ + "NotCreatable", + "NotReplicated" + ], + "Superclass": "Object", + "Properties": {}, + "DefaultProperties": {} + }, + "TerrainReadOperation": { + "Name": "TerrainReadOperation", + "Tags": [ + "NotCreatable", + "NotReplicated" + ], + "Superclass": "Object", + "Properties": {}, + "DefaultProperties": {} + }, "TerrainRegion": { "Name": "TerrainRegion", "Tags": [], @@ -66949,6 +72356,16 @@ } } }, + "TerrainWriteOperation": { + "Name": "TerrainWriteOperation", + "Tags": [ + "NotCreatable", + "NotReplicated" + ], + "Superclass": "Object", + "Properties": {}, + "DefaultProperties": {} + }, "TestService": { "Name": "TestService", "Tags": [ @@ -67351,6 +72768,24 @@ } } }, + "LocalizedPlaceholderText": { + "Name": "LocalizedPlaceholderText", + "Scriptability": "None", + "DataType": { + "Value": "String" + }, + "Tags": [ + "Hidden", + "NotReplicated", + "NotScriptable", + "ReadOnly" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, "ManualFocusRelease": { "Name": "ManualFocusRelease", "Scriptability": "None", @@ -67522,6 +72957,22 @@ } } }, + "ShouldEmitTabEvents": { + "Name": "ShouldEmitTabEvents", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [ + "Hidden", + "NotReplicated" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, "ShouldEmitUpAndDownArrowEvents": { "Name": "ShouldEmitUpAndDownArrowEvents", "Scriptability": "None", @@ -69204,6 +74655,21 @@ "Serialization": "Serializes" } } + }, + "IsLegacyChatDisabled": { + "Name": "IsLegacyChatDisabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [ + "Hidden" + ], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } } }, "DefaultProperties": { @@ -69237,6 +74703,9 @@ "HistoryId": { "UniqueId": "00000000000000000000000000000000" }, + "IsLegacyChatDisabled": { + "Bool": false + }, "Sandboxed": { "Bool": false }, @@ -69324,6 +74793,103 @@ } } }, + "TextGenerator": { + "Name": "TextGenerator", + "Tags": [], + "Superclass": "Instance", + "Properties": { + "Seed": { + "Name": "Seed", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Int32" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "SystemPrompt": { + "Name": "SystemPrompt", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "String" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "Temperature": { + "Name": "Temperature", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Float32" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "TopP": { + "Name": "TopP", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Float32" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + } + }, + "DefaultProperties": { + "Archivable": { + "Bool": true + }, + "Attributes": { + "Attributes": {} + }, + "Capabilities": { + "SecurityCapabilities": 0 + }, + "HistoryId": { + "UniqueId": "00000000000000000000000000000000" + }, + "Sandboxed": { + "Bool": false + }, + "Seed": { + "Int32": 0 + }, + "SourceAssetId": { + "Int64": -1 + }, + "SystemPrompt": { + "String": "" + }, + "Tags": { + "Tags": [] + }, + "Temperature": { + "Float32": 0.7 + }, + "TopP": { + "Float32": 0.9 + }, + "UniqueId": { + "UniqueId": "00000000000000000000000000000000" + } + } + }, "TextLabel": { "Name": "TextLabel", "Tags": [], @@ -70122,12 +75688,21 @@ "HistoryId": { "UniqueId": "00000000000000000000000000000000" }, + "MetalnessMapContent": { + "Content": "None" + }, + "NormalMapContent": { + "Content": "None" + }, "OffsetStudsU": { "Float32": 0.0 }, "OffsetStudsV": { "Float32": 0.0 }, + "RoughnessMapContent": { + "Content": "None" + }, "Sandboxed": { "Bool": false }, @@ -70146,9 +75721,27 @@ "TextureContent": { "Content": "None" }, + "TexturePack": { + "ContentId": "" + }, + "TexturePackMetadata": { + "String": "" + }, "Transparency": { "Float32": 0.0 }, + "UVOffset": { + "Vector2": [ + 0.0, + 0.0 + ] + }, + "UVScale": { + "Vector2": [ + 1.0, + 1.0 + ] + }, "UniqueId": { "UniqueId": "00000000000000000000000000000000" }, @@ -70226,7 +75819,24 @@ "Service" ], "Superclass": "Instance", - "Properties": {}, + "Properties": { + "FriendCommunicationRestrictionStatus": { + "Name": "FriendCommunicationRestrictionStatus", + "Scriptability": "None", + "DataType": { + "Enum": "ChatRestrictionStatus" + }, + "Tags": [ + "NotReplicated", + "ReadOnly" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + } + }, "DefaultProperties": { "Archivable": { "Bool": true @@ -70640,8 +76250,8 @@ "Tags": { "Tags": [] }, - "TextureId": { - "ContentId": "" + "TextureContent": { + "Content": "None" }, "ToolTip": { "String": "" @@ -71630,6 +77240,22 @@ } } }, + "ConvexDecompHolder": { + "Name": "ConvexDecompHolder", + "Scriptability": "None", + "DataType": { + "Value": "NetAssetRef" + }, + "Tags": [ + "Hidden", + "NotScriptable" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, "FluidFidelity": { "Name": "FluidFidelity", "Scriptability": "ReadWrite", @@ -71661,6 +77287,22 @@ } } }, + "InertiaMigrated": { + "Name": "InertiaMigrated", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [ + "Hidden", + "NotScriptable" + ], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "MeshSize": { "Name": "MeshSize", "Scriptability": "Read", @@ -72436,6 +78078,24 @@ "Value": "ContentId" }, "Tags": [], + "Kind": { + "Canonical": { + "Serialization": { + "Migrate": { + "To": "ActivatedCursorIconContent", + "Migration": "ContentIdToContent" + } + } + } + } + }, + "ActivatedCursorIconContent": { + "Name": "ActivatedCursorIconContent", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Content" + }, + "Tags": [], "Kind": { "Canonical": { "Serialization": "Serializes" @@ -72475,6 +78135,24 @@ "Value": "ContentId" }, "Tags": [], + "Kind": { + "Canonical": { + "Serialization": { + "Migrate": { + "To": "CursorIconContent", + "Migration": "ContentIdToContent" + } + } + } + } + }, + "CursorIconContent": { + "Name": "CursorIconContent", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Content" + }, + "Tags": [], "Kind": { "Canonical": { "Serialization": "Serializes" @@ -72691,8 +78369,8 @@ } }, "DefaultProperties": { - "ActivatedCursorIcon": { - "ContentId": "" + "ActivatedCursorIconContent": { + "Content": "None" }, "Archivable": { "Bool": true @@ -72706,8 +78384,8 @@ "Capabilities": { "SecurityCapabilities": 0 }, - "CursorIcon": { - "ContentId": "" + "CursorIconContent": { + "Content": "None" }, "DragAxis": { "Vector2": [ @@ -73958,6 +79636,32 @@ } } }, + "BorderOffset": { + "Name": "BorderOffset", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "UDim" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "BorderStrokePosition": { + "Name": "BorderStrokePosition", + "Scriptability": "ReadWrite", + "DataType": { + "Enum": "BorderStrokePosition" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "Color": { "Name": "Color", "Scriptability": "ReadWrite", @@ -73997,6 +79701,19 @@ } } }, + "StrokeSizingMode": { + "Name": "StrokeSizingMode", + "Scriptability": "ReadWrite", + "DataType": { + "Enum": "StrokeSizingMode" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "Thickness": { "Name": "Thickness", "Scriptability": "ReadWrite", @@ -74022,6 +79739,19 @@ "Serialization": "Serializes" } } + }, + "ZIndex": { + "Name": "ZIndex", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Int32" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } } }, "DefaultProperties": { @@ -74034,6 +79764,15 @@ "Attributes": { "Attributes": {} }, + "BorderOffset": { + "UDim": [ + 0.0, + 0 + ] + }, + "BorderStrokePosition": { + "Enum": 0 + }, "Capabilities": { "SecurityCapabilities": 0 }, @@ -74059,6 +79798,9 @@ "SourceAssetId": { "Int64": -1 }, + "StrokeSizingMode": { + "Enum": 0 + }, "Tags": { "Tags": [] }, @@ -74070,6 +79812,9 @@ }, "UniqueId": { "UniqueId": "00000000000000000000000000000000" + }, + "ZIndex": { + "Int32": 1 } } }, @@ -74364,6 +80109,9 @@ 255 ] }, + "ComponentIndex": { + "Int32": -1 + }, "CustomPhysicalProperties": { "PhysicalProperties": "Default" }, @@ -74391,6 +80139,9 @@ "HistoryId": { "UniqueId": "00000000000000000000000000000000" }, + "InertiaMigrated": { + "Bool": true + }, "InitialSize": { "Vector3": [ 1.0, @@ -74428,6 +80179,9 @@ "MeshData2": { "SharedString": "" }, + "OffCentered": { + "Bool": false + }, "PhysicalConfigData": { "SharedString": "" }, @@ -74527,27 +80281,27 @@ }, "UnscaledCofm": { "Vector3": [ - null, - null, - null + 0.0, + 0.0, + 0.0 ] }, "UnscaledVolInertiaDiags": { "Vector3": [ - null, - null, - null + 0.16666666, + 0.16666666, + 0.16666666 ] }, "UnscaledVolInertiaOffDiags": { "Vector3": [ - null, - null, - null + 0.0, + 0.0, + 0.0 ] }, "UnscaledVolume": { - "Float32": null + "Float32": 1.0 }, "UsePartColor": { "Bool": false @@ -74796,6 +80550,19 @@ } } }, + "BadgeVisible": { + "Name": "BadgeVisible", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "CameraMode": { "Name": "CameraMode", "Scriptability": "None", @@ -75312,6 +81079,19 @@ } } }, + "PeoplePageLayout": { + "Name": "PeoplePageLayout", + "Scriptability": "None", + "DataType": { + "Enum": "PeoplePageLayout" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "PerformanceStatsVisible": { "Name": "PerformanceStatsVisible", "Scriptability": "None", @@ -75343,6 +81123,32 @@ } } }, + "PlayerListVisible": { + "Name": "PlayerListVisible", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "PlayerNamesEnabled": { + "Name": "PlayerNamesEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "PreferredTextSize": { "Name": "PreferredTextSize", "Scriptability": "None", @@ -75416,6 +81222,22 @@ } } }, + "ReadAloud": { + "Name": "ReadAloud", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [ + "Hidden", + "NotReplicated" + ], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "ReducedMotion": { "Name": "ReducedMotion", "Scriptability": "None", @@ -75971,6 +81793,24 @@ "Value": "ContentId" }, "Tags": [], + "Kind": { + "Canonical": { + "Serialization": { + "Migrate": { + "To": "MouseIconContent", + "Migration": "ContentIdToContent" + } + } + } + } + }, + "MouseIconContent": { + "Name": "MouseIconContent", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Content" + }, + "Tags": [], "Kind": { "Canonical": { "Serialization": "Serializes" @@ -76087,6 +81927,22 @@ } } }, + "PreferredInput": { + "Name": "PreferredInput", + "Scriptability": "Read", + "DataType": { + "Enum": "PreferredInput" + }, + "Tags": [ + "NotReplicated", + "ReadOnly" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, "RightBarSize": { "Name": "RightBarSize", "Scriptability": "None", @@ -76137,6 +81993,22 @@ } } }, + "TouchScreenEnabled": { + "Name": "TouchScreenEnabled", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [ + "NotReplicated", + "ReadOnly" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, "UserHeadCFrame": { "Name": "UserHeadCFrame", "Scriptability": "Read", @@ -77407,6 +83279,49 @@ } } }, + "VideoCapture": { + "Name": "VideoCapture", + "Tags": [ + "NotCreatable", + "NotReplicated" + ], + "Superclass": "Capture", + "Properties": { + "FilePath": { + "Name": "FilePath", + "Scriptability": "None", + "DataType": { + "Value": "String" + }, + "Tags": [ + "NotReplicated", + "ReadOnly" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, + "TimeLength": { + "Name": "TimeLength", + "Scriptability": "Read", + "DataType": { + "Value": "Float64" + }, + "Tags": [ + "NotReplicated", + "ReadOnly" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + } + }, + "DefaultProperties": {} + }, "VideoCaptureService": { "Name": "VideoCaptureService", "Tags": [ @@ -77958,6 +83873,24 @@ "Value": "ContentId" }, "Tags": [], + "Kind": { + "Canonical": { + "Serialization": { + "Migrate": { + "To": "VideoContent", + "Migration": "ContentIdToContent" + } + } + } + } + }, + "VideoContent": { + "Name": "VideoContent", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Content" + }, + "Tags": [], "Kind": { "Canonical": { "Serialization": "Serializes" @@ -78113,8 +84046,8 @@ "UniqueId": { "UniqueId": "00000000000000000000000000000000" }, - "Video": { - "ContentId": "" + "VideoContent": { + "Content": "None" }, "Visible": { "Bool": true @@ -78134,19 +84067,6 @@ ], "Superclass": "Instance", "Properties": { - "Asset": { - "Name": "Asset", - "Scriptability": "ReadWrite", - "DataType": { - "Value": "ContentId" - }, - "Tags": [], - "Kind": { - "Canonical": { - "Serialization": "Serializes" - } - } - }, "AutoLoadInStudio": { "Name": "AutoLoadInStudio", "Scriptability": "None", @@ -78237,6 +84157,22 @@ } } }, + "PlayingReplicating": { + "Name": "PlayingReplicating", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [ + "Hidden", + "NotScriptable" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, "Resolution": { "Name": "Resolution", "Scriptability": "Read", @@ -78282,6 +84218,19 @@ } } }, + "VideoContent": { + "Name": "VideoContent", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Content" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "Volume": { "Name": "Volume", "Scriptability": "ReadWrite", @@ -78300,9 +84249,6 @@ "Archivable": { "Bool": true }, - "Asset": { - "ContentId": "" - }, "Attributes": { "Attributes": {} }, @@ -78333,6 +84279,9 @@ "UniqueId": { "UniqueId": "00000000000000000000000000000000" }, + "VideoContent": { + "Content": "None" + }, "Volume": { "Float32": 1.0 } @@ -79127,6 +85076,21 @@ } } }, + "UseStreamSwitching": { + "Name": "UseStreamSwitching", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [ + "Hidden" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, "VoiceChatEnabledForPlaceOnRcc": { "Name": "VoiceChatEnabledForPlaceOnRcc", "Scriptability": "None", @@ -79252,6 +85216,33 @@ } } }, + "WebStreamClient": { + "Name": "WebStreamClient", + "Tags": [ + "NotCreatable", + "NotReplicated" + ], + "Superclass": "Object", + "Properties": { + "ConnectionState": { + "Name": "ConnectionState", + "Scriptability": "Read", + "DataType": { + "Enum": "WebStreamClientState" + }, + "Tags": [ + "NotReplicated", + "ReadOnly" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + } + }, + "DefaultProperties": {} + }, "WebViewService": { "Name": "WebViewService", "Tags": [ @@ -80058,6 +86049,19 @@ } } }, + "AirTurbulenceIntensity": { + "Name": "AirTurbulenceIntensity", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Float32" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "AllowThirdPartySales": { "Name": "AllowThirdPartySales", "Scriptability": "ReadWrite", @@ -80073,6 +86077,19 @@ } } }, + "AuthorityMode": { + "Name": "AuthorityMode", + "Scriptability": "ReadWrite", + "DataType": { + "Enum": "AuthorityMode" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "AvatarUnificationMode": { "Name": "AvatarUnificationMode", "Scriptability": "None", @@ -80135,6 +86152,22 @@ } } }, + "ConvexDecompCompressed": { + "Name": "ConvexDecompCompressed", + "Scriptability": "None", + "DataType": { + "Value": "Bool" + }, + "Tags": [ + "Hidden", + "NotScriptable" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, "CurrentCamera": { "Name": "CurrentCamera", "Scriptability": "ReadWrite", @@ -80328,6 +86361,19 @@ } } }, + "LuauTypeCheckMode": { + "Name": "LuauTypeCheckMode", + "Scriptability": "ReadWrite", + "DataType": { + "Enum": "LuauTypeCheckMode" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, "MeshPartHeadsAndAccessories": { "Name": "MeshPartHeadsAndAccessories", "Scriptability": "None", @@ -80706,12 +86752,45 @@ "Serialization": "Serializes" } } + }, + "UseImprovedModelLod": { + "Name": "UseImprovedModelLod", + "Scriptability": "None", + "DataType": { + "Enum": "RolloutState" + }, + "Tags": [ + "NotScriptable" + ], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "UseNewLuauTypeSolver": { + "Name": "UseNewLuauTypeSolver", + "Scriptability": "None", + "DataType": { + "Enum": "RolloutState" + }, + "Tags": [ + "NotScriptable" + ], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } } }, "DefaultProperties": { "AirDensity": { "Float32": 0.0012 }, + "AirTurbulenceIntensity": { + "Float32": 0.0 + }, "AllowThirdPartySales": { "Bool": false }, @@ -80721,6 +86800,9 @@ "Attributes": { "Attributes": {} }, + "AuthorityMode": { + "Enum": 1 + }, "AvatarUnificationMode": { "Enum": 0 }, @@ -80767,6 +86849,9 @@ "LevelOfDetail": { "Enum": 0 }, + "LuauTypeCheckMode": { + "Enum": 0 + }, "MeshPartHeadsAndAccessories": { "Enum": 0 }, @@ -80890,6 +86975,12 @@ "UniqueId": { "UniqueId": "00000000000000000000000000000000" }, + "UseImprovedModelLod": { + "Enum": 0 + }, + "UseNewLuauTypeSolver": { + "Enum": 0 + }, "WorldPivotData": { "OptionalCFrame": null } @@ -81032,7 +87123,24 @@ "NotCreatable" ], "Superclass": "Model", - "Properties": {}, + "Properties": { + "PhysicsStepTime": { + "Name": "PhysicsStepTime", + "Scriptability": "None", + "DataType": { + "Value": "Float32" + }, + "Tags": [ + "Hidden", + "NotReplicated" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + } + }, "DefaultProperties": { "Archivable": { "Bool": true @@ -81223,6 +87331,36 @@ } } }, + "MaxSize": { + "Name": "MaxSize", + "Scriptability": "None", + "DataType": { + "Value": "Vector3" + }, + "Tags": [ + "Hidden" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, + "Offset": { + "Name": "Offset", + "Scriptability": "None", + "DataType": { + "Value": "Vector3" + }, + "Tags": [ + "Hidden" + ], + "Kind": { + "Canonical": { + "Serialization": "DoesNotSerialize" + } + } + }, "Order": { "Name": "Order", "Scriptability": "ReadWrite", @@ -81653,6 +87791,95 @@ "UniqueId": "00000000000000000000000000000000" } } + }, + "WrapTextureTransfer": { + "Name": "WrapTextureTransfer", + "Tags": [ + "NotBrowsable" + ], + "Superclass": "Instance", + "Properties": { + "ReferenceCageMeshContent": { + "Name": "ReferenceCageMeshContent", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Content" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "UVMaxBound": { + "Name": "UVMaxBound", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Vector2" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + }, + "UVMinBound": { + "Name": "UVMinBound", + "Scriptability": "ReadWrite", + "DataType": { + "Value": "Vector2" + }, + "Tags": [], + "Kind": { + "Canonical": { + "Serialization": "Serializes" + } + } + } + }, + "DefaultProperties": { + "Archivable": { + "Bool": true + }, + "Attributes": { + "Attributes": {} + }, + "Capabilities": { + "SecurityCapabilities": 0 + }, + "HistoryId": { + "UniqueId": "00000000000000000000000000000000" + }, + "ReferenceCageMeshContent": { + "Content": "None" + }, + "Sandboxed": { + "Bool": false + }, + "SourceAssetId": { + "Int64": -1 + }, + "Tags": { + "Tags": [] + }, + "UVMaxBound": { + "Vector2": [ + null, + null + ] + }, + "UVMinBound": { + "Vector2": [ + null, + null + ] + }, + "UniqueId": { + "UniqueId": "00000000000000000000000000000000" + } + } } }, "Enums": { @@ -81810,6 +88037,16 @@ "Never": 1 } }, + "AdornShading": { + "name": "AdornShading", + "items": { + "AlwaysOnTop": 4, + "Default": 0, + "Shaded": 1, + "XRay": 2, + "XRayShaded": 3 + } + }, "AlignType": { "name": "AlignType", "items": { @@ -81930,6 +88167,14 @@ "Enabled": 2 } }, + "AnnotationChannelContentPreference": { + "name": "AnnotationChannelContentPreference", + "items": { + "All": 1, + "None": 0, + "Unknown": 2 + } + }, "AnnotationEditingMode": { "name": "AnnotationEditingMode", "items": { @@ -81938,6 +88183,15 @@ "WritingNew": 2 } }, + "AnnotationPlaceContentPreference": { + "name": "AnnotationPlaceContentPreference", + "items": { + "All": 1, + "MentionsAndReplies": 2, + "None": 0, + "Unknown": 3 + } + }, "AnnotationRequestStatus": { "name": "AnnotationRequestStatus", "items": { @@ -81999,6 +88253,7 @@ "name": "AppUpdateStatus", "items": { "Available": 4, + "AvailableBetaProgram": 6, "AvailableBoundChannel": 5, "Failed": 2, "NotAvailable": 3, @@ -82117,6 +88372,10 @@ "Enabled": 2 } }, + "AudioCaptureMode": { + "name": "AudioCaptureMode", + "items": {} + }, "AudioChannelLayout": { "name": "AudioChannelLayout", "items": { @@ -82168,6 +88427,13 @@ "Small": 0 } }, + "AuthorityMode": { + "name": "AuthorityMode", + "items": { + "Automatic": 1, + "Server": 0 + } + }, "AutoIndentRule": { "name": "AutoIndentRule", "items": { @@ -82282,6 +88548,116 @@ "Success": 1 } }, + "AvatarSettingsAccessoryLimitMethod": { + "name": "AvatarSettingsAccessoryLimitMethod", + "items": { + "PreviewRemove": 3, + "PreviewScale": 2, + "Remove": 1, + "Scale": 0 + } + }, + "AvatarSettingsAccessoryMode": { + "name": "AvatarSettingsAccessoryMode", + "items": { + "CustomLimit": 1, + "PlayerChoice": 0 + } + }, + "AvatarSettingsAnimationClipsMode": { + "name": "AvatarSettingsAnimationClipsMode", + "items": { + "CustomClips": 1, + "PlayerChoice": 0 + } + }, + "AvatarSettingsAnimationPacksMode": { + "name": "AvatarSettingsAnimationPacksMode", + "items": { + "PlayerChoice": 0, + "StandardR15": 1, + "StandardR6": 2 + } + }, + "AvatarSettingsAppearanceMode": { + "name": "AvatarSettingsAppearanceMode", + "items": { + "CustomBody": 2, + "CustomParts": 1, + "PlayerChoice": 0 + } + }, + "AvatarSettingsBuildMode": { + "name": "AvatarSettingsBuildMode", + "items": { + "CustomBuild": 1, + "PlayerChoice": 0 + } + }, + "AvatarSettingsClothingMode": { + "name": "AvatarSettingsClothingMode", + "items": { + "CustomLimit": 1, + "PlayerChoice": 0 + } + }, + "AvatarSettingsCollisionMode": { + "name": "AvatarSettingsCollisionMode", + "items": { + "Default": 0, + "Legacy": 2, + "SingleCollider": 1 + } + }, + "AvatarSettingsCustomAccessoryMode": { + "name": "AvatarSettingsCustomAccessoryMode", + "items": { + "CustomAccessories": 1, + "PlayerChoice": 0 + } + }, + "AvatarSettingsCustomBodyType": { + "name": "AvatarSettingsCustomBodyType", + "items": { + "AvatarReference": 0, + "BundleId": 1 + } + }, + "AvatarSettingsCustomClothingMode": { + "name": "AvatarSettingsCustomClothingMode", + "items": { + "CustomClothing": 1, + "PlayerChoice": 0 + } + }, + "AvatarSettingsHitAndTouchDetectionMode": { + "name": "AvatarSettingsHitAndTouchDetectionMode", + "items": { + "UseCollider": 1, + "UseParts": 0 + } + }, + "AvatarSettingsJumpMode": { + "name": "AvatarSettingsJumpMode", + "items": { + "JumpHeight": 0, + "JumpPower": 1 + } + }, + "AvatarSettingsLegacyCollisionMode": { + "name": "AvatarSettingsLegacyCollisionMode", + "items": { + "InnerBoxColliders": 1, + "R6Colliders": 0 + } + }, + "AvatarSettingsScaleMode": { + "name": "AvatarSettingsScaleMode", + "items": { + "CustomScale": 1, + "PlayerChoice": 0 + } + }, "AvatarThumbnailCustomizationType": { "name": "AvatarThumbnailCustomizationType", "items": { @@ -82364,6 +88740,14 @@ "Outline": 0 } }, + "BorderStrokePosition": { + "name": "BorderStrokePosition", + "items": { + "Center": 1, + "Inner": 2, + "Outer": 0 + } + }, "BreakReason": { "name": "BreakReason", "items": { @@ -82458,6 +88842,19 @@ "Watch": 2 } }, + "CaptureGalleryPermission": { + "name": "CaptureGalleryPermission", + "items": { + "ReadAndUpload": 0 + } + }, + "CaptureType": { + "name": "CaptureType", + "items": { + "Screenshot": 1, + "Video": 2 + } + }, "CatalogCategoryFilter": { "name": "CatalogCategoryFilter", "items": { @@ -82751,8 +89148,12 @@ "ConnectionError": { "name": "ConnectionError", "items": { + "AlreadyConnected": 3, "AndroidAnticheatKick": 304, "AndroidEmulatorKick": 305, + "AndroidRootedKick": 306, + "ConnectErrors": 2, + "ConnectionBanned": 5, "DisconnectBadhash": 257, "DisconnectBlockedIP": 282, "DisconnectBySecurityPolicy": 281, @@ -82788,12 +89189,18 @@ "DisconnectSendPacketError": 262, "DisconnectTimeout": 266, "DisconnectWrongVersion": 280, + "DisconnectionNotification": 308, + "IPRecentlyConnected": 8, + "IncompatibleProtocolVersion": 7, + "InvalidPassword": 6, "NetworkInternal": 296, "NetworkMisbehavior": 299, "NetworkSecurity": 300, "NetworkSend": 297, "NetworkTimeout": 298, + "NoFreeIncomingConnections": 4, "OK": 0, + "OurSystemRequiresSecurity": 9, "PhantomFreeze": 303, "PlacelaunchCreatorBan": 600, "PlacelaunchCustomMessage": 610, @@ -82815,6 +89222,8 @@ "PlayerRemoved": 291, "ReplacementReady": 301, "ReplicatorTimeout": 290, + "ScreentimeLockoutKick": 307, + "SecurityKeyMismatch": 10, "ServerEmpty": 302, "ServerShutdown": 288, "TeleportErrors": 768, @@ -82840,6 +89249,7 @@ "items": { "None": 0, "Object": 2, + "Opaque": 3, "Uri": 1 } }, @@ -83044,10 +89454,12 @@ "name": "DeveloperMemoryTag", "items": { "Animation": 22, + "BaseParts": 7, "GeometryCSG": 24, "GraphicsMeshParts": 10, "GraphicsParticles": 11, "GraphicsParts": 12, + "GraphicsSlimModels": 25, "GraphicsSolidModels": 8, "GraphicsSpatialHash": 13, "GraphicsTerrain": 14, @@ -83060,7 +89472,6 @@ "LuaHeap": 4, "Navigation": 23, "PhysicsCollision": 6, - "PhysicsParts": 7, "Script": 5, "Signals": 3, "Sounds": 17, @@ -83124,6 +89535,14 @@ "Neutral": 0 } }, + "DisplaySize": { + "name": "DisplaySize", + "items": { + "Large": 2, + "Medium": 1, + "Small": 0 + } + }, "DominantAxis": { "name": "DominantAxis", "items": { @@ -83251,6 +89670,30 @@ "DefaultScope": 0 } }, + "ExperienceStateRecordingLoadMode": { + "name": "ExperienceStateRecordingLoadMode", + "items": { + "ContiguousSlice": 1, + "NewReplay": 0, + "NoncontiguousSlice": 2 + } + }, + "ExperienceStateRecordingLoadSourceType": { + "name": "ExperienceStateRecordingLoadSourceType", + "items": { + "File": 1, + "S3Url": 0 + } + }, + "ExperienceStateRecordingPlaybackMode": { + "name": "ExperienceStateRecordingPlaybackMode", + "items": { + "Playing": 2, + "Rewinding": 3, + "Stopped": 1, + "Undefined": 0 + } + }, "ExplosionType": { "name": "ExplosionType", "items": { @@ -83339,15 +89782,11 @@ "UpperLipSuck": 10 } }, - "FeedRankingScoreType": { - "name": "FeedRankingScoreType", + "FeatureRestrictionAbuseVector": { + "name": "FeatureRestrictionAbuseVector", "items": { - "Content": 0, - "Final": 1, - "GameJoin": 2, - "Interaction": 3, - "Invalid": 4, - "Sharing": 5 + "Communication": 1, + "ExperienceChat": 0 } }, "FieldOfViewMode": { @@ -83676,6 +90115,14 @@ "PlayerNameplates": 2 } }, + "HandRigDescriptionSide": { + "name": "HandRigDescriptionSide", + "items": { + "Left": 1, + "None": 0, + "Right": 2 + } + }, "HandlesStyle": { "name": "HandlesStyle", "items": { @@ -83751,12 +90198,15 @@ "items": { "Aborted": 9, "ConnectFail": 3, + "ConnectionClosed": 13, + "CreatorEnvironmentsNotSupportedByService": 15, "DnsResolve": 2, "InvalidRedirect": 7, "InvalidUrl": 1, "NetFail": 8, "OK": 0, "OutOfMemory": 4, + "ServerProtocolError": 14, "SslConnectFail": 10, "SslVerificationFail": 11, "TimedOut": 5, @@ -83915,8 +90365,10 @@ "name": "InputActionType", "items": { "Bool": 0, + "Direction1D": 1, "Direction2D": 2, - "Float": 1 + "Direction3D": 3, + "ViewportPosition": 4 } }, "InputType": { @@ -84083,6 +90535,7 @@ "MouseLeftButton": 1018, "MouseMiddleButton": 1020, "MouseNoButton": 1022, + "MousePosition": 1025, "MouseRightButton": 1019, "MouseX": 1023, "MouseY": 1024, @@ -84386,6 +90839,14 @@ "ObjectPosition": 2 } }, + "LuauTypeCheckMode": { + "name": "LuauTypeCheckMode", + "items": { + "NoCheck": 0, + "Nonstrict": 1, + "Strict": 2 + } + }, "MarketplaceBulkPurchasePromptStatus": { "name": "MarketplaceBulkPurchasePromptStatus", "items": { @@ -84643,6 +91104,14 @@ "OccludeAll": 2 } }, + "NegateOperationHiddenHistory": { + "name": "NegateOperationHiddenHistory", + "items": { + "NegatedIntersection": 2, + "NegatedUnion": 1, + "None": 0 + } + }, "NetworkOwnership": { "name": "NetworkOwnership", "items": { @@ -84713,7 +91182,8 @@ "items": { "All": 1, "Avatar": 2, - "DynamicHead": 3 + "DynamicHead": 3, + "Shoes": 4 } }, "OutputLayoutMode": { @@ -84779,6 +91249,7 @@ "ParticleFlipbookLayout": { "name": "ParticleFlipbookLayout", "items": { + "Custom": 4, "Grid2x2": 1, "Grid4x4": 2, "Grid8x8": 3, @@ -84838,6 +91309,22 @@ "Enabled": 2 } }, + "PeoplePageLayout": { + "name": "PeoplePageLayout", + "items": { + "Card": 0, + "List": 1 + } + }, + "PerformanceOverlayMode": { + "name": "PerformanceOverlayMode", + "items": { + "Decals": 2, + "Lights": 3, + "Overdraw": 0, + "Transparent": 1 + } + }, "PermissionLevelShown": { "name": "PermissionLevelShown", "items": { @@ -84864,6 +91351,23 @@ "Fixed": 1 } }, + "PlaceContentPreference": { + "name": "PlaceContentPreference", + "items": { + "All": 1, + "MentionsAndReplies": 2, + "None": 0, + "Unknown": 3 + } + }, + "PlacePublishType": { + "name": "PlacePublishType", + "items": { + "None": 0, + "Publish": 1, + "Save": 2 + } + }, "Platform": { "name": "Platform", "items": { @@ -84945,6 +91449,14 @@ "Kick": 2 } }, + "PlayerExitReason": { + "name": "PlayerExitReason", + "items": { + "CreatorKick": 2, + "PlatformKick": 1, + "Unknown": 0 + } + }, "PoseEasingDirection": { "name": "PoseEasingDirection", "items": { @@ -84971,6 +91483,31 @@ "TwoAttachment": 1 } }, + "PredictionMode": { + "name": "PredictionMode", + "items": { + "Automatic": 0, + "Off": 2, + "On": 1 + } + }, + "PredictionState": { + "name": "PredictionState", + "items": { + "Idle": 0, + "Resimulating": 3, + "RollingBack": 2, + "Simulating": 1 + } + }, + "PreferredInput": { + "name": "PreferredInput", + "items": { + "Gamepad": 1, + "KeyboardAndMouse": 0, + "Touch": 2 + } + }, "PreferredTextSize": { "name": "PreferredTextSize", "items": { @@ -85036,10 +91573,14 @@ "PromptCreateAssetResult": { "name": "PromptCreateAssetResult", "items": { + "ModeratedName": 8, "NoUserInput": 5, "PermissionDenied": 2, + "PurchaseFailure": 9, "Success": 1, "Timeout": 3, + "TokenInvalid": 10, + "UGCValidationFailed": 7, "UnknownFailure": 6, "UploadFailed": 4 } @@ -85061,6 +91602,14 @@ "UploadFailed": 4 } }, + "PromptLinkSharingResult": { + "name": "PromptLinkSharingResult", + "items": { + "InvalidLaunchData": 3, + "PlayerLeft": 2, + "Success": 1 + } + }, "PromptPublishAssetResult": { "name": "PromptPublishAssetResult", "items": { @@ -85144,6 +91693,55 @@ "Include": 1 } }, + "ReadCapturesFromGalleryResult": { + "name": "ReadCapturesFromGalleryResult", + "items": { + "NeedPermission": 1, + "Success": 0 + } + }, + "RecommendationActionType": { + "name": "RecommendationActionType", + "items": { + "AddReaction": 0, + "Comment": 4, + "Play": 5, + "Purchase": 6, + "RemoveReaction": 1, + "Report": 3, + "Share": 2 + } + }, + "RecommendationDepartureIntent": { + "name": "RecommendationDepartureIntent", + "items": { + "Negative": 2, + "Neutral": 0, + "Positive": 1 + } + }, + "RecommendationImpressionType": { + "name": "RecommendationImpressionType", + "items": { + "NotViewable": 1, + "View": 0 + } + }, + "RecommendationItemContentType": { + "name": "RecommendationItemContentType", + "items": { + "Dynamic": 1, + "Interactive": 2, + "Static": 0 + } + }, + "RecommendationItemVisibility": { + "name": "RecommendationItemVisibility", + "items": { + "Private": 0, + "Public": 1 + } + }, "RejectCharacterDeletions": { "name": "RejectCharacterDeletions", "items": { @@ -85324,6 +91922,14 @@ "MovementRelative": 0 } }, + "RsvpStatus": { + "name": "RsvpStatus", + "items": { + "Going": 1, + "None": 0, + "NotGoing": 2 + } + }, "RtlTextSupport": { "name": "RtlTextSupport", "items": { @@ -85688,6 +92294,13 @@ "Disabled": 1 } }, + "StrokeSizingMode": { + "name": "StrokeSizingMode", + "items": { + "FixedSize": 0, + "ScaledSize": 1 + } + }, "StudioCloseMode": { "name": "StudioCloseMode", "items": { @@ -86029,9 +92642,10 @@ "TeamCreateErrorState": { "name": "TeamCreateErrorState", "items": { - "NoError": 2, + "NoError": 3, "PlaceSizeApproachingLimit": 1, - "PlaceSizeTooLarge": 0 + "PlaceSizeTooLarge": 0, + "PlaceUploadFailing": 2 } }, "Technology": { @@ -86467,6 +93081,26 @@ "UiMessageInfo": 1 } }, + "UpdateState": { + "name": "UpdateState", + "items": { + "UpdateAvailable": 1, + "UpdateFailed": 4, + "UpdateInProgress": 2, + "UpdateNotAvailable": 0, + "UpdateReady": 3 + } + }, + "UploadCaptureResult": { + "name": "UploadCaptureResult", + "items": { + "CaptureModerated": 2, + "CaptureNotInGallery": 3, + "IneligibleCapture": 4, + "NeedPermission": 1, + "Success": 0 + } + }, "UsageContext": { "name": "UsageContext", "items": { @@ -86627,6 +93261,25 @@ "Small": 1 } }, + "VideoCaptureResult": { + "name": "VideoCaptureResult", + "items": { + "OtherError": 1, + "ScreenSizeChanged": 2, + "Success": 0, + "TimeLimitReached": 3 + } + }, + "VideoCaptureStartedResult": { + "name": "VideoCaptureStartedResult", + "items": { + "CapturingAlready": 2, + "NoDeviceSupport": 3, + "NoSpaceOnDevice": 4, + "OtherError": 1, + "Success": 0 + } + }, "VideoDeviceCaptureQuality": { "name": "VideoDeviceCaptureQuality", "items": { @@ -86704,6 +93357,20 @@ "Leaving": 4 } }, + "VoiceClientLeaveReasons": { + "name": "VoiceClientLeaveReasons", + "items": { + "ClientNetworkDisconnected": 1, + "ClientShutdown": 3, + "ImguiDebugLeave": 7, + "LuaInitiated": 8, + "PlayerLeft": 2, + "PublishFailed": 4, + "RejoinReceived": 5, + "Unknown": 0, + "VoiceReboot": 6 + } + }, "VoiceControlPath": { "name": "VoiceControlPath", "items": { @@ -86750,6 +93417,22 @@ "Open": 1 } }, + "WebStreamClientState": { + "name": "WebStreamClientState", + "items": { + "Closed": 3, + "Connecting": 0, + "Error": 2, + "Open": 1 + } + }, + "WebStreamClientType": { + "name": "WebStreamClientType", + "items": { + "RawStream": 1, + "SSE": 0 + } + }, "WeldConstraintPreserve": { "name": "WeldConstraintPreserve", "items": { @@ -86787,6 +93470,7 @@ "LayerCageFittedToPrev": 13, "None": 0, "OuterCage": 6, + "PreWrapDeformerOuterCage": 14, "Rbf": 5, "Reference": 4, "ReferenceMeshAfterMorph": 7 @@ -86797,6 +93481,7 @@ "items": { "None": 0, "OuterCageDetail": 8, + "PreWrapDeformerCage": 9, "Rbf": 7, "TargetCageCompressed": 2, "TargetCageInterface": 3, diff --git a/src/resolution.rs b/src/resolution.rs index 73ae3676..cb6bd057 100644 --- a/src/resolution.rs +++ b/src/resolution.rs @@ -62,7 +62,7 @@ impl AmbiguousValue { match &property.data_type { DataType::Enum(enum_name) => { - let database = rbx_reflection_database::get(); + let database = rbx_reflection_database::get().unwrap(); let enum_descriptor = database.enums.get(enum_name).ok_or_else(|| { format_err!("Unknown enum {}. This is a Rojo bug!", enum_name) @@ -203,7 +203,7 @@ fn find_descriptor( class_name: &str, prop_name: &str, ) -> Option<&'static PropertyDescriptor<'static>> { - let database = rbx_reflection_database::get(); + let database = rbx_reflection_database::get().unwrap(); let mut current_class_name = class_name; loop { diff --git a/src/snapshot_middleware/lua.rs b/src/snapshot_middleware/lua.rs index bea6f043..c4174cf0 100644 --- a/src/snapshot_middleware/lua.rs +++ b/src/snapshot_middleware/lua.rs @@ -31,6 +31,7 @@ pub fn snapshot_lua( script_type: ScriptType, ) -> anyhow::Result> { let run_context_enums = &rbx_reflection_database::get() + .unwrap() .enums .get("RunContext") .expect("Unable to get RunContext enums!") diff --git a/src/snapshot_middleware/project.rs b/src/snapshot_middleware/project.rs index 819e57b3..7d951f86 100644 --- a/src/snapshot_middleware/project.rs +++ b/src/snapshot_middleware/project.rs @@ -313,7 +313,7 @@ fn infer_class_name(name: &str, parent_class: Option<&str>) -> Option { // Members of DataModel with names that match known services are // probably supposed to be those services. - let descriptor = rbx_reflection_database::get().classes.get(name)?; + let descriptor = rbx_reflection_database::get().unwrap().classes.get(name)?; if descriptor.tags.contains(&ClassTag::Service) { return Some(ustr(name)); From cd78f5c02c876d74a8e94945b906a5f07cac2738 Mon Sep 17 00:00:00 2001 From: boatbomber Date: Tue, 14 Oct 2025 12:13:59 -0700 Subject: [PATCH 21/22] Fix postcommit callbacks being skipped (#1132) --- CHANGELOG.md | 6 +++++ plugin/src/App/init.lua | 52 ++++++++++++++----------------------- plugin/src/ServeSession.lua | 27 +++++++------------ 3 files changed, 34 insertions(+), 51 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4422d9be..eb83f987 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Rojo Changelog +## Unreleased + +* Fixed a bug where the last sync timestamp was not updating correctly in the plugin ([#1132]) + +[#1132]: https://github.com/rojo-rbx/rojo/pull/1132 + ## 7.6.0 - October 10th, 2025 * Added flag to `rojo init` to skip initializing a git repository ([#1122]) * Added fallback method for when an Instance can't be synced through normal means ([#1030]) diff --git a/plugin/src/App/init.lua b/plugin/src/App/init.lua index ae03a6f1..f38596dd 100644 --- a/plugin/src/App/init.lua +++ b/plugin/src/App/init.lua @@ -602,46 +602,32 @@ function App:startSession() }) end) self.cleanupPostcommit = serveSession:hookPostcommit(function(patch, instanceMap, unappliedPatch) - -- Update tree with unapplied metadata + local now = DateTime.now().UnixTimestamp self:setState(function(prevState) + local oldPatchData = prevState.patchData + local newPatchData = { + patch = patch, + unapplied = unappliedPatch, + timestamp = now, + } + + if PatchSet.isEmpty(patch) then + -- Keep existing patch info, but use new timestamp + newPatchData.patch = oldPatchData.patch + newPatchData.unapplied = oldPatchData.unapplied + elseif now - oldPatchData.timestamp < 2 then + -- Patches that apply in the same second are combined for human clarity + newPatchData.patch = PatchSet.assign(PatchSet.newEmpty(), oldPatchData.patch, patch) + newPatchData.unapplied = PatchSet.assign(PatchSet.newEmpty(), oldPatchData.unapplied, unappliedPatch) + end + return { patchTree = PatchTree.updateMetadata(prevState.patchTree, patch, instanceMap, unappliedPatch), + patchData = newPatchData, } end) end) - serveSession:hookPostcommit(function(patch, _instanceMap, unapplied) - local now = DateTime.now().UnixTimestamp - local old = self.state.patchData - - if PatchSet.isEmpty(patch) then - -- Ignore empty patch, but update timestamp - self:setState({ - patchData = { - patch = old.patch, - unapplied = old.unapplied, - timestamp = now, - }, - }) - return - end - - if now - old.timestamp < 2 then - -- Patches that apply in the same second are - -- considered to be part of the same change for human clarity - patch = PatchSet.assign(PatchSet.newEmpty(), old.patch, patch) - unapplied = PatchSet.assign(PatchSet.newEmpty(), old.unapplied, unapplied) - end - - self:setState({ - patchData = { - patch = patch, - unapplied = unapplied, - timestamp = now, - }, - }) - end) - serveSession:onStatusChanged(function(status, details) if status == ServeSession.Status.Connecting then if self.dismissSyncReminder then diff --git a/plugin/src/ServeSession.lua b/plugin/src/ServeSession.lua index 9994be10..38971379 100644 --- a/plugin/src/ServeSession.lua +++ b/plugin/src/ServeSession.lua @@ -349,18 +349,11 @@ function ServeSession:__applyPatch(patch) error(unappliedPatch) end - if PatchSet.isEmpty(unappliedPatch) then - if historyRecording then - ChangeHistoryService:FinishRecording(historyRecording, Enum.FinishRecordingOperation.Commit) - end - return - end + if Settings:get("enableSyncFallback") and not PatchSet.isEmpty(unappliedPatch) then + -- Some changes did not apply, let's try replacing them instead + local addedIdList = PatchSet.addedIdList(unappliedPatch) + local updatedIdList = PatchSet.updatedIdList(unappliedPatch) - local addedIdList = PatchSet.addedIdList(unappliedPatch) - local updatedIdList = PatchSet.updatedIdList(unappliedPatch) - - local actualUnappliedPatches = PatchSet.newEmpty() - if Settings:get("enableSyncFallback") then Log.debug("ServeSession:__replaceInstances(unappliedPatch.added)") Timer.start("ServeSession:__replaceInstances(unappliedPatch.added)") local addSuccess, unappliedAddedRefs = self:__replaceInstances(addedIdList) @@ -371,20 +364,18 @@ function ServeSession:__applyPatch(patch) local updateSuccess, unappliedUpdateRefs = self:__replaceInstances(updatedIdList) Timer.stop() + -- Update the unapplied patch to reflect which Instances were replaced successfully if addSuccess then table.clear(unappliedPatch.added) - PatchSet.assign(actualUnappliedPatches, unappliedAddedRefs) + PatchSet.assign(unappliedPatch, unappliedAddedRefs) end if updateSuccess then table.clear(unappliedPatch.updated) - PatchSet.assign(actualUnappliedPatches, unappliedUpdateRefs) + PatchSet.assign(unappliedPatch, unappliedUpdateRefs) end - else - Log.debug("Skipping ServeSession:__replaceInstances because of setting") end - PatchSet.assign(actualUnappliedPatches, unappliedPatch) - if not PatchSet.isEmpty(actualUnappliedPatches) then + if not PatchSet.isEmpty(unappliedPatch) then Log.debug( "Could not apply all changes requested by the Rojo server:\n{}", PatchSet.humanSummary(self.__instanceMap, unappliedPatch) @@ -396,7 +387,7 @@ function ServeSession:__applyPatch(patch) -- guaranteed to be called after the commit for _, callback in self.__postcommitCallbacks do task.spawn(function() - local success, err = pcall(callback, patch, self.__instanceMap, actualUnappliedPatches) + local success, err = pcall(callback, patch, self.__instanceMap, unappliedPatch) if not success then Log.warn("Postcommit hook errored: {}", err) end From 181cc377442a8b9b18447639d24472cb558104bd Mon Sep 17 00:00:00 2001 From: boatbomber Date: Mon, 20 Oct 2025 20:13:47 -0700 Subject: [PATCH 22/22] Improve sync fallback robustness (#1135) --- CHANGELOG.md | 2 ++ plugin/src/ServeSession.lua | 46 ++++++++++++++++++++++++++++++++++--- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eb83f987..a97a2f67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,10 @@ ## Unreleased * Fixed a bug where the last sync timestamp was not updating correctly in the plugin ([#1132]) +* Improved the reliability of sync replacements by adding better error handling and recovery ([#1135]) [#1132]: https://github.com/rojo-rbx/rojo/pull/1132 +[#1135]: https://github.com/rojo-rbx/rojo/pull/1135 ## 7.6.0 - October 10th, 2025 * Added flag to `rojo init` to skip initializing a git repository ([#1122]) diff --git a/plugin/src/ServeSession.lua b/plugin/src/ServeSession.lua index 38971379..749c9188 100644 --- a/plugin/src/ServeSession.lua +++ b/plugin/src/ServeSession.lua @@ -48,6 +48,12 @@ local function debugPatch(object) end) end +local function attemptReparent(instance, parent) + return pcall(function() + instance.Parent = parent + end) +end + local ServeSession = {} ServeSession.__index = ServeSession @@ -291,18 +297,52 @@ function ServeSession:__replaceInstances(idList) for id, replacement in replacements do local oldInstance = self.__instanceMap.fromIds[id] + if not oldInstance then + -- TODO: Why would this happen? + Log.warn("Instance {} not found in InstanceMap during sync replacement", id) + continue + end + self.__instanceMap:insert(id, replacement) Log.trace("Swapping Instance {} out via api/models/ endpoint", id) local oldParent = oldInstance.Parent for _, child in oldInstance:GetChildren() do - child.Parent = replacement + -- Some children cannot be reparented, such as a TouchTransmitter + local reparentSuccess, reparentError = attemptReparent(child, replacement) + if not reparentSuccess then + Log.warn( + "Could not reparent child {} of instance {} during sync replacement: {}", + child.Name, + oldInstance.Name, + reparentError + ) + end end - replacement.Parent = oldParent -- ChangeHistoryService doesn't like it if an Instance has been -- Destroyed. So, we have to accept the potential memory hit and -- just set the parent to `nil`. - oldInstance.Parent = nil + local deleteSuccess, deleteError = attemptReparent(oldInstance, nil) + local replaceSuccess, replaceError = attemptReparent(replacement, oldParent) + + if not (deleteSuccess and replaceSuccess) then + Log.warn( + "Could not swap instances {} and {} during sync replacement: {}", + oldInstance.Name, + replacement.Name, + (deleteError or "") .. "\n" .. (replaceError or "") + ) + + -- We need to revert the failed swap to avoid losing the old instance and children. + for _, child in replacement:GetChildren() do + attemptReparent(child, oldInstance) + end + attemptReparent(oldInstance, oldParent) + + -- Our replacement should never have existed in the first place, so we can just destroy it. + replacement:Destroy() + continue + end if selectionMap[oldInstance] then -- This is a bit funky, but it saves the order of Selection