Compare commits

..

3 Commits

Author SHA1 Message Date
Lucien Greathouse
0599b50235 Release 6.1.0 2021-04-12 17:19:35 -04:00
Lucien Greathouse
21f7ef6186 Update dependencies 2021-04-09 18:44:03 -04:00
MSAA
de6470bb45 change server bind address (#403)
* web/mod.rs - change server bind address

127.0.0.1 is a loopback interface, and only works on the same host
0.0.0.0 will allow connections from other hosts

ideally, this should be a console arg - but it's a quick fix

* implement --address option, revert default bind address to 127.0.0.1

* revert silly autoformatting

* ok, actually using rustfmt now

* More precise --address flag description

* Use SocketAddr where available, take advantage of const-ness

* Display 'localhost' if address is loopback

* Update Changelog

Co-authored-by: Lucien Greathouse <me@lpghatguy.com>
2021-03-31 16:44:10 -04:00
211 changed files with 23794 additions and 55163 deletions

1
.github/FUNDING.yml vendored
View File

@@ -1 +0,0 @@
patreon: lpghatguy

View File

@@ -11,49 +11,29 @@ on:
jobs:
build:
name: Build and Test
runs-on: ubuntu-latest
strategy:
matrix:
rust_version: [stable, 1.55.0]
rust_version: [stable, "1.43.1"]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v1
with:
submodules: true
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.rust_version }}
override: true
profile: minimal
- name: Setup Rust toolchain
run: rustup default ${{ matrix.rust_version }}
- name: Build
run: cargo build --locked --verbose
- name: Test
- name: Run tests
run: cargo test --locked --verbose
lint:
name: Rustfmt and Clippy
runs-on: ubuntu-latest
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
- name: Rustfmt and Clippy
run: |
cargo fmt -- --check
cargo clippy
if: matrix.rust_version == 'stable'

View File

@@ -2,152 +2,65 @@ name: Release
on:
push:
tags: ["v*"]
tags: ["*"]
jobs:
create-release:
name: Create Release
windows:
runs-on: windows-latest
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
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
steps:
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: ${{ github.ref }}
draft: true
prerelease: false
- uses: actions/checkout@v1
with:
submodules: true
build-plugin:
needs: ["create-release"]
name: Build Roblox Studio Plugin
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
submodules: true
- name: Build
run: cargo build --locked --verbose --release
env:
OPENSSL_STATIC: 1
- name: Setup Foreman
uses: Roblox/setup-foreman@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
- 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-latest
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
- name: Upload artifacts
uses: actions/upload-artifact@v1
with:
name: rojo-linux
path: target/release/rojo

View File

@@ -1,157 +1,13 @@
# Rojo Changelog
## Unreleased Changes
* Switched from structopt to clap for command line argument parsing.
## [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:
```json
{
"className": "Part",
"properties": {
"Position": {
"Type": "Vector3",
"Value": [1, 2, 3]
}
}
}
```
For Rojo 7, this will need to be changed to:
```json
{
"className": "Part",
"properties": {
"Position": {
"Vector3": [1, 2, 3]
}
}
}
```
The shorthand property format that most users use is not impacted. For reference, it looks like this:
```json
{
"className": "Part",
"properties": {
"Position": [1, 2, 3]
}
}
```
* Major breaking change: changed property syntax for project files; shorthand syntax is unchanged.
* Added the `fmt-project` subcommand for formatting Rojo project files.
* Improved error output for many subcommands.
* Updated to stable versions of rbx-dom libraries.
* Updated async infrastructure, which should fix a handful of bugs. ([#459])
* Fixed syncing refs in the Roblox Studio plugin ([#462], [#466])
* Added support for long paths on Windows. ([#464])
[#459]: https://github.com/rojo-rbx/rojo/pull/459
[#462]: https://github.com/rojo-rbx/rojo/pull/462
[#464]: https://github.com/rojo-rbx/rojo/pull/464
[#466]: https://github.com/rojo-rbx/rojo/pull/466
[7.0.0-rc.1]: https://github.com/rojo-rbx/rojo/releases/tag/v7.0.0-rc.1
## [7.0.0-alpha.4][7.0.0-alpha.4] (May 5, 2021)
* 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.
* 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.
* Fixed `Name` and `Parent` properties being allowed in Rojo projects. ([#413][pr-413])
* Fixed "Open Scripts Externally" feature crashing Studio. ([#369][issue-369])
* Empty `.model.json` files will no longer cause errors. ([#420][pr-420])
* When specifying `$path` on a service, Rojo now keeps the correct class name. ([#331][issue-331])
* Improved error messages for misconfigured projects.
[issue-331]: https://github.com/rojo-rbx/rojo/issues/331
[issue-369]: https://github.com/rojo-rbx/rojo/issues/369
[pr-420]: https://github.com/rojo-rbx/rojo/pull/420
[pr-413]: https://github.com/rojo-rbx/rojo/pull/413
[7.0.0-alpha.4]: https://github.com/rojo-rbx/rojo/releases/tag/v7.0.0-alpha.4
## [7.0.0-alpha.3][7.0.0-alpha.3] (February 19, 2021)
* Updated dependencies, fixing `OptionalCoordinateFrame`-related issues.
## [6.1.0][6.1.0] (April 12, 2021)
* Updated dependencies, fixing OptionalCoordinateFrame-related issues.
* Added `--address` flag to `rojo serve` to allow for external connections. ([#403][pr-403])
[pr-403]: https://github.com/rojo-rbx/rojo/pull/403
[7.0.0-alpha.3]: https://github.com/rojo-rbx/rojo/releases/tag/v7.0.0-alpha.3
## [7.0.0-alpha.2][7.0.0-alpha.2] (February 19, 2021)
* Fixed incorrect protocol version between the client and server.
[7.0.0-alpha.2]: https://github.com/rojo-rbx/rojo/releases/tag/v7.0.0-alpha.2
## [7.0.0-alpha.1][7.0.0-alpha.1] (February 18, 2021)
This release includes a brand new implementation of the Roblox DOM. It brings performance improvements, much better support for `rbxl` and `rbxm` files, and a better internal API.
* Added support for all remaining property types.
* Added support for the entire Roblox binary model format.
* Changed `rojo upload` to upload binary places and models instead of XML.
* This should make using `rojo upload` much more feasible for large places.
* **Breaking**: Changed format of some types of values in `project.json`, `model.json`, and `meta.json` files.
* This should impact few projects. See [this file][allValues.json] for new examples of each property type.
Formatting of types will change more before the stable release of Rojo 7. We're hoping to use this opportunity to normalize some of the case inconsistency introduced in Rojo 0.5.
[7.0.0-alpha.1]: https://github.com/rojo-rbx/rojo/releases/tag/v7.0.0-alpha.1
[allValues.json]: https://github.com/rojo-rbx/rojo/blob/f4a790eb50b74e482000bad1dcfe22533992fb20/plugin/rbx_dom_lua/src/allValues.json
[6.1.0]: https://github.com/rojo-rbx/rojo/releases/tag/v6.1.0
## [6.0.2](https://github.com/rojo-rbx/rojo/releases/tag/v6.0.2) (February 9, 2021)
* Fixed `rojo upload` to handle CSRF challenges.

View File

@@ -29,29 +29,25 @@ 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.
## 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
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)
2. Bump plugin version in [`plugin/src/Config.lua`](plugin/src/Config.lua)
3. Run `cargo test` to update `Cargo.lock` and run tests
3. Run `cargo test` to update `Cargo.lock` and double-check tests
4. Update [`CHANGELOG.md`](CHANGELOG.md)
5. Commit!
* `git add . && git commit -m "Release vX.Y.Z"`
6. Tag the commit
* `git tag vX.Y.Z`
6. Tag the commit with the version from `Cargo.toml` prepended with a v, like `v0.4.13`
7. Publish the CLI
* `cargo publish`
8. Publish the Plugin
* `cargo run -- upload plugin --asset_id 6415005344`
8. Build and upload the plugin
* `rojo build plugin -o Rojo.rbxm`
* Upload `Rojo.rbxm` to Roblox.com, keep it for later
9. Push commits and tags
* `git push && git push --tags`
10. Copy GitHub release content from previous release
* Update the leading text with a summary about the release
* 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

2103
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package]
name = "rojo-smallstring"
version = "7.1.1"
name = "rojo"
version = "6.1.0"
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
description = "Enables professional-grade development tools for Roblox developers"
license = "MPL-2.0"
@@ -9,7 +9,6 @@ documentation = "https://rojo.space/docs"
repository = "https://github.com/rojo-rbx/rojo"
readme = "README.md"
edition = "2018"
build = "build.rs"
exclude = [
"/test-projects/**",
@@ -28,80 +27,79 @@ default = []
dev_live_assets = []
[workspace]
members = ["crates/*"]
members = [
"rojo-insta-ext",
"memofs",
]
[lib]
name = "librojo"
path = "src/lib.rs"
[[bin]]
name = "rojo"
path = "src/bin.rs"
[[bench]]
name = "build"
harness = false
[dependencies]
memofs = { version = "0.2.0", path = "crates/memofs" }
memofs = { version = "0.1.2", path = "memofs" }
# These dependencies can be uncommented when working on rbx-dom simultaneously
# rbx_binary = { path = "../rbx-dom/rbx_binary" }
# rbx_dom_weak = { path = "../rbx-dom/rbx_dom_weak" }
# rbx_reflection = { path = "../rbx-dom/rbx_reflection" }
# rbx_reflection_database = { path = "../rbx-dom/rbx_reflection_database" }
# rbx_xml = { path = "../rbx-dom/rbx_xml" }
rbx_binary = "0.6.4"
rbx_dom_weak = "2.3.0"
rbx_reflection = "4.2.0"
rbx_reflection_database = "0.2.2"
rbx_xml = "0.12.3"
anyhow = "1.0.44"
backtrace = "0.3.61"
bincode = "1.3.3"
crossbeam-channel = "0.5.1"
csv = "1.1.6"
env_logger = "0.9.0"
fs-err = "2.6.0"
futures = "0.3.17"
globset = "0.4.8"
humantime = "2.1.0"
hyper = { version = "0.14.13", features = ["server", "tcp", "http1"] }
jod-thread = "0.1.2"
log = "0.4.14"
maplit = "1.0.2"
notify = "4.0.17"
opener = "0.5.0"
reqwest = { version = "0.11.10", features = ["blocking", "json"] }
anyhow = "1.0.27"
backtrace = "0.3"
bincode = "1.2.1"
crossbeam-channel = "0.4.0"
csv = "1.1.1"
env_logger = "0.7.1"
fs-err = "2.2.0"
futures = "0.1.29"
globset = "0.4.4"
humantime = "1.3.0"
hyper = "0.12.35"
jod-thread = "0.1.0"
lazy_static = "1.4.0"
log = "0.4.8"
maplit = "1.0.1"
notify = "4.0.14"
opener = "0.4.1"
rbx_binary = "0.5.0"
rbx_dom_weak = "1.10.1"
rbx_reflection = "3.3.408"
rbx_xml = "0.11.3"
regex = "1.3.1"
reqwest = "0.9.20"
ritz = "0.1.0"
roblox_install = "1.0.0"
serde = { version = "1.0.130", features = ["derive", "rc"] }
serde_json = "1.0.68"
termcolor = "1.1.2"
thiserror = "1.0.30"
tokio = { version = "1.12.0", features = ["rt", "rt-multi-thread"] }
uuid = { version = "1.0.0", features = ["v4", "serde"] }
clap = { version = "3.1.18", features = ["derive"] }
smol_str = "0.1.23"
profiling = { version = "1.0.6", features = ["profile-with-superluminal"] }
rlua = "0.17.0"
roblox_install = "0.2.2"
serde = { version = "1.0", features = ["derive", "rc"] }
serde_json = "1.0"
structopt = "0.3.5"
termcolor = "1.0.5"
thiserror = "1.0.11"
tokio = "0.1.22"
uuid = { version = "0.8.1", features = ["v4", "serde"] }
[target.'cfg(windows)'.dependencies]
winreg = "0.10.1"
winreg = "0.6.2"
[build-dependencies]
memofs = { version = "0.2.0", path = "crates/memofs" }
memofs = { version = "0.1.3", path = "memofs" }
embed-resource = "1.6.4"
anyhow = "1.0.44"
bincode = "1.3.3"
fs-err = "2.6.0"
maplit = "1.0.2"
anyhow = "1.0.27"
bincode = "1.2.1"
fs-err = "2.3.0"
maplit = "1.0.1"
[dev-dependencies]
rojo-insta-ext = { path = "crates/rojo-insta-ext" }
rojo-insta-ext = { path = "rojo-insta-ext" }
criterion = "0.3.5"
insta = { version = "1.8.0", features = ["redactions"] }
paste = "1.0.5"
pretty_assertions = "1.2.1"
serde_yaml = "0.8.21"
tempfile = "3.2.0"
walkdir = "2.3.2"
criterion = "0.3"
insta = { version = "1.3.0", features = ["redactions"] }
lazy_static = "1.2"
paste = "0.1"
pretty_assertions = "0.6.1"
serde_yaml = "0.8.9"
tempfile = "3.0"
walkdir = "2.1"

View File

@@ -1,13 +1,21 @@
<div align="center">
<a href="https://rojo.space"><img src="assets/logo-512.png" alt="Rojo" height="217" /></a>
<a href="https://rojo.space">
<img src="assets/logo-512.png" alt="Rojo" height="217" />
</a>
</div>
<div>&nbsp;</div>
<div align="center">
<a href="https://github.com/rojo-rbx/rojo/actions"><img src="https://github.com/rojo-rbx/rojo/workflows/CI/badge.svg" alt="Actions status" /></a>
<a href="https://crates.io/crates/rojo"><img src="https://img.shields.io/crates/v/rojo.svg?label=latest%20release" alt="Latest server version" /></a>
<a href="https://rojo.space/docs"><img src="https://img.shields.io/badge/docs-website-brightgreen.svg" alt="Rojo Documentation" /></a>
<a href="https://github.com/rojo-rbx/rojo/actions">
<img src="https://github.com/rojo-rbx/rojo/workflows/CI/badge.svg" alt="Actions status" />
</a>
<a href="https://crates.io/crates/rojo">
<img src="https://img.shields.io/crates/v/rojo.svg?label=latest%20release" alt="Latest server version" />
</a>
<a href="https://rojo.space/docs">
<img src="https://img.shields.io/badge/docs-website-brightgreen.svg" alt="Rojo Documentation" />
</a>
</div>
<hr />
@@ -40,7 +48,7 @@ Check out our [contribution guide](CONTRIBUTING.md) for detailed instructions fo
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.43.1 and newer. The minimum supported version of Rust is based on the latest versions of the dependencies that Rojo has.
## License
Rojo is available under the terms of the Mozilla Public License, Version 2.0. See [LICENSE.txt](LICENSE.txt) for details.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

5
bin/dev-plugin.sh Executable file
View File

@@ -0,0 +1,5 @@
#!/bin/sh
set -e
watchexec -c -w plugin "sh -c './bin/install-dev-plugin.sh'"

13
bin/install-dev-plugin.sh Executable file
View File

@@ -0,0 +1,13 @@
#!/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"

5
bin/install-release-plugin.sh Executable file
View File

@@ -0,0 +1,5 @@
#!/bin/sh
set -e
rojo build plugin -o "$LOCALAPPDATA/Roblox/Plugins/Rojo.rbxm"

View File

@@ -0,0 +1,12 @@
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)

View File

@@ -0,0 +1,8 @@
local pluginPath, placePath = ...
local plugin = remodel.readModelFile(pluginPath)[1]
local place = remodel.readPlaceFile(placePath)
plugin.Parent = place:GetService("ReplicatedStorage")
remodel.writePlaceFile(place, placePath)

6
bin/run-all-tests.sh Executable file
View File

@@ -0,0 +1,6 @@
#!/bin/sh
set -e
./bin/run-cli-tests.sh
./bin/run-plugin-tests.sh

9
bin/run-cli-tests.sh Executable file
View File

@@ -0,0 +1,9 @@
#!/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

16
bin/run-plugin-tests.sh Executable file
View File

@@ -0,0 +1,16 @@
#!/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

View File

@@ -73,9 +73,5 @@ fn main() -> Result<(), anyhow::Error> {
bincode::serialize_into(out_file, &snapshot)?;
println!("cargo:rerun-if-changed=build/windows/rojo-manifest.rc");
println!("cargo:rerun-if-changed=build/windows/rojo.manifest");
embed_resource::compile("build/windows/rojo-manifest.rc");
Ok(())
}

View File

@@ -1,2 +0,0 @@
#define RT_MANIFEST 24
1 RT_MANIFEST "rojo.manifest"

View File

@@ -1,8 +0,0 @@
<?xml version='1.0' encoding='utf-8' standalone='yes'?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
<ws2:longPathAware>true</ws2:longPathAware>
</windowsSettings>
</application>
</assembly>

View File

@@ -1,4 +1,3 @@
[tools]
rojo = { source = "rojo-rbx/rojo", version = "7.1.1" }
rojo = { source = "rojo-rbx/rojo", version = "6.0.0-rc.3" }
run-in-roblox = { source = "rojo-rbx/run-in-roblox", version = "0.3.0" }
selene = { source = "Kampfkarren/selene", version = "0.17.0" }

View File

@@ -2,9 +2,6 @@
## Unreleased Changes
## 0.2.0 (2021-08-23)
* Updated to `crossbeam-channel` 0.5.1.
## 0.1.3 (2020-11-19)
* Added `set_watch_enabled` to `Vfs` and `VfsLock` to allow turning off file watching.

View File

@@ -1,7 +1,7 @@
[package]
name = "memofs"
description = "Virtual filesystem with configurable backends."
version = "0.2.0"
version = "0.1.3"
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
edition = "2018"
readme = "README.md"
@@ -11,7 +11,7 @@ homepage = "https://github.com/rojo-rbx/rojo/tree/master/memofs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
crossbeam-channel = "0.5.1"
crossbeam-channel = "0.4.0"
fs-err = "2.3.0"
notify = "4.0.15"
serde = { version = "1.0", features = ["derive"] }

View File

@@ -0,0 +1,44 @@
stds.roblox = {
read_globals = {
game = {
other_fields = true,
},
-- Roblox globals
"script",
-- Extra functions
"tick", "warn",
"wait", "typeof",
-- Types
"CFrame",
"Color3",
"Enum",
"Instance",
"NumberRange",
"Rect",
"UDim", "UDim2",
"Vector2", "Vector3",
"Vector2int16", "Vector3int16",
}
}
stds.testez = {
read_globals = {
"describe",
"it", "itFOCUS", "itSKIP",
"FOCUS", "SKIP", "HACK_NO_XPCALL",
"expect",
}
}
ignore = {
"212", -- unused arguments
}
std = "lua51+roblox"
files["**/*.spec.lua"] = {
std = "+testez",
}

View File

@@ -1,462 +0,0 @@
local base64 = require(script.Parent.base64)
local function identity(...)
return ...
end
local function unpackDecoder(f)
return function(value)
return f(unpack(value))
end
end
local function serializeFloat(value)
-- TODO: Figure out a better way to serialize infinity and NaN, neither of
-- which fit into JSON.
if value == math.huge or value == -math.huge then
return 999999999 * math.sign(value)
end
return value
end
local ALL_AXES = {"X", "Y", "Z"}
local ALL_FACES = {"Right", "Top", "Back", "Left", "Bottom", "Front"}
local types
types = {
Axes = {
fromPod = function(pod)
local axes = {}
for index, axisName in ipairs(pod) do
axes[index] = Enum.Axis[axisName]
end
return Axes.new(unpack(axes))
end,
toPod = function(roblox)
local json = {}
for _, axis in ipairs(ALL_AXES) do
if roblox[axis] then
table.insert(json, axis)
end
end
return json
end,
},
BinaryString = {
fromPod = base64.decode,
toPod = base64.encode,
},
Bool = {
fromPod = identity,
toPod = identity,
},
BrickColor = {
fromPod = function(pod)
return BrickColor.new(pod)
end,
toPod = function(roblox)
return roblox.Number
end,
},
CFrame = {
fromPod = function(pod)
local pos = pod.position
local orient = pod.orientation
return CFrame.new(
pos[1], pos[2], pos[3],
orient[1][1], orient[1][2], orient[1][3],
orient[2][1], orient[2][2], orient[2][3],
orient[3][1], orient[3][2], orient[3][3]
)
end,
toPod = function(roblox)
local x, y, z,
r00, r01, r02,
r10, r11, r12,
r20, r21, r22 = roblox:GetComponents()
return {
position = {x, y, z},
orientation = {
{r00, r01, r02},
{r10, r11, r12},
{r20, r21, r22},
},
}
end,
},
Color3 = {
fromPod = unpackDecoder(Color3.new),
toPod = function(roblox)
return {roblox.r, roblox.g, roblox.b}
end,
},
Color3uint8 = {
fromPod = unpackDecoder(Color3.fromRGB),
toPod = function(roblox)
return {
math.round(roblox.R * 255),
math.round(roblox.G * 255),
math.round(roblox.B * 255),
}
end,
},
ColorSequence = {
fromPod = function(pod)
local keypoints = {}
for index, keypoint in ipairs(pod.keypoints) do
keypoints[index] = ColorSequenceKeypoint.new(
keypoint.time,
types.Color3.fromPod(keypoint.color)
)
end
return ColorSequence.new(keypoints)
end,
toPod = function(roblox)
local keypoints = {}
for index, keypoint in ipairs(roblox.Keypoints) do
keypoints[index] = {
time = keypoint.Time,
color = types.Color3.toPod(keypoint.Value),
}
end
return {
keypoints = keypoints,
}
end,
},
Content = {
fromPod = identity,
toPod = identity,
},
Enum = {
fromPod = identity,
toPod = function(roblox)
-- FIXME: More robust handling of enums
if typeof(roblox) == "number" then
return roblox
else
return roblox.Value
end
end,
},
Faces = {
fromPod = function(pod)
local faces = {}
for index, faceName in ipairs(pod) do
faces[index] = Enum.NormalId[faceName]
end
return Faces.new(unpack(faces))
end,
toPod = function(roblox)
local pod = {}
for _, face in ipairs(ALL_FACES) do
if roblox[face] then
table.insert(pod, face)
end
end
return pod
end,
},
Float32 = {
fromPod = identity,
toPod = serializeFloat,
},
Float64 = {
fromPod = identity,
toPod = serializeFloat,
},
Int32 = {
fromPod = identity,
toPod = identity,
},
Int64 = {
fromPod = identity,
toPod = identity,
},
NumberRange = {
fromPod = unpackDecoder(NumberRange.new),
toPod = function(roblox)
return {roblox.Min, roblox.Max}
end,
},
NumberSequence = {
fromPod = function(pod)
local keypoints = {}
for index, keypoint in ipairs(pod.keypoints) do
keypoints[index] = NumberSequenceKeypoint.new(
keypoint.time,
keypoint.value,
keypoint.envelope
)
end
return NumberSequence.new(keypoints)
end,
toPod = function(roblox)
local keypoints = {}
for index, keypoint in ipairs(roblox.Keypoints) do
keypoints[index] = {
time = keypoint.Time,
value = keypoint.Value,
envelope = keypoint.Envelope,
}
end
return {
keypoints = keypoints,
}
end,
},
PhysicalProperties = {
fromPod = function(pod)
if pod == "Default" then
return nil
else
return PhysicalProperties.new(
pod.density,
pod.friction,
pod.elasticity,
pod.frictionWeight,
pod.elasticityWeight
)
end
end,
toPod = function(roblox)
if roblox == nil then
return "Default"
else
return {
density = roblox.Density,
friction = roblox.Friction,
elasticity = roblox.Elasticity,
frictionWeight = roblox.FrictionWeight,
elasticityWeight = roblox.ElasticityWeight,
}
end
end,
},
Ray = {
fromPod = function(pod)
return Ray.new(
types.Vector3.fromPod(pod.origin),
types.Vector3.fromPod(pod.direction)
)
end,
toPod = function(roblox)
return {
origin = types.Vector3.toPod(roblox.Origin),
direction = types.Vector3.toPod(roblox.Direction),
}
end,
},
Rect = {
fromPod = function(pod)
return Rect.new(
types.Vector2.fromPod(pod[1]),
types.Vector2.fromPod(pod[2])
)
end,
toPod = function(roblox)
return {
types.Vector2.toPod(roblox.Min),
types.Vector2.toPod(roblox.Max),
}
end,
},
Ref = {
fromPod = function(_pod)
error("Ref cannot be decoded on its own")
end,
toPod = function(_roblox)
error("Ref can not be encoded on its own")
end,
},
Region3 = {
fromPod = function(pod)
error("Region3 is not implemented")
end,
toPod = function(roblox)
error("Region3 is not implemented")
end,
},
Region3int16 = {
fromPod = function(pod)
return Region3int16.new(
types.Vector3int16.fromPod(pod[1]),
types.Vector3int16.fromPod(pod[2])
)
end,
toPod = function(roblox)
return {
types.Vector3int16.toPod(roblox.Min),
types.Vector3int16.toPod(roblox.Max),
}
end,
},
SharedString = {
fromPod = function(pod)
error("SharedString is not supported")
end,
toPod = function(roblox)
error("SharedString is not supported")
end,
},
String = {
fromPod = identity,
toPod = identity,
},
UDim = {
fromPod = unpackDecoder(UDim.new),
toPod = function(roblox)
return {roblox.Scale, roblox.Offset}
end,
},
UDim2 = {
fromPod = function(pod)
return UDim2.new(
types.UDim.fromPod(pod[1]),
types.UDim.fromPod(pod[2])
)
end,
toPod = function(roblox)
return {
types.UDim.toPod(roblox.X),
types.UDim.toPod(roblox.Y),
}
end,
},
Tags = {
fromPod = identity,
toPod = identity,
},
Vector2 = {
fromPod = unpackDecoder(Vector2.new),
toPod = function(roblox)
return {
serializeFloat(roblox.X),
serializeFloat(roblox.Y),
}
end,
},
Vector2int16 = {
fromPod = unpackDecoder(Vector2int16.new),
toPod = function(roblox)
return {roblox.X, roblox.Y}
end,
},
Vector3 = {
fromPod = unpackDecoder(Vector3.new),
toPod = function(roblox)
return {
serializeFloat(roblox.X),
serializeFloat(roblox.Y),
serializeFloat(roblox.Z),
}
end,
},
Vector3int16 = {
fromPod = unpackDecoder(Vector3int16.new),
toPod = function(roblox)
return {roblox.X, roblox.Y, roblox.Z}
end,
},
}
local EncodedValue = {}
function EncodedValue.decode(encodedValue)
local ty, value = next(encodedValue)
local typeImpl = types[ty]
if typeImpl == nil then
return false, "Couldn't decode value " .. tostring(ty)
end
return true, typeImpl.fromPod(value)
end
function EncodedValue.encode(rbxValue, propertyType)
assert(propertyType ~= nil, "Property type descriptor is required")
local typeImpl = types[propertyType]
if typeImpl == nil then
return false, ("Missing encoder for property type %q"):format(propertyType)
end
return true, {
[propertyType] = typeImpl.toPod(rbxValue),
}
end
return EncodedValue

View File

@@ -1,72 +0,0 @@
return function()
local HttpService = game:GetService("HttpService")
local EncodedValue = require(script.Parent.EncodedValue)
local allValues = require(script.Parent.allValues)
local function deepEq(a, b)
if typeof(a) ~= typeof(b) then
return false
end
local ty = typeof(a)
if ty == "table" then
local visited = {}
for key, valueA in pairs(a) do
visited[key] = true
if not deepEq(valueA, b[key]) then
return false
end
end
for key, valueB in pairs(b) do
if visited[key] then
continue
end
if not deepEq(valueB, a[key]) then
return false
end
end
return true
else
return a == b
end
end
local extraAssertions = {
CFrame = function(value)
expect(value).to.equal(CFrame.new(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12))
end,
}
for testName, testEntry in pairs(allValues) do
it("round trip " .. testName, function()
local ok, decoded = EncodedValue.decode(testEntry.value)
assert(ok, decoded)
if extraAssertions[testName] ~= nil then
extraAssertions[testName](decoded)
end
local ok, encoded = EncodedValue.encode(decoded, testEntry.ty)
assert(ok, encoded)
if not deepEq(encoded, testEntry.value) then
local expected = HttpService:JSONEncode(testEntry.value)
local actual = HttpService:JSONEncode(encoded)
local message = string.format(
"Round-trip results did not match.\nExpected:\n%s\nActual:\n%s",
expected, actual
)
error(message)
end
end)
end
end

View File

@@ -0,0 +1,2 @@
# rbx_dom_lua
Roblox Lua implementation of rbx-dom mechanisms, intended to work with rbx_dom_weak and friends.

View File

@@ -1,326 +0,0 @@
{
"Axes": {
"value": {
"Axes": [
"X",
"Y",
"Z"
]
},
"ty": "Axes"
},
"BinaryString": {
"value": {
"BinaryString": "SGVsbG8h"
},
"ty": "BinaryString"
},
"Bool": {
"value": {
"Bool": true
},
"ty": "Bool"
},
"BrickColor": {
"value": {
"BrickColor": 1004
},
"ty": "BrickColor"
},
"CFrame": {
"value": {
"CFrame": {
"position": [
1.0,
2.0,
3.0
],
"orientation": [
[
4.0,
5.0,
6.0
],
[
7.0,
8.0,
9.0
],
[
10.0,
11.0,
12.0
]
]
}
},
"ty": "CFrame"
},
"Color3": {
"value": {
"Color3": [
1.0,
2.0,
3.0
]
},
"ty": "Color3"
},
"Color3uint8": {
"value": {
"Color3uint8": [
0,
128,
255
]
},
"ty": "Color3uint8"
},
"ColorSequence": {
"value": {
"ColorSequence": {
"keypoints": [
{
"time": 0.0,
"color": [
1.0,
1.0,
0.5
]
},
{
"time": 1.0,
"color": [
0.0,
0.0,
0.0
]
}
]
}
},
"ty": "ColorSequence"
},
"Content": {
"value": {
"Content": "rbxassetid://12345"
},
"ty": "Content"
},
"Enum": {
"value": {
"Enum": 1234
},
"ty": "Enum"
},
"Faces": {
"value": {
"Faces": [
"Right",
"Top",
"Back",
"Left",
"Bottom",
"Front"
]
},
"ty": "Faces"
},
"Float32": {
"value": {
"Float32": 15.0
},
"ty": "Float32"
},
"Float64": {
"value": {
"Float64": 15123.0
},
"ty": "Float64"
},
"Int32": {
"value": {
"Int32": 6014
},
"ty": "Int32"
},
"Int64": {
"value": {
"Int64": 23491023
},
"ty": "Int64"
},
"NumberRange": {
"value": {
"NumberRange": [
-36.0,
94.0
]
},
"ty": "NumberRange"
},
"NumberSequence": {
"value": {
"NumberSequence": {
"keypoints": [
{
"time": 0.0,
"value": 5.0,
"envelope": 2.0
},
{
"time": 1.0,
"value": 22.0,
"envelope": 0.0
}
]
}
},
"ty": "NumberSequence"
},
"PhysicalProperties-Custom": {
"value": {
"PhysicalProperties": {
"density": 0.5,
"friction": 1.0,
"elasticity": 0.0,
"frictionWeight": 50.0,
"elasticityWeight": 25.0
}
},
"ty": "PhysicalProperties"
},
"PhysicalProperties-Default": {
"value": {
"PhysicalProperties": "Default"
},
"ty": "PhysicalProperties"
},
"Ray": {
"value": {
"Ray": {
"origin": [
1.0,
2.0,
3.0
],
"direction": [
4.0,
5.0,
6.0
]
}
},
"ty": "Ray"
},
"Rect": {
"value": {
"Rect": [
[
0.0,
5.0
],
[
10.0,
15.0
]
]
},
"ty": "Rect"
},
"Region3int16": {
"value": {
"Region3int16": [
[
-10,
-5,
0
],
[
5,
10,
15
]
]
},
"ty": "Region3int16"
},
"String": {
"value": {
"String": "Hello, world!"
},
"ty": "String"
},
"Tags": {
"value": {
"Tags": [
"foo",
"con'fusion?!",
"bar"
]
},
"ty": "Tags"
},
"UDim": {
"value": {
"UDim": [
1.0,
32
]
},
"ty": "UDim"
},
"UDim2": {
"value": {
"UDim2": [
[
-1.0,
100
],
[
1.0,
-100
]
]
},
"ty": "UDim2"
},
"Vector2": {
"value": {
"Vector2": [
-50.0,
50.0
]
},
"ty": "Vector2"
},
"Vector2int16": {
"value": {
"Vector2int16": [
-300,
300
]
},
"ty": "Vector2int16"
},
"Vector3": {
"value": {
"Vector3": [
-300.0,
0.0,
1500.0
]
},
"ty": "Vector3"
},
"Vector3int16": {
"value": {
"Vector3int16": [
60,
37,
-450
]
},
"ty": "Vector3int16"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
{
"name": "empty_folder",
"name": "rbx_dom_lua",
"tree": {
"$path": "src"
}

View File

@@ -0,0 +1,242 @@
local base64 = require(script.Parent.base64)
local function identity(...)
return ...
end
local function unpackDecoder(f)
return function(value)
return f(unpack(value))
end
end
local function serializeFloat(value)
-- TODO: Figure out a better way to serialize infinity and NaN, neither of
-- which fit into JSON.
if value == math.huge or value == -math.huge then
return 999999999 * math.sign(value)
end
return value
end
local encoders
encoders = {
Bool = identity,
Content = identity,
Float32 = serializeFloat,
Float64 = serializeFloat,
Int32 = identity,
Int64 = identity,
String = identity,
BinaryString = base64.encode,
SharedString = base64.encode,
BrickColor = function(value)
return value.Number
end,
CFrame = function(value)
return {value:GetComponents()}
end,
Color3 = function(value)
return {value.r, value.g, value.b}
end,
NumberRange = function(value)
return {value.Min, value.Max}
end,
NumberSequence = function(value)
local keypoints = {}
for index, keypoint in ipairs(value.Keypoints) do
keypoints[index] = {
Time = keypoint.Time,
Value = keypoint.Value,
Envelope = keypoint.Envelope,
}
end
return {
Keypoints = keypoints,
}
end,
ColorSequence = function(value)
local keypoints = {}
for index, keypoint in ipairs(value.Keypoints) do
keypoints[index] = {
Time = keypoint.Time,
Color = encoders.Color3(keypoint.Value),
}
end
return {
Keypoints = keypoints,
}
end,
Rect = function(value)
return {
Min = {value.Min.X, value.Min.Y},
Max = {value.Max.X, value.Max.Y},
}
end,
UDim = function(value)
return {value.Scale, value.Offset}
end,
UDim2 = function(value)
return {value.X.Scale, value.X.Offset, value.Y.Scale, value.Y.Offset}
end,
Vector2 = function(value)
return {
serializeFloat(value.X),
serializeFloat(value.Y),
}
end,
Vector2int16 = function(value)
return {value.X, value.Y}
end,
Vector3 = function(value)
return {
serializeFloat(value.X),
serializeFloat(value.Y),
serializeFloat(value.Z),
}
end,
Vector3int16 = function(value)
return {value.X, value.Y, value.Z}
end,
PhysicalProperties = function(value)
if value == nil then
return nil
else
return {
Density = value.Density,
Friction = value.Friction,
Elasticity = value.Elasticity,
FrictionWeight = value.FrictionWeight,
ElasticityWeight = value.ElasticityWeight,
}
end
end,
Ref = function(value)
return nil
end,
}
local decoders = {
Bool = identity,
Content = identity,
Enum = identity,
Float32 = identity,
Float64 = identity,
Int32 = identity,
Int64 = identity,
String = identity,
BinaryString = base64.decode,
SharedString = base64.decode,
BrickColor = BrickColor.new,
CFrame = unpackDecoder(CFrame.new),
Color3 = unpackDecoder(Color3.new),
Color3uint8 = unpackDecoder(Color3.fromRGB),
NumberRange = unpackDecoder(NumberRange.new),
UDim = unpackDecoder(UDim.new),
UDim2 = unpackDecoder(UDim2.new),
Vector2 = unpackDecoder(Vector2.new),
Vector2int16 = unpackDecoder(Vector2int16.new),
Vector3 = unpackDecoder(Vector3.new),
Vector3int16 = unpackDecoder(Vector3int16.new),
Rect = function(value)
return Rect.new(value.Min[1], value.Min[2], value.Max[1], value.Max[2])
end,
NumberSequence = function(value)
local keypoints = {}
for index, keypoint in ipairs(value.Keypoints) do
keypoints[index] = NumberSequenceKeypoint.new(
keypoint.Time,
keypoint.Value,
keypoint.Envelope
)
end
return NumberSequence.new(keypoints)
end,
ColorSequence = function(value)
local keypoints = {}
for index, keypoint in ipairs(value.Keypoints) do
keypoints[index] = ColorSequenceKeypoint.new(
keypoint.Time,
Color3.new(unpack(keypoint.Color))
)
end
return ColorSequence.new(keypoints)
end,
PhysicalProperties = function(properties)
if properties == nil then
return nil
else
return PhysicalProperties.new(
properties.Density,
properties.Friction,
properties.Elasticity,
properties.FrictionWeight,
properties.ElasticityWeight
)
end
end,
Ref = function()
return nil
end,
}
local EncodedValue = {}
function EncodedValue.decode(encodedValue)
local decoder = decoders[encodedValue.Type]
if decoder ~= nil then
return true, decoder(encodedValue.Value)
end
return false, "Couldn't decode value " .. tostring(encodedValue.Type)
end
function EncodedValue.encode(rbxValue, propertyType)
assert(propertyType ~= nil, "Property type descriptor is required")
if propertyType.type == "Data" then
local encoder = encoders[propertyType.name]
if encoder == nil then
return false, ("Missing encoder for property type %q"):format(propertyType.name)
end
if encoder ~= nil then
return true, {
Type = propertyType.name,
Value = encoder(rbxValue),
}
end
elseif propertyType.type == "Enum" then
return true, {
Type = "Enum",
Value = rbxValue.Value,
}
end
return false, ("Unknown property descriptor type %q"):format(tostring(propertyType.type))
end
return EncodedValue

View File

@@ -0,0 +1,127 @@
return function()
local RbxDom = require(script.Parent)
local EncodedValue = require(script.Parent.EncodedValue)
it("should decode Rect values", function()
local input = {
Type = "Rect",
Value = {
Min = {1, 2},
Max = {3, 4},
},
}
local output = Rect.new(1, 2, 3, 4)
local ok, decoded = EncodedValue.decode(input)
assert(ok, decoded)
expect(decoded).to.equal(output)
end)
it("should decode ColorSequence values", function()
local input = {
Type = "ColorSequence",
Value = {
Keypoints = {
{
Time = 0,
Color = { 0.12, 0.34, 0.56 },
},
{
Time = 1,
Color = { 0.13, 0.33, 0.37 },
},
}
},
}
local output = ColorSequence.new({
ColorSequenceKeypoint.new(0, Color3.new(0.12, 0.34, 0.56)),
ColorSequenceKeypoint.new(1, Color3.new(0.13, 0.33, 0.37)),
})
local ok, decoded = EncodedValue.decode(input)
assert(ok, decoded)
expect(decoded).to.equal(output)
end)
it("should decode NumberSequence values", function()
local input = {
Type = "NumberSequence",
Value = {
Keypoints = {
{
Time = 0,
Value = 0.5,
Envelope = 0,
},
{
Time = 1,
Value = 0.5,
Envelope = 0,
},
}
},
}
local output = NumberSequence.new({
NumberSequenceKeypoint.new(0, 0.5, 0),
NumberSequenceKeypoint.new(1, 0.5, 0),
})
local ok, decoded = EncodedValue.decode(input)
assert(ok, decoded)
expect(decoded).to.equal(output)
end)
it("should decode PhysicalProperties values", function()
local input = {
Type = "PhysicalProperties",
Value = {
Density = 0.1,
Friction = 0.2,
Elasticity = 0.3,
FrictionWeight = 0.4,
ElasticityWeight = 0.5,
},
}
local output = PhysicalProperties.new(
0.1,
0.2,
0.3,
0.4,
0.5
)
local ok, decoded = EncodedValue.decode(input)
assert(ok, decoded)
expect(decoded).to.equal(output)
end)
-- This part of rbx_dom_lua needs some work still.
itSKIP("should encode Rect values", function()
local input = Rect.new(10, 20, 30, 40)
local output = {
Type = "Rect",
Value = {
Min = {10, 20},
Max = {30, 40},
},
}
local descriptor = RbxDom.findCanonicalPropertyDescriptor("ImageLabel", "SliceCenter")
local ok, encoded = EncodedValue.encode(input, descriptor)
assert(ok, encoded)
expect(encoded.Type).to.equal(output.Type)
expect(encoded.Value.Min[1]).to.equal(output.Value.Min[1])
expect(encoded.Value.Min[2]).to.equal(output.Value.Min[2])
expect(encoded.Value.Max[1]).to.equal(output.Value.Max[1])
expect(encoded.Value.Max[2]).to.equal(output.Value.Max[2])
end)
end

View File

@@ -20,22 +20,8 @@ local function set(container, key, value)
end
function PropertyDescriptor.fromRaw(data, className, propertyName)
local key, value = next(data.DataType)
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,
name = propertyName,
}, PropertyDescriptor)
@@ -91,4 +77,4 @@ function PropertyDescriptor:write(instance, value)
end
end
return PropertyDescriptor
return PropertyDescriptor

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,3 @@
return {
classes = require(script.classes)
}

View File

@@ -6,10 +6,12 @@ local CollectionService = game:GetService("CollectionService")
return {
Instance = {
Tags = {
read = function(instance)
return true, CollectionService:GetTags(instance)
read = function(instance, key)
local tagList = CollectionService:GetTags(instance)
return true, table.concat(tagList, "\0")
end,
write = function(instance, _, value)
write = function(instance, key, value)
local existingTags = CollectionService:GetTags(instance)
local unseenTags = {}
@@ -17,7 +19,8 @@ return {
unseenTags[tag] = true
end
for _, tag in ipairs(value) do
local tagList = string.split(value, "\0")
for _, tag in ipairs(tagList) do
unseenTags[tag] = nil
CollectionService:AddTag(instance, tag)
end
@@ -41,4 +44,4 @@ return {
end,
},
},
}
}

View File

@@ -1,4 +1,4 @@
local database = require(script.database)
local ReflectionDatabase = require(script.ReflectionDatabase)
local Error = require(script.Error)
local PropertyDescriptor = require(script.PropertyDescriptor)
@@ -6,31 +6,29 @@ local function findCanonicalPropertyDescriptor(className, propertyName)
local currentClassName = className
repeat
local currentClass = database.Classes[currentClassName]
local currentClass = ReflectionDatabase.classes[currentClassName]
if currentClass == nil then
return currentClass
end
local propertyData = currentClass.Properties[propertyName]
local propertyData = currentClass.properties[propertyName]
if propertyData ~= nil then
local canonicalData = propertyData.Kind.Canonical
if canonicalData ~= nil then
if propertyData.isCanonical then
return PropertyDescriptor.fromRaw(propertyData, currentClassName, propertyName)
end
local aliasData = propertyData.Kind.Alias
if aliasData ~= nil then
if propertyData.canonicalName ~= nil then
return PropertyDescriptor.fromRaw(
currentClass.Properties[aliasData.AliasFor],
currentClass.properties[propertyData.canonicalName],
currentClassName,
aliasData.AliasFor)
propertyData.canonicalName)
end
return nil
end
currentClassName = currentClass.Superclass
currentClassName = currentClass.superclass
until currentClassName == nil
return nil
@@ -66,4 +64,4 @@ return {
findCanonicalPropertyDescriptor = findCanonicalPropertyDescriptor,
Error = Error,
EncodedValue = require(script.EncodedValue),
}
}

View File

@@ -0,0 +1,35 @@
{
"name": "rbx_dom_lua test place",
"tree": {
"$className": "DataModel",
"ReplicatedStorage": {
"$className": "ReplicatedStorage",
"RbxDom": {
"$path": "src"
},
"TestEZ": {
"$path": "modules/testez/lib"
}
},
"ServerScriptService": {
"$className": "ServerScriptService",
"Run Tests": {
"$path": "test.server.lua"
}
},
"Players": {
"$className": "Players",
"$properties": {
"CharacterAutoLoads": false
}
},
"HttpService": {
"$className": "HttpService",
"$properties": {
"HttpEnabled": true
}
}
}
}

View File

@@ -0,0 +1,7 @@
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local LIB_ROOT = ReplicatedStorage.RbxDom
local TestEZ = require(ReplicatedStorage.TestEZ)
TestEZ.TestBootstrap:run({LIB_ROOT})

View File

@@ -1,40 +0,0 @@
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

View File

@@ -44,10 +44,6 @@ function StudioToggleButton:didUpdate(lastProps)
self.button.Enabled = self.props.enabled
end
if self.props.icon ~= lastProps.icon then
self.button.Icon = self.props.icon
end
if self.props.active ~= lastProps.active then
self.button:SetActive(self.props.active)
end
@@ -67,4 +63,4 @@ local function StudioToggleButtonWrapper(props)
})
end
return StudioToggleButtonWrapper
return StudioToggleButtonWrapper

View File

@@ -24,7 +24,7 @@ local function AddressEntry(props)
layoutOrder = props.layoutOrder,
}, {
Host = e("TextBox", {
Text = props.host or "",
Text = "",
Font = Enum.Font.Code,
TextSize = 18,
TextColor3 = theme.AddressEntry.TextColor,
@@ -32,7 +32,6 @@ local function AddressEntry(props)
TextTransparency = props.transparency,
PlaceholderText = Config.defaultHost,
PlaceholderColor3 = theme.AddressEntry.PlaceholderColor,
ClearTextOnFocus = false,
Size = UDim2.new(1, -(HOST_OFFSET + DIVIDER_WIDTH + PORT_WIDTH), 1, 0),
Position = UDim2.new(0, HOST_OFFSET, 0, 0),
@@ -40,22 +39,17 @@ local function AddressEntry(props)
ClipsDescendants = true,
BackgroundTransparency = 1,
[Roact.Change.Text] = function(object)
if props.onHostChange ~= nil then
props.onHostChange(object.Text)
end
end
[Roact.Ref] = props.hostRef,
}),
Port = e("TextBox", {
Text = props.port or "",
Text = "",
Font = Enum.Font.Code,
TextSize = 18,
TextColor3 = theme.AddressEntry.TextColor,
TextTransparency = props.transparency,
PlaceholderText = Config.defaultPort,
PlaceholderColor3 = theme.AddressEntry.PlaceholderColor,
ClearTextOnFocus = false,
Size = UDim2.new(0, PORT_WIDTH, 1, 0),
Position = UDim2.new(1, 0, 0, 0),
@@ -64,14 +58,12 @@ local function AddressEntry(props)
ClipsDescendants = true,
BackgroundTransparency = 1,
[Roact.Ref] = props.portRef,
[Roact.Change.Text] = function(object)
local text = object.Text
text = text:gsub("%D", "")
object.Text = text
if props.onPortChange ~= nil then
props.onPortChange(text)
end
end,
}, {
Divider = e("Frame", {
@@ -88,6 +80,11 @@ end
local NotConnectedPage = Roact.Component:extend("NotConnectedPage")
function NotConnectedPage:init()
self.hostRef = Roact.createRef()
self.portRef = Roact.createRef()
end
function NotConnectedPage:render()
return Roact.createFragment({
Header = e(Header, {
@@ -96,10 +93,8 @@ function NotConnectedPage:render()
}),
AddressEntry = e(AddressEntry, {
host = self.props.host,
port = self.props.port,
onHostChange = self.props.onHostChange,
onPortChange = self.props.onPortChange,
hostRef = self.hostRef,
portRef = self.portRef,
transparency = self.props.transparency,
layoutOrder = 2,
}),
@@ -122,7 +117,15 @@ function NotConnectedPage:render()
style = "Solid",
transparency = self.props.transparency,
layoutOrder = 2,
onClick = self.props.onConnect,
onClick = function()
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", {

View File

@@ -205,7 +205,7 @@ function SettingsPage:render()
TwoWaySync = e(Setting, {
id = "twoWaySync",
name = "Two-Way Sync",
description = "EXPERIMENTAL! Editing files in Studio will sync them into the filesystem",
description = "Editing files in Studio will sync them into the filesystem",
transparency = self.props.transparency,
layoutOrder = 2,
}),

View File

@@ -34,7 +34,7 @@ end
local BRAND_COLOR = hexColor(0xE13835)
local lightTheme = strict("LightTheme", {
BackgroundColor = hexColor(0xFFFFFF),
BackgroundColor = hexColor(0xF0F0F0),
Button = {
Solid = {
ActionFillColor = hexColor(0xFFFFFF),
@@ -67,7 +67,7 @@ local lightTheme = strict("LightTheme", {
BackgroundColor = BRAND_COLOR,
},
Inactive = {
IconColor = hexColor(0xEEEEEE),
IconColor = hexColor(0xCACACA),
BorderColor = hexColor(0xAFAFAF),
},
},
@@ -77,11 +77,11 @@ local lightTheme = strict("LightTheme", {
},
BorderedContainer = {
BorderColor = hexColor(0xCBCBCB),
BackgroundColor = hexColor(0xEEEEEE),
BackgroundColor = hexColor(0xE0E0E0),
},
Spinner = {
ForegroundColor = BRAND_COLOR,
BackgroundColor = hexColor(0xEEEEEE),
BackgroundColor = hexColor(0xE0E0E0),
},
ConnectionDetails = {
ProjectNameColor = hexColor(0x00000),
@@ -108,7 +108,7 @@ local lightTheme = strict("LightTheme", {
})
local darkTheme = strict("DarkTheme", {
BackgroundColor = hexColor(0x2E2E2E),
BackgroundColor = hexColor(0x272727),
Button = {
Solid = {
ActionFillColor = hexColor(0xFFFFFF),
@@ -147,15 +147,15 @@ local darkTheme = strict("DarkTheme", {
},
AddressEntry = {
TextColor = hexColor(0xFFFFFF),
PlaceholderColor = hexColor(0x8B8B8B)
PlaceholderColor = hexColor(0x717171)
},
BorderedContainer = {
BorderColor = hexColor(0x535353),
BackgroundColor = hexColor(0x2B2B2B),
BackgroundColor = hexColor(0x323232),
},
Spinner = {
ForegroundColor = BRAND_COLOR,
BackgroundColor = hexColor(0x2B2B2B),
BackgroundColor = hexColor(0x323232),
},
ConnectionDetails = {
ProjectNameColor = hexColor(0xFFFFFF),
@@ -236,4 +236,4 @@ return {
StudioProvider = StudioProvider,
Consumer = Context.Consumer,
with = with,
}
}

View File

@@ -16,7 +16,6 @@ local Theme = require(script.Theme)
local PluginSettings = require(script.PluginSettings)
local Page = require(script.Page)
local StudioPluginAction = require(script.Components.Studio.StudioPluginAction)
local StudioToolbar = require(script.Components.Studio.StudioToolbar)
local StudioToggleButton = require(script.Components.Studio.StudioToggleButton)
local StudioPluginGui = require(script.Components.Studio.StudioPluginGui)
@@ -38,34 +37,13 @@ local App = Roact.Component:extend("App")
function App:init()
preloadAssets()
self.host, self.setHost = Roact.createBinding("")
self.port, self.setPort = Roact.createBinding("")
self:setState({
appStatus = AppStatus.NotConnected,
guiEnabled = false,
toolbarIcon = Assets.Images.PluginButton,
})
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"),
}
function App:startSession(host, port, sessionOptions)
local baseUrl = ("http://%s:%s"):format(host, port)
local apiContext = ApiContext.new(baseUrl)
@@ -79,7 +57,6 @@ function App:startSession()
if status == ServeSession.Status.Connecting then
self:setState({
appStatus = AppStatus.Connecting,
toolbarIcon = Assets.Images.PluginButton,
})
elseif status == ServeSession.Status.Connected then
local address = ("%s:%s"):format(host, port)
@@ -87,10 +64,7 @@ function App:startSession()
appStatus = AppStatus.Connected,
projectName = details,
address = address,
toolbarIcon = Assets.Images.PluginButtonConnected,
})
Log.info("Connected to session '{}' at {}", details, address)
elseif status == ServeSession.Status.Disconnected then
self.serveSession = nil
@@ -102,15 +76,11 @@ function App:startSession()
self:setState({
appStatus = AppStatus.Error,
errorMessage = tostring(details),
toolbarIcon = Assets.Images.PluginButtonWarning,
})
else
self:setState({
appStatus = AppStatus.NotConnected,
toolbarIcon = Assets.Images.PluginButton,
})
Log.info("Disconnected session")
end
end
end)
@@ -120,22 +90,6 @@ function App:startSession()
self.serveSession = serveSession
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()
local pluginName = "Rojo " .. Version.display(Config.version)
@@ -154,160 +108,119 @@ function App:render()
value = self.props.plugin,
}, {
e(Theme.StudioProvider, nil, {
gui = e(StudioPluginGui, {
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,
e(PluginSettings.StudioProvider, {
plugin = self.props.plugin,
}, {
NotConnectedPage = createPageElement(AppStatus.NotConnected, {
host = self.host,
onHostChange = self.setHost,
port = self.port,
onPortChange = self.setPort,
gui = e(StudioPluginGui, {
id = pluginName,
title = pluginName,
active = self.state.guiEnabled,
onConnect = function()
self:startSession()
end,
initDockState = Enum.InitialDockState.Right,
initEnabled = false,
overridePreviousState = false,
floatingSize = Vector2.new(300, 200),
minimumSize = Vector2.new(300, 200),
onNavigateSettings = function()
zIndexBehavior = Enum.ZIndexBehavior.Sibling,
onInitialState = function(initialState)
self:setState({
appStatus = AppStatus.Settings,
guiEnabled = initialState,
})
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()
self:setState({
appStatus = AppStatus.NotConnected,
toolbarIcon = Assets.Images.PluginButton,
guiEnabled = false,
})
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),
}),
Background = Theme.with(function(theme)
return e("Frame", {
Size = UDim2.new(1, 0, 1, 0),
BackgroundColor3 = theme.BackgroundColor,
ZIndex = 0,
BorderSizePixel = 0,
toolbar = e(StudioToolbar, {
name = pluginName,
}, {
button = e(StudioToggleButton, {
name = "Rojo",
tooltip = "Show or hide the Rojo panel",
icon = Assets.Images.PluginButton,
active = self.state.guiEnabled,
enabled = true,
onClick = function()
self:setState(function(state)
return {
guiEnabled = not state.guiEnabled,
}
end)
end,
})
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
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
return App

View File

@@ -18,8 +18,6 @@ local Assets = {
Images = {
Logo = "rbxassetid://5990772764",
PluginButton = "rbxassetid://3405341609",
PluginButtonConnected = "rbxassetid://9529783993",
PluginButtonWarning = "rbxassetid://9529784530",
Icons = {
Close = "rbxassetid://6012985953",
Back = "rbxassetid://6017213752",

View File

@@ -1,40 +0,0 @@
--[[
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

View File

@@ -1,74 +0,0 @@
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

View File

@@ -1,39 +0,0 @@
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

View File

@@ -1,62 +0,0 @@
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

View File

@@ -1,21 +0,0 @@
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

View File

@@ -1,81 +0,0 @@
--[[
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

View File

@@ -1,101 +0,0 @@
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

View File

@@ -5,9 +5,9 @@ local isDevBuild = script.Parent.Parent:FindFirstChild("ROJO_DEV_BUILD") ~= nil
return strict("Config", {
isDevBuild = isDevBuild,
codename = "Epiphany",
version = {7, 1, 1},
expectedServerVersionString = "7.0 or newer",
protocolVersion = 4,
version = {6, 1, 0},
expectedServerVersionString = "6.0 or newer",
protocolVersion = 3,
defaultHost = "localhost",
defaultPort = 34872,
})

View File

@@ -1,5 +1,3 @@
local RunService = game:GetService("RunService")
local Log = require(script.Parent.Parent.Log)
--[[
@@ -137,31 +135,29 @@ function InstanceMap:destroyId(id)
end
--[[
Pause updates for an instance.
Pause updates for an instance momentarily and invoke a callback.
If the callback throws an error, InstanceMap will still be kept in a
consistent state.
]]
function InstanceMap:pauseInstance(instance)
function InstanceMap:pauseInstance(instance, callback)
local id = self.fromInstances[instance]
-- If we don't know about this instance, ignore it.
-- If we don't know about this instance, ignore it and do not invoke the
-- callback.
if id == nil then
return
end
self.pausedUpdateInstances[instance] = true
end
local success, result = xpcall(callback, debug.traceback)
self.pausedUpdateInstances[instance] = false
--[[
Unpause updates for an instance.
]]
function InstanceMap:unpauseInstance(instance)
self.pausedUpdateInstances[instance] = nil
end
--[[
Unpause updates for all instances.
]]
function InstanceMap:unpauseAllInstances()
table.clear(self.pausedUpdateInstances)
if success then
return result
else
error(result, 2)
end
end
function InstanceMap:__connectSignals(instance)
@@ -204,12 +200,6 @@ function InstanceMap:__maybeFireInstanceChanged(instance, propertyName)
return
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)
end
@@ -232,4 +222,4 @@ function InstanceMap:__disconnectSignals(instance)
end
end
return InstanceMap
return InstanceMap

View File

@@ -63,7 +63,7 @@ local function applyPatch(instanceMap, patch)
local failedToReify = reify(instanceMap, patch.added, id, parentInstance)
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)
end
end
@@ -77,10 +77,6 @@ local function applyPatch(instanceMap, patch)
continue
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.
local unappliedUpdate = {
id = update.id,
@@ -201,4 +197,4 @@ local function applyPatch(instanceMap, patch)
return unappliedPatch
end
return applyPatch
return applyPatch

View File

@@ -146,7 +146,8 @@ return function()
id = "VALUE",
changedProperties = {
Value = {
String = "WORLD",
Type = "String",
Value = "WORLD",
},
},
})
@@ -175,7 +176,8 @@ return function()
changedClassName = "StringValue",
changedProperties = {
Value = {
String = "I am Root",
Type = "String",
Value = "I am Root",
},
},
})

View File

@@ -6,31 +6,29 @@
local RbxDom = require(script.Parent.Parent.Parent.RbxDom)
local Error = require(script.Parent.Error)
local function decodeValue(encodedValue, instanceMap)
local ty, value = next(encodedValue)
local function decodeValue(virtualValue, instanceMap)
-- Refs are represented as IDs in the same space that Rojo's protocol uses.
if ty == "Ref" then
if value == "00000000000000000000000000000000" then
if virtualValue.Type == "Ref" then
if virtualValue.Value == nil then
return true, nil
end
local instance = instanceMap.fromIds[value]
local instance = instanceMap.fromIds[virtualValue.Value]
if instance ~= nil then
return true, instance
else
return false, Error.new(Error.RefDidNotExist, {
encodedValue = encodedValue,
virtualValue = virtualValue,
})
end
end
local ok, decodedValue = RbxDom.EncodedValue.decode(encodedValue)
local ok, decodedValue = RbxDom.EncodedValue.decode(virtualValue)
if not ok then
return false, Error.new(Error.CannotDecodeValue, {
encodedValue = encodedValue,
virtualValue = virtualValue,
innerError = decodedValue,
})
end

View File

@@ -75,13 +75,7 @@ local function diff(instanceMap, virtualInstances, rootId)
changedProperties[propertyName] = virtualValue
end
else
local propertyType = next(virtualValue)
Log.warn(
"Failed to decode property {}.{}. Encoded property was: {:#?}",
virtualInstance.ClassName,
propertyName,
virtualValue
)
Log.warn("Failed to decode property of type {}", virtualValue.Type)
end
else
local err = existingValueOrErr

View File

@@ -80,7 +80,8 @@ return function()
Name = "Value",
Properties = {
Value = {
String = "Hello, world!",
Type = "String",
Value = "Hello, world!",
},
},
Children = {},
@@ -106,9 +107,8 @@ return function()
local patchProperty = update.changedProperties["Value"]
expect(patchProperty).to.be.a("table")
local ty, value = next(patchProperty)
expect(ty).to.equal("String")
expect(value).to.equal("Hello, world!")
expect(patchProperty.Type).to.equal("String")
expect(patchProperty.Value).to.equal("Hello, world!")
end)
it("should generate an empty patch if no properties changed", function()
@@ -119,7 +119,8 @@ return function()
Name = "Value",
Properties = {
Value = {
String = "Hello, world!",
Type = "String",
Value = "Hello, world!",
},
},
Children = {},
@@ -144,7 +145,8 @@ return function()
Name = "Folder",
Properties = {
FAKE_PROPERTY = {
String = "Hello, world!",
Type = "String",
Value = "Hello, world!",
},
},
Children = {},
@@ -181,7 +183,8 @@ return function()
-- heat_xml is a serialization-only property that is not
-- exposed to Lua.
heat_xml = {
Float32 = 5,
Type = "Float32",
Value = 5,
},
},
Children = {},

View File

@@ -40,13 +40,6 @@ local function getProperty(instance, propertyName)
})
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, {
className = instance.ClassName,
propertyName = propertyName,

View File

@@ -70,7 +70,7 @@ function reifyInner(instanceMap, virtualInstances, id, parentInstance, unapplied
for propertyName, virtualValue in pairs(virtualInstance.Properties) do
-- Because refs may refer to instances that we haven't constructed yet,
-- we defer applying any ref properties until all instances are created.
if next(virtualValue) == "Ref" then
if virtualValue.Type == "Ref" then
table.insert(deferredRefs, {
id = id,
instance = instance,
@@ -136,23 +136,23 @@ function applyDeferredRefs(instanceMap, deferredRefs, unappliedPatch)
end
for _, entry in ipairs(deferredRefs) do
local _, refId = next(entry.virtualValue)
local virtualValue = entry.virtualValue
if refId == nil then
if virtualValue.Value == nil then
continue
end
local targetInstance = instanceMap.fromIds[refId]
local targetInstance = instanceMap.fromIds[virtualValue.Value]
if targetInstance == nil then
markFailed(entry.id, entry.propertyName, entry.virtualValue)
markFailed(entry.id, entry.propertyName, virtualValue)
continue
end
local ok = setProperty(entry.instance, entry.propertyName, targetInstance)
if not ok then
markFailed(entry.id, entry.propertyName, entry.virtualValue)
markFailed(entry.id, entry.propertyName, virtualValue)
end
end
end
return reify
return reify

View File

@@ -54,7 +54,8 @@ return function()
Name = "Spaghetti",
Properties = {
Value = {
String = "Hello, world!",
Type = "String",
Value = "Hello, world!",
},
},
Children = {},
@@ -190,7 +191,8 @@ return function()
Name = "Child",
Properties = {
Value = {
Ref = "ROOT",
Type = "Ref",
Value = "ROOT",
},
},
Children = {},
@@ -217,7 +219,8 @@ return function()
Name = "Root",
Properties = {
Value = {
Ref = "EXISTING",
Type = "Ref",
Value = "EXISTING",
},
},
Children = {},
@@ -255,7 +258,8 @@ return function()
Name = "Child A",
Properties = {
Value = {
Ref = "CHILD_B",
Type = "Ref",
Value = "Child B",
},
},
Children = {},
@@ -287,14 +291,15 @@ return function()
-- 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
-- case to test.
it("should apply properties containing refs to later children correctly", function()
it("should apply properties containing refs to later siblings correctly", function()
local virtualInstances = {
ROOT = {
ClassName = "ObjectValue",
Name = "Root",
Properties = {
Value = {
Ref = "CHILD",
Type = "Ref",
Value = "CHILD",
},
},
Children = {"CHILD"},
@@ -344,4 +349,4 @@ return function()
expect(update.id).to.equal("ROOT")
expect(update.changedProperties.Value).to.equal(virtualInstances["ROOT"].Properties.Value)
end)
end
end

View File

@@ -1,11 +1,9 @@
local StudioService = game:GetService("StudioService")
local RunService = game:GetService("RunService")
local Log = require(script.Parent.Parent.Log)
local Fmt = require(script.Parent.Parent.Fmt)
local t = require(script.Parent.Parent.t)
local ChangeBatcher = require(script.Parent.ChangeBatcher)
local InstanceMap = require(script.Parent.InstanceMap)
local PatchSet = require(script.Parent.PatchSet)
local Reconciler = require(script.Parent.Reconciler)
@@ -57,19 +55,10 @@ function ServeSession.new(options)
-- Declare self ahead of time to capture it in a closure
local self
local function onInstanceChanged(instance, propertyName)
if not self.__twoWaySync then
return
end
self.__changeBatcher:add(instance, propertyName)
end
local function onChangesFlushed(patch)
self.__apiContext:write(patch)
self:__onInstanceChanged(instance, propertyName)
end
local instanceMap = InstanceMap.new(onInstanceChanged)
local changeBatcher = ChangeBatcher.new(instanceMap, onChangesFlushed)
local reconciler = Reconciler.new(instanceMap)
local connections = {}
@@ -92,7 +81,6 @@ function ServeSession.new(options)
__twoWaySync = options.twoWaySync,
__reconciler = reconciler,
__instanceMap = instanceMap,
__changeBatcher = changeBatcher,
__statusChangedCallback = nil,
__connections = connections,
}
@@ -113,10 +101,6 @@ function ServeSession:__fmtDebug(output)
output:write("}")
end
function ServeSession:getStatus()
return self.__status
end
function ServeSession:onStatusChanged(callback)
self.__statusChangedCallback = callback
end
@@ -127,7 +111,6 @@ function ServeSession:start()
self.__apiContext:connect()
:andThen(function(serverInfo)
self:__setStatus(Status.Connected, serverInfo.projectName)
self:__applyGameAndPlaceId(serverInfo)
local rootInstanceId = serverInfo.rootInstanceId
@@ -145,16 +128,6 @@ function ServeSession:stop()
self:__stopInternal()
end
function ServeSession:__applyGameAndPlaceId(serverInfo)
if serverInfo.gameId ~= nil then
game:SetUniverseId(serverInfo.gameId)
end
if serverInfo.placeId ~= nil then
game:SetPlaceId(serverInfo.placeId)
end
end
function ServeSession:__onActiveScriptChanged(activeScript)
if not self.__openScriptsExternally then
Log.trace("Not opening script {} because feature not enabled.", activeScript)
@@ -177,23 +150,64 @@ function ServeSession:__onActiveScriptChanged(activeScript)
Log.debug("Trying to open script {} externally...", activeScript)
-- Force-close the script inside Studio... with a small delay in the middle
-- to prevent Studio from crashing.
spawn(function()
local existingParent = activeScript.Parent
activeScript.Parent = nil
for i = 1, 3 do
RunService.Heartbeat:Wait()
end
activeScript.Parent = existingParent
end)
-- Force-close the script inside Studio
local existingParent = activeScript.Parent
activeScript.Parent = nil
activeScript.Parent = existingParent
-- Notify the Rojo server to open this script
self.__apiContext:open(scriptId)
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)
return self.__apiContext:read({ rootInstanceId })
:andThen(function(readResponseBody)
@@ -256,7 +270,6 @@ function ServeSession:__stopInternal(err)
self:__setStatus(Status.Disconnected, err)
self.__apiContext:disconnect()
self.__instanceMap:stop()
self.__changeBatcher:stop()
for _, connection in ipairs(self.__connections) do
connection:Disconnect()
@@ -272,4 +285,4 @@ function ServeSession:__setStatus(status, detail)
end
end
return ServeSession
return ServeSession

View File

@@ -5,7 +5,10 @@ local strict = require(script.Parent.strict)
local RbxId = t.string
local ApiValue = t.keys(t.string)
local ApiValue = t.interface({
Type = t.string,
Value = t.optional(t.any),
})
local ApiInstanceMetadata = t.interface({
ignoreUnknownInstances = t.optional(t.boolean),
@@ -93,4 +96,4 @@ return strict("Types", {
VirtualInstance = ApiInstance,
VirtualMetadata = ApiInstanceMetadata,
VirtualValue = ApiValue,
})
})

View File

@@ -9,7 +9,7 @@
},
"TestEZ": {
"$path": "modules/testez"
"$path": "modules/testez/lib"
}
},
@@ -25,4 +25,4 @@
}
}
}
}
}

View File

@@ -10,7 +10,7 @@ expression: contents
<Item class="IntValue" referent="1">
<Properties>
<string name="Name">simple-model</string>
<int64 name="Value">5</int64>
<int name="Value">5</int>
</Properties>
<Item class="Folder" referent="2">
<Properties>

View File

@@ -1,24 +0,0 @@
---
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>

View File

@@ -1,17 +0,0 @@
---
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>

View File

@@ -1,22 +0,0 @@
---
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>

View File

@@ -1,12 +0,0 @@
---
source: tests/tests/build.rs
expression: contents
---
<roblox version="4">
<Item class="Folder" referent="0">
<Properties>
<string name="Name">root</string>
</Properties>
</Item>
</roblox>

View File

@@ -1,7 +1,6 @@
---
source: tests/tests/build.rs
expression: contents
---
<roblox version="4">
<Item class="Folder" referent="0">
@@ -26,12 +25,14 @@ expression: contents
<R22>1</R22>
</CoordinateFrame>
<Ref name="PrimaryPart">null</Ref>
<BinaryString name="Tags"></BinaryString>
<BinaryString name="Tags">
</BinaryString>
</Properties>
<Item class="StringValue" referent="2">
<Properties>
<string name="Name">Cool StringValue</string>
<BinaryString name="Tags"></BinaryString>
<BinaryString name="Tags">
</BinaryString>
<string name="Value">Did you know that BaseValue.Changed is different than Instance.Changed?</string>
</Properties>
</Item>

View File

@@ -1,25 +1,27 @@
---
source: tests/tests/build.rs
expression: contents
---
<roblox version="4">
<Item class="Folder" referent="0">
<Properties>
<string name="Name">rbxmx_ref</string>
<BinaryString name="Tags"></BinaryString>
<BinaryString name="Tags">
</BinaryString>
</Properties>
<Item class="StringValue" referent="1">
<Properties>
<string name="Name">Target</string>
<BinaryString name="Tags"></BinaryString>
<BinaryString name="Tags">
</BinaryString>
<string name="Value">Pointed to by ObjectValue</string>
</Properties>
</Item>
<Item class="ObjectValue" referent="2">
<Properties>
<string name="Name">Pointer</string>
<BinaryString name="Tags"></BinaryString>
<BinaryString name="Tags">
</BinaryString>
<Ref name="Value">1</Ref>
</Properties>
</Item>

View File

@@ -1,66 +0,0 @@
---
source: tests/tests/build.rs
expression: contents
---
<roblox version="4">
<Item class="DataModel" referent="0">
<Properties>
<string name="Name">unresolved-values</string>
</Properties>
<Item class="Lighting" referent="1">
<Properties>
<string name="Name">Lighting</string>
<Color3 name="Ambient">
<R>1</R>
<G>0</G>
<B>0</B>
</Color3>
<token name="Technology">1</token>
</Properties>
</Item>
<Item class="Workspace" referent="2">
<Properties>
<string name="Name">Workspace</string>
</Properties>
<Item class="BoolValue" referent="3">
<Properties>
<string name="Name">Bool</string>
<bool name="Value">true</bool>
</Properties>
</Item>
<Item class="Part" referent="4">
<Properties>
<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>
</Properties>
</Item>
<Item class="NumberValue" referent="5">
<Properties>
<string name="Name">Float</string>
<double name="Value">123.5</double>
</Properties>
</Item>
<Item class="IntValue" referent="6">
<Properties>
<string name="Name">Int</string>
<int64 name="Value">65</int64>
</Properties>
</Item>
</Item>
</Item>
</roblox>

View File

@@ -1,230 +0,0 @@
---
source: tests/tests/build.rs
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>

View File

@@ -1,15 +0,0 @@
{
"name": "optional",
"tree": {
"$className": "Folder",
"foo-required": {
"$path": "foo.txt"
},
"foo-optional":{
"$path": { "optional": "foo.txt" }
},
"bar-optional":{
"$path": { "optional": "bar.txt" }
}
}
}

View File

@@ -1 +0,0 @@
Hello, from foo.txt!

View File

@@ -1,9 +0,0 @@
{
"name": "root",
"tree": {
"$className": "Folder",
"folder": {
"$path": "folder"
}
}
}

View File

@@ -1,6 +0,0 @@
{
"name": "child-projectname",
"tree": {
"$className": "Folder"
}
}

View File

@@ -1,9 +0,0 @@
{
"name": "root",
"tree": {
"$className": "Folder",
"folder": {
"$path": "folder"
}
}
}

View File

@@ -1,6 +0,0 @@
{
"name": "child-projectname",
"tree": {
"$className": "Folder"
}
}

Some files were not shown because too many files have changed in this diff Show More