mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-04-20 20:55:50 +00:00
Compare commits
73 Commits
v7.0.0-rc.
...
v7.2.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0987b44e23 | ||
|
|
58098e96d4 | ||
|
|
f649c180cf | ||
|
|
966478b131 | ||
|
|
ca0759a011 | ||
|
|
f1cdf2fe79 | ||
|
|
04fa5e2719 | ||
|
|
eccb95690c | ||
|
|
acf7456371 | ||
|
|
8ea41480b7 | ||
|
|
0d1bc0d7fe | ||
|
|
f9b7774286 | ||
|
|
2e672badf2 | ||
|
|
cd5d6fd15c | ||
|
|
cf76982cfa | ||
|
|
2624ea7d2a | ||
|
|
2e7c4b6dff | ||
|
|
e5dbee1073 | ||
|
|
c06463b61d | ||
|
|
10341e3776 | ||
|
|
824cdc5dcd | ||
|
|
7aa7a35aa5 | ||
|
|
79b57b3359 | ||
|
|
c7aeffe586 | ||
|
|
79c02f2457 | ||
|
|
b9ed68fa9e | ||
|
|
6c6d6c9c8d | ||
|
|
e169d7be68 | ||
|
|
192fd7d4dd | ||
|
|
1f1193e857 | ||
|
|
0a412ade88 | ||
|
|
3cef2fe9aa | ||
|
|
18e53f06fe | ||
|
|
eaac539087 | ||
|
|
57005c4fd5 | ||
|
|
ea58999a2a | ||
|
|
a5a69fd9fc | ||
|
|
f1d0f1c1c9 | ||
|
|
83492d7495 | ||
|
|
10abc2254a | ||
|
|
5d5536a95e | ||
|
|
fe81e55925 | ||
|
|
654690d73e | ||
|
|
256aba4bc1 | ||
|
|
49f8845105 | ||
|
|
12370846b4 | ||
|
|
07637dfe96 | ||
|
|
f389a4a1db | ||
|
|
af077c796c | ||
|
|
1c319f2fa8 | ||
|
|
e8afa03f7b | ||
|
|
9b22545842 | ||
|
|
adc733d25c | ||
|
|
6896257647 | ||
|
|
1d9845a6cb | ||
|
|
8461339e9a | ||
|
|
9904d94e4c | ||
|
|
da25c80d0b | ||
|
|
5fa63733fd | ||
|
|
8b54bf0ba1 | ||
|
|
173dc12cb3 | ||
|
|
e136529ff0 | ||
|
|
75542dacb3 | ||
|
|
07abfbde43 | ||
|
|
96112fe118 | ||
|
|
9d0b313261 | ||
|
|
277ddfa9be | ||
|
|
5d88bdb256 | ||
|
|
8d29b43155 | ||
|
|
cc071a6415 | ||
|
|
8954def25c | ||
|
|
d484098781 | ||
|
|
9f06cbf3a0 |
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
patreon: lpghatguy
|
||||||
42
.github/workflows/ci.yml
vendored
42
.github/workflows/ci.yml
vendored
@@ -11,29 +11,49 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
name: Build and Test
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
rust_version: [stable, "1.46.0"]
|
rust_version: [stable, 1.57.0]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
- name: Setup Rust toolchain
|
- name: Install Rust
|
||||||
run: rustup default ${{ matrix.rust_version }}
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: ${{ matrix.rust_version }}
|
||||||
|
override: true
|
||||||
|
profile: minimal
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cargo build --locked --verbose
|
run: cargo build --locked --verbose
|
||||||
|
|
||||||
- name: Run tests
|
- name: Test
|
||||||
run: cargo test --locked --verbose
|
run: cargo test --locked --verbose
|
||||||
|
|
||||||
- name: Rustfmt and Clippy
|
lint:
|
||||||
run: |
|
name: Rustfmt and Clippy
|
||||||
cargo fmt -- --check
|
runs-on: ubuntu-latest
|
||||||
cargo clippy
|
|
||||||
if: matrix.rust_version == 'stable'
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
|
- name: Install Rust
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
override: true
|
||||||
|
components: rustfmt, clippy
|
||||||
|
|
||||||
|
- name: Rustfmt
|
||||||
|
run: cargo fmt -- --check
|
||||||
|
|
||||||
|
- name: Clippy
|
||||||
|
run: cargo clippy
|
||||||
199
.github/workflows/release.yml
vendored
199
.github/workflows/release.yml
vendored
@@ -2,65 +2,152 @@ name: Release
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags: ["*"]
|
tags: ["v*"]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
windows:
|
create-release:
|
||||||
runs-on: windows-latest
|
name: Create Release
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v1
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
|
|
||||||
- name: Build release binary
|
|
||||||
run: cargo build --verbose --locked --release
|
|
||||||
|
|
||||||
- name: Upload artifacts
|
|
||||||
uses: actions/upload-artifact@v1
|
|
||||||
with:
|
|
||||||
name: rojo-win64
|
|
||||||
path: target/release/rojo.exe
|
|
||||||
|
|
||||||
macos:
|
|
||||||
runs-on: macos-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v1
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
|
|
||||||
- name: Install Rust
|
|
||||||
run: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
|
||||||
|
|
||||||
- name: Build release binary
|
|
||||||
run: |
|
|
||||||
source $HOME/.cargo/env
|
|
||||||
cargo build --verbose --locked --release
|
|
||||||
env:
|
|
||||||
OPENSSL_STATIC: 1
|
|
||||||
|
|
||||||
- name: Upload artifacts
|
|
||||||
uses: actions/upload-artifact@v1
|
|
||||||
with:
|
|
||||||
name: rojo-macos
|
|
||||||
path: target/release/rojo
|
|
||||||
|
|
||||||
linux:
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- name: Create Release
|
||||||
with:
|
id: create_release
|
||||||
submodules: true
|
uses: actions/create-release@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
tag_name: ${{ github.ref }}
|
||||||
|
release_name: ${{ github.ref }}
|
||||||
|
draft: true
|
||||||
|
prerelease: false
|
||||||
|
|
||||||
- name: Build
|
build-plugin:
|
||||||
run: cargo build --locked --verbose --release
|
needs: ["create-release"]
|
||||||
env:
|
name: Build Roblox Studio Plugin
|
||||||
OPENSSL_STATIC: 1
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Setup Foreman
|
||||||
uses: actions/upload-artifact@v1
|
uses: Roblox/setup-foreman@v1
|
||||||
with:
|
with:
|
||||||
name: rojo-linux
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
path: target/release/rojo
|
|
||||||
|
- name: Build Plugin
|
||||||
|
run: rojo build plugin --output Rojo.rbxm
|
||||||
|
|
||||||
|
- name: Upload Plugin to Release
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ needs.create-release.outputs.upload_url }}
|
||||||
|
asset_path: Rojo.rbxm
|
||||||
|
asset_name: Rojo.rbxm
|
||||||
|
asset_content_type: application/octet-stream
|
||||||
|
|
||||||
|
- name: Upload Plugin to Artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: Rojo.rbxm
|
||||||
|
path: Rojo.rbxm
|
||||||
|
|
||||||
|
build:
|
||||||
|
needs: ["create-release"]
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
# https://doc.rust-lang.org/rustc/platform-support.html
|
||||||
|
#
|
||||||
|
# FIXME: After the Rojo VS Code extension updates, add architecture
|
||||||
|
# names to each of these releases. We'll rename win64 to windows and add
|
||||||
|
# -x86_64 to each release.
|
||||||
|
include:
|
||||||
|
- host: linux
|
||||||
|
os: ubuntu-18.04
|
||||||
|
target: x86_64-unknown-linux-gnu
|
||||||
|
label: linux
|
||||||
|
|
||||||
|
- host: windows
|
||||||
|
os: windows-latest
|
||||||
|
target: x86_64-pc-windows-msvc
|
||||||
|
label: win64
|
||||||
|
|
||||||
|
- host: macos
|
||||||
|
os: macos-latest
|
||||||
|
target: x86_64-apple-darwin
|
||||||
|
label: macos
|
||||||
|
|
||||||
|
- host: macos
|
||||||
|
os: macos-latest
|
||||||
|
target: aarch64-apple-darwin
|
||||||
|
label: macos-aarch64
|
||||||
|
|
||||||
|
name: Build (${{ matrix.target }})
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
env:
|
||||||
|
BIN: rojo
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
|
||||||
|
- name: Get Version from Tag
|
||||||
|
shell: bash
|
||||||
|
# https://github.community/t/how-to-get-just-the-tag-name/16241/7#M1027
|
||||||
|
run: |
|
||||||
|
echo "PROJECT_VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_ENV
|
||||||
|
echo "Version is: ${{ env.PROJECT_VERSION }}"
|
||||||
|
|
||||||
|
- name: Install Rust
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
target: ${{ matrix.target }}
|
||||||
|
override: true
|
||||||
|
profile: minimal
|
||||||
|
|
||||||
|
- name: Build Release
|
||||||
|
run: cargo build --release --locked --verbose
|
||||||
|
env:
|
||||||
|
# Build into a known directory so we can find our build artifact more
|
||||||
|
# easily.
|
||||||
|
CARGO_TARGET_DIR: output
|
||||||
|
|
||||||
|
# On platforms that use OpenSSL, ensure it is statically linked to
|
||||||
|
# make binaries more portable.
|
||||||
|
OPENSSL_STATIC: 1
|
||||||
|
|
||||||
|
- name: Create Release Archive
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
mkdir staging
|
||||||
|
|
||||||
|
if [ "${{ matrix.host }}" = "windows" ]; then
|
||||||
|
cp "output/release/$BIN.exe" staging/
|
||||||
|
cd staging
|
||||||
|
7z a ../release.zip *
|
||||||
|
else
|
||||||
|
cp "output/release/$BIN" staging/
|
||||||
|
cd staging
|
||||||
|
zip ../release.zip *
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Upload Archive to Release
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ needs.create-release.outputs.upload_url }}
|
||||||
|
asset_path: release.zip
|
||||||
|
asset_name: ${{ env.BIN }}-${{ env.PROJECT_VERSION }}-${{ matrix.label }}.zip
|
||||||
|
asset_content_type: application/octet-stream
|
||||||
|
|
||||||
|
- name: Upload Archive to Artifacts
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: ${{ env.BIN }}-${{ env.PROJECT_VERSION }}-${{ matrix.label }}.zip
|
||||||
|
path: release.zip
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -19,6 +19,3 @@
|
|||||||
|
|
||||||
# Snapshot files from the 'insta' Rust crate
|
# Snapshot files from the 'insta' Rust crate
|
||||||
**/*.snap.new
|
**/*.snap.new
|
||||||
|
|
||||||
# Selene generates a roblox.toml file that should not be checked in.
|
|
||||||
/roblox.toml
|
|
||||||
58
.luacheckrc
58
.luacheckrc
@@ -1,58 +0,0 @@
|
|||||||
stds.roblox = {
|
|
||||||
read_globals = {
|
|
||||||
game = {
|
|
||||||
other_fields = true,
|
|
||||||
},
|
|
||||||
|
|
||||||
-- Roblox globals
|
|
||||||
"script",
|
|
||||||
|
|
||||||
-- Extra functions
|
|
||||||
"tick", "warn", "spawn",
|
|
||||||
"wait", "settings", "typeof",
|
|
||||||
|
|
||||||
-- Types
|
|
||||||
"Vector2", "Vector3",
|
|
||||||
"Vector2int16", "Vector3int16",
|
|
||||||
"Color3",
|
|
||||||
"UDim", "UDim2",
|
|
||||||
"Rect",
|
|
||||||
"CFrame",
|
|
||||||
"Enum",
|
|
||||||
"Instance",
|
|
||||||
"DockWidgetPluginGuiInfo",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stds.plugin = {
|
|
||||||
read_globals = {
|
|
||||||
"plugin",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stds.testez = {
|
|
||||||
read_globals = {
|
|
||||||
"describe",
|
|
||||||
"it", "itFOCUS", "itSKIP", "itFIXME",
|
|
||||||
"FOCUS", "SKIP", "HACK_NO_XPCALL",
|
|
||||||
"expect",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ignore = {
|
|
||||||
"212", -- unused arguments
|
|
||||||
"421", -- shadowing local variable
|
|
||||||
"422", -- shadowing argument
|
|
||||||
"431", -- shadowing upvalue
|
|
||||||
"432", -- shadowing upvalue argument
|
|
||||||
}
|
|
||||||
|
|
||||||
std = "lua51+roblox"
|
|
||||||
|
|
||||||
files["**/*.server.lua"] = {
|
|
||||||
std = "+plugin",
|
|
||||||
}
|
|
||||||
|
|
||||||
files["**/*.spec.lua"] = {
|
|
||||||
std = "+testez",
|
|
||||||
}
|
|
||||||
86
CHANGELOG.md
86
CHANGELOG.md
@@ -2,7 +2,83 @@
|
|||||||
|
|
||||||
## Unreleased Changes
|
## Unreleased Changes
|
||||||
|
|
||||||
## [7.0.0-rc.1][7.0.0-rc.1] (August 23, 2021)
|
## [7.2.0] - June 29, 2022
|
||||||
|
* Added support for `.luau` files. ([#552])
|
||||||
|
* Added support for live syncing Attributes and Tags. ([#553])
|
||||||
|
* Added notification popups in the Roblox Studio plugin. ([#540])
|
||||||
|
* Fixed `init.meta.json` when used with `init.lua` and related files. ([#549])
|
||||||
|
* Fixed incorrect output when serving from a non-default address or port ([#556])
|
||||||
|
* Fixed Linux binaries not running on systems with older glibc. ([#561])
|
||||||
|
* Added `camelCase` casing for JSON models, deprecating `PascalCase` names. ([#563])
|
||||||
|
* Switched from structopt to clap for command line argument parsing.
|
||||||
|
* Significantly improved performance of building and serving. ([#548])
|
||||||
|
* Increased minimum supported Rust version to 1.57.0. ([#564])
|
||||||
|
|
||||||
|
[#540]: https://github.com/rojo-rbx/rojo/pull/540
|
||||||
|
[#548]: https://github.com/rojo-rbx/rojo/pull/548
|
||||||
|
[#549]: https://github.com/rojo-rbx/rojo/pull/549
|
||||||
|
[#552]: https://github.com/rojo-rbx/rojo/pull/552
|
||||||
|
[#553]: https://github.com/rojo-rbx/rojo/pull/553
|
||||||
|
[#556]: https://github.com/rojo-rbx/rojo/pull/556
|
||||||
|
[#561]: https://github.com/rojo-rbx/rojo/pull/561
|
||||||
|
[#563]: https://github.com/rojo-rbx/rojo/pull/563
|
||||||
|
[#564]: https://github.com/rojo-rbx/rojo/pull/564
|
||||||
|
[7.2.0]: https://github.com/rojo-rbx/rojo/releases/tag/v7.2.0
|
||||||
|
|
||||||
|
## [7.1.1] - May 26, 2022
|
||||||
|
* Fixed sourcemap command not stripping paths correctly ([#544])
|
||||||
|
* Fixed Studio plugin settings not saving correctly.
|
||||||
|
|
||||||
|
[#544]: https://github.com/rojo-rbx/rojo/pull/544
|
||||||
|
[#545]: https://github.com/rojo-rbx/rojo/pull/545
|
||||||
|
[7.1.1]: https://github.com/rojo-rbx/rojo/releases/tag/v7.1.1
|
||||||
|
|
||||||
|
## [7.1.0] - May 22, 2022
|
||||||
|
* Added support for specifying an address to be used by default in project files. ([#507])
|
||||||
|
* Added support for optional paths in project files. ([#472])
|
||||||
|
* Added support for the new Open Cloud API when uploading. ([#504])
|
||||||
|
* Added `sourcemap` command for generating sourcemaps to feed into other tools. ([#530])
|
||||||
|
* Added PluginActions for connecting/disconnecting a session ([#537])
|
||||||
|
* Added changing toolbar icon to indicate state ([#538])
|
||||||
|
|
||||||
|
[#472]: https://github.com/rojo-rbx/rojo/pull/472
|
||||||
|
[#504]: https://github.com/rojo-rbx/rojo/pull/504
|
||||||
|
[#507]: https://github.com/rojo-rbx/rojo/pull/507
|
||||||
|
[#530]: https://github.com/rojo-rbx/rojo/pull/530
|
||||||
|
[#537]: https://github.com/rojo-rbx/rojo/pull/537
|
||||||
|
[#538]: https://github.com/rojo-rbx/rojo/pull/538
|
||||||
|
[7.1.0]: https://github.com/rojo-rbx/rojo/releases/tag/v7.1.0
|
||||||
|
|
||||||
|
## [7.0.0] - December 10, 2021
|
||||||
|
* Fixed Rojo's interactions with properties enabled by FFlags that are not yet enabled. ([#493])
|
||||||
|
* Improved output in Roblox Studio plugin when bad property data is encountered.
|
||||||
|
* Reintroduced support for CFrame shorthand syntax in Rojo project and `.meta.json` files, matching Rojo 6. ([#430])
|
||||||
|
* Connection settings are now remembered when reconnecting in Roblox Studio. ([#500])
|
||||||
|
* Updated reflection database to Roblox v503.
|
||||||
|
|
||||||
|
[#430]: https://github.com/rojo-rbx/rojo/issues/430
|
||||||
|
[#493]: https://github.com/rojo-rbx/rojo/pull/493
|
||||||
|
[#500]: https://github.com/rojo-rbx/rojo/pull/500
|
||||||
|
[7.0.0]: https://github.com/rojo-rbx/rojo/releases/tag/v7.0.0
|
||||||
|
|
||||||
|
## [7.0.0-rc.3] - October 19, 2021
|
||||||
|
This is the last release candidate for Rojo 7. In an effort to get Rojo 7 out the door, we'll be freezing features from here on out, something we should've done a couple months ago.
|
||||||
|
|
||||||
|
Expect to see Rojo 7 stable soon!
|
||||||
|
|
||||||
|
* Added support for writing `Tags` in project files, model files, and meta files. ([#484])
|
||||||
|
* Adjusted Studio plugin colors to match Roblox Studio palette. ([#482])
|
||||||
|
* Improved experimental two-way sync feature by batching changes. ([#478])
|
||||||
|
|
||||||
|
[#482]: https://github.com/rojo-rbx/rojo/pull/482
|
||||||
|
[#484]: https://github.com/rojo-rbx/rojo/pull/484
|
||||||
|
[#478]: https://github.com/rojo-rbx/rojo/pull/478
|
||||||
|
[7.0.0-rc.3]: https://github.com/rojo-rbx/rojo/releases/tag/v7.0.0-rc.3
|
||||||
|
|
||||||
|
## 7.0.0-rc.2 - October 19, 2021
|
||||||
|
(Botched release due to Git mishap, oops!)
|
||||||
|
|
||||||
|
## [7.0.0-rc.1] - August 23, 2021
|
||||||
In Rojo 6 and previous Rojo 7 alphas, an explicit Vector3 property would be written like this:
|
In Rojo 6 and previous Rojo 7 alphas, an explicit Vector3 property would be written like this:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
@@ -45,9 +121,9 @@ The shorthand property format that most users use is not impacted. For reference
|
|||||||
* Added the `fmt-project` subcommand for formatting Rojo project files.
|
* Added the `fmt-project` subcommand for formatting Rojo project files.
|
||||||
* Improved error output for many subcommands.
|
* Improved error output for many subcommands.
|
||||||
* Updated to stable versions of rbx-dom libraries.
|
* Updated to stable versions of rbx-dom libraries.
|
||||||
* Updated async infrastructure, which should fix a handful of bugs. ([#459][#459])
|
* Updated async infrastructure, which should fix a handful of bugs. ([#459])
|
||||||
* Fixed syncing refs in the Roblox Studio plugin ([#462][#462], [#466][#466])
|
* Fixed syncing refs in the Roblox Studio plugin ([#462], [#466])
|
||||||
* Added support for long paths on Windows. ([#464][#464])
|
* Added support for long paths on Windows. ([#464])
|
||||||
|
|
||||||
[#459]: https://github.com/rojo-rbx/rojo/pull/459
|
[#459]: https://github.com/rojo-rbx/rojo/pull/459
|
||||||
[#462]: https://github.com/rojo-rbx/rojo/pull/462
|
[#462]: https://github.com/rojo-rbx/rojo/pull/462
|
||||||
@@ -58,7 +134,7 @@ The shorthand property format that most users use is not impacted. For reference
|
|||||||
## [7.0.0-alpha.4][7.0.0-alpha.4] (May 5, 2021)
|
## [7.0.0-alpha.4][7.0.0-alpha.4] (May 5, 2021)
|
||||||
* Added the `gameId` and `placeId` optional properties to project files.
|
* Added the `gameId` and `placeId` optional properties to project files.
|
||||||
* When connecting from the Rojo Roblox Studio plugin, Rojo will set the game and place ID of the current place to these values, if set.
|
* When connecting from the Rojo Roblox Studio plugin, Rojo will set the game and place ID of the current place to these values, if set.
|
||||||
* This is equivalent to running `game:SetUniverseId(...)` and `game:SetPlaceId(...)` from the command bar in Studio.
|
* This is equivalent to running `game:SetUniverseId(...)` and `game:SetPlaceId(...)` from the command bar in Studio.
|
||||||
* Added "EXPERIMENTAL!" label to two-way sync toggle in Rojo's Roblox Studio plugin.
|
* Added "EXPERIMENTAL!" label to two-way sync toggle in Rojo's Roblox Studio plugin.
|
||||||
* Fixed `Name` and `Parent` properties being allowed in Rojo projects. ([#413][pr-413])
|
* Fixed `Name` and `Parent` properties being allowed in Rojo projects. ([#413][pr-413])
|
||||||
* Fixed "Open Scripts Externally" feature crashing Studio. ([#369][issue-369])
|
* Fixed "Open Scripts Externally" feature crashing Studio. ([#369][issue-369])
|
||||||
|
|||||||
@@ -29,25 +29,29 @@ Sometimes there's something that Rojo doesn't do that it probably should.
|
|||||||
|
|
||||||
Please file issues and we'll try to help figure out what the best way forward is.
|
Please file issues and we'll try to help figure out what the best way forward is.
|
||||||
|
|
||||||
|
## Local Development Gotchas
|
||||||
|
|
||||||
|
If your build fails with "Error: failed to open file `D:\code\rojo\plugin\modules\roact\src`" you need to update your Git submodules.
|
||||||
|
Run the command and try building again: `git submodule update --init --recursive`.
|
||||||
|
|
||||||
## Pushing a Rojo Release
|
## Pushing a Rojo Release
|
||||||
The Rojo release process is pretty manual right now. If you need to do it, here's how:
|
The Rojo release process is pretty manual right now. If you need to do it, here's how:
|
||||||
|
|
||||||
1. Bump server version in [`Cargo.toml`](Cargo.toml)
|
1. Bump server version in [`Cargo.toml`](Cargo.toml)
|
||||||
2. Bump plugin version in [`plugin/src/Config.lua`](plugin/src/Config.lua)
|
2. Bump plugin version in [`plugin/src/Config.lua`](plugin/src/Config.lua)
|
||||||
3. Run `cargo test` to update `Cargo.lock` and double-check tests
|
3. Run `cargo test` to update `Cargo.lock` and run tests
|
||||||
4. Update [`CHANGELOG.md`](CHANGELOG.md)
|
4. Update [`CHANGELOG.md`](CHANGELOG.md)
|
||||||
5. Commit!
|
5. Commit!
|
||||||
* `git add . && git commit -m "Release vX.Y.Z"`
|
* `git add . && git commit -m "Release vX.Y.Z"`
|
||||||
6. Tag the commit with the version from `Cargo.toml` prepended with a v, like `v0.4.13`
|
6. Tag the commit
|
||||||
|
* `git tag vX.Y.Z`
|
||||||
7. Publish the CLI
|
7. Publish the CLI
|
||||||
* `cargo publish`
|
* `cargo publish`
|
||||||
8. Publish the Plugin
|
8. Publish the Plugin
|
||||||
* `rojo publish plugin --asset_id 6415005344`
|
* `cargo run -- upload plugin --asset_id 6415005344`
|
||||||
* `rojo build plugin -o Rojo.rbxm`
|
|
||||||
9. Push commits and tags
|
9. Push commits and tags
|
||||||
* `git push && git push --tags`
|
* `git push && git push --tags`
|
||||||
10. Copy GitHub release content from previous release
|
10. Copy GitHub release content from previous release
|
||||||
* Update the leading text with a summary about the release
|
* Update the leading text with a summary about the release
|
||||||
* Paste the changelog notes (as-is!) from [`CHANGELOG.md`](CHANGELOG.md)
|
* Paste the changelog notes (as-is!) from [`CHANGELOG.md`](CHANGELOG.md)
|
||||||
* Write a small summary of each major feature
|
* Write a small summary of each major feature
|
||||||
* Attach release artifacts from GitHub Actions for each platform
|
|
||||||
1983
Cargo.lock
generated
1983
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
102
Cargo.toml
102
Cargo.toml
@@ -1,6 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rojo"
|
name = "rojo"
|
||||||
version = "7.0.0-rc.1"
|
version = "7.2.0"
|
||||||
|
rust-version = "1.57.0"
|
||||||
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
|
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
|
||||||
description = "Enables professional-grade development tools for Roblox developers"
|
description = "Enables professional-grade development tools for Roblox developers"
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
@@ -8,7 +9,7 @@ homepage = "https://rojo.space"
|
|||||||
documentation = "https://rojo.space/docs"
|
documentation = "https://rojo.space/docs"
|
||||||
repository = "https://github.com/rojo-rbx/rojo"
|
repository = "https://github.com/rojo-rbx/rojo"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
edition = "2018"
|
edition = "2021"
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
exclude = [
|
exclude = [
|
||||||
@@ -27,26 +28,21 @@ default = []
|
|||||||
# Enable this feature to live-reload assets from the web UI.
|
# Enable this feature to live-reload assets from the web UI.
|
||||||
dev_live_assets = []
|
dev_live_assets = []
|
||||||
|
|
||||||
|
profile-with-tracy = ["profiling/profile-with-tracy", "tracy-client"]
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = ["crates/*"]
|
||||||
"rojo-insta-ext",
|
|
||||||
"memofs",
|
|
||||||
]
|
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "librojo"
|
name = "librojo"
|
||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "rojo"
|
|
||||||
path = "src/bin.rs"
|
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "build"
|
name = "build"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
memofs = { version = "0.2.0", path = "memofs" }
|
memofs = { version = "0.2.0", path = "crates/memofs" }
|
||||||
|
|
||||||
# These dependencies can be uncommented when working on rbx-dom simultaneously
|
# These dependencies can be uncommented when working on rbx-dom simultaneously
|
||||||
# rbx_binary = { path = "../rbx-dom/rbx_binary" }
|
# rbx_binary = { path = "../rbx-dom/rbx_binary" }
|
||||||
@@ -55,62 +51,60 @@ memofs = { version = "0.2.0", path = "memofs" }
|
|||||||
# rbx_reflection_database = { path = "../rbx-dom/rbx_reflection_database" }
|
# rbx_reflection_database = { path = "../rbx-dom/rbx_reflection_database" }
|
||||||
# rbx_xml = { path = "../rbx-dom/rbx_xml" }
|
# rbx_xml = { path = "../rbx-dom/rbx_xml" }
|
||||||
|
|
||||||
rbx_binary = "0.6.1"
|
rbx_binary = "0.6.5"
|
||||||
rbx_dom_weak = "2.1.0"
|
rbx_dom_weak = "2.4.0"
|
||||||
rbx_reflection = "4.1.0"
|
rbx_reflection = "4.2.0"
|
||||||
rbx_reflection_database = "0.2.1"
|
rbx_reflection_database = "0.2.2"
|
||||||
rbx_xml = "0.12.1"
|
rbx_xml = "0.12.3"
|
||||||
|
|
||||||
anyhow = "1.0.27"
|
anyhow = "1.0.44"
|
||||||
backtrace = "0.3"
|
backtrace = "0.3.61"
|
||||||
bincode = "1.2.1"
|
bincode = "1.3.3"
|
||||||
crossbeam-channel = "0.5.1"
|
crossbeam-channel = "0.5.1"
|
||||||
csv = "1.1.1"
|
csv = "1.1.6"
|
||||||
env_logger = "0.9.0"
|
env_logger = "0.9.0"
|
||||||
fs-err = "2.2.0"
|
fs-err = "2.6.0"
|
||||||
futures = "0.3.16"
|
futures = "0.3.17"
|
||||||
globset = "0.4.4"
|
globset = "0.4.8"
|
||||||
humantime = "2.1.0"
|
humantime = "2.1.0"
|
||||||
hyper = { version = "0.14.11", features = ["server", "tcp", "http1"] }
|
hyper = { version = "0.14.13", features = ["server", "tcp", "http1"] }
|
||||||
jod-thread = "0.1.0"
|
jod-thread = "0.1.2"
|
||||||
lazy_static = "1.4.0"
|
log = "0.4.14"
|
||||||
log = "0.4.8"
|
maplit = "1.0.2"
|
||||||
maplit = "1.0.1"
|
notify = "4.0.17"
|
||||||
notify = "4.0.14"
|
|
||||||
opener = "0.5.0"
|
opener = "0.5.0"
|
||||||
regex = "1.3.1"
|
reqwest = { version = "0.11.10", features = ["blocking", "json"] }
|
||||||
reqwest = "0.9.20"
|
|
||||||
ritz = "0.1.0"
|
ritz = "0.1.0"
|
||||||
rlua = "0.17.0"
|
|
||||||
roblox_install = "1.0.0"
|
roblox_install = "1.0.0"
|
||||||
serde = { version = "1.0", features = ["derive", "rc"] }
|
serde = { version = "1.0.130", features = ["derive", "rc"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0.68"
|
||||||
structopt = "0.3.5"
|
termcolor = "1.1.2"
|
||||||
termcolor = "1.0.5"
|
thiserror = "1.0.30"
|
||||||
thiserror = "1.0.11"
|
tokio = { version = "1.12.0", features = ["rt", "rt-multi-thread"] }
|
||||||
tokio = { version = "1.9.0", features = ["rt", "rt-multi-thread"] }
|
uuid = { version = "1.0.0", features = ["v4", "serde"] }
|
||||||
uuid = { version = "0.8.1", features = ["v4", "serde"] }
|
clap = { version = "3.1.18", features = ["derive"] }
|
||||||
|
profiling = "1.0.6"
|
||||||
|
tracy-client = { version = "0.13.2", optional = true }
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
winreg = "0.9.0"
|
winreg = "0.10.1"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
memofs = { version = "0.2.0", path = "memofs" }
|
memofs = { version = "0.2.0", path = "crates/memofs" }
|
||||||
|
|
||||||
embed-resource = "1.6"
|
embed-resource = "1.6.4"
|
||||||
anyhow = "1.0.27"
|
anyhow = "1.0.44"
|
||||||
bincode = "1.2.1"
|
bincode = "1.3.3"
|
||||||
fs-err = "2.3.0"
|
fs-err = "2.6.0"
|
||||||
maplit = "1.0.1"
|
maplit = "1.0.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rojo-insta-ext = { path = "rojo-insta-ext" }
|
rojo-insta-ext = { path = "crates/rojo-insta-ext" }
|
||||||
|
|
||||||
criterion = "0.3"
|
criterion = "0.3.5"
|
||||||
insta = { version = "1.3.0", features = ["redactions"] }
|
insta = { version = "1.8.0", features = ["redactions"] }
|
||||||
lazy_static = "1.2"
|
|
||||||
paste = "1.0.5"
|
paste = "1.0.5"
|
||||||
pretty_assertions = "0.7.2"
|
pretty_assertions = "1.2.1"
|
||||||
serde_yaml = "0.8.9"
|
serde_yaml = "0.8.21"
|
||||||
tempfile = "3.0"
|
tempfile = "3.2.0"
|
||||||
walkdir = "2.1"
|
walkdir = "2.3.2"
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ Check out our [contribution guide](CONTRIBUTING.md) for detailed instructions fo
|
|||||||
|
|
||||||
Pull requests are welcome!
|
Pull requests are welcome!
|
||||||
|
|
||||||
Rojo supports Rust 1.46.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.57.0 and newer. The minimum supported version of Rust is based on the latest versions of the dependencies that Rojo has.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
Rojo is available under the terms of the Mozilla Public License, Version 2.0. See [LICENSE.txt](LICENSE.txt) for details.
|
Rojo is available under the terms of the Mozilla Public License, Version 2.0. See [LICENSE.txt](LICENSE.txt) for details.
|
||||||
BIN
assets/NotificationPop.mp3
Normal file
BIN
assets/NotificationPop.mp3
Normal file
Binary file not shown.
BIN
assets/icon-link-32.png
Normal file
BIN
assets/icon-link-32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
BIN
assets/icon-warn-32.png
Normal file
BIN
assets/icon-warn-32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
@@ -1,5 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
watchexec -c -w plugin "sh -c './bin/install-dev-plugin.sh'"
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
DIR="$( mktemp -d )"
|
|
||||||
PLUGIN_FILE="$DIR/Rojo.rbxm"
|
|
||||||
TESTEZ_FILE="$DIR/TestEZ.rbxm"
|
|
||||||
|
|
||||||
rojo build plugin -o "$PLUGIN_FILE"
|
|
||||||
rojo build plugin/testez.project.json -o "$TESTEZ_FILE"
|
|
||||||
remodel bin/mark-plugin-as-dev.lua "$PLUGIN_FILE" "$TESTEZ_FILE" 2>/dev/null
|
|
||||||
|
|
||||||
cp "$PLUGIN_FILE" "$LOCALAPPDATA/Roblox/Plugins/Rojo.rbxm"
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
rojo build plugin -o "$LOCALAPPDATA/Roblox/Plugins/Rojo.rbxm"
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
local pluginPath, testezPath = ...
|
|
||||||
|
|
||||||
local plugin = remodel.readModelFile(pluginPath)[1]
|
|
||||||
local testez = remodel.readModelFile(testezPath)[1]
|
|
||||||
|
|
||||||
local marker = Instance.new("Folder")
|
|
||||||
marker.Name = "ROJO_DEV_BUILD"
|
|
||||||
marker.Parent = plugin
|
|
||||||
|
|
||||||
testez.Parent = plugin
|
|
||||||
|
|
||||||
remodel.writeModelFile(plugin, pluginPath)
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
local pluginPath, placePath = ...
|
|
||||||
|
|
||||||
local plugin = remodel.readModelFile(pluginPath)[1]
|
|
||||||
local place = remodel.readPlaceFile(placePath)
|
|
||||||
|
|
||||||
plugin.Parent = place:GetService("ReplicatedStorage")
|
|
||||||
|
|
||||||
remodel.writePlaceFile(place, placePath)
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
./bin/run-cli-tests.sh
|
|
||||||
./bin/run-plugin-tests.sh
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
cargo test --all --locked
|
|
||||||
cargo fmt -- --check
|
|
||||||
|
|
||||||
touch src/lib.rs # Nudge Rust source to make Clippy actually check things
|
|
||||||
cargo clippy
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
DIR="$( mktemp -d )"
|
|
||||||
PLUGIN_FILE="$DIR/Rojo.rbxmx"
|
|
||||||
PLACE_FILE="$DIR/RojoTestPlace.rbxlx"
|
|
||||||
|
|
||||||
rojo build plugin -o "$PLUGIN_FILE"
|
|
||||||
rojo build plugin/place.project.json -o "$PLACE_FILE"
|
|
||||||
|
|
||||||
remodel bin/put-plugin-in-test-place.lua "$PLUGIN_FILE" "$PLACE_FILE"
|
|
||||||
|
|
||||||
run-in-roblox -s plugin/testBootstrap.server.lua "$PLACE_FILE"
|
|
||||||
|
|
||||||
luacheck plugin/src plugin/log plugin/http
|
|
||||||
2
build.rs
2
build.rs
@@ -21,7 +21,7 @@ fn snapshot_from_fs_path(path: &Path) -> io::Result<VfsSnapshot> {
|
|||||||
|
|
||||||
// We can skip any TestEZ test files since they aren't necessary for
|
// We can skip any TestEZ test files since they aren't necessary for
|
||||||
// the plugin to run.
|
// the plugin to run.
|
||||||
if file_name.ends_with(".spec.lua") {
|
if file_name.ends_with(".spec.lua") || file_name.ends_with(".spec.luau") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
[tools]
|
[tools]
|
||||||
rojo = { source = "rojo-rbx/rojo", version = "6.1.0" }
|
rojo = { source = "rojo-rbx/rojo", version = "7.1.1" }
|
||||||
run-in-roblox = { source = "rojo-rbx/run-in-roblox", version = "0.3.0" }
|
run-in-roblox = { source = "rojo-rbx/run-in-roblox", version = "0.3.0" }
|
||||||
|
selene = { source = "Kampfkarren/selene", version = "0.18.2" }
|
||||||
|
|||||||
Submodule plugin/modules/testez updated: 6e9157db3c...25d957d4d5
@@ -23,8 +23,45 @@ end
|
|||||||
local ALL_AXES = {"X", "Y", "Z"}
|
local ALL_AXES = {"X", "Y", "Z"}
|
||||||
local ALL_FACES = {"Right", "Top", "Back", "Left", "Bottom", "Front"}
|
local ALL_FACES = {"Right", "Top", "Back", "Left", "Bottom", "Front"}
|
||||||
|
|
||||||
|
local EncodedValue = {}
|
||||||
|
|
||||||
local types
|
local types
|
||||||
types = {
|
types = {
|
||||||
|
Attributes = {
|
||||||
|
fromPod = function(pod)
|
||||||
|
local output = {}
|
||||||
|
|
||||||
|
for key, value in pairs(pod) do
|
||||||
|
local ok, result = EncodedValue.decode(value)
|
||||||
|
|
||||||
|
if ok then
|
||||||
|
output[key] = result
|
||||||
|
else
|
||||||
|
local warning = ("Could not decode attribute value of type %q: %s"):format(typeof(value), tostring(result))
|
||||||
|
warn(warning)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return output
|
||||||
|
end,
|
||||||
|
toPod = function(roblox)
|
||||||
|
local output = {}
|
||||||
|
|
||||||
|
for key, value in pairs(roblox) do
|
||||||
|
local ok, result = EncodedValue.encodeNaive(value)
|
||||||
|
|
||||||
|
if ok then
|
||||||
|
output[key] = result
|
||||||
|
else
|
||||||
|
local warning = ("Could not encode attribute value of type %q: %s"):format(typeof(value), tostring(result))
|
||||||
|
warn(warning)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return output
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
|
||||||
Axes = {
|
Axes = {
|
||||||
fromPod = function(pod)
|
fromPod = function(pod)
|
||||||
local axes = {}
|
local axes = {}
|
||||||
@@ -388,6 +425,11 @@ types = {
|
|||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Tags = {
|
||||||
|
fromPod = identity,
|
||||||
|
toPod = identity,
|
||||||
|
},
|
||||||
|
|
||||||
Vector2 = {
|
Vector2 = {
|
||||||
fromPod = unpackDecoder(Vector2.new),
|
fromPod = unpackDecoder(Vector2.new),
|
||||||
|
|
||||||
@@ -428,8 +470,6 @@ types = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
local EncodedValue = {}
|
|
||||||
|
|
||||||
function EncodedValue.decode(encodedValue)
|
function EncodedValue.decode(encodedValue)
|
||||||
local ty, value = next(encodedValue)
|
local ty, value = next(encodedValue)
|
||||||
|
|
||||||
@@ -454,4 +494,19 @@ function EncodedValue.encode(rbxValue, propertyType)
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local propertyTypeRenames = {
|
||||||
|
number = "Float64",
|
||||||
|
boolean = "Bool",
|
||||||
|
string = "String",
|
||||||
|
}
|
||||||
|
|
||||||
|
function EncodedValue.encodeNaive(rbxValue)
|
||||||
|
local propertyType = typeof(rbxValue)
|
||||||
|
if propertyTypeRenames[propertyType] ~= nil then
|
||||||
|
propertyType = propertyTypeRenames[propertyType]
|
||||||
|
end
|
||||||
|
|
||||||
|
return EncodedValue.encode(rbxValue, propertyType)
|
||||||
|
end
|
||||||
|
|
||||||
return EncodedValue
|
return EncodedValue
|
||||||
|
|||||||
@@ -20,7 +20,21 @@ local function set(container, key, value)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function PropertyDescriptor.fromRaw(data, className, propertyName)
|
function PropertyDescriptor.fromRaw(data, className, propertyName)
|
||||||
|
local key, value = next(data.DataType)
|
||||||
|
|
||||||
return setmetatable({
|
return setmetatable({
|
||||||
|
-- The meanings of the key and value in DataType differ when the type of
|
||||||
|
-- the property is Enum. When the property is of type Enum, the key is
|
||||||
|
-- the name of the type:
|
||||||
|
--
|
||||||
|
-- { Enum = "<name of enum>" }
|
||||||
|
--
|
||||||
|
-- When the property is not of type Enum, the value is the name of the
|
||||||
|
-- type:
|
||||||
|
--
|
||||||
|
-- { Value = "<data type>" }
|
||||||
|
dataType = key == "Enum" and key or value,
|
||||||
|
|
||||||
scriptability = data.Scriptability,
|
scriptability = data.Scriptability,
|
||||||
className = className,
|
className = className,
|
||||||
name = propertyName,
|
name = propertyName,
|
||||||
@@ -77,4 +91,4 @@ function PropertyDescriptor:write(instance, value)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return PropertyDescriptor
|
return PropertyDescriptor
|
||||||
|
|||||||
@@ -1,4 +1,73 @@
|
|||||||
{
|
{
|
||||||
|
"Attributes": {
|
||||||
|
"value": {
|
||||||
|
"Attributes": {
|
||||||
|
"TestBool": {
|
||||||
|
"Bool": true
|
||||||
|
},
|
||||||
|
"TestBrickColor": {
|
||||||
|
"BrickColor": 24
|
||||||
|
},
|
||||||
|
"TestColor3": {
|
||||||
|
"Color3": [
|
||||||
|
1.0,
|
||||||
|
0.5,
|
||||||
|
0.0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"TestNumber": {
|
||||||
|
"Float64": 1337.0
|
||||||
|
},
|
||||||
|
"TestRect": {
|
||||||
|
"Rect": [
|
||||||
|
[
|
||||||
|
1.0,
|
||||||
|
2.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
3.0,
|
||||||
|
4.0
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"TestString": {
|
||||||
|
"String": "Test"
|
||||||
|
},
|
||||||
|
"TestUDim": {
|
||||||
|
"UDim": [
|
||||||
|
1.0,
|
||||||
|
2
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"TestUDim2": {
|
||||||
|
"UDim2": [
|
||||||
|
[
|
||||||
|
1.0,
|
||||||
|
2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
3.0,
|
||||||
|
4
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"TestVector2": {
|
||||||
|
"Vector2": [
|
||||||
|
1.0,
|
||||||
|
2.0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"TestVector3": {
|
||||||
|
"Vector3": [
|
||||||
|
1.0,
|
||||||
|
2.0,
|
||||||
|
3.0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ty": "Attributes"
|
||||||
|
},
|
||||||
"Axes": {
|
"Axes": {
|
||||||
"value": {
|
"value": {
|
||||||
"Axes": [
|
"Axes": [
|
||||||
@@ -251,6 +320,16 @@
|
|||||||
},
|
},
|
||||||
"ty": "String"
|
"ty": "String"
|
||||||
},
|
},
|
||||||
|
"Tags": {
|
||||||
|
"value": {
|
||||||
|
"Tags": [
|
||||||
|
"foo",
|
||||||
|
"con'fusion?!",
|
||||||
|
"bar"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ty": "Tags"
|
||||||
|
},
|
||||||
"UDim": {
|
"UDim": {
|
||||||
"value": {
|
"value": {
|
||||||
"UDim": [
|
"UDim": [
|
||||||
|
|||||||
@@ -5,13 +5,31 @@ local CollectionService = game:GetService("CollectionService")
|
|||||||
-- The reflection database refers to these as having scriptability = "Custom"
|
-- The reflection database refers to these as having scriptability = "Custom"
|
||||||
return {
|
return {
|
||||||
Instance = {
|
Instance = {
|
||||||
Tags = {
|
Attributes = {
|
||||||
read = function(instance, key)
|
read = function(instance)
|
||||||
local tagList = CollectionService:GetTags(instance)
|
return true, instance:GetAttributes()
|
||||||
|
|
||||||
return true, table.concat(tagList, "\0")
|
|
||||||
end,
|
end,
|
||||||
write = function(instance, key, value)
|
write = function(instance, _, value)
|
||||||
|
local existing = instance:GetAttributes()
|
||||||
|
|
||||||
|
for key, attr in pairs(value) do
|
||||||
|
instance:SetAttribute(key, attr)
|
||||||
|
end
|
||||||
|
|
||||||
|
for key in pairs(existing) do
|
||||||
|
if value[key] == nil then
|
||||||
|
instance:SetAttribute(key, nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
Tags = {
|
||||||
|
read = function(instance)
|
||||||
|
return true, CollectionService:GetTags(instance)
|
||||||
|
end,
|
||||||
|
write = function(instance, _, value)
|
||||||
local existingTags = CollectionService:GetTags(instance)
|
local existingTags = CollectionService:GetTags(instance)
|
||||||
|
|
||||||
local unseenTags = {}
|
local unseenTags = {}
|
||||||
@@ -19,8 +37,7 @@ return {
|
|||||||
unseenTags[tag] = true
|
unseenTags[tag] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
local tagList = string.split(value, "\0")
|
for _, tag in ipairs(value) do
|
||||||
for _, tag in ipairs(tagList) do
|
|
||||||
unseenTags[tag] = nil
|
unseenTags[tag] = nil
|
||||||
CollectionService:AddTag(instance, tag)
|
CollectionService:AddTag(instance, tag)
|
||||||
end
|
end
|
||||||
@@ -44,4 +61,4 @@ return {
|
|||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
40
plugin/src/App/Components/Studio/StudioPluginAction.lua
Normal file
40
plugin/src/App/Components/Studio/StudioPluginAction.lua
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
local Plugin = Rojo.Plugin
|
||||||
|
|
||||||
|
local Roact = require(Rojo.Roact)
|
||||||
|
|
||||||
|
local Dictionary = require(Plugin.Dictionary)
|
||||||
|
|
||||||
|
local StudioPluginContext = require(script.Parent.StudioPluginContext)
|
||||||
|
|
||||||
|
local e = Roact.createElement
|
||||||
|
|
||||||
|
local StudioPluginAction = Roact.Component:extend("StudioPluginAction")
|
||||||
|
|
||||||
|
function StudioPluginAction:init()
|
||||||
|
self.pluginAction = self.props.plugin:CreatePluginAction(
|
||||||
|
self.props.name, self.props.title, self.props.description, self.props.icon, self.props.bindable
|
||||||
|
)
|
||||||
|
|
||||||
|
self.pluginAction.Triggered:Connect(self.props.onTriggered)
|
||||||
|
end
|
||||||
|
|
||||||
|
function StudioPluginAction:render()
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function StudioPluginAction:willUnmount()
|
||||||
|
self.pluginAction:Destroy()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function StudioPluginActionWrapper(props)
|
||||||
|
return e(StudioPluginContext.Consumer, {
|
||||||
|
render = function(plugin)
|
||||||
|
return e(StudioPluginAction, Dictionary.merge(props, {
|
||||||
|
plugin = plugin,
|
||||||
|
}))
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return StudioPluginActionWrapper
|
||||||
@@ -44,6 +44,10 @@ function StudioToggleButton:didUpdate(lastProps)
|
|||||||
self.button.Enabled = self.props.enabled
|
self.button.Enabled = self.props.enabled
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if self.props.icon ~= lastProps.icon then
|
||||||
|
self.button.Icon = self.props.icon
|
||||||
|
end
|
||||||
|
|
||||||
if self.props.active ~= lastProps.active then
|
if self.props.active ~= lastProps.active then
|
||||||
self.button:SetActive(self.props.active)
|
self.button:SetActive(self.props.active)
|
||||||
end
|
end
|
||||||
@@ -63,4 +67,4 @@ local function StudioToggleButtonWrapper(props)
|
|||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
return StudioToggleButtonWrapper
|
return StudioToggleButtonWrapper
|
||||||
|
|||||||
198
plugin/src/App/Notifications.lua
Normal file
198
plugin/src/App/Notifications.lua
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
local TextService = game:GetService("TextService")
|
||||||
|
local StudioService = game:GetService("StudioService")
|
||||||
|
|
||||||
|
local Rojo = script:FindFirstAncestor("Rojo")
|
||||||
|
local Plugin = Rojo.Plugin
|
||||||
|
|
||||||
|
local Roact = require(Rojo.Roact)
|
||||||
|
local Flipper = require(Rojo.Flipper)
|
||||||
|
|
||||||
|
local bindingUtil = require(script.Parent.bindingUtil)
|
||||||
|
|
||||||
|
local Theme = require(Plugin.App.Theme)
|
||||||
|
local Assets = require(Plugin.Assets)
|
||||||
|
local playSound = require(Plugin.playSound)
|
||||||
|
|
||||||
|
local BorderedContainer = require(Plugin.App.Components.BorderedContainer)
|
||||||
|
|
||||||
|
local baseClock = DateTime.now().UnixTimestampMillis
|
||||||
|
|
||||||
|
local e = Roact.createElement
|
||||||
|
|
||||||
|
local Notification = Roact.Component:extend("Notification")
|
||||||
|
|
||||||
|
function Notification:init()
|
||||||
|
self.motor = Flipper.SingleMotor.new(0)
|
||||||
|
self.binding = bindingUtil.fromMotor(self.motor)
|
||||||
|
|
||||||
|
self.lifetime = self.props.timeout
|
||||||
|
|
||||||
|
self.motor:onStep(function(value)
|
||||||
|
if value <= 0 then
|
||||||
|
if self.props.onClose then
|
||||||
|
self.props.onClose()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Notification:dismiss()
|
||||||
|
self.motor:setGoal(
|
||||||
|
Flipper.Spring.new(0, {
|
||||||
|
frequency = 5,
|
||||||
|
dampingRatio = 1,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Notification:didMount()
|
||||||
|
self.motor:setGoal(
|
||||||
|
Flipper.Spring.new(1, {
|
||||||
|
frequency = 3,
|
||||||
|
dampingRatio = 1,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
playSound(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
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Notification:willUnmount()
|
||||||
|
task.cancel(self.timeout)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Notification:render()
|
||||||
|
local time = DateTime.fromUnixTimestampMillis(self.props.timestamp)
|
||||||
|
|
||||||
|
local textBounds = TextService:GetTextSize(
|
||||||
|
self.props.text,
|
||||||
|
15,
|
||||||
|
Enum.Font.GothamSemibold,
|
||||||
|
Vector2.new(350, 700)
|
||||||
|
)
|
||||||
|
|
||||||
|
local transparency = self.binding:map(function(value)
|
||||||
|
return 1 - value
|
||||||
|
end)
|
||||||
|
|
||||||
|
local size = self.binding:map(function(value)
|
||||||
|
return UDim2.fromOffset(
|
||||||
|
(35+40+textBounds.X)*value,
|
||||||
|
math.max(14+20+textBounds.Y, 32+20)
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
|
||||||
|
return Theme.with(function(theme)
|
||||||
|
return e("TextButton", {
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
Size = size,
|
||||||
|
LayoutOrder = self.props.layoutOrder,
|
||||||
|
Text = "",
|
||||||
|
ClipsDescendants = true,
|
||||||
|
|
||||||
|
[Roact.Event.Activated] = function()
|
||||||
|
self:dismiss()
|
||||||
|
end,
|
||||||
|
}, {
|
||||||
|
e(BorderedContainer, {
|
||||||
|
transparency = transparency,
|
||||||
|
size = UDim2.new(1, 0, 1, 0),
|
||||||
|
}, {
|
||||||
|
TextContainer = e("Frame", {
|
||||||
|
Size = UDim2.new(0, 35+textBounds.X, 1, -20),
|
||||||
|
Position = UDim2.new(0, 0, 0, 10),
|
||||||
|
BackgroundTransparency = 1
|
||||||
|
}, {
|
||||||
|
Logo = e("ImageLabel", {
|
||||||
|
ImageTransparency = transparency,
|
||||||
|
Image = Assets.Images.PluginButton,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
Size = UDim2.new(0, 32, 0, 32),
|
||||||
|
Position = UDim2.new(0, 0, 0.5, 0),
|
||||||
|
AnchorPoint = Vector2.new(0, 0.5),
|
||||||
|
}),
|
||||||
|
Info = e("TextLabel", {
|
||||||
|
Text = self.props.text,
|
||||||
|
Font = Enum.Font.GothamSemibold,
|
||||||
|
TextSize = 15,
|
||||||
|
TextColor3 = theme.Notification.InfoColor,
|
||||||
|
TextTransparency = transparency,
|
||||||
|
TextXAlignment = Enum.TextXAlignment.Left,
|
||||||
|
TextWrapped = true,
|
||||||
|
|
||||||
|
Size = UDim2.new(0, textBounds.X, 0, textBounds.Y),
|
||||||
|
Position = UDim2.fromOffset(35, 0),
|
||||||
|
|
||||||
|
LayoutOrder = 1,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
}),
|
||||||
|
Time = e("TextLabel", {
|
||||||
|
Text = time:FormatLocalTime("LTS", "en-us"),
|
||||||
|
Font = Enum.Font.Code,
|
||||||
|
TextSize = 12,
|
||||||
|
TextColor3 = theme.Notification.InfoColor,
|
||||||
|
TextTransparency = transparency,
|
||||||
|
TextXAlignment = Enum.TextXAlignment.Left,
|
||||||
|
|
||||||
|
Size = UDim2.new(1, -35, 0, 14),
|
||||||
|
Position = UDim2.new(0, 35, 1, -14),
|
||||||
|
|
||||||
|
LayoutOrder = 1,
|
||||||
|
BackgroundTransparency = 1,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
|
Padding = e("UIPadding", {
|
||||||
|
PaddingLeft = UDim.new(0, 17),
|
||||||
|
PaddingRight = UDim.new(0, 15),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local Notifications = Roact.Component:extend("Notifications")
|
||||||
|
|
||||||
|
function Notifications:render()
|
||||||
|
local notifs = {}
|
||||||
|
|
||||||
|
for index, notif in ipairs(self.props.notifications) do
|
||||||
|
notifs[notif] = e(Notification, {
|
||||||
|
text = notif.text,
|
||||||
|
timestamp = notif.timestamp,
|
||||||
|
timeout = notif.timeout,
|
||||||
|
layoutOrder = (notif.timestamp - baseClock),
|
||||||
|
onClose = function()
|
||||||
|
self.props.onClose(index)
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return Roact.createFragment(notifs)
|
||||||
|
end
|
||||||
|
|
||||||
|
return Notifications
|
||||||
@@ -9,6 +9,7 @@ local Roact = require(Rojo.Roact)
|
|||||||
local defaultSettings = {
|
local defaultSettings = {
|
||||||
openScriptsExternally = false,
|
openScriptsExternally = false,
|
||||||
twoWaySync = false,
|
twoWaySync = false,
|
||||||
|
showNotifications = true,
|
||||||
}
|
}
|
||||||
|
|
||||||
local Settings = {}
|
local Settings = {}
|
||||||
@@ -118,4 +119,4 @@ end
|
|||||||
return {
|
return {
|
||||||
StudioProvider = StudioProvider,
|
StudioProvider = StudioProvider,
|
||||||
with = with,
|
with = with,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ local function AddressEntry(props)
|
|||||||
layoutOrder = props.layoutOrder,
|
layoutOrder = props.layoutOrder,
|
||||||
}, {
|
}, {
|
||||||
Host = e("TextBox", {
|
Host = e("TextBox", {
|
||||||
Text = "",
|
Text = props.host or "",
|
||||||
Font = Enum.Font.Code,
|
Font = Enum.Font.Code,
|
||||||
TextSize = 18,
|
TextSize = 18,
|
||||||
TextColor3 = theme.AddressEntry.TextColor,
|
TextColor3 = theme.AddressEntry.TextColor,
|
||||||
@@ -32,6 +32,7 @@ local function AddressEntry(props)
|
|||||||
TextTransparency = props.transparency,
|
TextTransparency = props.transparency,
|
||||||
PlaceholderText = Config.defaultHost,
|
PlaceholderText = Config.defaultHost,
|
||||||
PlaceholderColor3 = theme.AddressEntry.PlaceholderColor,
|
PlaceholderColor3 = theme.AddressEntry.PlaceholderColor,
|
||||||
|
ClearTextOnFocus = false,
|
||||||
|
|
||||||
Size = UDim2.new(1, -(HOST_OFFSET + DIVIDER_WIDTH + PORT_WIDTH), 1, 0),
|
Size = UDim2.new(1, -(HOST_OFFSET + DIVIDER_WIDTH + PORT_WIDTH), 1, 0),
|
||||||
Position = UDim2.new(0, HOST_OFFSET, 0, 0),
|
Position = UDim2.new(0, HOST_OFFSET, 0, 0),
|
||||||
@@ -39,17 +40,22 @@ local function AddressEntry(props)
|
|||||||
ClipsDescendants = true,
|
ClipsDescendants = true,
|
||||||
BackgroundTransparency = 1,
|
BackgroundTransparency = 1,
|
||||||
|
|
||||||
[Roact.Ref] = props.hostRef,
|
[Roact.Change.Text] = function(object)
|
||||||
|
if props.onHostChange ~= nil then
|
||||||
|
props.onHostChange(object.Text)
|
||||||
|
end
|
||||||
|
end
|
||||||
}),
|
}),
|
||||||
|
|
||||||
Port = e("TextBox", {
|
Port = e("TextBox", {
|
||||||
Text = "",
|
Text = props.port or "",
|
||||||
Font = Enum.Font.Code,
|
Font = Enum.Font.Code,
|
||||||
TextSize = 18,
|
TextSize = 18,
|
||||||
TextColor3 = theme.AddressEntry.TextColor,
|
TextColor3 = theme.AddressEntry.TextColor,
|
||||||
TextTransparency = props.transparency,
|
TextTransparency = props.transparency,
|
||||||
PlaceholderText = Config.defaultPort,
|
PlaceholderText = Config.defaultPort,
|
||||||
PlaceholderColor3 = theme.AddressEntry.PlaceholderColor,
|
PlaceholderColor3 = theme.AddressEntry.PlaceholderColor,
|
||||||
|
ClearTextOnFocus = false,
|
||||||
|
|
||||||
Size = UDim2.new(0, PORT_WIDTH, 1, 0),
|
Size = UDim2.new(0, PORT_WIDTH, 1, 0),
|
||||||
Position = UDim2.new(1, 0, 0, 0),
|
Position = UDim2.new(1, 0, 0, 0),
|
||||||
@@ -58,12 +64,14 @@ local function AddressEntry(props)
|
|||||||
ClipsDescendants = true,
|
ClipsDescendants = true,
|
||||||
BackgroundTransparency = 1,
|
BackgroundTransparency = 1,
|
||||||
|
|
||||||
[Roact.Ref] = props.portRef,
|
|
||||||
|
|
||||||
[Roact.Change.Text] = function(object)
|
[Roact.Change.Text] = function(object)
|
||||||
local text = object.Text
|
local text = object.Text
|
||||||
text = text:gsub("%D", "")
|
text = text:gsub("%D", "")
|
||||||
object.Text = text
|
object.Text = text
|
||||||
|
|
||||||
|
if props.onPortChange ~= nil then
|
||||||
|
props.onPortChange(text)
|
||||||
|
end
|
||||||
end,
|
end,
|
||||||
}, {
|
}, {
|
||||||
Divider = e("Frame", {
|
Divider = e("Frame", {
|
||||||
@@ -80,11 +88,6 @@ end
|
|||||||
|
|
||||||
local NotConnectedPage = Roact.Component:extend("NotConnectedPage")
|
local NotConnectedPage = Roact.Component:extend("NotConnectedPage")
|
||||||
|
|
||||||
function NotConnectedPage:init()
|
|
||||||
self.hostRef = Roact.createRef()
|
|
||||||
self.portRef = Roact.createRef()
|
|
||||||
end
|
|
||||||
|
|
||||||
function NotConnectedPage:render()
|
function NotConnectedPage:render()
|
||||||
return Roact.createFragment({
|
return Roact.createFragment({
|
||||||
Header = e(Header, {
|
Header = e(Header, {
|
||||||
@@ -93,8 +96,10 @@ function NotConnectedPage:render()
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
AddressEntry = e(AddressEntry, {
|
AddressEntry = e(AddressEntry, {
|
||||||
hostRef = self.hostRef,
|
host = self.props.host,
|
||||||
portRef = self.portRef,
|
port = self.props.port,
|
||||||
|
onHostChange = self.props.onHostChange,
|
||||||
|
onPortChange = self.props.onPortChange,
|
||||||
transparency = self.props.transparency,
|
transparency = self.props.transparency,
|
||||||
layoutOrder = 2,
|
layoutOrder = 2,
|
||||||
}),
|
}),
|
||||||
@@ -117,15 +122,7 @@ function NotConnectedPage:render()
|
|||||||
style = "Solid",
|
style = "Solid",
|
||||||
transparency = self.props.transparency,
|
transparency = self.props.transparency,
|
||||||
layoutOrder = 2,
|
layoutOrder = 2,
|
||||||
onClick = function()
|
onClick = self.props.onConnect,
|
||||||
local hostText = self.hostRef.current.Text
|
|
||||||
local portText = self.portRef.current.Text
|
|
||||||
|
|
||||||
self.props.onConnect(
|
|
||||||
#hostText > 0 and hostText or Config.defaultHost,
|
|
||||||
#portText > 0 and portText or Config.defaultPort
|
|
||||||
)
|
|
||||||
end,
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
Layout = e("UIListLayout", {
|
Layout = e("UIListLayout", {
|
||||||
|
|||||||
@@ -202,12 +202,20 @@ function SettingsPage:render()
|
|||||||
layoutOrder = 1,
|
layoutOrder = 1,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
ShowNotifications = e(Setting, {
|
||||||
|
id = "showNotifications",
|
||||||
|
name = "Show Notifications",
|
||||||
|
description = "Popup notifications in viewport",
|
||||||
|
transparency = self.props.transparency,
|
||||||
|
layoutOrder = 2,
|
||||||
|
}),
|
||||||
|
|
||||||
TwoWaySync = e(Setting, {
|
TwoWaySync = e(Setting, {
|
||||||
id = "twoWaySync",
|
id = "twoWaySync",
|
||||||
name = "Two-Way Sync",
|
name = "Two-Way Sync",
|
||||||
description = "EXPERIMENTAL! Editing files in Studio will sync them into the filesystem",
|
description = "EXPERIMENTAL! Editing files in Studio will sync them into the filesystem",
|
||||||
transparency = self.props.transparency,
|
transparency = self.props.transparency,
|
||||||
layoutOrder = 2,
|
layoutOrder = 3,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
Layout = e("UIListLayout", {
|
Layout = e("UIListLayout", {
|
||||||
@@ -227,4 +235,4 @@ function SettingsPage:render()
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
return SettingsPage
|
return SettingsPage
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ end
|
|||||||
local BRAND_COLOR = hexColor(0xE13835)
|
local BRAND_COLOR = hexColor(0xE13835)
|
||||||
|
|
||||||
local lightTheme = strict("LightTheme", {
|
local lightTheme = strict("LightTheme", {
|
||||||
BackgroundColor = hexColor(0xF0F0F0),
|
BackgroundColor = hexColor(0xFFFFFF),
|
||||||
Button = {
|
Button = {
|
||||||
Solid = {
|
Solid = {
|
||||||
ActionFillColor = hexColor(0xFFFFFF),
|
ActionFillColor = hexColor(0xFFFFFF),
|
||||||
@@ -67,7 +67,7 @@ local lightTheme = strict("LightTheme", {
|
|||||||
BackgroundColor = BRAND_COLOR,
|
BackgroundColor = BRAND_COLOR,
|
||||||
},
|
},
|
||||||
Inactive = {
|
Inactive = {
|
||||||
IconColor = hexColor(0xCACACA),
|
IconColor = hexColor(0xEEEEEE),
|
||||||
BorderColor = hexColor(0xAFAFAF),
|
BorderColor = hexColor(0xAFAFAF),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -77,11 +77,11 @@ local lightTheme = strict("LightTheme", {
|
|||||||
},
|
},
|
||||||
BorderedContainer = {
|
BorderedContainer = {
|
||||||
BorderColor = hexColor(0xCBCBCB),
|
BorderColor = hexColor(0xCBCBCB),
|
||||||
BackgroundColor = hexColor(0xE0E0E0),
|
BackgroundColor = hexColor(0xEEEEEE),
|
||||||
},
|
},
|
||||||
Spinner = {
|
Spinner = {
|
||||||
ForegroundColor = BRAND_COLOR,
|
ForegroundColor = BRAND_COLOR,
|
||||||
BackgroundColor = hexColor(0xE0E0E0),
|
BackgroundColor = hexColor(0xEEEEEE),
|
||||||
},
|
},
|
||||||
ConnectionDetails = {
|
ConnectionDetails = {
|
||||||
ProjectNameColor = hexColor(0x00000),
|
ProjectNameColor = hexColor(0x00000),
|
||||||
@@ -103,12 +103,16 @@ local lightTheme = strict("LightTheme", {
|
|||||||
LogoColor = BRAND_COLOR,
|
LogoColor = BRAND_COLOR,
|
||||||
VersionColor = hexColor(0x727272),
|
VersionColor = hexColor(0x727272),
|
||||||
},
|
},
|
||||||
|
Notification = {
|
||||||
|
InfoColor = hexColor(0x00000),
|
||||||
|
CloseColor = BRAND_COLOR,
|
||||||
|
},
|
||||||
ErrorColor = hexColor(0x000000),
|
ErrorColor = hexColor(0x000000),
|
||||||
ScrollBarColor = hexColor(0x000000),
|
ScrollBarColor = hexColor(0x000000),
|
||||||
})
|
})
|
||||||
|
|
||||||
local darkTheme = strict("DarkTheme", {
|
local darkTheme = strict("DarkTheme", {
|
||||||
BackgroundColor = hexColor(0x272727),
|
BackgroundColor = hexColor(0x2E2E2E),
|
||||||
Button = {
|
Button = {
|
||||||
Solid = {
|
Solid = {
|
||||||
ActionFillColor = hexColor(0xFFFFFF),
|
ActionFillColor = hexColor(0xFFFFFF),
|
||||||
@@ -147,15 +151,15 @@ local darkTheme = strict("DarkTheme", {
|
|||||||
},
|
},
|
||||||
AddressEntry = {
|
AddressEntry = {
|
||||||
TextColor = hexColor(0xFFFFFF),
|
TextColor = hexColor(0xFFFFFF),
|
||||||
PlaceholderColor = hexColor(0x717171)
|
PlaceholderColor = hexColor(0x8B8B8B)
|
||||||
},
|
},
|
||||||
BorderedContainer = {
|
BorderedContainer = {
|
||||||
BorderColor = hexColor(0x535353),
|
BorderColor = hexColor(0x535353),
|
||||||
BackgroundColor = hexColor(0x323232),
|
BackgroundColor = hexColor(0x2B2B2B),
|
||||||
},
|
},
|
||||||
Spinner = {
|
Spinner = {
|
||||||
ForegroundColor = BRAND_COLOR,
|
ForegroundColor = BRAND_COLOR,
|
||||||
BackgroundColor = hexColor(0x323232),
|
BackgroundColor = hexColor(0x2B2B2B),
|
||||||
},
|
},
|
||||||
ConnectionDetails = {
|
ConnectionDetails = {
|
||||||
ProjectNameColor = hexColor(0xFFFFFF),
|
ProjectNameColor = hexColor(0xFFFFFF),
|
||||||
@@ -177,6 +181,10 @@ local darkTheme = strict("DarkTheme", {
|
|||||||
LogoColor = BRAND_COLOR,
|
LogoColor = BRAND_COLOR,
|
||||||
VersionColor = hexColor(0xD3D3D3)
|
VersionColor = hexColor(0xD3D3D3)
|
||||||
},
|
},
|
||||||
|
Notification = {
|
||||||
|
InfoColor = hexColor(0xFFFFFF),
|
||||||
|
CloseColor = hexColor(0xFFFFFF),
|
||||||
|
},
|
||||||
ErrorColor = hexColor(0xFFFFFF),
|
ErrorColor = hexColor(0xFFFFFF),
|
||||||
ScrollBarColor = hexColor(0xFFFFFF),
|
ScrollBarColor = hexColor(0xFFFFFF),
|
||||||
})
|
})
|
||||||
@@ -236,4 +244,4 @@ return {
|
|||||||
StudioProvider = StudioProvider,
|
StudioProvider = StudioProvider,
|
||||||
Consumer = Context.Consumer,
|
Consumer = Context.Consumer,
|
||||||
with = with,
|
with = with,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ local Theme = require(script.Theme)
|
|||||||
local PluginSettings = require(script.PluginSettings)
|
local PluginSettings = require(script.PluginSettings)
|
||||||
|
|
||||||
local Page = require(script.Page)
|
local Page = require(script.Page)
|
||||||
|
local Notifications = require(script.Notifications)
|
||||||
|
local StudioPluginAction = require(script.Components.Studio.StudioPluginAction)
|
||||||
local StudioToolbar = require(script.Components.Studio.StudioToolbar)
|
local StudioToolbar = require(script.Components.Studio.StudioToolbar)
|
||||||
local StudioToggleButton = require(script.Components.Studio.StudioToggleButton)
|
local StudioToggleButton = require(script.Components.Studio.StudioToggleButton)
|
||||||
local StudioPluginGui = require(script.Components.Studio.StudioPluginGui)
|
local StudioPluginGui = require(script.Components.Studio.StudioPluginGui)
|
||||||
@@ -37,13 +39,61 @@ local App = Roact.Component:extend("App")
|
|||||||
function App:init()
|
function App:init()
|
||||||
preloadAssets()
|
preloadAssets()
|
||||||
|
|
||||||
|
self.host, self.setHost = Roact.createBinding("")
|
||||||
|
self.port, self.setPort = Roact.createBinding("")
|
||||||
|
|
||||||
self:setState({
|
self:setState({
|
||||||
appStatus = AppStatus.NotConnected,
|
appStatus = AppStatus.NotConnected,
|
||||||
guiEnabled = false,
|
guiEnabled = false,
|
||||||
|
notifications = {},
|
||||||
|
toolbarIcon = Assets.Images.PluginButton,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
function App:startSession(host, port, sessionOptions)
|
function App:addNotification(text: string, timeout: number?)
|
||||||
|
if not self.props.settings:get("showNotifications") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local notifications = table.clone(self.state.notifications)
|
||||||
|
table.insert(notifications, {
|
||||||
|
text = text,
|
||||||
|
timestamp = DateTime.now().UnixTimestampMillis,
|
||||||
|
timeout = timeout or 3,
|
||||||
|
})
|
||||||
|
|
||||||
|
self:setState({
|
||||||
|
notifications = notifications,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
function App:closeNotification(index: number)
|
||||||
|
local notifications = table.clone(self.state.notifications)
|
||||||
|
table.remove(notifications, index)
|
||||||
|
|
||||||
|
self:setState({
|
||||||
|
notifications = notifications,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
function App:getHostAndPort()
|
||||||
|
local host = self.host:getValue()
|
||||||
|
local port = self.port:getValue()
|
||||||
|
|
||||||
|
local host = if #host > 0 then host else Config.defaultHost
|
||||||
|
local port = if #port > 0 then port else Config.defaultPort
|
||||||
|
|
||||||
|
return host, port
|
||||||
|
end
|
||||||
|
|
||||||
|
function App:startSession()
|
||||||
|
local host, port = self:getHostAndPort()
|
||||||
|
|
||||||
|
local sessionOptions = {
|
||||||
|
openScriptsExternally = self.props.settings:get("openScriptsExternally"),
|
||||||
|
twoWaySync = self.props.settings:get("twoWaySync"),
|
||||||
|
}
|
||||||
|
|
||||||
local baseUrl = ("http://%s:%s"):format(host, port)
|
local baseUrl = ("http://%s:%s"):format(host, port)
|
||||||
local apiContext = ApiContext.new(baseUrl)
|
local apiContext = ApiContext.new(baseUrl)
|
||||||
|
|
||||||
@@ -57,14 +107,18 @@ function App:startSession(host, port, sessionOptions)
|
|||||||
if status == ServeSession.Status.Connecting then
|
if status == ServeSession.Status.Connecting then
|
||||||
self:setState({
|
self:setState({
|
||||||
appStatus = AppStatus.Connecting,
|
appStatus = AppStatus.Connecting,
|
||||||
|
toolbarIcon = Assets.Images.PluginButton,
|
||||||
})
|
})
|
||||||
|
self:addNotification("Connecting to session...")
|
||||||
elseif status == ServeSession.Status.Connected then
|
elseif status == ServeSession.Status.Connected then
|
||||||
local address = ("%s:%s"):format(host, port)
|
local address = ("%s:%s"):format(host, port)
|
||||||
self:setState({
|
self:setState({
|
||||||
appStatus = AppStatus.Connected,
|
appStatus = AppStatus.Connected,
|
||||||
projectName = details,
|
projectName = details,
|
||||||
address = address,
|
address = address,
|
||||||
|
toolbarIcon = Assets.Images.PluginButtonConnected,
|
||||||
})
|
})
|
||||||
|
self:addNotification(string.format("Connected to session '%s' at %s.", details, address), 5)
|
||||||
elseif status == ServeSession.Status.Disconnected then
|
elseif status == ServeSession.Status.Disconnected then
|
||||||
self.serveSession = nil
|
self.serveSession = nil
|
||||||
|
|
||||||
@@ -76,11 +130,15 @@ function App:startSession(host, port, sessionOptions)
|
|||||||
self:setState({
|
self:setState({
|
||||||
appStatus = AppStatus.Error,
|
appStatus = AppStatus.Error,
|
||||||
errorMessage = tostring(details),
|
errorMessage = tostring(details),
|
||||||
|
toolbarIcon = Assets.Images.PluginButtonWarning,
|
||||||
})
|
})
|
||||||
|
self:addNotification(tostring(details), 10)
|
||||||
else
|
else
|
||||||
self:setState({
|
self:setState({
|
||||||
appStatus = AppStatus.NotConnected,
|
appStatus = AppStatus.NotConnected,
|
||||||
|
toolbarIcon = Assets.Images.PluginButton,
|
||||||
})
|
})
|
||||||
|
self:addNotification("Disconnected from session.")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
@@ -90,6 +148,22 @@ function App:startSession(host, port, sessionOptions)
|
|||||||
self.serveSession = serveSession
|
self.serveSession = serveSession
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function App:endSession()
|
||||||
|
if self.serveSession == nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
Log.trace("Disconnecting session")
|
||||||
|
|
||||||
|
self.serveSession:stop()
|
||||||
|
self.serveSession = nil
|
||||||
|
self:setState({
|
||||||
|
appStatus = AppStatus.NotConnected,
|
||||||
|
})
|
||||||
|
|
||||||
|
Log.trace("Session terminated by user")
|
||||||
|
end
|
||||||
|
|
||||||
function App:render()
|
function App:render()
|
||||||
local pluginName = "Rojo " .. Version.display(Config.version)
|
local pluginName = "Rojo " .. Version.display(Config.version)
|
||||||
|
|
||||||
@@ -108,119 +182,175 @@ function App:render()
|
|||||||
value = self.props.plugin,
|
value = self.props.plugin,
|
||||||
}, {
|
}, {
|
||||||
e(Theme.StudioProvider, nil, {
|
e(Theme.StudioProvider, nil, {
|
||||||
e(PluginSettings.StudioProvider, {
|
gui = e(StudioPluginGui, {
|
||||||
plugin = self.props.plugin,
|
id = pluginName,
|
||||||
|
title = pluginName,
|
||||||
|
active = self.state.guiEnabled,
|
||||||
|
|
||||||
|
initDockState = Enum.InitialDockState.Right,
|
||||||
|
initEnabled = false,
|
||||||
|
overridePreviousState = false,
|
||||||
|
floatingSize = Vector2.new(300, 200),
|
||||||
|
minimumSize = Vector2.new(300, 200),
|
||||||
|
|
||||||
|
zIndexBehavior = Enum.ZIndexBehavior.Sibling,
|
||||||
|
|
||||||
|
onInitialState = function(initialState)
|
||||||
|
self:setState({
|
||||||
|
guiEnabled = initialState,
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
|
||||||
|
onClose = function()
|
||||||
|
self:setState({
|
||||||
|
guiEnabled = false,
|
||||||
|
})
|
||||||
|
end,
|
||||||
}, {
|
}, {
|
||||||
gui = e(StudioPluginGui, {
|
NotConnectedPage = createPageElement(AppStatus.NotConnected, {
|
||||||
id = pluginName,
|
host = self.host,
|
||||||
title = pluginName,
|
onHostChange = self.setHost,
|
||||||
active = self.state.guiEnabled,
|
port = self.port,
|
||||||
|
onPortChange = self.setPort,
|
||||||
|
|
||||||
initDockState = Enum.InitialDockState.Right,
|
onConnect = function()
|
||||||
initEnabled = false,
|
self:startSession()
|
||||||
overridePreviousState = false,
|
end,
|
||||||
floatingSize = Vector2.new(300, 200),
|
|
||||||
minimumSize = Vector2.new(300, 200),
|
|
||||||
|
|
||||||
zIndexBehavior = Enum.ZIndexBehavior.Sibling,
|
onNavigateSettings = function()
|
||||||
|
|
||||||
onInitialState = function(initialState)
|
|
||||||
self:setState({
|
self:setState({
|
||||||
guiEnabled = initialState,
|
appStatus = AppStatus.Settings,
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Connecting = createPageElement(AppStatus.Connecting),
|
||||||
|
|
||||||
|
Connected = createPageElement(AppStatus.Connected, {
|
||||||
|
projectName = self.state.projectName,
|
||||||
|
address = self.state.address,
|
||||||
|
|
||||||
|
onDisconnect = function()
|
||||||
|
self:endSession()
|
||||||
|
end,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Settings = createPageElement(AppStatus.Settings, {
|
||||||
|
onBack = function()
|
||||||
|
self:setState({
|
||||||
|
appStatus = AppStatus.NotConnected,
|
||||||
|
})
|
||||||
|
end,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Error = createPageElement(AppStatus.Error, {
|
||||||
|
errorMessage = self.state.errorMessage,
|
||||||
|
|
||||||
onClose = function()
|
onClose = function()
|
||||||
self:setState({
|
self:setState({
|
||||||
guiEnabled = false,
|
appStatus = AppStatus.NotConnected,
|
||||||
|
toolbarIcon = Assets.Images.PluginButton,
|
||||||
})
|
})
|
||||||
end,
|
end,
|
||||||
}, {
|
|
||||||
NotConnectedPage = PluginSettings.with(function(settings)
|
|
||||||
return createPageElement(AppStatus.NotConnected, {
|
|
||||||
onConnect = function(host, port)
|
|
||||||
self:startSession(host, port, {
|
|
||||||
openScriptsExternally = settings:get("openScriptsExternally"),
|
|
||||||
twoWaySync = settings:get("twoWaySync"),
|
|
||||||
})
|
|
||||||
end,
|
|
||||||
|
|
||||||
onNavigateSettings = function()
|
|
||||||
self:setState({
|
|
||||||
appStatus = AppStatus.Settings,
|
|
||||||
})
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
end),
|
|
||||||
|
|
||||||
Connecting = createPageElement(AppStatus.Connecting),
|
|
||||||
|
|
||||||
Connected = createPageElement(AppStatus.Connected, {
|
|
||||||
projectName = self.state.projectName,
|
|
||||||
address = self.state.address,
|
|
||||||
|
|
||||||
onDisconnect = function()
|
|
||||||
Log.trace("Disconnecting session")
|
|
||||||
|
|
||||||
self.serveSession:stop()
|
|
||||||
self.serveSession = nil
|
|
||||||
self:setState({
|
|
||||||
appStatus = AppStatus.NotConnected,
|
|
||||||
})
|
|
||||||
|
|
||||||
Log.trace("Session terminated by user")
|
|
||||||
end,
|
|
||||||
}),
|
|
||||||
|
|
||||||
Settings = createPageElement(AppStatus.Settings, {
|
|
||||||
onBack = function()
|
|
||||||
self:setState({
|
|
||||||
appStatus = AppStatus.NotConnected,
|
|
||||||
})
|
|
||||||
end,
|
|
||||||
}),
|
|
||||||
|
|
||||||
Error = createPageElement(AppStatus.Error, {
|
|
||||||
errorMessage = self.state.errorMessage,
|
|
||||||
|
|
||||||
onClose = function()
|
|
||||||
self:setState({
|
|
||||||
appStatus = AppStatus.NotConnected,
|
|
||||||
})
|
|
||||||
end,
|
|
||||||
}),
|
|
||||||
|
|
||||||
Background = Theme.with(function(theme)
|
|
||||||
return e("Frame", {
|
|
||||||
Size = UDim2.new(1, 0, 1, 0),
|
|
||||||
BackgroundColor3 = theme.BackgroundColor,
|
|
||||||
ZIndex = 0,
|
|
||||||
BorderSizePixel = 0,
|
|
||||||
})
|
|
||||||
end),
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
toolbar = e(StudioToolbar, {
|
Background = Theme.with(function(theme)
|
||||||
name = pluginName,
|
return e("Frame", {
|
||||||
}, {
|
Size = UDim2.new(1, 0, 1, 0),
|
||||||
button = e(StudioToggleButton, {
|
BackgroundColor3 = theme.BackgroundColor,
|
||||||
name = "Rojo",
|
ZIndex = 0,
|
||||||
tooltip = "Show or hide the Rojo panel",
|
BorderSizePixel = 0,
|
||||||
icon = Assets.Images.PluginButton,
|
|
||||||
active = self.state.guiEnabled,
|
|
||||||
enabled = true,
|
|
||||||
onClick = function()
|
|
||||||
self:setState(function(state)
|
|
||||||
return {
|
|
||||||
guiEnabled = not state.guiEnabled,
|
|
||||||
}
|
|
||||||
end)
|
|
||||||
end,
|
|
||||||
})
|
})
|
||||||
|
end),
|
||||||
|
}),
|
||||||
|
|
||||||
|
RojoNotifications = e("ScreenGui", {}, {
|
||||||
|
layout = e("UIListLayout", {
|
||||||
|
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||||
|
HorizontalAlignment = Enum.HorizontalAlignment.Right,
|
||||||
|
VerticalAlignment = Enum.VerticalAlignment.Bottom,
|
||||||
|
Padding = UDim.new(0, 5),
|
||||||
}),
|
}),
|
||||||
|
notifs = e(Notifications, {
|
||||||
|
notifications = self.state.notifications,
|
||||||
|
onClose = function(index)
|
||||||
|
self:closeNotification(index)
|
||||||
|
end,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
|
toggleAction = e(StudioPluginAction, {
|
||||||
|
name = "RojoConnection",
|
||||||
|
title = "Rojo: Connect/Disconnect",
|
||||||
|
description = "Toggles the server for a Rojo sync session",
|
||||||
|
icon = Assets.Images.PluginButton,
|
||||||
|
bindable = true,
|
||||||
|
onTriggered = function()
|
||||||
|
if self.serveSession == nil or self.serveSession:getStatus() == ServeSession.Status.NotStarted then
|
||||||
|
self:startSession()
|
||||||
|
elseif self.serveSession ~= nil and self.serveSession:getStatus() == ServeSession.Status.Connected then
|
||||||
|
self:endSession()
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
}),
|
||||||
|
|
||||||
|
connectAction = e(StudioPluginAction, {
|
||||||
|
name = "RojoConnect",
|
||||||
|
title = "Rojo: Connect",
|
||||||
|
description = "Connects the server for a Rojo sync session",
|
||||||
|
icon = Assets.Images.PluginButton,
|
||||||
|
bindable = true,
|
||||||
|
onTriggered = function()
|
||||||
|
if self.serveSession == nil or self.serveSession:getStatus() == ServeSession.Status.NotStarted then
|
||||||
|
self:startSession()
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
}),
|
||||||
|
|
||||||
|
disconnectAction = e(StudioPluginAction, {
|
||||||
|
name = "RojoDisconnect",
|
||||||
|
title = "Rojo: Disconnect",
|
||||||
|
description = "Disconnects the server for a Rojo sync session",
|
||||||
|
icon = Assets.Images.PluginButton,
|
||||||
|
bindable = true,
|
||||||
|
onTriggered = function()
|
||||||
|
if self.serveSession ~= nil and self.serveSession:getStatus() == ServeSession.Status.Connected then
|
||||||
|
self:endSession()
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
}),
|
||||||
|
|
||||||
|
toolbar = e(StudioToolbar, {
|
||||||
|
name = pluginName,
|
||||||
|
}, {
|
||||||
|
button = e(StudioToggleButton, {
|
||||||
|
name = "Rojo",
|
||||||
|
tooltip = "Show or hide the Rojo panel",
|
||||||
|
icon = self.state.toolbarIcon,
|
||||||
|
active = self.state.guiEnabled,
|
||||||
|
enabled = true,
|
||||||
|
onClick = function()
|
||||||
|
self:setState(function(state)
|
||||||
|
return {
|
||||||
|
guiEnabled = not state.guiEnabled,
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
end,
|
||||||
|
})
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
return App
|
return function(props)
|
||||||
|
return e(PluginSettings.StudioProvider, {
|
||||||
|
plugin = props.plugin,
|
||||||
|
}, {
|
||||||
|
App = PluginSettings.with(function(settings)
|
||||||
|
local settingsProps = Dictionary.merge(props, {
|
||||||
|
settings = settings,
|
||||||
|
})
|
||||||
|
return e(App, settingsProps)
|
||||||
|
end),
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ local Assets = {
|
|||||||
Images = {
|
Images = {
|
||||||
Logo = "rbxassetid://5990772764",
|
Logo = "rbxassetid://5990772764",
|
||||||
PluginButton = "rbxassetid://3405341609",
|
PluginButton = "rbxassetid://3405341609",
|
||||||
|
PluginButtonConnected = "rbxassetid://9529783993",
|
||||||
|
PluginButtonWarning = "rbxassetid://9529784530",
|
||||||
Icons = {
|
Icons = {
|
||||||
Close = "rbxassetid://6012985953",
|
Close = "rbxassetid://6012985953",
|
||||||
Back = "rbxassetid://6017213752",
|
Back = "rbxassetid://6017213752",
|
||||||
@@ -43,6 +45,9 @@ local Assets = {
|
|||||||
[500] = "rbxassetid://2609138523"
|
[500] = "rbxassetid://2609138523"
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Sounds = {
|
||||||
|
Notification = "rbxassetid://9716079936",
|
||||||
|
},
|
||||||
StartSession = "",
|
StartSession = "",
|
||||||
SessionActive = "",
|
SessionActive = "",
|
||||||
Configure = "",
|
Configure = "",
|
||||||
@@ -60,4 +65,4 @@ end
|
|||||||
|
|
||||||
guardForTypos("Assets", Assets)
|
guardForTypos("Assets", Assets)
|
||||||
|
|
||||||
return Assets
|
return Assets
|
||||||
|
|||||||
40
plugin/src/ChangeBatcher/createPatchSet.lua
Normal file
40
plugin/src/ChangeBatcher/createPatchSet.lua
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
--[[
|
||||||
|
Take an InstanceMap and a dictionary mapping instances to sets of property
|
||||||
|
names. Populate a patch with the encoded values of all the given properties
|
||||||
|
on all the given instances (or, if any changes set Parent to nil, removals
|
||||||
|
of instances) and return the patch.
|
||||||
|
]]
|
||||||
|
|
||||||
|
local Log = require(script.Parent.Parent.Parent.Log)
|
||||||
|
|
||||||
|
local PatchSet = require(script.Parent.Parent.PatchSet)
|
||||||
|
|
||||||
|
local encodePatchUpdate = require(script.Parent.encodePatchUpdate)
|
||||||
|
|
||||||
|
return function(instanceMap, propertyChanges)
|
||||||
|
local patch = PatchSet.newEmpty()
|
||||||
|
|
||||||
|
for instance, properties in pairs(propertyChanges) do
|
||||||
|
local instanceId = instanceMap.fromInstances[instance]
|
||||||
|
|
||||||
|
if instanceId == nil then
|
||||||
|
Log.warn("Ignoring change for instance {:?} as it is unknown to Rojo", instance)
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
if properties.Parent then
|
||||||
|
if instance.Parent == nil then
|
||||||
|
table.insert(patch.removed, instanceId)
|
||||||
|
else
|
||||||
|
Log.warn("Cannot sync non-nil Parent property changes yet")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local update = encodePatchUpdate(instance, instanceId, properties)
|
||||||
|
table.insert(patch.updated, update)
|
||||||
|
end
|
||||||
|
|
||||||
|
propertyChanges[instance] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return patch
|
||||||
|
end
|
||||||
74
plugin/src/ChangeBatcher/createPatchSet.spec.lua
Normal file
74
plugin/src/ChangeBatcher/createPatchSet.spec.lua
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
return function()
|
||||||
|
local PatchSet = require(script.Parent.Parent.PatchSet)
|
||||||
|
local InstanceMap = require(script.Parent.Parent.InstanceMap)
|
||||||
|
|
||||||
|
local createPatchSet = require(script.Parent.createPatchSet)
|
||||||
|
|
||||||
|
it("should return a patch", function()
|
||||||
|
local patch = createPatchSet(InstanceMap.new(), {})
|
||||||
|
|
||||||
|
assert(PatchSet.validate(patch))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should contain updates for every instance with property changes", function()
|
||||||
|
local instanceMap = InstanceMap.new()
|
||||||
|
|
||||||
|
local part1 = Instance.new("Part")
|
||||||
|
instanceMap:insert("PART_1", part1)
|
||||||
|
|
||||||
|
local part2 = Instance.new("Part")
|
||||||
|
instanceMap:insert("PART_2", part2)
|
||||||
|
|
||||||
|
local changes = {
|
||||||
|
[part1] = {
|
||||||
|
Position = true,
|
||||||
|
Size = true,
|
||||||
|
Color = true,
|
||||||
|
},
|
||||||
|
[part2] = {
|
||||||
|
CFrame = true,
|
||||||
|
Velocity = true,
|
||||||
|
Transparency = true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
local patch = createPatchSet(instanceMap, changes)
|
||||||
|
|
||||||
|
expect(#patch.updated).to.equal(2)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should not contain any updates for removed instances", function()
|
||||||
|
local instanceMap = InstanceMap.new()
|
||||||
|
|
||||||
|
local part1 = Instance.new("Part")
|
||||||
|
instanceMap:insert("PART_1", part1)
|
||||||
|
|
||||||
|
local changes = {
|
||||||
|
[part1] = {
|
||||||
|
Parent = true,
|
||||||
|
Position = true,
|
||||||
|
Size = true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
local patch = createPatchSet(instanceMap, changes)
|
||||||
|
|
||||||
|
expect(#patch.removed).to.equal(1)
|
||||||
|
expect(#patch.updated).to.equal(0)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should remove instances from the property change table", function()
|
||||||
|
local instanceMap = InstanceMap.new()
|
||||||
|
|
||||||
|
local part1 = Instance.new("Part")
|
||||||
|
instanceMap:insert("PART_1", part1)
|
||||||
|
|
||||||
|
local changes = {
|
||||||
|
[part1] = {},
|
||||||
|
}
|
||||||
|
|
||||||
|
createPatchSet(instanceMap, changes)
|
||||||
|
|
||||||
|
expect(next(changes)).to.equal(nil)
|
||||||
|
end)
|
||||||
|
end
|
||||||
39
plugin/src/ChangeBatcher/encodePatchUpdate.lua
Normal file
39
plugin/src/ChangeBatcher/encodePatchUpdate.lua
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
local Log = require(script.Parent.Parent.Parent.Log)
|
||||||
|
local RbxDom = require(script.Parent.Parent.Parent.RbxDom)
|
||||||
|
|
||||||
|
local encodeProperty = require(script.Parent.encodeProperty)
|
||||||
|
|
||||||
|
return function(instance, instanceId, properties)
|
||||||
|
local update = {
|
||||||
|
id = instanceId,
|
||||||
|
changedProperties = {},
|
||||||
|
}
|
||||||
|
|
||||||
|
for propertyName in pairs(properties) do
|
||||||
|
if propertyName == "Name" then
|
||||||
|
update.changedName = instance.Name
|
||||||
|
else
|
||||||
|
local descriptor = RbxDom.findCanonicalPropertyDescriptor(instance.ClassName, propertyName)
|
||||||
|
|
||||||
|
if not descriptor then
|
||||||
|
Log.debug("Could not sync back property {:?}.{}", instance, propertyName)
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
local encodeSuccess, encodeResult = encodeProperty(instance, propertyName, descriptor)
|
||||||
|
|
||||||
|
if not encodeSuccess then
|
||||||
|
Log.debug("Could not sync back property {:?}.{}: {}", instance, propertyName, encodeResult)
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
update.changedProperties[propertyName] = encodeResult
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if next(update.changedProperties) == nil and update.changedName == nil then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return update
|
||||||
|
end
|
||||||
62
plugin/src/ChangeBatcher/encodePatchUpdate.spec.lua
Normal file
62
plugin/src/ChangeBatcher/encodePatchUpdate.spec.lua
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
return function()
|
||||||
|
local encodePatchUpdate = require(script.Parent.encodePatchUpdate)
|
||||||
|
|
||||||
|
it("should return an update when there are property changes", function()
|
||||||
|
local part = Instance.new("Part")
|
||||||
|
local properties = {
|
||||||
|
CFrame = true,
|
||||||
|
Color = true,
|
||||||
|
}
|
||||||
|
local update = encodePatchUpdate(part, "PART", properties)
|
||||||
|
|
||||||
|
expect(update.id).to.equal("PART")
|
||||||
|
expect(update.changedProperties.CFrame).to.be.ok()
|
||||||
|
expect(update.changedProperties.Color).to.be.ok()
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should return nil when there are no property changes", function()
|
||||||
|
local part = Instance.new("Part")
|
||||||
|
local properties = {
|
||||||
|
NonExistentProperty = true,
|
||||||
|
}
|
||||||
|
local update = encodePatchUpdate(part, "PART", properties)
|
||||||
|
|
||||||
|
expect(update).to.equal(nil)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should set changedName in the update when the instance's Name changes", function()
|
||||||
|
local part = Instance.new("Part")
|
||||||
|
local properties = {
|
||||||
|
Name = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
part.Name = "We'reGettingToTheCoolPart"
|
||||||
|
|
||||||
|
local update = encodePatchUpdate(part, "PART", properties)
|
||||||
|
|
||||||
|
expect(update.changedName).to.equal("We'reGettingToTheCoolPart")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should correctly encode property values", function()
|
||||||
|
local part = Instance.new("Part")
|
||||||
|
local properties = {
|
||||||
|
Position = true,
|
||||||
|
Color = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
part.Position = Vector3.new(0, 100, 0)
|
||||||
|
part.Color = Color3.new(0.8, 0.2, 0.9)
|
||||||
|
|
||||||
|
local update = encodePatchUpdate(part, "PART", properties)
|
||||||
|
local position = update.changedProperties.Position
|
||||||
|
local color = update.changedProperties.Color
|
||||||
|
|
||||||
|
expect(position.Vector3[1]).to.equal(0)
|
||||||
|
expect(position.Vector3[2]).to.equal(100)
|
||||||
|
expect(position.Vector3[3]).to.equal(0)
|
||||||
|
|
||||||
|
expect(color.Color3[1]).to.be.near(0.8, 0.01)
|
||||||
|
expect(color.Color3[2]).to.be.near(0.2, 0.01)
|
||||||
|
expect(color.Color3[3]).to.be.near(0.9, 0.01)
|
||||||
|
end)
|
||||||
|
end
|
||||||
21
plugin/src/ChangeBatcher/encodeProperty.lua
Normal file
21
plugin/src/ChangeBatcher/encodeProperty.lua
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
local Log = require(script.Parent.Parent.Parent.Log)
|
||||||
|
local RbxDom = require(script.Parent.Parent.Parent.RbxDom)
|
||||||
|
|
||||||
|
return function(instance, propertyName, propertyDescriptor)
|
||||||
|
local readSuccess, readResult = propertyDescriptor:read(instance)
|
||||||
|
|
||||||
|
if not readSuccess then
|
||||||
|
Log.warn("Could not sync back property {:?}.{}: {}", instance, propertyName, readResult)
|
||||||
|
return false, nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local dataType = propertyDescriptor.dataType
|
||||||
|
local encodeSuccess, encodeResult = RbxDom.EncodedValue.encode(readResult, dataType)
|
||||||
|
|
||||||
|
if not encodeSuccess then
|
||||||
|
Log.warn("Could not sync back property {:?}.{}: {}", instance, propertyName, encodeResult)
|
||||||
|
return false, nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return true, encodeResult
|
||||||
|
end
|
||||||
81
plugin/src/ChangeBatcher/init.lua
Normal file
81
plugin/src/ChangeBatcher/init.lua
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
--[[
|
||||||
|
The ChangeBatcher is responsible for collecting and dispatching changes made
|
||||||
|
to tracked instances during two-way sync.
|
||||||
|
]]
|
||||||
|
|
||||||
|
local RunService = game:GetService("RunService")
|
||||||
|
|
||||||
|
local PatchSet = require(script.Parent.PatchSet)
|
||||||
|
|
||||||
|
local createPatchSet = require(script.createPatchSet)
|
||||||
|
|
||||||
|
local ChangeBatcher = {}
|
||||||
|
ChangeBatcher.__index = ChangeBatcher
|
||||||
|
|
||||||
|
local BATCH_INTERVAL = 0.2
|
||||||
|
|
||||||
|
function ChangeBatcher.new(instanceMap, onChangesFlushed)
|
||||||
|
local self
|
||||||
|
|
||||||
|
local renderSteppedConnection = RunService.RenderStepped:Connect(function(dt)
|
||||||
|
self:__cycle(dt)
|
||||||
|
end)
|
||||||
|
|
||||||
|
self = setmetatable({
|
||||||
|
__accumulator = 0,
|
||||||
|
__renderSteppedConnection = renderSteppedConnection,
|
||||||
|
__instanceMap = instanceMap,
|
||||||
|
__onChangesFlushed = onChangesFlushed,
|
||||||
|
__pendingPropertyChanges = {},
|
||||||
|
}, ChangeBatcher)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function ChangeBatcher:stop()
|
||||||
|
self.__renderSteppedConnection:Disconnect()
|
||||||
|
self.__pendingPropertyChanges = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
function ChangeBatcher:add(instance, propertyName)
|
||||||
|
local properties = self.__pendingPropertyChanges[instance]
|
||||||
|
|
||||||
|
if not properties then
|
||||||
|
properties = {}
|
||||||
|
self.__pendingPropertyChanges[instance] = properties
|
||||||
|
end
|
||||||
|
|
||||||
|
properties[propertyName] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
function ChangeBatcher:__cycle(dt)
|
||||||
|
self.__accumulator += dt
|
||||||
|
|
||||||
|
if self.__accumulator >= BATCH_INTERVAL then
|
||||||
|
self.__accumulator -= BATCH_INTERVAL
|
||||||
|
|
||||||
|
local patch = self:__flush()
|
||||||
|
|
||||||
|
if patch then
|
||||||
|
self.__onChangesFlushed(patch)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self.__instanceMap:unpauseAllInstances()
|
||||||
|
end
|
||||||
|
|
||||||
|
function ChangeBatcher:__flush()
|
||||||
|
if next(self.__pendingPropertyChanges) == nil then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local patch = createPatchSet(self.__instanceMap, self.__pendingPropertyChanges)
|
||||||
|
|
||||||
|
if PatchSet.isEmpty(patch) then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return patch
|
||||||
|
end
|
||||||
|
|
||||||
|
return ChangeBatcher
|
||||||
101
plugin/src/ChangeBatcher/init.spec.lua
Normal file
101
plugin/src/ChangeBatcher/init.spec.lua
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
return function()
|
||||||
|
local ChangeBatcher = require(script.Parent)
|
||||||
|
local InstanceMap = require(script.Parent.Parent.InstanceMap)
|
||||||
|
local PatchSet = require(script.Parent.Parent.PatchSet)
|
||||||
|
|
||||||
|
local noop = function() end
|
||||||
|
|
||||||
|
describe("new", function()
|
||||||
|
it("should create a new ChangeBatcher", function()
|
||||||
|
local instanceMap = InstanceMap.new()
|
||||||
|
local changeBatcher = ChangeBatcher.new(instanceMap, noop)
|
||||||
|
|
||||||
|
expect(changeBatcher.__pendingPropertyChanges).to.be.a("table")
|
||||||
|
expect(next(changeBatcher.__pendingPropertyChanges)).to.equal(nil)
|
||||||
|
expect(changeBatcher.__onChangesFlushed).to.equal(noop)
|
||||||
|
expect(changeBatcher.__instanceMap).to.equal(instanceMap)
|
||||||
|
expect(typeof(changeBatcher.__renderSteppedConnection)).to.equal("RBXScriptConnection")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("stop", function()
|
||||||
|
it("should disconnect the RenderStepped connection", function()
|
||||||
|
local changeBatcher = ChangeBatcher.new(InstanceMap.new(), noop)
|
||||||
|
|
||||||
|
changeBatcher:stop()
|
||||||
|
|
||||||
|
expect(changeBatcher.__renderSteppedConnection.Connected).to.equal(false)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("add", function()
|
||||||
|
it("should add property changes to be considered for the current batch", function()
|
||||||
|
local instanceMap = InstanceMap.new()
|
||||||
|
local changeBatcher = ChangeBatcher.new(instanceMap, noop)
|
||||||
|
local part = Instance.new("Part")
|
||||||
|
|
||||||
|
instanceMap:insert("PART", part)
|
||||||
|
changeBatcher:add(part, "Name")
|
||||||
|
|
||||||
|
local properties = changeBatcher.__pendingPropertyChanges[part]
|
||||||
|
|
||||||
|
expect(properties).to.be.a("table")
|
||||||
|
expect(properties.Name).to.be.ok()
|
||||||
|
|
||||||
|
changeBatcher:add(part, "Position")
|
||||||
|
expect(properties.Position).to.be.ok()
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("__cycle", function()
|
||||||
|
it("should immediately unpause any paused instances after each cycle", function()
|
||||||
|
local instanceMap = InstanceMap.new()
|
||||||
|
local changeBatcher = ChangeBatcher.new(instanceMap, noop)
|
||||||
|
local part = Instance.new("Part")
|
||||||
|
|
||||||
|
instanceMap.pausedUpdateInstances[part] = true
|
||||||
|
|
||||||
|
changeBatcher:__cycle(0)
|
||||||
|
|
||||||
|
expect(instanceMap.pausedUpdateInstances[part]).to.equal(nil)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("__flush", function()
|
||||||
|
it("should return nil when there are no changes to process", function()
|
||||||
|
local changeBatcher = ChangeBatcher.new(InstanceMap.new(), noop)
|
||||||
|
expect(changeBatcher:__flush()).to.equal(nil)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should return a patch when there are changes to process and the resulting patch is non-empty", function()
|
||||||
|
local instanceMap = InstanceMap.new()
|
||||||
|
local changeBatcher = ChangeBatcher.new(instanceMap, noop)
|
||||||
|
local part = Instance.new("Part")
|
||||||
|
|
||||||
|
instanceMap:insert("PART", part)
|
||||||
|
|
||||||
|
changeBatcher.__pendingPropertyChanges[part] = {
|
||||||
|
Position = true,
|
||||||
|
Name = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
local patch = changeBatcher:__flush()
|
||||||
|
|
||||||
|
assert(PatchSet.validate(patch))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should return nil when there are changes to process and the resulting patch is empty", function()
|
||||||
|
local instanceMap = InstanceMap.new()
|
||||||
|
local changeBatcher = ChangeBatcher.new(instanceMap, noop)
|
||||||
|
local part = Instance.new("Part")
|
||||||
|
|
||||||
|
instanceMap:insert("PART", part)
|
||||||
|
|
||||||
|
changeBatcher.__pendingPropertyChanges[part] = {
|
||||||
|
NonExistentProperty = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(changeBatcher:__flush()).to.equal(nil)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end
|
||||||
@@ -5,7 +5,7 @@ local isDevBuild = script.Parent.Parent:FindFirstChild("ROJO_DEV_BUILD") ~= nil
|
|||||||
return strict("Config", {
|
return strict("Config", {
|
||||||
isDevBuild = isDevBuild,
|
isDevBuild = isDevBuild,
|
||||||
codename = "Epiphany",
|
codename = "Epiphany",
|
||||||
version = {7, 0, 0, "-rc.1"},
|
version = {7, 2, 0},
|
||||||
expectedServerVersionString = "7.0 or newer",
|
expectedServerVersionString = "7.0 or newer",
|
||||||
protocolVersion = 4,
|
protocolVersion = 4,
|
||||||
defaultHost = "localhost",
|
defaultHost = "localhost",
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
local RunService = game:GetService("RunService")
|
||||||
|
|
||||||
local Log = require(script.Parent.Parent.Log)
|
local Log = require(script.Parent.Parent.Log)
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
@@ -135,29 +137,31 @@ function InstanceMap:destroyId(id)
|
|||||||
end
|
end
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
Pause updates for an instance momentarily and invoke a callback.
|
Pause updates for an instance.
|
||||||
|
|
||||||
If the callback throws an error, InstanceMap will still be kept in a
|
|
||||||
consistent state.
|
|
||||||
]]
|
]]
|
||||||
function InstanceMap:pauseInstance(instance, callback)
|
function InstanceMap:pauseInstance(instance)
|
||||||
local id = self.fromInstances[instance]
|
local id = self.fromInstances[instance]
|
||||||
|
|
||||||
-- If we don't know about this instance, ignore it and do not invoke the
|
-- If we don't know about this instance, ignore it.
|
||||||
-- callback.
|
|
||||||
if id == nil then
|
if id == nil then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
self.pausedUpdateInstances[instance] = true
|
self.pausedUpdateInstances[instance] = true
|
||||||
local success, result = xpcall(callback, debug.traceback)
|
end
|
||||||
self.pausedUpdateInstances[instance] = false
|
|
||||||
|
|
||||||
if success then
|
--[[
|
||||||
return result
|
Unpause updates for an instance.
|
||||||
else
|
]]
|
||||||
error(result, 2)
|
function InstanceMap:unpauseInstance(instance)
|
||||||
end
|
self.pausedUpdateInstances[instance] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Unpause updates for all instances.
|
||||||
|
]]
|
||||||
|
function InstanceMap:unpauseAllInstances()
|
||||||
|
table.clear(self.pausedUpdateInstances)
|
||||||
end
|
end
|
||||||
|
|
||||||
function InstanceMap:__connectSignals(instance)
|
function InstanceMap:__connectSignals(instance)
|
||||||
@@ -200,6 +204,12 @@ function InstanceMap:__maybeFireInstanceChanged(instance, propertyName)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if RunService:IsRunning() then
|
||||||
|
-- We probably don't want to pick up property changes to save to the
|
||||||
|
-- filesystem in a running game.
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
self.onInstanceChanged(instance, propertyName)
|
self.onInstanceChanged(instance, propertyName)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -222,4 +232,4 @@ function InstanceMap:__disconnectSignals(instance)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return InstanceMap
|
return InstanceMap
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ local function applyPatch(instanceMap, patch)
|
|||||||
local failedToReify = reify(instanceMap, patch.added, id, parentInstance)
|
local failedToReify = reify(instanceMap, patch.added, id, parentInstance)
|
||||||
|
|
||||||
if not PatchSet.isEmpty(failedToReify) then
|
if not PatchSet.isEmpty(failedToReify) then
|
||||||
Log.debug("Failed to reify as part of applying a patch: {}", failedToReify)
|
Log.debug("Failed to reify as part of applying a patch: {:#?}", failedToReify)
|
||||||
PatchSet.assign(unappliedPatch, failedToReify)
|
PatchSet.assign(unappliedPatch, failedToReify)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -77,6 +77,10 @@ local function applyPatch(instanceMap, patch)
|
|||||||
continue
|
continue
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Pause updates on this instance to avoid picking up our changes when
|
||||||
|
-- two-way sync is enabled.
|
||||||
|
instanceMap:pauseInstance(instance)
|
||||||
|
|
||||||
-- Track any part of this update that could not be applied.
|
-- Track any part of this update that could not be applied.
|
||||||
local unappliedUpdate = {
|
local unappliedUpdate = {
|
||||||
id = update.id,
|
id = update.id,
|
||||||
@@ -197,4 +201,4 @@ local function applyPatch(instanceMap, patch)
|
|||||||
return unappliedPatch
|
return unappliedPatch
|
||||||
end
|
end
|
||||||
|
|
||||||
return applyPatch
|
return applyPatch
|
||||||
|
|||||||
@@ -75,9 +75,13 @@ local function diff(instanceMap, virtualInstances, rootId)
|
|||||||
changedProperties[propertyName] = virtualValue
|
changedProperties[propertyName] = virtualValue
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
-- virtualValue can be empty in certain cases, and this may print out nil to the user.
|
|
||||||
local propertyType = next(virtualValue)
|
local propertyType = next(virtualValue)
|
||||||
Log.warn("Failed to decode property of type {}", propertyType)
|
Log.warn(
|
||||||
|
"Failed to decode property {}.{}. Encoded property was: {:#?}",
|
||||||
|
virtualInstance.ClassName,
|
||||||
|
propertyName,
|
||||||
|
virtualValue
|
||||||
|
)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
local err = existingValueOrErr
|
local err = existingValueOrErr
|
||||||
|
|||||||
@@ -40,6 +40,13 @@ local function getProperty(instance, propertyName)
|
|||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if err.kind == RbxDom.Error.Kind.Roblox and err.extra:find("is not a valid member of") then
|
||||||
|
return false, Error.new(Error.UnknownProperty, {
|
||||||
|
className = instance.ClassName,
|
||||||
|
propertyName = propertyName,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
return false, Error.new(Error.OtherPropertyError, {
|
return false, Error.new(Error.OtherPropertyError, {
|
||||||
className = instance.ClassName,
|
className = instance.ClassName,
|
||||||
propertyName = propertyName,
|
propertyName = propertyName,
|
||||||
|
|||||||
@@ -255,7 +255,7 @@ return function()
|
|||||||
Name = "Child A",
|
Name = "Child A",
|
||||||
Properties = {
|
Properties = {
|
||||||
Value = {
|
Value = {
|
||||||
Ref = "Child B",
|
Ref = "CHILD_B",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Children = {},
|
Children = {},
|
||||||
@@ -287,7 +287,7 @@ return function()
|
|||||||
-- constructed as part of a recursive call before the parent has totally
|
-- constructed as part of a recursive call before the parent has totally
|
||||||
-- finished. Given deferred refs, this should not fail, but it is a good
|
-- finished. Given deferred refs, this should not fail, but it is a good
|
||||||
-- case to test.
|
-- case to test.
|
||||||
it("should apply properties containing refs to later siblings correctly", function()
|
it("should apply properties containing refs to later children correctly", function()
|
||||||
local virtualInstances = {
|
local virtualInstances = {
|
||||||
ROOT = {
|
ROOT = {
|
||||||
ClassName = "ObjectValue",
|
ClassName = "ObjectValue",
|
||||||
@@ -344,4 +344,4 @@ return function()
|
|||||||
expect(update.id).to.equal("ROOT")
|
expect(update.id).to.equal("ROOT")
|
||||||
expect(update.changedProperties.Value).to.equal(virtualInstances["ROOT"].Properties.Value)
|
expect(update.changedProperties.Value).to.equal(virtualInstances["ROOT"].Properties.Value)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ local Log = require(script.Parent.Parent.Log)
|
|||||||
local Fmt = require(script.Parent.Parent.Fmt)
|
local Fmt = require(script.Parent.Parent.Fmt)
|
||||||
local t = require(script.Parent.Parent.t)
|
local t = require(script.Parent.Parent.t)
|
||||||
|
|
||||||
|
local ChangeBatcher = require(script.Parent.ChangeBatcher)
|
||||||
local InstanceMap = require(script.Parent.InstanceMap)
|
local InstanceMap = require(script.Parent.InstanceMap)
|
||||||
local PatchSet = require(script.Parent.PatchSet)
|
local PatchSet = require(script.Parent.PatchSet)
|
||||||
local Reconciler = require(script.Parent.Reconciler)
|
local Reconciler = require(script.Parent.Reconciler)
|
||||||
@@ -56,10 +57,19 @@ function ServeSession.new(options)
|
|||||||
-- Declare self ahead of time to capture it in a closure
|
-- Declare self ahead of time to capture it in a closure
|
||||||
local self
|
local self
|
||||||
local function onInstanceChanged(instance, propertyName)
|
local function onInstanceChanged(instance, propertyName)
|
||||||
self:__onInstanceChanged(instance, propertyName)
|
if not self.__twoWaySync then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
self.__changeBatcher:add(instance, propertyName)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function onChangesFlushed(patch)
|
||||||
|
self.__apiContext:write(patch)
|
||||||
end
|
end
|
||||||
|
|
||||||
local instanceMap = InstanceMap.new(onInstanceChanged)
|
local instanceMap = InstanceMap.new(onInstanceChanged)
|
||||||
|
local changeBatcher = ChangeBatcher.new(instanceMap, onChangesFlushed)
|
||||||
local reconciler = Reconciler.new(instanceMap)
|
local reconciler = Reconciler.new(instanceMap)
|
||||||
|
|
||||||
local connections = {}
|
local connections = {}
|
||||||
@@ -82,6 +92,7 @@ function ServeSession.new(options)
|
|||||||
__twoWaySync = options.twoWaySync,
|
__twoWaySync = options.twoWaySync,
|
||||||
__reconciler = reconciler,
|
__reconciler = reconciler,
|
||||||
__instanceMap = instanceMap,
|
__instanceMap = instanceMap,
|
||||||
|
__changeBatcher = changeBatcher,
|
||||||
__statusChangedCallback = nil,
|
__statusChangedCallback = nil,
|
||||||
__connections = connections,
|
__connections = connections,
|
||||||
}
|
}
|
||||||
@@ -102,6 +113,10 @@ function ServeSession:__fmtDebug(output)
|
|||||||
output:write("}")
|
output:write("}")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function ServeSession:getStatus()
|
||||||
|
return self.__status
|
||||||
|
end
|
||||||
|
|
||||||
function ServeSession:onStatusChanged(callback)
|
function ServeSession:onStatusChanged(callback)
|
||||||
self.__statusChangedCallback = callback
|
self.__statusChangedCallback = callback
|
||||||
end
|
end
|
||||||
@@ -179,55 +194,6 @@ function ServeSession:__onActiveScriptChanged(activeScript)
|
|||||||
self.__apiContext:open(scriptId)
|
self.__apiContext:open(scriptId)
|
||||||
end
|
end
|
||||||
|
|
||||||
function ServeSession:__onInstanceChanged(instance, propertyName)
|
|
||||||
if not self.__twoWaySync then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local instanceId = self.__instanceMap.fromInstances[instance]
|
|
||||||
|
|
||||||
if instanceId == nil then
|
|
||||||
Log.warn("Ignoring change for instance {:?} as it is unknown to Rojo", instance)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local remove = nil
|
|
||||||
|
|
||||||
local update = {
|
|
||||||
id = instanceId,
|
|
||||||
changedProperties = {},
|
|
||||||
}
|
|
||||||
|
|
||||||
if propertyName == "Name" then
|
|
||||||
update.changedName = instance.Name
|
|
||||||
elseif propertyName == "Parent" then
|
|
||||||
if instance.Parent == nil then
|
|
||||||
update = nil
|
|
||||||
remove = instanceId
|
|
||||||
else
|
|
||||||
Log.warn("Cannot sync non-nil Parent property changes yet")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
else
|
|
||||||
local success, encoded = self.__reconciler:encodeApiValue(instance[propertyName])
|
|
||||||
|
|
||||||
if not success then
|
|
||||||
Log.warn("Could not sync back property {:?}.{}", instance, propertyName)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
update.changedProperties[propertyName] = encoded
|
|
||||||
end
|
|
||||||
|
|
||||||
local patch = {
|
|
||||||
removed = {remove},
|
|
||||||
added = {},
|
|
||||||
updated = {update},
|
|
||||||
}
|
|
||||||
|
|
||||||
self.__apiContext:write(patch)
|
|
||||||
end
|
|
||||||
|
|
||||||
function ServeSession:__initialSync(rootInstanceId)
|
function ServeSession:__initialSync(rootInstanceId)
|
||||||
return self.__apiContext:read({ rootInstanceId })
|
return self.__apiContext:read({ rootInstanceId })
|
||||||
:andThen(function(readResponseBody)
|
:andThen(function(readResponseBody)
|
||||||
@@ -290,6 +256,7 @@ function ServeSession:__stopInternal(err)
|
|||||||
self:__setStatus(Status.Disconnected, err)
|
self:__setStatus(Status.Disconnected, err)
|
||||||
self.__apiContext:disconnect()
|
self.__apiContext:disconnect()
|
||||||
self.__instanceMap:stop()
|
self.__instanceMap:stop()
|
||||||
|
self.__changeBatcher:stop()
|
||||||
|
|
||||||
for _, connection in ipairs(self.__connections) do
|
for _, connection in ipairs(self.__connections) do
|
||||||
connection:Disconnect()
|
connection:Disconnect()
|
||||||
@@ -305,4 +272,4 @@ function ServeSession:__setStatus(status, detail)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return ServeSession
|
return ServeSession
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ local App = require(script.App)
|
|||||||
local app = Roact.createElement(App, {
|
local app = Roact.createElement(App, {
|
||||||
plugin = plugin
|
plugin = plugin
|
||||||
})
|
})
|
||||||
local tree = Roact.mount(app, nil, "Rojo UI")
|
local tree = Roact.mount(app, game:GetService("CoreGui"), "Rojo UI")
|
||||||
|
|
||||||
plugin.Unloading:Connect(function()
|
plugin.Unloading:Connect(function()
|
||||||
Roact.unmount(tree)
|
Roact.unmount(tree)
|
||||||
@@ -28,4 +28,4 @@ if Config.isDevBuild then
|
|||||||
local TestEZ = require(script.Parent.TestEZ)
|
local TestEZ = require(script.Parent.TestEZ)
|
||||||
|
|
||||||
require(script.runTests)(TestEZ)
|
require(script.runTests)(TestEZ)
|
||||||
end
|
end
|
||||||
|
|||||||
22
plugin/src/playSound.lua
Normal file
22
plugin/src/playSound.lua
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
-- Roblox decided that sounds only play in Edit mode when parented to a plugin widget, for some reason
|
||||||
|
local plugin = plugin or script:FindFirstAncestorWhichIsA("Plugin")
|
||||||
|
local widget = plugin:CreateDockWidgetPluginGui("Rojo_soundPlayer", DockWidgetPluginGuiInfo.new(
|
||||||
|
Enum.InitialDockState.Float,
|
||||||
|
false, true,
|
||||||
|
10, 10,
|
||||||
|
10, 10
|
||||||
|
))
|
||||||
|
widget.Name = "Rojo_soundPlayer"
|
||||||
|
widget.Title = "Rojo Sound Player"
|
||||||
|
|
||||||
|
return function(soundId)
|
||||||
|
local sound = Instance.new("Sound")
|
||||||
|
sound.SoundId = soundId
|
||||||
|
sound.Parent = widget
|
||||||
|
|
||||||
|
sound.Ended:Connect(function()
|
||||||
|
sound:Destroy()
|
||||||
|
end)
|
||||||
|
|
||||||
|
sound:Play()
|
||||||
|
end
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
"TestEZ": {
|
"TestEZ": {
|
||||||
"$path": "modules/testez/lib"
|
"$path": "modules/testez"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -25,4 +25,4 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
source: tests/tests/build.rs
|
||||||
|
assertion_line: 99
|
||||||
|
expression: contents
|
||||||
|
---
|
||||||
|
<roblox version="4">
|
||||||
|
<Item class="Folder" referent="0">
|
||||||
|
<Properties>
|
||||||
|
<string name="Name">attributes</string>
|
||||||
|
</Properties>
|
||||||
|
<Item class="Folder" referent="1">
|
||||||
|
<Properties>
|
||||||
|
<string name="Name">Explicit</string>
|
||||||
|
<BinaryString name="AttributesSerialize">AgAAAAUAAABIZWxsbwIFAAAAV29ybGQGAAAAVmVjdG9yEQAAgD8AAABAAABAQA==</BinaryString>
|
||||||
|
</Properties>
|
||||||
|
</Item>
|
||||||
|
<Item class="Folder" referent="2">
|
||||||
|
<Properties>
|
||||||
|
<string name="Name">ImplicitAttributes</string>
|
||||||
|
<BinaryString name="AttributesSerialize">AgAAAAMAAABIZXkCBwAAAEdyYW5kbWEGAAAAVmVjdG9yEQAAgEAAAKBAAADAQA==</BinaryString>
|
||||||
|
</Properties>
|
||||||
|
</Item>
|
||||||
|
</Item>
|
||||||
|
</roblox>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
source: tests/tests/build.rs
|
||||||
|
assertion_line: 98
|
||||||
|
expression: contents
|
||||||
|
---
|
||||||
|
<roblox version="4">
|
||||||
|
<Item class="LocalScript" referent="0">
|
||||||
|
<Properties>
|
||||||
|
<string name="Name">issue_546</string>
|
||||||
|
<bool name="Disabled">true</bool>
|
||||||
|
<string name="Source">print("Hello, world!")</string>
|
||||||
|
</Properties>
|
||||||
|
</Item>
|
||||||
|
</roblox>
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
source: tests/tests/build.rs
|
||||||
|
expression: contents
|
||||||
|
|
||||||
|
---
|
||||||
|
<roblox version="4">
|
||||||
|
<Item class="Folder" referent="0">
|
||||||
|
<Properties>
|
||||||
|
<string name="Name">optional</string>
|
||||||
|
</Properties>
|
||||||
|
<Item class="StringValue" referent="1">
|
||||||
|
<Properties>
|
||||||
|
<string name="Name">foo-optional</string>
|
||||||
|
<string name="Value">Hello, from foo.txt!</string>
|
||||||
|
</Properties>
|
||||||
|
</Item>
|
||||||
|
<Item class="StringValue" referent="2">
|
||||||
|
<Properties>
|
||||||
|
<string name="Name">foo-required</string>
|
||||||
|
<string name="Value">Hello, from foo.txt!</string>
|
||||||
|
</Properties>
|
||||||
|
</Item>
|
||||||
|
</Item>
|
||||||
|
</roblox>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
source: tests/tests/build.rs
|
||||||
|
expression: contents
|
||||||
|
|
||||||
|
---
|
||||||
|
<roblox version="4">
|
||||||
|
<Item class="Folder" referent="0">
|
||||||
|
<Properties>
|
||||||
|
<string name="Name">root</string>
|
||||||
|
</Properties>
|
||||||
|
<Item class="Folder" referent="1">
|
||||||
|
<Properties>
|
||||||
|
<string name="Name">folder</string>
|
||||||
|
</Properties>
|
||||||
|
</Item>
|
||||||
|
</Item>
|
||||||
|
</roblox>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
source: tests/tests/build.rs
|
||||||
|
expression: contents
|
||||||
|
|
||||||
|
---
|
||||||
|
<roblox version="4">
|
||||||
|
<Item class="Folder" referent="0">
|
||||||
|
<Properties>
|
||||||
|
<string name="Name">root</string>
|
||||||
|
</Properties>
|
||||||
|
<Item class="Folder" referent="1">
|
||||||
|
<Properties>
|
||||||
|
<string name="Name">folder</string>
|
||||||
|
</Properties>
|
||||||
|
<Item class="Folder" referent="2">
|
||||||
|
<Properties>
|
||||||
|
<string name="Name">child-projectname</string>
|
||||||
|
</Properties>
|
||||||
|
</Item>
|
||||||
|
</Item>
|
||||||
|
</Item>
|
||||||
|
</roblox>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
source: tests/tests/build.rs
|
||||||
|
expression: contents
|
||||||
|
|
||||||
|
---
|
||||||
|
<roblox version="4">
|
||||||
|
<Item class="Folder" referent="0">
|
||||||
|
<Properties>
|
||||||
|
<string name="Name">root</string>
|
||||||
|
</Properties>
|
||||||
|
</Item>
|
||||||
|
</roblox>
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
source: tests/tests/build.rs
|
source: tests/tests/build.rs
|
||||||
expression: contents
|
expression: contents
|
||||||
|
|
||||||
---
|
---
|
||||||
<roblox version="4">
|
<roblox version="4">
|
||||||
<Item class="Folder" referent="0">
|
<Item class="Folder" referent="0">
|
||||||
@@ -25,14 +26,12 @@ expression: contents
|
|||||||
<R22>1</R22>
|
<R22>1</R22>
|
||||||
</CoordinateFrame>
|
</CoordinateFrame>
|
||||||
<Ref name="PrimaryPart">null</Ref>
|
<Ref name="PrimaryPart">null</Ref>
|
||||||
<BinaryString name="Tags">
|
<BinaryString name="Tags"></BinaryString>
|
||||||
</BinaryString>
|
|
||||||
</Properties>
|
</Properties>
|
||||||
<Item class="StringValue" referent="2">
|
<Item class="StringValue" referent="2">
|
||||||
<Properties>
|
<Properties>
|
||||||
<string name="Name">Cool StringValue</string>
|
<string name="Name">Cool StringValue</string>
|
||||||
<BinaryString name="Tags">
|
<BinaryString name="Tags"></BinaryString>
|
||||||
</BinaryString>
|
|
||||||
<string name="Value">Did you know that BaseValue.Changed is different than Instance.Changed?</string>
|
<string name="Value">Did you know that BaseValue.Changed is different than Instance.Changed?</string>
|
||||||
</Properties>
|
</Properties>
|
||||||
</Item>
|
</Item>
|
||||||
|
|||||||
@@ -1,27 +1,25 @@
|
|||||||
---
|
---
|
||||||
source: tests/tests/build.rs
|
source: tests/tests/build.rs
|
||||||
expression: contents
|
expression: contents
|
||||||
|
|
||||||
---
|
---
|
||||||
<roblox version="4">
|
<roblox version="4">
|
||||||
<Item class="Folder" referent="0">
|
<Item class="Folder" referent="0">
|
||||||
<Properties>
|
<Properties>
|
||||||
<string name="Name">rbxmx_ref</string>
|
<string name="Name">rbxmx_ref</string>
|
||||||
<BinaryString name="Tags">
|
<BinaryString name="Tags"></BinaryString>
|
||||||
</BinaryString>
|
|
||||||
</Properties>
|
</Properties>
|
||||||
<Item class="StringValue" referent="1">
|
<Item class="StringValue" referent="1">
|
||||||
<Properties>
|
<Properties>
|
||||||
<string name="Name">Target</string>
|
<string name="Name">Target</string>
|
||||||
<BinaryString name="Tags">
|
<BinaryString name="Tags"></BinaryString>
|
||||||
</BinaryString>
|
|
||||||
<string name="Value">Pointed to by ObjectValue</string>
|
<string name="Value">Pointed to by ObjectValue</string>
|
||||||
</Properties>
|
</Properties>
|
||||||
</Item>
|
</Item>
|
||||||
<Item class="ObjectValue" referent="2">
|
<Item class="ObjectValue" referent="2">
|
||||||
<Properties>
|
<Properties>
|
||||||
<string name="Name">Pointer</string>
|
<string name="Name">Pointer</string>
|
||||||
<BinaryString name="Tags">
|
<BinaryString name="Tags"></BinaryString>
|
||||||
</BinaryString>
|
|
||||||
<Ref name="Value">1</Ref>
|
<Ref name="Value">1</Ref>
|
||||||
</Properties>
|
</Properties>
|
||||||
</Item>
|
</Item>
|
||||||
|
|||||||
@@ -32,6 +32,20 @@ expression: contents
|
|||||||
<Item class="Part" referent="4">
|
<Item class="Part" referent="4">
|
||||||
<Properties>
|
<Properties>
|
||||||
<string name="Name">Color</string>
|
<string name="Name">Color</string>
|
||||||
|
<CoordinateFrame name="CFrame">
|
||||||
|
<X>1</X>
|
||||||
|
<Y>2</Y>
|
||||||
|
<Z>3</Z>
|
||||||
|
<R00>0</R00>
|
||||||
|
<R01>1</R01>
|
||||||
|
<R02>0</R02>
|
||||||
|
<R10>0</R10>
|
||||||
|
<R11>0</R11>
|
||||||
|
<R12>1</R12>
|
||||||
|
<R20>1</R20>
|
||||||
|
<R21>0</R21>
|
||||||
|
<R22>0</R22>
|
||||||
|
</CoordinateFrame>
|
||||||
<Color3uint8 name="Color3uint8">8404992</Color3uint8>
|
<Color3uint8 name="Color3uint8">8404992</Color3uint8>
|
||||||
</Properties>
|
</Properties>
|
||||||
</Item>
|
</Item>
|
||||||
|
|||||||
@@ -0,0 +1,226 @@
|
|||||||
|
---
|
||||||
|
source: tests/tests/build.rs
|
||||||
|
assertion_line: 99
|
||||||
|
expression: contents
|
||||||
|
---
|
||||||
|
<roblox version="4">
|
||||||
|
<Item class="Folder" referent="0">
|
||||||
|
<Properties>
|
||||||
|
<string name="Name">weldconstraint</string>
|
||||||
|
<BinaryString name="AttributesSerialize"></BinaryString>
|
||||||
|
<int64 name="SourceAssetId">-1</int64>
|
||||||
|
<BinaryString name="Tags"></BinaryString>
|
||||||
|
</Properties>
|
||||||
|
<Item class="Part" referent="1">
|
||||||
|
<Properties>
|
||||||
|
<string name="Name">A</string>
|
||||||
|
<bool name="Anchored">false</bool>
|
||||||
|
<BinaryString name="AttributesSerialize"></BinaryString>
|
||||||
|
<float name="BackParamA">-0.5</float>
|
||||||
|
<float name="BackParamB">0.5</float>
|
||||||
|
<token name="BackSurface">0</token>
|
||||||
|
<token name="BackSurfaceInput">0</token>
|
||||||
|
<float name="BottomParamA">-0.5</float>
|
||||||
|
<float name="BottomParamB">0.5</float>
|
||||||
|
<token name="BottomSurface">0</token>
|
||||||
|
<token name="BottomSurfaceInput">0</token>
|
||||||
|
<CoordinateFrame name="CFrame">
|
||||||
|
<X>-14</X>
|
||||||
|
<Y>0.5</Y>
|
||||||
|
<Z>-5</Z>
|
||||||
|
<R00>1</R00>
|
||||||
|
<R01>0</R01>
|
||||||
|
<R02>0</R02>
|
||||||
|
<R10>0</R10>
|
||||||
|
<R11>1</R11>
|
||||||
|
<R12>0</R12>
|
||||||
|
<R20>0</R20>
|
||||||
|
<R21>0</R21>
|
||||||
|
<R22>1</R22>
|
||||||
|
</CoordinateFrame>
|
||||||
|
<bool name="CanCollide">true</bool>
|
||||||
|
<bool name="CanQuery">true</bool>
|
||||||
|
<bool name="CanTouch">true</bool>
|
||||||
|
<bool name="CastShadow">true</bool>
|
||||||
|
<int name="CollisionGroupId">0</int>
|
||||||
|
<Color3uint8 name="Color3uint8">10724005</Color3uint8>
|
||||||
|
<PhysicalProperties name="CustomPhysicalProperties">
|
||||||
|
<CustomPhysics>false</CustomPhysics>
|
||||||
|
</PhysicalProperties>
|
||||||
|
<token name="formFactorRaw">1</token>
|
||||||
|
<float name="FrontParamA">-0.5</float>
|
||||||
|
<float name="FrontParamB">0.5</float>
|
||||||
|
<token name="FrontSurface">0</token>
|
||||||
|
<token name="FrontSurfaceInput">0</token>
|
||||||
|
<float name="LeftParamA">-0.5</float>
|
||||||
|
<float name="LeftParamB">0.5</float>
|
||||||
|
<token name="LeftSurface">0</token>
|
||||||
|
<token name="LeftSurfaceInput">0</token>
|
||||||
|
<bool name="Locked">false</bool>
|
||||||
|
<bool name="Massless">false</bool>
|
||||||
|
<token name="Material">256</token>
|
||||||
|
<CoordinateFrame name="PivotOffset">
|
||||||
|
<X>0</X>
|
||||||
|
<Y>0</Y>
|
||||||
|
<Z>0</Z>
|
||||||
|
<R00>1</R00>
|
||||||
|
<R01>0</R01>
|
||||||
|
<R02>0</R02>
|
||||||
|
<R10>0</R10>
|
||||||
|
<R11>1</R11>
|
||||||
|
<R12>0</R12>
|
||||||
|
<R20>0</R20>
|
||||||
|
<R21>0</R21>
|
||||||
|
<R22>1</R22>
|
||||||
|
</CoordinateFrame>
|
||||||
|
<float name="Reflectance">0</float>
|
||||||
|
<float name="RightParamA">-0.5</float>
|
||||||
|
<float name="RightParamB">0.5</float>
|
||||||
|
<token name="RightSurface">0</token>
|
||||||
|
<token name="RightSurfaceInput">0</token>
|
||||||
|
<int name="RootPriority">0</int>
|
||||||
|
<Vector3 name="RotVelocity">
|
||||||
|
<X>0</X>
|
||||||
|
<Y>0</Y>
|
||||||
|
<Z>0</Z>
|
||||||
|
</Vector3>
|
||||||
|
<token name="shape">1</token>
|
||||||
|
<Vector3 name="size">
|
||||||
|
<X>4</X>
|
||||||
|
<Y>1</Y>
|
||||||
|
<Z>2</Z>
|
||||||
|
</Vector3>
|
||||||
|
<int64 name="SourceAssetId">-1</int64>
|
||||||
|
<BinaryString name="Tags"></BinaryString>
|
||||||
|
<float name="TopParamA">-0.5</float>
|
||||||
|
<float name="TopParamB">0.5</float>
|
||||||
|
<token name="TopSurface">0</token>
|
||||||
|
<token name="TopSurfaceInput">0</token>
|
||||||
|
<float name="Transparency">0</float>
|
||||||
|
<Vector3 name="Velocity">
|
||||||
|
<X>0</X>
|
||||||
|
<Y>0</Y>
|
||||||
|
<Z>0</Z>
|
||||||
|
</Vector3>
|
||||||
|
</Properties>
|
||||||
|
<Item class="WeldConstraint" referent="2">
|
||||||
|
<Properties>
|
||||||
|
<string name="Name">WeldConstraint</string>
|
||||||
|
<BinaryString name="AttributesSerialize"></BinaryString>
|
||||||
|
<CoordinateFrame name="CFrame0">
|
||||||
|
<X>7</X>
|
||||||
|
<Y>0.000001013279</Y>
|
||||||
|
<Z>-3</Z>
|
||||||
|
<R00>1</R00>
|
||||||
|
<R01>0</R01>
|
||||||
|
<R02>0</R02>
|
||||||
|
<R10>0</R10>
|
||||||
|
<R11>1</R11>
|
||||||
|
<R12>0</R12>
|
||||||
|
<R20>0</R20>
|
||||||
|
<R21>0</R21>
|
||||||
|
<R22>1</R22>
|
||||||
|
</CoordinateFrame>
|
||||||
|
<Ref name="Part0Internal">1</Ref>
|
||||||
|
<Ref name="Part1Internal">3</Ref>
|
||||||
|
<int64 name="SourceAssetId">-1</int64>
|
||||||
|
<int name="State">3</int>
|
||||||
|
<BinaryString name="Tags"></BinaryString>
|
||||||
|
</Properties>
|
||||||
|
</Item>
|
||||||
|
</Item>
|
||||||
|
<Item class="Part" referent="3">
|
||||||
|
<Properties>
|
||||||
|
<string name="Name">B</string>
|
||||||
|
<bool name="Anchored">false</bool>
|
||||||
|
<BinaryString name="AttributesSerialize"></BinaryString>
|
||||||
|
<float name="BackParamA">-0.5</float>
|
||||||
|
<float name="BackParamB">0.5</float>
|
||||||
|
<token name="BackSurface">0</token>
|
||||||
|
<token name="BackSurfaceInput">0</token>
|
||||||
|
<float name="BottomParamA">-0.5</float>
|
||||||
|
<float name="BottomParamB">0.5</float>
|
||||||
|
<token name="BottomSurface">0</token>
|
||||||
|
<token name="BottomSurfaceInput">0</token>
|
||||||
|
<CoordinateFrame name="CFrame">
|
||||||
|
<X>-7</X>
|
||||||
|
<Y>0.500001</Y>
|
||||||
|
<Z>-8</Z>
|
||||||
|
<R00>1</R00>
|
||||||
|
<R01>0</R01>
|
||||||
|
<R02>0</R02>
|
||||||
|
<R10>0</R10>
|
||||||
|
<R11>1</R11>
|
||||||
|
<R12>0</R12>
|
||||||
|
<R20>0</R20>
|
||||||
|
<R21>0</R21>
|
||||||
|
<R22>1</R22>
|
||||||
|
</CoordinateFrame>
|
||||||
|
<bool name="CanCollide">true</bool>
|
||||||
|
<bool name="CanQuery">true</bool>
|
||||||
|
<bool name="CanTouch">true</bool>
|
||||||
|
<bool name="CastShadow">true</bool>
|
||||||
|
<int name="CollisionGroupId">0</int>
|
||||||
|
<Color3uint8 name="Color3uint8">10724005</Color3uint8>
|
||||||
|
<PhysicalProperties name="CustomPhysicalProperties">
|
||||||
|
<CustomPhysics>false</CustomPhysics>
|
||||||
|
</PhysicalProperties>
|
||||||
|
<token name="formFactorRaw">1</token>
|
||||||
|
<float name="FrontParamA">-0.5</float>
|
||||||
|
<float name="FrontParamB">0.5</float>
|
||||||
|
<token name="FrontSurface">0</token>
|
||||||
|
<token name="FrontSurfaceInput">0</token>
|
||||||
|
<float name="LeftParamA">-0.5</float>
|
||||||
|
<float name="LeftParamB">0.5</float>
|
||||||
|
<token name="LeftSurface">0</token>
|
||||||
|
<token name="LeftSurfaceInput">0</token>
|
||||||
|
<bool name="Locked">false</bool>
|
||||||
|
<bool name="Massless">false</bool>
|
||||||
|
<token name="Material">256</token>
|
||||||
|
<CoordinateFrame name="PivotOffset">
|
||||||
|
<X>0</X>
|
||||||
|
<Y>0</Y>
|
||||||
|
<Z>0</Z>
|
||||||
|
<R00>1</R00>
|
||||||
|
<R01>0</R01>
|
||||||
|
<R02>0</R02>
|
||||||
|
<R10>0</R10>
|
||||||
|
<R11>1</R11>
|
||||||
|
<R12>0</R12>
|
||||||
|
<R20>0</R20>
|
||||||
|
<R21>0</R21>
|
||||||
|
<R22>1</R22>
|
||||||
|
</CoordinateFrame>
|
||||||
|
<float name="Reflectance">0</float>
|
||||||
|
<float name="RightParamA">-0.5</float>
|
||||||
|
<float name="RightParamB">0.5</float>
|
||||||
|
<token name="RightSurface">0</token>
|
||||||
|
<token name="RightSurfaceInput">0</token>
|
||||||
|
<int name="RootPriority">0</int>
|
||||||
|
<Vector3 name="RotVelocity">
|
||||||
|
<X>0</X>
|
||||||
|
<Y>0</Y>
|
||||||
|
<Z>0</Z>
|
||||||
|
</Vector3>
|
||||||
|
<token name="shape">1</token>
|
||||||
|
<Vector3 name="size">
|
||||||
|
<X>4</X>
|
||||||
|
<Y>1</Y>
|
||||||
|
<Z>2</Z>
|
||||||
|
</Vector3>
|
||||||
|
<int64 name="SourceAssetId">-1</int64>
|
||||||
|
<BinaryString name="Tags"></BinaryString>
|
||||||
|
<float name="TopParamA">-0.5</float>
|
||||||
|
<float name="TopParamB">0.5</float>
|
||||||
|
<token name="TopSurface">0</token>
|
||||||
|
<token name="TopSurfaceInput">0</token>
|
||||||
|
<float name="Transparency">0</float>
|
||||||
|
<Vector3 name="Velocity">
|
||||||
|
<X>0</X>
|
||||||
|
<Y>0</Y>
|
||||||
|
<Z>0</Z>
|
||||||
|
</Vector3>
|
||||||
|
</Properties>
|
||||||
|
</Item>
|
||||||
|
</Item>
|
||||||
|
</roblox>
|
||||||
36
rojo-test/build-tests/attributes/default.project.json
Normal file
36
rojo-test/build-tests/attributes/default.project.json
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"name": "attributes",
|
||||||
|
"tree": {
|
||||||
|
"$className": "Folder",
|
||||||
|
|
||||||
|
"Explicit": {
|
||||||
|
"$className": "Folder",
|
||||||
|
"$properties": {
|
||||||
|
"Attributes": {
|
||||||
|
"Attributes": {
|
||||||
|
"Hello": {
|
||||||
|
"String": "World"
|
||||||
|
},
|
||||||
|
"Vector": {
|
||||||
|
"Vector3": [1, 2, 3]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"ImplicitAttributes": {
|
||||||
|
"$className": "Folder",
|
||||||
|
"$properties": {
|
||||||
|
"Attributes": {
|
||||||
|
"Hey": {
|
||||||
|
"String": "Grandma"
|
||||||
|
},
|
||||||
|
"Vector": {
|
||||||
|
"Vector3": [4, 5, 6]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
rojo-test/build-tests/issue_546/README.md
Normal file
2
rojo-test/build-tests/issue_546/README.md
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Issue #546 (https://github.com/rojo-rbx/rojo/issues/546)
|
||||||
|
Regression from Rojo 6.2.0 to Rojo 7.0.0. Meta files named as init.meta.json should apply after init.client.lua and other init files.
|
||||||
6
rojo-test/build-tests/issue_546/default.project.json
Normal file
6
rojo-test/build-tests/issue_546/default.project.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "issue_546",
|
||||||
|
"tree": {
|
||||||
|
"$path": "hello"
|
||||||
|
}
|
||||||
|
}
|
||||||
1
rojo-test/build-tests/issue_546/hello/init.client.lua
Normal file
1
rojo-test/build-tests/issue_546/hello/init.client.lua
Normal file
@@ -0,0 +1 @@
|
|||||||
|
print("Hello, world!")
|
||||||
5
rojo-test/build-tests/issue_546/hello/init.meta.json
Normal file
5
rojo-test/build-tests/issue_546/hello/init.meta.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"properties": {
|
||||||
|
"Disabled": true
|
||||||
|
}
|
||||||
|
}
|
||||||
15
rojo-test/build-tests/optional/default.project.json
Normal file
15
rojo-test/build-tests/optional/default.project.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"name": "optional",
|
||||||
|
"tree": {
|
||||||
|
"$className": "Folder",
|
||||||
|
"foo-required": {
|
||||||
|
"$path": "foo.txt"
|
||||||
|
},
|
||||||
|
"foo-optional":{
|
||||||
|
"$path": { "optional": "foo.txt" }
|
||||||
|
},
|
||||||
|
"bar-optional":{
|
||||||
|
"$path": { "optional": "bar.txt" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
rojo-test/build-tests/optional/foo.txt
Normal file
1
rojo-test/build-tests/optional/foo.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Hello, from foo.txt!
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"name": "root",
|
||||||
|
"tree": {
|
||||||
|
"$className": "Folder",
|
||||||
|
"folder": {
|
||||||
|
"$path": "folder"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "child-projectname",
|
||||||
|
"tree": {
|
||||||
|
"$className": "Folder"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"name": "root",
|
||||||
|
"tree": {
|
||||||
|
"$className": "Folder",
|
||||||
|
"folder": {
|
||||||
|
"$path": "folder"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "child-projectname",
|
||||||
|
"tree": {
|
||||||
|
"$className": "Folder"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "root",
|
||||||
|
"tree": {
|
||||||
|
"$className": "Folder"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,7 +14,13 @@
|
|||||||
"Color": {
|
"Color": {
|
||||||
"$className": "Part",
|
"$className": "Part",
|
||||||
"$properties": {
|
"$properties": {
|
||||||
"Color": [0.5, 0.25, 0]
|
"Color": [0.5, 0.25, 0],
|
||||||
|
"CFrame": [
|
||||||
|
1, 2, 3,
|
||||||
|
0, 1, 0,
|
||||||
|
0, 0, 1,
|
||||||
|
1, 0, 0
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "weldconstraint",
|
||||||
|
"tree": {
|
||||||
|
"$path": "two-parts-welded.rbxmx"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
source: tests/tests/serve.rs
|
||||||
|
expression: redactions.redacted_yaml(info)
|
||||||
|
|
||||||
|
---
|
||||||
|
expectedPlaceIds: ~
|
||||||
|
gameId: ~
|
||||||
|
placeId: ~
|
||||||
|
projectName: optional
|
||||||
|
protocolVersion: 4
|
||||||
|
rootInstanceId: id-2
|
||||||
|
serverVersion: "[server-version]"
|
||||||
|
sessionId: id-1
|
||||||
|
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
---
|
||||||
|
source: tests/tests/serve.rs
|
||||||
|
expression: "read_response.intern_and_redact(&mut redactions, root_id)"
|
||||||
|
|
||||||
|
---
|
||||||
|
instances:
|
||||||
|
id-2:
|
||||||
|
Children:
|
||||||
|
- id-3
|
||||||
|
ClassName: Folder
|
||||||
|
Id: id-2
|
||||||
|
Metadata:
|
||||||
|
ignoreUnknownInstances: true
|
||||||
|
Name: optional
|
||||||
|
Parent: "00000000000000000000000000000000"
|
||||||
|
Properties: {}
|
||||||
|
id-3:
|
||||||
|
Children:
|
||||||
|
- id-4
|
||||||
|
- id-5
|
||||||
|
ClassName: Folder
|
||||||
|
Id: id-3
|
||||||
|
Metadata:
|
||||||
|
ignoreUnknownInstances: false
|
||||||
|
Name: src
|
||||||
|
Parent: id-2
|
||||||
|
Properties: {}
|
||||||
|
id-4:
|
||||||
|
Children: []
|
||||||
|
ClassName: StringValue
|
||||||
|
Id: id-4
|
||||||
|
Metadata:
|
||||||
|
ignoreUnknownInstances: false
|
||||||
|
Name: foo
|
||||||
|
Parent: id-3
|
||||||
|
Properties:
|
||||||
|
Value:
|
||||||
|
String: "Hello, from foo.txt!"
|
||||||
|
id-5:
|
||||||
|
Children:
|
||||||
|
- id-6
|
||||||
|
ClassName: Folder
|
||||||
|
Id: id-5
|
||||||
|
Metadata:
|
||||||
|
ignoreUnknownInstances: false
|
||||||
|
Name: node_modules
|
||||||
|
Parent: id-3
|
||||||
|
Properties: {}
|
||||||
|
id-6:
|
||||||
|
Children: []
|
||||||
|
ClassName: StringValue
|
||||||
|
Id: id-6
|
||||||
|
Metadata:
|
||||||
|
ignoreUnknownInstances: false
|
||||||
|
Name: bar
|
||||||
|
Parent: id-5
|
||||||
|
Properties:
|
||||||
|
Value:
|
||||||
|
String: Hello from bar.txt
|
||||||
|
messageCursor: 2
|
||||||
|
sessionId: id-1
|
||||||
|
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
---
|
||||||
|
source: tests/tests/serve.rs
|
||||||
|
expression: "read_response.intern_and_redact(&mut redactions, root_id)"
|
||||||
|
|
||||||
|
---
|
||||||
|
instances:
|
||||||
|
id-2:
|
||||||
|
Children:
|
||||||
|
- id-3
|
||||||
|
ClassName: Folder
|
||||||
|
Id: id-2
|
||||||
|
Metadata:
|
||||||
|
ignoreUnknownInstances: true
|
||||||
|
Name: optional
|
||||||
|
Parent: "00000000000000000000000000000000"
|
||||||
|
Properties: {}
|
||||||
|
id-3:
|
||||||
|
Children:
|
||||||
|
- id-4
|
||||||
|
ClassName: Folder
|
||||||
|
Id: id-3
|
||||||
|
Metadata:
|
||||||
|
ignoreUnknownInstances: false
|
||||||
|
Name: src
|
||||||
|
Parent: id-2
|
||||||
|
Properties: {}
|
||||||
|
id-4:
|
||||||
|
Children: []
|
||||||
|
ClassName: StringValue
|
||||||
|
Id: id-4
|
||||||
|
Metadata:
|
||||||
|
ignoreUnknownInstances: false
|
||||||
|
Name: foo
|
||||||
|
Parent: id-3
|
||||||
|
Properties:
|
||||||
|
Value:
|
||||||
|
String: "Hello, from foo.txt!"
|
||||||
|
messageCursor: 0
|
||||||
|
sessionId: id-1
|
||||||
|
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
---
|
||||||
|
source: tests/tests/serve.rs
|
||||||
|
expression: "subscribe_response.intern_and_redact(&mut redactions, ())"
|
||||||
|
|
||||||
|
---
|
||||||
|
messageCursor: 2
|
||||||
|
messages:
|
||||||
|
- added:
|
||||||
|
id-5:
|
||||||
|
Children:
|
||||||
|
- id-6
|
||||||
|
ClassName: Folder
|
||||||
|
Id: id-5
|
||||||
|
Metadata:
|
||||||
|
ignoreUnknownInstances: false
|
||||||
|
Name: node_modules
|
||||||
|
Parent: id-3
|
||||||
|
Properties: {}
|
||||||
|
id-6:
|
||||||
|
Children: []
|
||||||
|
ClassName: StringValue
|
||||||
|
Id: id-6
|
||||||
|
Metadata:
|
||||||
|
ignoreUnknownInstances: false
|
||||||
|
Name: bar
|
||||||
|
Parent: id-5
|
||||||
|
Properties:
|
||||||
|
Value:
|
||||||
|
String: Hello from bar.txt
|
||||||
|
removed: []
|
||||||
|
updated: []
|
||||||
|
- added: {}
|
||||||
|
removed: []
|
||||||
|
updated: []
|
||||||
|
sessionId: id-1
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"name": "optional",
|
||||||
|
"tree": {
|
||||||
|
"$className": "Folder",
|
||||||
|
"create-later": {
|
||||||
|
"$path": { "optional": "create-later" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
std = "roblox"
|
std = "roblox+testez"
|
||||||
|
|
||||||
[config]
|
[config]
|
||||||
unused_variable = { allow_unused_self = true }
|
unused_variable = { allow_unused_self = true }
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user