forked from rojo-rbx/rojo
Compare commits
77 Commits
v7.0.0-alp
...
v7.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
07637dfe96 | ||
|
|
f389a4a1db | ||
|
|
af077c796c | ||
|
|
1c319f2fa8 | ||
|
|
e8afa03f7b | ||
|
|
9b22545842 | ||
|
|
adc733d25c | ||
|
|
6896257647 | ||
|
|
1d9845a6cb | ||
|
|
8461339e9a | ||
|
|
9904d94e4c | ||
|
|
da25c80d0b | ||
|
|
5fa63733fd | ||
|
|
8b54bf0ba1 | ||
|
|
173dc12cb3 | ||
|
|
e136529ff0 | ||
|
|
75542dacb3 | ||
|
|
07abfbde43 | ||
|
|
96112fe118 | ||
|
|
9d0b313261 | ||
|
|
277ddfa9be | ||
|
|
5d88bdb256 | ||
|
|
8d29b43155 | ||
|
|
cc071a6415 | ||
|
|
8954def25c | ||
|
|
d484098781 | ||
|
|
9f06cbf3a0 | ||
|
|
af4a3ca0af | ||
|
|
43715143e4 | ||
|
|
16aa354d36 | ||
|
|
c739025453 | ||
|
|
f0526d17de | ||
|
|
6cc2e919c0 | ||
|
|
e1f9eaefa9 | ||
|
|
5d62bf9b60 | ||
|
|
4aa5814a0a | ||
|
|
5bca244062 | ||
|
|
7cf57714a4 | ||
|
|
92e6f862ad | ||
|
|
2377f41036 | ||
|
|
26a08f4d9f | ||
|
|
672d207961 | ||
|
|
a3d8e50f26 | ||
|
|
d3abca46a8 | ||
|
|
17fdd18c55 | ||
|
|
e482d038c6 | ||
|
|
d0482a004e | ||
|
|
561a3e3256 | ||
|
|
158dac5e1c | ||
|
|
1413f8c0b6 | ||
|
|
ffb2aa332a | ||
|
|
45e8208e9c | ||
|
|
7f230a8bf4 | ||
|
|
afe26b8c16 | ||
|
|
d153f62b8a | ||
|
|
5c80cd6e50 | ||
|
|
df1aced95d | ||
|
|
5a0a8f5077 | ||
|
|
1c281539e0 | ||
|
|
ef41d14f50 | ||
|
|
aa29397732 | ||
|
|
ca8865a3ce | ||
|
|
7599ef6626 | ||
|
|
43e71f9242 | ||
|
|
aac9b25efe | ||
|
|
532d170585 | ||
|
|
3dcb14013b | ||
|
|
a4c782cd35 | ||
|
|
0779baa0ac | ||
|
|
0849fab644 | ||
|
|
d85bca2e7e | ||
|
|
c7ab6c435c | ||
|
|
98db3b4f08 | ||
|
|
0e7ba839ed | ||
|
|
2c27691e57 | ||
|
|
934506bdfd | ||
|
|
f313fa4ae1 |
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
|||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
rust_version: [stable, "1.43.1"]
|
rust_version: [stable, "1.55.0"]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
|
|||||||
106
CHANGELOG.md
106
CHANGELOG.md
@@ -2,6 +2,112 @@
|
|||||||
|
|
||||||
## Unreleased Changes
|
## Unreleased Changes
|
||||||
|
|
||||||
|
## [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.
|
||||||
|
* 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)
|
## [7.0.0-alpha.2][7.0.0-alpha.2] (February 19, 2021)
|
||||||
* Fixed incorrect protocol version between the client and server.
|
* Fixed incorrect protocol version between the client and server.
|
||||||
|
|
||||||
|
|||||||
@@ -29,25 +29,31 @@ Sometimes there's something that Rojo doesn't do that it probably should.
|
|||||||
|
|
||||||
Please file issues and we'll try to help figure out what the best way forward is.
|
Please file issues and we'll try to help figure out what the best way forward is.
|
||||||
|
|
||||||
|
## Local Development Gotchas
|
||||||
|
|
||||||
|
If your build fails with "Error: failed to open file `D:\code\rojo\plugin\modules\roact\src`" you need to update your Git submodules.
|
||||||
|
Run the command and try building again: `git submodule update --init --recursive`.
|
||||||
|
|
||||||
## Pushing a Rojo Release
|
## Pushing a Rojo Release
|
||||||
The Rojo release process is pretty manual right now. If you need to do it, here's how:
|
The Rojo release process is pretty manual right now. If you need to do it, here's how:
|
||||||
|
|
||||||
1. Bump server version in [`Cargo.toml`](Cargo.toml)
|
1. Bump server version in [`Cargo.toml`](Cargo.toml)
|
||||||
2. Bump plugin version in [`plugin/src/Config.lua`](plugin/src/Config.lua)
|
2. Bump plugin version in [`plugin/src/Config.lua`](plugin/src/Config.lua)
|
||||||
3. Run `cargo test` to update `Cargo.lock` and double-check tests
|
3. Run `cargo test` to update `Cargo.lock` and run tests
|
||||||
4. Update [`CHANGELOG.md`](CHANGELOG.md)
|
4. Update [`CHANGELOG.md`](CHANGELOG.md)
|
||||||
5. Commit!
|
5. Commit!
|
||||||
* `git add . && git commit -m "Release vX.Y.Z"`
|
* `git add . && git commit -m "Release vX.Y.Z"`
|
||||||
6. Tag the commit with the version from `Cargo.toml` prepended with a v, like `v0.4.13`
|
6. Tag the commit
|
||||||
|
* `git tag vX.Y.Z`
|
||||||
7. Publish the CLI
|
7. Publish the CLI
|
||||||
* `cargo publish`
|
* `cargo publish`
|
||||||
8. Publish the Plugin
|
8. Publish the Plugin
|
||||||
* `rojo publish plugin --asset_id 6415005344`
|
* `cargo run -- upload plugin --asset_id 6415005344`
|
||||||
* `rojo build plugin -o Rojo.rbxm`
|
* `cargo run -- build plugin --output Rojo.rbxm`
|
||||||
9. Push commits and tags
|
9. Push commits and tags
|
||||||
* `git push && git push --tags`
|
* `git push && git push --tags`
|
||||||
10. Copy GitHub release content from previous release
|
10. Copy GitHub release content from previous release
|
||||||
* Update the leading text with a summary about the release
|
* Update the leading text with a summary about the release
|
||||||
* Paste the changelog notes (as-is!) from [`CHANGELOG.md`](CHANGELOG.md)
|
* Paste the changelog notes (as-is!) from [`CHANGELOG.md`](CHANGELOG.md)
|
||||||
* Write a small summary of each major feature
|
* Write a small summary of each major feature
|
||||||
* Attach release artifacts from GitHub Actions for each platform
|
* Attach release artifacts from GitHub Actions for each platform
|
||||||
|
|||||||
1280
Cargo.lock
generated
1280
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
102
Cargo.toml
102
Cargo.toml
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rojo"
|
name = "rojo"
|
||||||
version = "7.0.0-alpha.2"
|
version = "7.0.0"
|
||||||
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
|
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
|
||||||
description = "Enables professional-grade development tools for Roblox developers"
|
description = "Enables professional-grade development tools for Roblox developers"
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
@@ -9,6 +9,7 @@ documentation = "https://rojo.space/docs"
|
|||||||
repository = "https://github.com/rojo-rbx/rojo"
|
repository = "https://github.com/rojo-rbx/rojo"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
build = "build.rs"
|
||||||
|
|
||||||
exclude = [
|
exclude = [
|
||||||
"/test-projects/**",
|
"/test-projects/**",
|
||||||
@@ -36,16 +37,12 @@ members = [
|
|||||||
name = "librojo"
|
name = "librojo"
|
||||||
path = "src/lib.rs"
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "rojo"
|
|
||||||
path = "src/bin.rs"
|
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "build"
|
name = "build"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
memofs = { version = "0.1.2", path = "memofs" }
|
memofs = { version = "0.2.0", path = "memofs" }
|
||||||
|
|
||||||
# These dependencies can be uncommented when working on rbx-dom simultaneously
|
# These dependencies can be uncommented when working on rbx-dom simultaneously
|
||||||
# rbx_binary = { path = "../rbx-dom/rbx_binary" }
|
# rbx_binary = { path = "../rbx-dom/rbx_binary" }
|
||||||
@@ -54,61 +51,62 @@ memofs = { version = "0.1.2", path = "memofs" }
|
|||||||
# rbx_reflection_database = { path = "../rbx-dom/rbx_reflection_database" }
|
# rbx_reflection_database = { path = "../rbx-dom/rbx_reflection_database" }
|
||||||
# rbx_xml = { path = "../rbx-dom/rbx_xml" }
|
# rbx_xml = { path = "../rbx-dom/rbx_xml" }
|
||||||
|
|
||||||
rbx_binary = "0.6.0-alpha.1"
|
rbx_binary = "0.6.4"
|
||||||
rbx_dom_weak = "2.0.0-alpha.1"
|
rbx_dom_weak = "2.3.0"
|
||||||
rbx_reflection = "4.0.0-alpha.1"
|
rbx_reflection = "4.2.0"
|
||||||
rbx_reflection_database = "0.1.0"
|
rbx_reflection_database = "0.2.2"
|
||||||
rbx_xml = "0.12.0-alpha.1"
|
rbx_xml = "0.12.3"
|
||||||
|
|
||||||
anyhow = "1.0.27"
|
anyhow = "1.0.44"
|
||||||
backtrace = "0.3"
|
backtrace = "0.3.61"
|
||||||
bincode = "1.2.1"
|
bincode = "1.3.3"
|
||||||
crossbeam-channel = "0.4.0"
|
crossbeam-channel = "0.5.1"
|
||||||
csv = "1.1.1"
|
csv = "1.1.6"
|
||||||
env_logger = "0.7.1"
|
env_logger = "0.9.0"
|
||||||
fs-err = "2.2.0"
|
fs-err = "2.6.0"
|
||||||
futures = "0.1.29"
|
futures = "0.3.17"
|
||||||
globset = "0.4.4"
|
globset = "0.4.8"
|
||||||
humantime = "1.3.0"
|
humantime = "2.1.0"
|
||||||
hyper = "0.12.35"
|
hyper = { version = "0.14.13", features = ["server", "tcp", "http1"] }
|
||||||
jod-thread = "0.1.0"
|
jod-thread = "0.1.2"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
log = "0.4.8"
|
log = "0.4.14"
|
||||||
maplit = "1.0.1"
|
maplit = "1.0.2"
|
||||||
notify = "4.0.14"
|
notify = "4.0.17"
|
||||||
opener = "0.4.1"
|
opener = "0.5.0"
|
||||||
regex = "1.3.1"
|
regex = "1.5.4"
|
||||||
reqwest = "0.9.20"
|
reqwest = "0.9.24"
|
||||||
ritz = "0.1.0"
|
ritz = "0.1.0"
|
||||||
rlua = "0.17.0"
|
rlua = "0.17.1"
|
||||||
roblox_install = "0.2.2"
|
roblox_install = "1.0.0"
|
||||||
serde = { version = "1.0", features = ["derive", "rc"] }
|
serde = { version = "1.0.130", features = ["derive", "rc"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0.68"
|
||||||
structopt = "0.3.5"
|
structopt = "0.3.23"
|
||||||
termcolor = "1.0.5"
|
termcolor = "1.1.2"
|
||||||
thiserror = "1.0.11"
|
thiserror = "1.0.30"
|
||||||
tokio = "0.1.22"
|
tokio = { version = "1.12.0", features = ["rt", "rt-multi-thread"] }
|
||||||
uuid = { version = "0.8.1", features = ["v4", "serde"] }
|
uuid = { version = "0.8.2", features = ["v4", "serde"] }
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
winreg = "0.6.2"
|
winreg = "0.9.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
memofs = { version = "0.1.3", path = "memofs" }
|
memofs = { version = "0.2.0", path = "memofs" }
|
||||||
|
|
||||||
anyhow = "1.0.27"
|
embed-resource = "1.6.4"
|
||||||
bincode = "1.2.1"
|
anyhow = "1.0.44"
|
||||||
fs-err = "2.3.0"
|
bincode = "1.3.3"
|
||||||
maplit = "1.0.1"
|
fs-err = "2.6.0"
|
||||||
|
maplit = "1.0.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rojo-insta-ext = { path = "rojo-insta-ext" }
|
rojo-insta-ext = { path = "rojo-insta-ext" }
|
||||||
|
|
||||||
criterion = "0.3"
|
criterion = "0.3.5"
|
||||||
insta = { version = "1.3.0", features = ["redactions"] }
|
insta = { version = "1.8.0", features = ["redactions"] }
|
||||||
lazy_static = "1.2"
|
lazy_static = "1.4.0"
|
||||||
paste = "0.1"
|
paste = "1.0.5"
|
||||||
pretty_assertions = "0.6.1"
|
pretty_assertions = "0.7.2"
|
||||||
serde_yaml = "0.8.9"
|
serde_yaml = "0.8.21"
|
||||||
tempfile = "3.0"
|
tempfile = "3.2.0"
|
||||||
walkdir = "2.1"
|
walkdir = "2.3.2"
|
||||||
|
|||||||
18
README.md
18
README.md
@@ -1,21 +1,13 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
<a href="https://rojo.space">
|
<a href="https://rojo.space"><img src="assets/logo-512.png" alt="Rojo" height="217" /></a>
|
||||||
<img src="assets/logo-512.png" alt="Rojo" height="217" />
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div> </div>
|
<div> </div>
|
||||||
|
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<a href="https://github.com/rojo-rbx/rojo/actions">
|
<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>
|
||||||
<img src="https://github.com/rojo-rbx/rojo/workflows/CI/badge.svg" alt="Actions status" />
|
<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>
|
<a href="https://rojo.space/docs"><img src="https://img.shields.io/badge/docs-website-brightgreen.svg" alt="Rojo Documentation" /></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>
|
</div>
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
@@ -48,7 +40,7 @@ Check out our [contribution guide](CONTRIBUTING.md) for detailed instructions fo
|
|||||||
|
|
||||||
Pull requests are welcome!
|
Pull requests are welcome!
|
||||||
|
|
||||||
Rojo supports Rust 1.43.1 and newer. The minimum supported version of Rust is based on the latest versions of the dependencies that Rojo has.
|
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.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
Rojo is available under the terms of the Mozilla Public License, Version 2.0. See [LICENSE.txt](LICENSE.txt) for details.
|
Rojo is available under the terms of the Mozilla Public License, Version 2.0. See [LICENSE.txt](LICENSE.txt) for details.
|
||||||
4
build.rs
4
build.rs
@@ -73,5 +73,9 @@ fn main() -> Result<(), anyhow::Error> {
|
|||||||
|
|
||||||
bincode::serialize_into(out_file, &snapshot)?;
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
2
build/windows/rojo-manifest.rc
Normal file
2
build/windows/rojo-manifest.rc
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#define RT_MANIFEST 24
|
||||||
|
1 RT_MANIFEST "rojo.manifest"
|
||||||
8
build/windows/rojo.manifest
Normal file
8
build/windows/rojo.manifest
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?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>
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
[tools]
|
[tools]
|
||||||
rojo = { source = "rojo-rbx/rojo", version = "6.0.0-rc.3" }
|
rojo = { source = "rojo-rbx/rojo", version = "6.1.0" }
|
||||||
run-in-roblox = { source = "rojo-rbx/run-in-roblox", version = "0.3.0" }
|
run-in-roblox = { source = "rojo-rbx/run-in-roblox", version = "0.3.0" }
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
## Unreleased Changes
|
## Unreleased Changes
|
||||||
|
|
||||||
|
## 0.2.0 (2021-08-23)
|
||||||
|
* Updated to `crossbeam-channel` 0.5.1.
|
||||||
|
|
||||||
## 0.1.3 (2020-11-19)
|
## 0.1.3 (2020-11-19)
|
||||||
* Added `set_watch_enabled` to `Vfs` and `VfsLock` to allow turning off file watching.
|
* Added `set_watch_enabled` to `Vfs` and `VfsLock` to allow turning off file watching.
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "memofs"
|
name = "memofs"
|
||||||
description = "Virtual filesystem with configurable backends."
|
description = "Virtual filesystem with configurable backends."
|
||||||
version = "0.1.3"
|
version = "0.2.0"
|
||||||
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
|
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
readme = "README.md"
|
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
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
crossbeam-channel = "0.4.0"
|
crossbeam-channel = "0.5.1"
|
||||||
fs-err = "2.3.0"
|
fs-err = "2.3.0"
|
||||||
notify = "4.0.15"
|
notify = "4.0.15"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
|||||||
Submodule plugin/modules/testez updated: 6e9157db3c...25d957d4d5
@@ -1,44 +0,0 @@
|
|||||||
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",
|
|
||||||
}
|
|
||||||
@@ -71,8 +71,8 @@ types = {
|
|||||||
|
|
||||||
CFrame = {
|
CFrame = {
|
||||||
fromPod = function(pod)
|
fromPod = function(pod)
|
||||||
local pos = pod.Position
|
local pos = pod.position
|
||||||
local orient = pod.Orientation
|
local orient = pod.orientation
|
||||||
|
|
||||||
return CFrame.new(
|
return CFrame.new(
|
||||||
pos[1], pos[2], pos[3],
|
pos[1], pos[2], pos[3],
|
||||||
@@ -89,8 +89,8 @@ types = {
|
|||||||
r20, r21, r22 = roblox:GetComponents()
|
r20, r21, r22 = roblox:GetComponents()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
Position = {x, y, z},
|
position = {x, y, z},
|
||||||
Orientation = {
|
orientation = {
|
||||||
{r00, r01, r02},
|
{r00, r01, r02},
|
||||||
{r10, r11, r12},
|
{r10, r11, r12},
|
||||||
{r20, r21, r22},
|
{r20, r21, r22},
|
||||||
@@ -123,10 +123,10 @@ types = {
|
|||||||
fromPod = function(pod)
|
fromPod = function(pod)
|
||||||
local keypoints = {}
|
local keypoints = {}
|
||||||
|
|
||||||
for index, keypoint in ipairs(pod.Keypoints) do
|
for index, keypoint in ipairs(pod.keypoints) do
|
||||||
keypoints[index] = ColorSequenceKeypoint.new(
|
keypoints[index] = ColorSequenceKeypoint.new(
|
||||||
keypoint.Time,
|
keypoint.time,
|
||||||
types.Color3.fromPod(keypoint.Color)
|
types.Color3.fromPod(keypoint.color)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -138,13 +138,13 @@ types = {
|
|||||||
|
|
||||||
for index, keypoint in ipairs(roblox.Keypoints) do
|
for index, keypoint in ipairs(roblox.Keypoints) do
|
||||||
keypoints[index] = {
|
keypoints[index] = {
|
||||||
Time = keypoint.Time,
|
time = keypoint.Time,
|
||||||
Color = types.Color3.toPod(keypoint.Value),
|
color = types.Color3.toPod(keypoint.Value),
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
Keypoints = keypoints,
|
keypoints = keypoints,
|
||||||
}
|
}
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
@@ -223,11 +223,11 @@ types = {
|
|||||||
fromPod = function(pod)
|
fromPod = function(pod)
|
||||||
local keypoints = {}
|
local keypoints = {}
|
||||||
|
|
||||||
for index, keypoint in ipairs(pod.Keypoints) do
|
for index, keypoint in ipairs(pod.keypoints) do
|
||||||
keypoints[index] = NumberSequenceKeypoint.new(
|
keypoints[index] = NumberSequenceKeypoint.new(
|
||||||
keypoint.Time,
|
keypoint.time,
|
||||||
keypoint.Value,
|
keypoint.value,
|
||||||
keypoint.Envelope
|
keypoint.envelope
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -239,14 +239,14 @@ types = {
|
|||||||
|
|
||||||
for index, keypoint in ipairs(roblox.Keypoints) do
|
for index, keypoint in ipairs(roblox.Keypoints) do
|
||||||
keypoints[index] = {
|
keypoints[index] = {
|
||||||
Time = keypoint.Time,
|
time = keypoint.Time,
|
||||||
Value = keypoint.Value,
|
value = keypoint.Value,
|
||||||
Envelope = keypoint.Envelope,
|
envelope = keypoint.Envelope,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
Keypoints = keypoints,
|
keypoints = keypoints,
|
||||||
}
|
}
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
@@ -257,11 +257,11 @@ types = {
|
|||||||
return nil
|
return nil
|
||||||
else
|
else
|
||||||
return PhysicalProperties.new(
|
return PhysicalProperties.new(
|
||||||
pod.Density,
|
pod.density,
|
||||||
pod.Friction,
|
pod.friction,
|
||||||
pod.Elasticity,
|
pod.elasticity,
|
||||||
pod.FrictionWeight,
|
pod.frictionWeight,
|
||||||
pod.ElasticityWeight
|
pod.elasticityWeight
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
@@ -271,11 +271,11 @@ types = {
|
|||||||
return "Default"
|
return "Default"
|
||||||
else
|
else
|
||||||
return {
|
return {
|
||||||
Density = roblox.Density,
|
density = roblox.Density,
|
||||||
Friction = roblox.Friction,
|
friction = roblox.Friction,
|
||||||
Elasticity = roblox.Elasticity,
|
elasticity = roblox.Elasticity,
|
||||||
FrictionWeight = roblox.FrictionWeight,
|
frictionWeight = roblox.FrictionWeight,
|
||||||
ElasticityWeight = roblox.ElasticityWeight,
|
elasticityWeight = roblox.ElasticityWeight,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
@@ -284,15 +284,15 @@ types = {
|
|||||||
Ray = {
|
Ray = {
|
||||||
fromPod = function(pod)
|
fromPod = function(pod)
|
||||||
return Ray.new(
|
return Ray.new(
|
||||||
types.Vector3.fromPod(pod.Origin),
|
types.Vector3.fromPod(pod.origin),
|
||||||
types.Vector3.fromPod(pod.Direction)
|
types.Vector3.fromPod(pod.direction)
|
||||||
)
|
)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
toPod = function(roblox)
|
toPod = function(roblox)
|
||||||
return {
|
return {
|
||||||
Origin = types.Vector3.toPod(roblox.Origin),
|
origin = types.Vector3.toPod(roblox.Origin),
|
||||||
Direction = types.Vector3.toPod(roblox.Direction),
|
direction = types.Vector3.toPod(roblox.Direction),
|
||||||
}
|
}
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
@@ -388,6 +388,11 @@ types = {
|
|||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Tags = {
|
||||||
|
fromPod = identity,
|
||||||
|
toPod = identity,
|
||||||
|
},
|
||||||
|
|
||||||
Vector2 = {
|
Vector2 = {
|
||||||
fromPod = unpackDecoder(Vector2.new),
|
fromPod = unpackDecoder(Vector2.new),
|
||||||
|
|
||||||
@@ -431,12 +436,14 @@ types = {
|
|||||||
local EncodedValue = {}
|
local EncodedValue = {}
|
||||||
|
|
||||||
function EncodedValue.decode(encodedValue)
|
function EncodedValue.decode(encodedValue)
|
||||||
local typeImpl = types[encodedValue.Type]
|
local ty, value = next(encodedValue)
|
||||||
|
|
||||||
|
local typeImpl = types[ty]
|
||||||
if typeImpl == nil then
|
if typeImpl == nil then
|
||||||
return false, "Couldn't decode value " .. tostring(encodedValue.Type)
|
return false, "Couldn't decode value " .. tostring(ty)
|
||||||
end
|
end
|
||||||
|
|
||||||
return true, typeImpl.fromPod(encodedValue.Value)
|
return true, typeImpl.fromPod(value)
|
||||||
end
|
end
|
||||||
|
|
||||||
function EncodedValue.encode(rbxValue, propertyType)
|
function EncodedValue.encode(rbxValue, propertyType)
|
||||||
@@ -448,8 +455,7 @@ function EncodedValue.encode(rbxValue, propertyType)
|
|||||||
end
|
end
|
||||||
|
|
||||||
return true, {
|
return true, {
|
||||||
Type = propertyType,
|
[propertyType] = typeImpl.toPod(rbxValue),
|
||||||
Value = typeImpl.toPod(rbxValue),
|
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -20,7 +20,21 @@ local function set(container, key, value)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function PropertyDescriptor.fromRaw(data, className, propertyName)
|
function PropertyDescriptor.fromRaw(data, className, propertyName)
|
||||||
|
local key, value = next(data.DataType)
|
||||||
|
|
||||||
return setmetatable({
|
return setmetatable({
|
||||||
|
-- The meanings of the key and value in DataType differ when the type of
|
||||||
|
-- the property is Enum. When the property is of type Enum, the key is
|
||||||
|
-- the name of the type:
|
||||||
|
--
|
||||||
|
-- { Enum = "<name of enum>" }
|
||||||
|
--
|
||||||
|
-- When the property is not of type Enum, the value is the name of the
|
||||||
|
-- type:
|
||||||
|
--
|
||||||
|
-- { Value = "<data type>" }
|
||||||
|
dataType = key == "Enum" and key or value,
|
||||||
|
|
||||||
scriptability = data.Scriptability,
|
scriptability = data.Scriptability,
|
||||||
className = className,
|
className = className,
|
||||||
name = propertyName,
|
name = propertyName,
|
||||||
@@ -77,4 +91,4 @@ function PropertyDescriptor:write(instance, value)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return PropertyDescriptor
|
return PropertyDescriptor
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
# rbx_dom_lua
|
|
||||||
Roblox Lua implementation of rbx-dom mechanisms, intended to work with rbx_dom_weak and friends.
|
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
{
|
{
|
||||||
"Axes": {
|
"Axes": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Axes",
|
"Axes": [
|
||||||
"Value": [
|
|
||||||
"X",
|
"X",
|
||||||
"Y",
|
"Y",
|
||||||
"Z"
|
"Z"
|
||||||
@@ -12,35 +11,31 @@
|
|||||||
},
|
},
|
||||||
"BinaryString": {
|
"BinaryString": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "BinaryString",
|
"BinaryString": "SGVsbG8h"
|
||||||
"Value": "SGVsbG8h"
|
|
||||||
},
|
},
|
||||||
"ty": "BinaryString"
|
"ty": "BinaryString"
|
||||||
},
|
},
|
||||||
"Bool": {
|
"Bool": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Bool",
|
"Bool": true
|
||||||
"Value": true
|
|
||||||
},
|
},
|
||||||
"ty": "Bool"
|
"ty": "Bool"
|
||||||
},
|
},
|
||||||
"BrickColor": {
|
"BrickColor": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "BrickColor",
|
"BrickColor": 1004
|
||||||
"Value": 1004
|
|
||||||
},
|
},
|
||||||
"ty": "BrickColor"
|
"ty": "BrickColor"
|
||||||
},
|
},
|
||||||
"CFrame": {
|
"CFrame": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "CFrame",
|
"CFrame": {
|
||||||
"Value": {
|
"position": [
|
||||||
"Position": [
|
|
||||||
1.0,
|
1.0,
|
||||||
2.0,
|
2.0,
|
||||||
3.0
|
3.0
|
||||||
],
|
],
|
||||||
"Orientation": [
|
"orientation": [
|
||||||
[
|
[
|
||||||
4.0,
|
4.0,
|
||||||
5.0,
|
5.0,
|
||||||
@@ -63,8 +58,7 @@
|
|||||||
},
|
},
|
||||||
"Color3": {
|
"Color3": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Color3",
|
"Color3": [
|
||||||
"Value": [
|
|
||||||
1.0,
|
1.0,
|
||||||
2.0,
|
2.0,
|
||||||
3.0
|
3.0
|
||||||
@@ -74,8 +68,7 @@
|
|||||||
},
|
},
|
||||||
"Color3uint8": {
|
"Color3uint8": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Color3uint8",
|
"Color3uint8": [
|
||||||
"Value": [
|
|
||||||
0,
|
0,
|
||||||
128,
|
128,
|
||||||
255
|
255
|
||||||
@@ -85,20 +78,19 @@
|
|||||||
},
|
},
|
||||||
"ColorSequence": {
|
"ColorSequence": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "ColorSequence",
|
"ColorSequence": {
|
||||||
"Value": {
|
"keypoints": [
|
||||||
"Keypoints": [
|
|
||||||
{
|
{
|
||||||
"Time": 0.0,
|
"time": 0.0,
|
||||||
"Color": [
|
"color": [
|
||||||
1.0,
|
1.0,
|
||||||
1.0,
|
1.0,
|
||||||
0.5
|
0.5
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Time": 1.0,
|
"time": 1.0,
|
||||||
"Color": [
|
"color": [
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
0.0
|
0.0
|
||||||
@@ -111,22 +103,19 @@
|
|||||||
},
|
},
|
||||||
"Content": {
|
"Content": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Content",
|
"Content": "rbxassetid://12345"
|
||||||
"Value": "rbxassetid://12345"
|
|
||||||
},
|
},
|
||||||
"ty": "Content"
|
"ty": "Content"
|
||||||
},
|
},
|
||||||
"Enum": {
|
"Enum": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Enum",
|
"Enum": 1234
|
||||||
"Value": 1234
|
|
||||||
},
|
},
|
||||||
"ty": "Enum"
|
"ty": "Enum"
|
||||||
},
|
},
|
||||||
"Faces": {
|
"Faces": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Faces",
|
"Faces": [
|
||||||
"Value": [
|
|
||||||
"Right",
|
"Right",
|
||||||
"Top",
|
"Top",
|
||||||
"Back",
|
"Back",
|
||||||
@@ -139,36 +128,31 @@
|
|||||||
},
|
},
|
||||||
"Float32": {
|
"Float32": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Float32",
|
"Float32": 15.0
|
||||||
"Value": 15.0
|
|
||||||
},
|
},
|
||||||
"ty": "Float32"
|
"ty": "Float32"
|
||||||
},
|
},
|
||||||
"Float64": {
|
"Float64": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Float64",
|
"Float64": 15123.0
|
||||||
"Value": 15123.0
|
|
||||||
},
|
},
|
||||||
"ty": "Float64"
|
"ty": "Float64"
|
||||||
},
|
},
|
||||||
"Int32": {
|
"Int32": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Int32",
|
"Int32": 6014
|
||||||
"Value": 6014
|
|
||||||
},
|
},
|
||||||
"ty": "Int32"
|
"ty": "Int32"
|
||||||
},
|
},
|
||||||
"Int64": {
|
"Int64": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Int64",
|
"Int64": 23491023
|
||||||
"Value": 23491023
|
|
||||||
},
|
},
|
||||||
"ty": "Int64"
|
"ty": "Int64"
|
||||||
},
|
},
|
||||||
"NumberRange": {
|
"NumberRange": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "NumberRange",
|
"NumberRange": [
|
||||||
"Value": [
|
|
||||||
-36.0,
|
-36.0,
|
||||||
94.0
|
94.0
|
||||||
]
|
]
|
||||||
@@ -177,18 +161,17 @@
|
|||||||
},
|
},
|
||||||
"NumberSequence": {
|
"NumberSequence": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "NumberSequence",
|
"NumberSequence": {
|
||||||
"Value": {
|
"keypoints": [
|
||||||
"Keypoints": [
|
|
||||||
{
|
{
|
||||||
"Time": 0.0,
|
"time": 0.0,
|
||||||
"Value": 5.0,
|
"value": 5.0,
|
||||||
"Envelope": 2.0
|
"envelope": 2.0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"Time": 1.0,
|
"time": 1.0,
|
||||||
"Value": 22.0,
|
"value": 22.0,
|
||||||
"Envelope": 0.0
|
"envelope": 0.0
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -197,34 +180,31 @@
|
|||||||
},
|
},
|
||||||
"PhysicalProperties-Custom": {
|
"PhysicalProperties-Custom": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "PhysicalProperties",
|
"PhysicalProperties": {
|
||||||
"Value": {
|
"density": 0.5,
|
||||||
"Density": 0.5,
|
"friction": 1.0,
|
||||||
"Friction": 1.0,
|
"elasticity": 0.0,
|
||||||
"Elasticity": 0.0,
|
"frictionWeight": 50.0,
|
||||||
"FrictionWeight": 50.0,
|
"elasticityWeight": 25.0
|
||||||
"ElasticityWeight": 25.0
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ty": "PhysicalProperties"
|
"ty": "PhysicalProperties"
|
||||||
},
|
},
|
||||||
"PhysicalProperties-Default": {
|
"PhysicalProperties-Default": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "PhysicalProperties",
|
"PhysicalProperties": "Default"
|
||||||
"Value": "Default"
|
|
||||||
},
|
},
|
||||||
"ty": "PhysicalProperties"
|
"ty": "PhysicalProperties"
|
||||||
},
|
},
|
||||||
"Ray": {
|
"Ray": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Ray",
|
"Ray": {
|
||||||
"Value": {
|
"origin": [
|
||||||
"Origin": [
|
|
||||||
1.0,
|
1.0,
|
||||||
2.0,
|
2.0,
|
||||||
3.0
|
3.0
|
||||||
],
|
],
|
||||||
"Direction": [
|
"direction": [
|
||||||
4.0,
|
4.0,
|
||||||
5.0,
|
5.0,
|
||||||
6.0
|
6.0
|
||||||
@@ -235,8 +215,7 @@
|
|||||||
},
|
},
|
||||||
"Rect": {
|
"Rect": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Rect",
|
"Rect": [
|
||||||
"Value": [
|
|
||||||
[
|
[
|
||||||
0.0,
|
0.0,
|
||||||
5.0
|
5.0
|
||||||
@@ -251,8 +230,7 @@
|
|||||||
},
|
},
|
||||||
"Region3int16": {
|
"Region3int16": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Region3int16",
|
"Region3int16": [
|
||||||
"Value": [
|
|
||||||
[
|
[
|
||||||
-10,
|
-10,
|
||||||
-5,
|
-5,
|
||||||
@@ -269,15 +247,23 @@
|
|||||||
},
|
},
|
||||||
"String": {
|
"String": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "String",
|
"String": "Hello, world!"
|
||||||
"Value": "Hello, world!"
|
|
||||||
},
|
},
|
||||||
"ty": "String"
|
"ty": "String"
|
||||||
},
|
},
|
||||||
|
"Tags": {
|
||||||
|
"value": {
|
||||||
|
"Tags": [
|
||||||
|
"foo",
|
||||||
|
"con'fusion?!",
|
||||||
|
"bar"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ty": "Tags"
|
||||||
|
},
|
||||||
"UDim": {
|
"UDim": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "UDim",
|
"UDim": [
|
||||||
"Value": [
|
|
||||||
1.0,
|
1.0,
|
||||||
32
|
32
|
||||||
]
|
]
|
||||||
@@ -286,8 +272,7 @@
|
|||||||
},
|
},
|
||||||
"UDim2": {
|
"UDim2": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "UDim2",
|
"UDim2": [
|
||||||
"Value": [
|
|
||||||
[
|
[
|
||||||
-1.0,
|
-1.0,
|
||||||
100
|
100
|
||||||
@@ -302,8 +287,7 @@
|
|||||||
},
|
},
|
||||||
"Vector2": {
|
"Vector2": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Vector2",
|
"Vector2": [
|
||||||
"Value": [
|
|
||||||
-50.0,
|
-50.0,
|
||||||
50.0
|
50.0
|
||||||
]
|
]
|
||||||
@@ -312,8 +296,7 @@
|
|||||||
},
|
},
|
||||||
"Vector2int16": {
|
"Vector2int16": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Vector2int16",
|
"Vector2int16": [
|
||||||
"Value": [
|
|
||||||
-300,
|
-300,
|
||||||
300
|
300
|
||||||
]
|
]
|
||||||
@@ -322,8 +305,7 @@
|
|||||||
},
|
},
|
||||||
"Vector3": {
|
"Vector3": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Vector3",
|
"Vector3": [
|
||||||
"Value": [
|
|
||||||
-300.0,
|
-300.0,
|
||||||
0.0,
|
0.0,
|
||||||
1500.0
|
1500.0
|
||||||
@@ -333,8 +315,7 @@
|
|||||||
},
|
},
|
||||||
"Vector3int16": {
|
"Vector3int16": {
|
||||||
"value": {
|
"value": {
|
||||||
"Type": "Vector3int16",
|
"Vector3int16": [
|
||||||
"Value": [
|
|
||||||
60,
|
60,
|
||||||
37,
|
37,
|
||||||
-450
|
-450
|
||||||
@@ -6,12 +6,10 @@ local CollectionService = game:GetService("CollectionService")
|
|||||||
return {
|
return {
|
||||||
Instance = {
|
Instance = {
|
||||||
Tags = {
|
Tags = {
|
||||||
read = function(instance, key)
|
read = function(instance)
|
||||||
local tagList = CollectionService:GetTags(instance)
|
return true, CollectionService:GetTags(instance)
|
||||||
|
|
||||||
return true, table.concat(tagList, "\0")
|
|
||||||
end,
|
end,
|
||||||
write = function(instance, key, value)
|
write = function(instance, _, value)
|
||||||
local existingTags = CollectionService:GetTags(instance)
|
local existingTags = CollectionService:GetTags(instance)
|
||||||
|
|
||||||
local unseenTags = {}
|
local unseenTags = {}
|
||||||
@@ -19,8 +17,7 @@ return {
|
|||||||
unseenTags[tag] = true
|
unseenTags[tag] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
local tagList = string.split(value, "\0")
|
for _, tag in ipairs(value) do
|
||||||
for _, tag in ipairs(tagList) do
|
|
||||||
unseenTags[tag] = nil
|
unseenTags[tag] = nil
|
||||||
CollectionService:AddTag(instance, tag)
|
CollectionService:AddTag(instance, tag)
|
||||||
end
|
end
|
||||||
@@ -44,4 +41,4 @@ return {
|
|||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -22,7 +22,7 @@ local function findCanonicalPropertyDescriptor(className, propertyName)
|
|||||||
local aliasData = propertyData.Kind.Alias
|
local aliasData = propertyData.Kind.Alias
|
||||||
if aliasData ~= nil then
|
if aliasData ~= nil then
|
||||||
return PropertyDescriptor.fromRaw(
|
return PropertyDescriptor.fromRaw(
|
||||||
currentClass.properties[aliasData.AliasFor],
|
currentClass.Properties[aliasData.AliasFor],
|
||||||
currentClassName,
|
currentClassName,
|
||||||
aliasData.AliasFor)
|
aliasData.AliasFor)
|
||||||
end
|
end
|
||||||
@@ -66,4 +66,4 @@ return {
|
|||||||
findCanonicalPropertyDescriptor = findCanonicalPropertyDescriptor,
|
findCanonicalPropertyDescriptor = findCanonicalPropertyDescriptor,
|
||||||
Error = Error,
|
Error = Error,
|
||||||
EncodedValue = require(script.EncodedValue),
|
EncodedValue = require(script.EncodedValue),
|
||||||
}
|
}
|
||||||
@@ -1 +0,0 @@
|
|||||||
require(game.ReplicatedStorage.TestEZ).TestBootstrap:run({game.ReplicatedStorage.RbxDom})
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
rojo build test-place.project.json -o TestPlace.rbxlx
|
|
||||||
run-in-roblox --script run-tests.lua --place TestPlace.rbxlx
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
{
|
|
||||||
"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": "run-tests.lua"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Players": {
|
|
||||||
"$className": "Players",
|
|
||||||
"$properties": {
|
|
||||||
"CharacterAutoLoads": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"HttpService": {
|
|
||||||
"$className": "HttpService",
|
|
||||||
"$properties": {
|
|
||||||
"HttpEnabled": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -14,6 +14,8 @@ local PORT_WIDTH = 74
|
|||||||
local DIVIDER_WIDTH = 1
|
local DIVIDER_WIDTH = 1
|
||||||
local HOST_OFFSET = 12
|
local HOST_OFFSET = 12
|
||||||
|
|
||||||
|
local lastHost, lastPort
|
||||||
|
|
||||||
local e = Roact.createElement
|
local e = Roact.createElement
|
||||||
|
|
||||||
local function AddressEntry(props)
|
local function AddressEntry(props)
|
||||||
@@ -24,7 +26,7 @@ local function AddressEntry(props)
|
|||||||
layoutOrder = props.layoutOrder,
|
layoutOrder = props.layoutOrder,
|
||||||
}, {
|
}, {
|
||||||
Host = e("TextBox", {
|
Host = e("TextBox", {
|
||||||
Text = "",
|
Text = lastHost or "",
|
||||||
Font = Enum.Font.Code,
|
Font = Enum.Font.Code,
|
||||||
TextSize = 18,
|
TextSize = 18,
|
||||||
TextColor3 = theme.AddressEntry.TextColor,
|
TextColor3 = theme.AddressEntry.TextColor,
|
||||||
@@ -43,7 +45,7 @@ local function AddressEntry(props)
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
Port = e("TextBox", {
|
Port = e("TextBox", {
|
||||||
Text = "",
|
Text = lastPort or "",
|
||||||
Font = Enum.Font.Code,
|
Font = Enum.Font.Code,
|
||||||
TextSize = 18,
|
TextSize = 18,
|
||||||
TextColor3 = theme.AddressEntry.TextColor,
|
TextColor3 = theme.AddressEntry.TextColor,
|
||||||
@@ -121,6 +123,9 @@ function NotConnectedPage:render()
|
|||||||
local hostText = self.hostRef.current.Text
|
local hostText = self.hostRef.current.Text
|
||||||
local portText = self.portRef.current.Text
|
local portText = self.portRef.current.Text
|
||||||
|
|
||||||
|
lastHost = hostText
|
||||||
|
lastPort = portText
|
||||||
|
|
||||||
self.props.onConnect(
|
self.props.onConnect(
|
||||||
#hostText > 0 and hostText or Config.defaultHost,
|
#hostText > 0 and hostText or Config.defaultHost,
|
||||||
#portText > 0 and portText or Config.defaultPort
|
#portText > 0 and portText or Config.defaultPort
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ function SettingsPage:render()
|
|||||||
TwoWaySync = e(Setting, {
|
TwoWaySync = e(Setting, {
|
||||||
id = "twoWaySync",
|
id = "twoWaySync",
|
||||||
name = "Two-Way Sync",
|
name = "Two-Way Sync",
|
||||||
description = "Editing files in Studio will sync them into the filesystem",
|
description = "EXPERIMENTAL! Editing files in Studio will sync them into the filesystem",
|
||||||
transparency = self.props.transparency,
|
transparency = self.props.transparency,
|
||||||
layoutOrder = 2,
|
layoutOrder = 2,
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ end
|
|||||||
local BRAND_COLOR = hexColor(0xE13835)
|
local BRAND_COLOR = hexColor(0xE13835)
|
||||||
|
|
||||||
local lightTheme = strict("LightTheme", {
|
local lightTheme = strict("LightTheme", {
|
||||||
BackgroundColor = hexColor(0xF0F0F0),
|
BackgroundColor = hexColor(0xFFFFFF),
|
||||||
Button = {
|
Button = {
|
||||||
Solid = {
|
Solid = {
|
||||||
ActionFillColor = hexColor(0xFFFFFF),
|
ActionFillColor = hexColor(0xFFFFFF),
|
||||||
@@ -67,7 +67,7 @@ local lightTheme = strict("LightTheme", {
|
|||||||
BackgroundColor = BRAND_COLOR,
|
BackgroundColor = BRAND_COLOR,
|
||||||
},
|
},
|
||||||
Inactive = {
|
Inactive = {
|
||||||
IconColor = hexColor(0xCACACA),
|
IconColor = hexColor(0xEEEEEE),
|
||||||
BorderColor = hexColor(0xAFAFAF),
|
BorderColor = hexColor(0xAFAFAF),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -77,11 +77,11 @@ local lightTheme = strict("LightTheme", {
|
|||||||
},
|
},
|
||||||
BorderedContainer = {
|
BorderedContainer = {
|
||||||
BorderColor = hexColor(0xCBCBCB),
|
BorderColor = hexColor(0xCBCBCB),
|
||||||
BackgroundColor = hexColor(0xE0E0E0),
|
BackgroundColor = hexColor(0xEEEEEE),
|
||||||
},
|
},
|
||||||
Spinner = {
|
Spinner = {
|
||||||
ForegroundColor = BRAND_COLOR,
|
ForegroundColor = BRAND_COLOR,
|
||||||
BackgroundColor = hexColor(0xE0E0E0),
|
BackgroundColor = hexColor(0xEEEEEE),
|
||||||
},
|
},
|
||||||
ConnectionDetails = {
|
ConnectionDetails = {
|
||||||
ProjectNameColor = hexColor(0x00000),
|
ProjectNameColor = hexColor(0x00000),
|
||||||
@@ -108,7 +108,7 @@ local lightTheme = strict("LightTheme", {
|
|||||||
})
|
})
|
||||||
|
|
||||||
local darkTheme = strict("DarkTheme", {
|
local darkTheme = strict("DarkTheme", {
|
||||||
BackgroundColor = hexColor(0x272727),
|
BackgroundColor = hexColor(0x2E2E2E),
|
||||||
Button = {
|
Button = {
|
||||||
Solid = {
|
Solid = {
|
||||||
ActionFillColor = hexColor(0xFFFFFF),
|
ActionFillColor = hexColor(0xFFFFFF),
|
||||||
@@ -147,15 +147,15 @@ local darkTheme = strict("DarkTheme", {
|
|||||||
},
|
},
|
||||||
AddressEntry = {
|
AddressEntry = {
|
||||||
TextColor = hexColor(0xFFFFFF),
|
TextColor = hexColor(0xFFFFFF),
|
||||||
PlaceholderColor = hexColor(0x717171)
|
PlaceholderColor = hexColor(0x8B8B8B)
|
||||||
},
|
},
|
||||||
BorderedContainer = {
|
BorderedContainer = {
|
||||||
BorderColor = hexColor(0x535353),
|
BorderColor = hexColor(0x535353),
|
||||||
BackgroundColor = hexColor(0x323232),
|
BackgroundColor = hexColor(0x2B2B2B),
|
||||||
},
|
},
|
||||||
Spinner = {
|
Spinner = {
|
||||||
ForegroundColor = BRAND_COLOR,
|
ForegroundColor = BRAND_COLOR,
|
||||||
BackgroundColor = hexColor(0x323232),
|
BackgroundColor = hexColor(0x2B2B2B),
|
||||||
},
|
},
|
||||||
ConnectionDetails = {
|
ConnectionDetails = {
|
||||||
ProjectNameColor = hexColor(0xFFFFFF),
|
ProjectNameColor = hexColor(0xFFFFFF),
|
||||||
@@ -236,4 +236,4 @@ return {
|
|||||||
StudioProvider = StudioProvider,
|
StudioProvider = StudioProvider,
|
||||||
Consumer = Context.Consumer,
|
Consumer = Context.Consumer,
|
||||||
with = with,
|
with = with,
|
||||||
}
|
}
|
||||||
|
|||||||
40
plugin/src/ChangeBatcher/createPatchSet.lua
Normal file
40
plugin/src/ChangeBatcher/createPatchSet.lua
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
--[[
|
||||||
|
Take an InstanceMap and a dictionary mapping instances to sets of property
|
||||||
|
names. Populate a patch with the encoded values of all the given properties
|
||||||
|
on all the given instances (or, if any changes set Parent to nil, removals
|
||||||
|
of instances) and return the patch.
|
||||||
|
]]
|
||||||
|
|
||||||
|
local Log = require(script.Parent.Parent.Parent.Log)
|
||||||
|
|
||||||
|
local PatchSet = require(script.Parent.Parent.PatchSet)
|
||||||
|
|
||||||
|
local encodePatchUpdate = require(script.Parent.encodePatchUpdate)
|
||||||
|
|
||||||
|
return function(instanceMap, propertyChanges)
|
||||||
|
local patch = PatchSet.newEmpty()
|
||||||
|
|
||||||
|
for instance, properties in pairs(propertyChanges) do
|
||||||
|
local instanceId = instanceMap.fromInstances[instance]
|
||||||
|
|
||||||
|
if instanceId == nil then
|
||||||
|
Log.warn("Ignoring change for instance {:?} as it is unknown to Rojo", instance)
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
if properties.Parent then
|
||||||
|
if instance.Parent == nil then
|
||||||
|
table.insert(patch.removed, instanceId)
|
||||||
|
else
|
||||||
|
Log.warn("Cannot sync non-nil Parent property changes yet")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local update = encodePatchUpdate(instance, instanceId, properties)
|
||||||
|
table.insert(patch.updated, update)
|
||||||
|
end
|
||||||
|
|
||||||
|
propertyChanges[instance] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return patch
|
||||||
|
end
|
||||||
74
plugin/src/ChangeBatcher/createPatchSet.spec.lua
Normal file
74
plugin/src/ChangeBatcher/createPatchSet.spec.lua
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
return function()
|
||||||
|
local PatchSet = require(script.Parent.Parent.PatchSet)
|
||||||
|
local InstanceMap = require(script.Parent.Parent.InstanceMap)
|
||||||
|
|
||||||
|
local createPatchSet = require(script.Parent.createPatchSet)
|
||||||
|
|
||||||
|
it("should return a patch", function()
|
||||||
|
local patch = createPatchSet(InstanceMap.new(), {})
|
||||||
|
|
||||||
|
assert(PatchSet.validate(patch))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should contain updates for every instance with property changes", function()
|
||||||
|
local instanceMap = InstanceMap.new()
|
||||||
|
|
||||||
|
local part1 = Instance.new("Part")
|
||||||
|
instanceMap:insert("PART_1", part1)
|
||||||
|
|
||||||
|
local part2 = Instance.new("Part")
|
||||||
|
instanceMap:insert("PART_2", part2)
|
||||||
|
|
||||||
|
local changes = {
|
||||||
|
[part1] = {
|
||||||
|
Position = true,
|
||||||
|
Size = true,
|
||||||
|
Color = true,
|
||||||
|
},
|
||||||
|
[part2] = {
|
||||||
|
CFrame = true,
|
||||||
|
Velocity = true,
|
||||||
|
Transparency = true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
local patch = createPatchSet(instanceMap, changes)
|
||||||
|
|
||||||
|
expect(#patch.updated).to.equal(2)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should not contain any updates for removed instances", function()
|
||||||
|
local instanceMap = InstanceMap.new()
|
||||||
|
|
||||||
|
local part1 = Instance.new("Part")
|
||||||
|
instanceMap:insert("PART_1", part1)
|
||||||
|
|
||||||
|
local changes = {
|
||||||
|
[part1] = {
|
||||||
|
Parent = true,
|
||||||
|
Position = true,
|
||||||
|
Size = true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
local patch = createPatchSet(instanceMap, changes)
|
||||||
|
|
||||||
|
expect(#patch.removed).to.equal(1)
|
||||||
|
expect(#patch.updated).to.equal(0)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should remove instances from the property change table", function()
|
||||||
|
local instanceMap = InstanceMap.new()
|
||||||
|
|
||||||
|
local part1 = Instance.new("Part")
|
||||||
|
instanceMap:insert("PART_1", part1)
|
||||||
|
|
||||||
|
local changes = {
|
||||||
|
[part1] = {},
|
||||||
|
}
|
||||||
|
|
||||||
|
createPatchSet(instanceMap, changes)
|
||||||
|
|
||||||
|
expect(next(changes)).to.equal(nil)
|
||||||
|
end)
|
||||||
|
end
|
||||||
39
plugin/src/ChangeBatcher/encodePatchUpdate.lua
Normal file
39
plugin/src/ChangeBatcher/encodePatchUpdate.lua
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
local Log = require(script.Parent.Parent.Parent.Log)
|
||||||
|
local RbxDom = require(script.Parent.Parent.Parent.RbxDom)
|
||||||
|
|
||||||
|
local encodeProperty = require(script.Parent.encodeProperty)
|
||||||
|
|
||||||
|
return function(instance, instanceId, properties)
|
||||||
|
local update = {
|
||||||
|
id = instanceId,
|
||||||
|
changedProperties = {},
|
||||||
|
}
|
||||||
|
|
||||||
|
for propertyName in pairs(properties) do
|
||||||
|
if propertyName == "Name" then
|
||||||
|
update.changedName = instance.Name
|
||||||
|
else
|
||||||
|
local descriptor = RbxDom.findCanonicalPropertyDescriptor(instance.ClassName, propertyName)
|
||||||
|
|
||||||
|
if not descriptor then
|
||||||
|
Log.debug("Could not sync back property {:?}.{}", instance, propertyName)
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
local encodeSuccess, encodeResult = encodeProperty(instance, propertyName, descriptor)
|
||||||
|
|
||||||
|
if not encodeSuccess then
|
||||||
|
Log.debug("Could not sync back property {:?}.{}: {}", instance, propertyName, encodeResult)
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
|
update.changedProperties[propertyName] = encodeResult
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if next(update.changedProperties) == nil and update.changedName == nil then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return update
|
||||||
|
end
|
||||||
62
plugin/src/ChangeBatcher/encodePatchUpdate.spec.lua
Normal file
62
plugin/src/ChangeBatcher/encodePatchUpdate.spec.lua
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
return function()
|
||||||
|
local encodePatchUpdate = require(script.Parent.encodePatchUpdate)
|
||||||
|
|
||||||
|
it("should return an update when there are property changes", function()
|
||||||
|
local part = Instance.new("Part")
|
||||||
|
local properties = {
|
||||||
|
CFrame = true,
|
||||||
|
Color = true,
|
||||||
|
}
|
||||||
|
local update = encodePatchUpdate(part, "PART", properties)
|
||||||
|
|
||||||
|
expect(update.id).to.equal("PART")
|
||||||
|
expect(update.changedProperties.CFrame).to.be.ok()
|
||||||
|
expect(update.changedProperties.Color).to.be.ok()
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should return nil when there are no property changes", function()
|
||||||
|
local part = Instance.new("Part")
|
||||||
|
local properties = {
|
||||||
|
NonExistentProperty = true,
|
||||||
|
}
|
||||||
|
local update = encodePatchUpdate(part, "PART", properties)
|
||||||
|
|
||||||
|
expect(update).to.equal(nil)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should set changedName in the update when the instance's Name changes", function()
|
||||||
|
local part = Instance.new("Part")
|
||||||
|
local properties = {
|
||||||
|
Name = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
part.Name = "We'reGettingToTheCoolPart"
|
||||||
|
|
||||||
|
local update = encodePatchUpdate(part, "PART", properties)
|
||||||
|
|
||||||
|
expect(update.changedName).to.equal("We'reGettingToTheCoolPart")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should correctly encode property values", function()
|
||||||
|
local part = Instance.new("Part")
|
||||||
|
local properties = {
|
||||||
|
Position = true,
|
||||||
|
Color = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
part.Position = Vector3.new(0, 100, 0)
|
||||||
|
part.Color = Color3.new(0.8, 0.2, 0.9)
|
||||||
|
|
||||||
|
local update = encodePatchUpdate(part, "PART", properties)
|
||||||
|
local position = update.changedProperties.Position
|
||||||
|
local color = update.changedProperties.Color
|
||||||
|
|
||||||
|
expect(position.Vector3[1]).to.equal(0)
|
||||||
|
expect(position.Vector3[2]).to.equal(100)
|
||||||
|
expect(position.Vector3[3]).to.equal(0)
|
||||||
|
|
||||||
|
expect(color.Color3[1]).to.be.near(0.8, 0.01)
|
||||||
|
expect(color.Color3[2]).to.be.near(0.2, 0.01)
|
||||||
|
expect(color.Color3[3]).to.be.near(0.9, 0.01)
|
||||||
|
end)
|
||||||
|
end
|
||||||
21
plugin/src/ChangeBatcher/encodeProperty.lua
Normal file
21
plugin/src/ChangeBatcher/encodeProperty.lua
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
local Log = require(script.Parent.Parent.Parent.Log)
|
||||||
|
local RbxDom = require(script.Parent.Parent.Parent.RbxDom)
|
||||||
|
|
||||||
|
return function(instance, propertyName, propertyDescriptor)
|
||||||
|
local readSuccess, readResult = propertyDescriptor:read(instance)
|
||||||
|
|
||||||
|
if not readSuccess then
|
||||||
|
Log.warn("Could not sync back property {:?}.{}: {}", instance, propertyName, readResult)
|
||||||
|
return false, nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local dataType = propertyDescriptor.dataType
|
||||||
|
local encodeSuccess, encodeResult = RbxDom.EncodedValue.encode(readResult, dataType)
|
||||||
|
|
||||||
|
if not encodeSuccess then
|
||||||
|
Log.warn("Could not sync back property {:?}.{}: {}", instance, propertyName, encodeResult)
|
||||||
|
return false, nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return true, encodeResult
|
||||||
|
end
|
||||||
81
plugin/src/ChangeBatcher/init.lua
Normal file
81
plugin/src/ChangeBatcher/init.lua
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
--[[
|
||||||
|
The ChangeBatcher is responsible for collecting and dispatching changes made
|
||||||
|
to tracked instances during two-way sync.
|
||||||
|
]]
|
||||||
|
|
||||||
|
local RunService = game:GetService("RunService")
|
||||||
|
|
||||||
|
local PatchSet = require(script.Parent.PatchSet)
|
||||||
|
|
||||||
|
local createPatchSet = require(script.createPatchSet)
|
||||||
|
|
||||||
|
local ChangeBatcher = {}
|
||||||
|
ChangeBatcher.__index = ChangeBatcher
|
||||||
|
|
||||||
|
local BATCH_INTERVAL = 0.2
|
||||||
|
|
||||||
|
function ChangeBatcher.new(instanceMap, onChangesFlushed)
|
||||||
|
local self
|
||||||
|
|
||||||
|
local renderSteppedConnection = RunService.RenderStepped:Connect(function(dt)
|
||||||
|
self:__cycle(dt)
|
||||||
|
end)
|
||||||
|
|
||||||
|
self = setmetatable({
|
||||||
|
__accumulator = 0,
|
||||||
|
__renderSteppedConnection = renderSteppedConnection,
|
||||||
|
__instanceMap = instanceMap,
|
||||||
|
__onChangesFlushed = onChangesFlushed,
|
||||||
|
__pendingPropertyChanges = {},
|
||||||
|
}, ChangeBatcher)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
function ChangeBatcher:stop()
|
||||||
|
self.__renderSteppedConnection:Disconnect()
|
||||||
|
self.__pendingPropertyChanges = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
function ChangeBatcher:add(instance, propertyName)
|
||||||
|
local properties = self.__pendingPropertyChanges[instance]
|
||||||
|
|
||||||
|
if not properties then
|
||||||
|
properties = {}
|
||||||
|
self.__pendingPropertyChanges[instance] = properties
|
||||||
|
end
|
||||||
|
|
||||||
|
properties[propertyName] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
function ChangeBatcher:__cycle(dt)
|
||||||
|
self.__accumulator += dt
|
||||||
|
|
||||||
|
if self.__accumulator >= BATCH_INTERVAL then
|
||||||
|
self.__accumulator -= BATCH_INTERVAL
|
||||||
|
|
||||||
|
local patch = self:__flush()
|
||||||
|
|
||||||
|
if patch then
|
||||||
|
self.__onChangesFlushed(patch)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self.__instanceMap:unpauseAllInstances()
|
||||||
|
end
|
||||||
|
|
||||||
|
function ChangeBatcher:__flush()
|
||||||
|
if next(self.__pendingPropertyChanges) == nil then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local patch = createPatchSet(self.__instanceMap, self.__pendingPropertyChanges)
|
||||||
|
|
||||||
|
if PatchSet.isEmpty(patch) then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return patch
|
||||||
|
end
|
||||||
|
|
||||||
|
return ChangeBatcher
|
||||||
101
plugin/src/ChangeBatcher/init.spec.lua
Normal file
101
plugin/src/ChangeBatcher/init.spec.lua
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
return function()
|
||||||
|
local ChangeBatcher = require(script.Parent)
|
||||||
|
local InstanceMap = require(script.Parent.Parent.InstanceMap)
|
||||||
|
local PatchSet = require(script.Parent.Parent.PatchSet)
|
||||||
|
|
||||||
|
local noop = function() end
|
||||||
|
|
||||||
|
describe("new", function()
|
||||||
|
it("should create a new ChangeBatcher", function()
|
||||||
|
local instanceMap = InstanceMap.new()
|
||||||
|
local changeBatcher = ChangeBatcher.new(instanceMap, noop)
|
||||||
|
|
||||||
|
expect(changeBatcher.__pendingPropertyChanges).to.be.a("table")
|
||||||
|
expect(next(changeBatcher.__pendingPropertyChanges)).to.equal(nil)
|
||||||
|
expect(changeBatcher.__onChangesFlushed).to.equal(noop)
|
||||||
|
expect(changeBatcher.__instanceMap).to.equal(instanceMap)
|
||||||
|
expect(typeof(changeBatcher.__renderSteppedConnection)).to.equal("RBXScriptConnection")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("stop", function()
|
||||||
|
it("should disconnect the RenderStepped connection", function()
|
||||||
|
local changeBatcher = ChangeBatcher.new(InstanceMap.new(), noop)
|
||||||
|
|
||||||
|
changeBatcher:stop()
|
||||||
|
|
||||||
|
expect(changeBatcher.__renderSteppedConnection.Connected).to.equal(false)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("add", function()
|
||||||
|
it("should add property changes to be considered for the current batch", function()
|
||||||
|
local instanceMap = InstanceMap.new()
|
||||||
|
local changeBatcher = ChangeBatcher.new(instanceMap, noop)
|
||||||
|
local part = Instance.new("Part")
|
||||||
|
|
||||||
|
instanceMap:insert("PART", part)
|
||||||
|
changeBatcher:add(part, "Name")
|
||||||
|
|
||||||
|
local properties = changeBatcher.__pendingPropertyChanges[part]
|
||||||
|
|
||||||
|
expect(properties).to.be.a("table")
|
||||||
|
expect(properties.Name).to.be.ok()
|
||||||
|
|
||||||
|
changeBatcher:add(part, "Position")
|
||||||
|
expect(properties.Position).to.be.ok()
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("__cycle", function()
|
||||||
|
it("should immediately unpause any paused instances after each cycle", function()
|
||||||
|
local instanceMap = InstanceMap.new()
|
||||||
|
local changeBatcher = ChangeBatcher.new(instanceMap, noop)
|
||||||
|
local part = Instance.new("Part")
|
||||||
|
|
||||||
|
instanceMap.pausedUpdateInstances[part] = true
|
||||||
|
|
||||||
|
changeBatcher:__cycle(0)
|
||||||
|
|
||||||
|
expect(instanceMap.pausedUpdateInstances[part]).to.equal(nil)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe("__flush", function()
|
||||||
|
it("should return nil when there are no changes to process", function()
|
||||||
|
local changeBatcher = ChangeBatcher.new(InstanceMap.new(), noop)
|
||||||
|
expect(changeBatcher:__flush()).to.equal(nil)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should return a patch when there are changes to process and the resulting patch is non-empty", function()
|
||||||
|
local instanceMap = InstanceMap.new()
|
||||||
|
local changeBatcher = ChangeBatcher.new(instanceMap, noop)
|
||||||
|
local part = Instance.new("Part")
|
||||||
|
|
||||||
|
instanceMap:insert("PART", part)
|
||||||
|
|
||||||
|
changeBatcher.__pendingPropertyChanges[part] = {
|
||||||
|
Position = true,
|
||||||
|
Name = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
local patch = changeBatcher:__flush()
|
||||||
|
|
||||||
|
assert(PatchSet.validate(patch))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should return nil when there are changes to process and the resulting patch is empty", function()
|
||||||
|
local instanceMap = InstanceMap.new()
|
||||||
|
local changeBatcher = ChangeBatcher.new(instanceMap, noop)
|
||||||
|
local part = Instance.new("Part")
|
||||||
|
|
||||||
|
instanceMap:insert("PART", part)
|
||||||
|
|
||||||
|
changeBatcher.__pendingPropertyChanges[part] = {
|
||||||
|
NonExistentProperty = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(changeBatcher:__flush()).to.equal(nil)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end
|
||||||
@@ -5,7 +5,7 @@ local isDevBuild = script.Parent.Parent:FindFirstChild("ROJO_DEV_BUILD") ~= nil
|
|||||||
return strict("Config", {
|
return strict("Config", {
|
||||||
isDevBuild = isDevBuild,
|
isDevBuild = isDevBuild,
|
||||||
codename = "Epiphany",
|
codename = "Epiphany",
|
||||||
version = {7, 0, 0, "-alpha.2"},
|
version = {7, 0, 0},
|
||||||
expectedServerVersionString = "7.0 or newer",
|
expectedServerVersionString = "7.0 or newer",
|
||||||
protocolVersion = 4,
|
protocolVersion = 4,
|
||||||
defaultHost = "localhost",
|
defaultHost = "localhost",
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
local RunService = game:GetService("RunService")
|
||||||
|
|
||||||
local Log = require(script.Parent.Parent.Log)
|
local Log = require(script.Parent.Parent.Log)
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
@@ -135,29 +137,31 @@ function InstanceMap:destroyId(id)
|
|||||||
end
|
end
|
||||||
|
|
||||||
--[[
|
--[[
|
||||||
Pause updates for an instance momentarily and invoke a callback.
|
Pause updates for an instance.
|
||||||
|
|
||||||
If the callback throws an error, InstanceMap will still be kept in a
|
|
||||||
consistent state.
|
|
||||||
]]
|
]]
|
||||||
function InstanceMap:pauseInstance(instance, callback)
|
function InstanceMap:pauseInstance(instance)
|
||||||
local id = self.fromInstances[instance]
|
local id = self.fromInstances[instance]
|
||||||
|
|
||||||
-- If we don't know about this instance, ignore it and do not invoke the
|
-- If we don't know about this instance, ignore it.
|
||||||
-- callback.
|
|
||||||
if id == nil then
|
if id == nil then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
self.pausedUpdateInstances[instance] = true
|
self.pausedUpdateInstances[instance] = true
|
||||||
local success, result = xpcall(callback, debug.traceback)
|
end
|
||||||
self.pausedUpdateInstances[instance] = false
|
|
||||||
|
|
||||||
if success then
|
--[[
|
||||||
return result
|
Unpause updates for an instance.
|
||||||
else
|
]]
|
||||||
error(result, 2)
|
function InstanceMap:unpauseInstance(instance)
|
||||||
end
|
self.pausedUpdateInstances[instance] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
Unpause updates for all instances.
|
||||||
|
]]
|
||||||
|
function InstanceMap:unpauseAllInstances()
|
||||||
|
table.clear(self.pausedUpdateInstances)
|
||||||
end
|
end
|
||||||
|
|
||||||
function InstanceMap:__connectSignals(instance)
|
function InstanceMap:__connectSignals(instance)
|
||||||
@@ -200,6 +204,12 @@ function InstanceMap:__maybeFireInstanceChanged(instance, propertyName)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if RunService:IsRunning() then
|
||||||
|
-- We probably don't want to pick up property changes to save to the
|
||||||
|
-- filesystem in a running game.
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
self.onInstanceChanged(instance, propertyName)
|
self.onInstanceChanged(instance, propertyName)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -222,4 +232,4 @@ function InstanceMap:__disconnectSignals(instance)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return InstanceMap
|
return InstanceMap
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ local function applyPatch(instanceMap, patch)
|
|||||||
local failedToReify = reify(instanceMap, patch.added, id, parentInstance)
|
local failedToReify = reify(instanceMap, patch.added, id, parentInstance)
|
||||||
|
|
||||||
if not PatchSet.isEmpty(failedToReify) then
|
if not PatchSet.isEmpty(failedToReify) then
|
||||||
Log.debug("Failed to reify as part of applying a patch: {}", failedToReify)
|
Log.debug("Failed to reify as part of applying a patch: {:#?}", failedToReify)
|
||||||
PatchSet.assign(unappliedPatch, failedToReify)
|
PatchSet.assign(unappliedPatch, failedToReify)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -77,6 +77,10 @@ local function applyPatch(instanceMap, patch)
|
|||||||
continue
|
continue
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Pause updates on this instance to avoid picking up our changes when
|
||||||
|
-- two-way sync is enabled.
|
||||||
|
instanceMap:pauseInstance(instance)
|
||||||
|
|
||||||
-- Track any part of this update that could not be applied.
|
-- Track any part of this update that could not be applied.
|
||||||
local unappliedUpdate = {
|
local unappliedUpdate = {
|
||||||
id = update.id,
|
id = update.id,
|
||||||
@@ -197,4 +201,4 @@ local function applyPatch(instanceMap, patch)
|
|||||||
return unappliedPatch
|
return unappliedPatch
|
||||||
end
|
end
|
||||||
|
|
||||||
return applyPatch
|
return applyPatch
|
||||||
|
|||||||
@@ -146,8 +146,7 @@ return function()
|
|||||||
id = "VALUE",
|
id = "VALUE",
|
||||||
changedProperties = {
|
changedProperties = {
|
||||||
Value = {
|
Value = {
|
||||||
Type = "String",
|
String = "WORLD",
|
||||||
Value = "WORLD",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -176,8 +175,7 @@ return function()
|
|||||||
changedClassName = "StringValue",
|
changedClassName = "StringValue",
|
||||||
changedProperties = {
|
changedProperties = {
|
||||||
Value = {
|
Value = {
|
||||||
Type = "String",
|
String = "I am Root",
|
||||||
Value = "I am Root",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -6,29 +6,31 @@
|
|||||||
local RbxDom = require(script.Parent.Parent.Parent.RbxDom)
|
local RbxDom = require(script.Parent.Parent.Parent.RbxDom)
|
||||||
local Error = require(script.Parent.Error)
|
local Error = require(script.Parent.Error)
|
||||||
|
|
||||||
local function decodeValue(virtualValue, instanceMap)
|
local function decodeValue(encodedValue, instanceMap)
|
||||||
|
local ty, value = next(encodedValue)
|
||||||
|
|
||||||
-- Refs are represented as IDs in the same space that Rojo's protocol uses.
|
-- Refs are represented as IDs in the same space that Rojo's protocol uses.
|
||||||
if virtualValue.Type == "Ref" then
|
if ty == "Ref" then
|
||||||
if virtualValue.Value == nil then
|
if value == "00000000000000000000000000000000" then
|
||||||
return true, nil
|
return true, nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local instance = instanceMap.fromIds[virtualValue.Value]
|
local instance = instanceMap.fromIds[value]
|
||||||
|
|
||||||
if instance ~= nil then
|
if instance ~= nil then
|
||||||
return true, instance
|
return true, instance
|
||||||
else
|
else
|
||||||
return false, Error.new(Error.RefDidNotExist, {
|
return false, Error.new(Error.RefDidNotExist, {
|
||||||
virtualValue = virtualValue,
|
encodedValue = encodedValue,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local ok, decodedValue = RbxDom.EncodedValue.decode(virtualValue)
|
local ok, decodedValue = RbxDom.EncodedValue.decode(encodedValue)
|
||||||
|
|
||||||
if not ok then
|
if not ok then
|
||||||
return false, Error.new(Error.CannotDecodeValue, {
|
return false, Error.new(Error.CannotDecodeValue, {
|
||||||
virtualValue = virtualValue,
|
encodedValue = encodedValue,
|
||||||
innerError = decodedValue,
|
innerError = decodedValue,
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -75,7 +75,13 @@ local function diff(instanceMap, virtualInstances, rootId)
|
|||||||
changedProperties[propertyName] = virtualValue
|
changedProperties[propertyName] = virtualValue
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
Log.warn("Failed to decode property of type {}", virtualValue.Type)
|
local propertyType = next(virtualValue)
|
||||||
|
Log.warn(
|
||||||
|
"Failed to decode property {}.{}. Encoded property was: {:#?}",
|
||||||
|
virtualInstance.ClassName,
|
||||||
|
propertyName,
|
||||||
|
virtualValue
|
||||||
|
)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
local err = existingValueOrErr
|
local err = existingValueOrErr
|
||||||
|
|||||||
@@ -80,8 +80,7 @@ return function()
|
|||||||
Name = "Value",
|
Name = "Value",
|
||||||
Properties = {
|
Properties = {
|
||||||
Value = {
|
Value = {
|
||||||
Type = "String",
|
String = "Hello, world!",
|
||||||
Value = "Hello, world!",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Children = {},
|
Children = {},
|
||||||
@@ -107,8 +106,9 @@ return function()
|
|||||||
|
|
||||||
local patchProperty = update.changedProperties["Value"]
|
local patchProperty = update.changedProperties["Value"]
|
||||||
expect(patchProperty).to.be.a("table")
|
expect(patchProperty).to.be.a("table")
|
||||||
expect(patchProperty.Type).to.equal("String")
|
local ty, value = next(patchProperty)
|
||||||
expect(patchProperty.Value).to.equal("Hello, world!")
|
expect(ty).to.equal("String")
|
||||||
|
expect(value).to.equal("Hello, world!")
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it("should generate an empty patch if no properties changed", function()
|
it("should generate an empty patch if no properties changed", function()
|
||||||
@@ -119,8 +119,7 @@ return function()
|
|||||||
Name = "Value",
|
Name = "Value",
|
||||||
Properties = {
|
Properties = {
|
||||||
Value = {
|
Value = {
|
||||||
Type = "String",
|
String = "Hello, world!",
|
||||||
Value = "Hello, world!",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Children = {},
|
Children = {},
|
||||||
@@ -145,8 +144,7 @@ return function()
|
|||||||
Name = "Folder",
|
Name = "Folder",
|
||||||
Properties = {
|
Properties = {
|
||||||
FAKE_PROPERTY = {
|
FAKE_PROPERTY = {
|
||||||
Type = "String",
|
String = "Hello, world!",
|
||||||
Value = "Hello, world!",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Children = {},
|
Children = {},
|
||||||
@@ -183,8 +181,7 @@ return function()
|
|||||||
-- heat_xml is a serialization-only property that is not
|
-- heat_xml is a serialization-only property that is not
|
||||||
-- exposed to Lua.
|
-- exposed to Lua.
|
||||||
heat_xml = {
|
heat_xml = {
|
||||||
Type = "Float32",
|
Float32 = 5,
|
||||||
Value = 5,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Children = {},
|
Children = {},
|
||||||
|
|||||||
@@ -40,6 +40,13 @@ local function getProperty(instance, propertyName)
|
|||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if err.kind == RbxDom.Error.Kind.Roblox and err.extra:find("is not a valid member of") then
|
||||||
|
return false, Error.new(Error.UnknownProperty, {
|
||||||
|
className = instance.ClassName,
|
||||||
|
propertyName = propertyName,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
return false, Error.new(Error.OtherPropertyError, {
|
return false, Error.new(Error.OtherPropertyError, {
|
||||||
className = instance.ClassName,
|
className = instance.ClassName,
|
||||||
propertyName = propertyName,
|
propertyName = propertyName,
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ function reifyInner(instanceMap, virtualInstances, id, parentInstance, unapplied
|
|||||||
for propertyName, virtualValue in pairs(virtualInstance.Properties) do
|
for propertyName, virtualValue in pairs(virtualInstance.Properties) do
|
||||||
-- Because refs may refer to instances that we haven't constructed yet,
|
-- Because refs may refer to instances that we haven't constructed yet,
|
||||||
-- we defer applying any ref properties until all instances are created.
|
-- we defer applying any ref properties until all instances are created.
|
||||||
if virtualValue.Type == "Ref" then
|
if next(virtualValue) == "Ref" then
|
||||||
table.insert(deferredRefs, {
|
table.insert(deferredRefs, {
|
||||||
id = id,
|
id = id,
|
||||||
instance = instance,
|
instance = instance,
|
||||||
@@ -136,23 +136,23 @@ function applyDeferredRefs(instanceMap, deferredRefs, unappliedPatch)
|
|||||||
end
|
end
|
||||||
|
|
||||||
for _, entry in ipairs(deferredRefs) do
|
for _, entry in ipairs(deferredRefs) do
|
||||||
local virtualValue = entry.virtualValue
|
local _, refId = next(entry.virtualValue)
|
||||||
|
|
||||||
if virtualValue.Value == nil then
|
if refId == nil then
|
||||||
continue
|
continue
|
||||||
end
|
end
|
||||||
|
|
||||||
local targetInstance = instanceMap.fromIds[virtualValue.Value]
|
local targetInstance = instanceMap.fromIds[refId]
|
||||||
if targetInstance == nil then
|
if targetInstance == nil then
|
||||||
markFailed(entry.id, entry.propertyName, virtualValue)
|
markFailed(entry.id, entry.propertyName, entry.virtualValue)
|
||||||
continue
|
continue
|
||||||
end
|
end
|
||||||
|
|
||||||
local ok = setProperty(entry.instance, entry.propertyName, targetInstance)
|
local ok = setProperty(entry.instance, entry.propertyName, targetInstance)
|
||||||
if not ok then
|
if not ok then
|
||||||
markFailed(entry.id, entry.propertyName, virtualValue)
|
markFailed(entry.id, entry.propertyName, entry.virtualValue)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return reify
|
return reify
|
||||||
|
|||||||
@@ -54,8 +54,7 @@ return function()
|
|||||||
Name = "Spaghetti",
|
Name = "Spaghetti",
|
||||||
Properties = {
|
Properties = {
|
||||||
Value = {
|
Value = {
|
||||||
Type = "String",
|
String = "Hello, world!",
|
||||||
Value = "Hello, world!",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Children = {},
|
Children = {},
|
||||||
@@ -191,8 +190,7 @@ return function()
|
|||||||
Name = "Child",
|
Name = "Child",
|
||||||
Properties = {
|
Properties = {
|
||||||
Value = {
|
Value = {
|
||||||
Type = "Ref",
|
Ref = "ROOT",
|
||||||
Value = "ROOT",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Children = {},
|
Children = {},
|
||||||
@@ -219,8 +217,7 @@ return function()
|
|||||||
Name = "Root",
|
Name = "Root",
|
||||||
Properties = {
|
Properties = {
|
||||||
Value = {
|
Value = {
|
||||||
Type = "Ref",
|
Ref = "EXISTING",
|
||||||
Value = "EXISTING",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Children = {},
|
Children = {},
|
||||||
@@ -258,8 +255,7 @@ return function()
|
|||||||
Name = "Child A",
|
Name = "Child A",
|
||||||
Properties = {
|
Properties = {
|
||||||
Value = {
|
Value = {
|
||||||
Type = "Ref",
|
Ref = "CHILD_B",
|
||||||
Value = "Child B",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Children = {},
|
Children = {},
|
||||||
@@ -291,15 +287,14 @@ return function()
|
|||||||
-- constructed as part of a recursive call before the parent has totally
|
-- constructed as part of a recursive call before the parent has totally
|
||||||
-- finished. Given deferred refs, this should not fail, but it is a good
|
-- finished. Given deferred refs, this should not fail, but it is a good
|
||||||
-- case to test.
|
-- case to test.
|
||||||
it("should apply properties containing refs to later siblings correctly", function()
|
it("should apply properties containing refs to later children correctly", function()
|
||||||
local virtualInstances = {
|
local virtualInstances = {
|
||||||
ROOT = {
|
ROOT = {
|
||||||
ClassName = "ObjectValue",
|
ClassName = "ObjectValue",
|
||||||
Name = "Root",
|
Name = "Root",
|
||||||
Properties = {
|
Properties = {
|
||||||
Value = {
|
Value = {
|
||||||
Type = "Ref",
|
Ref = "CHILD",
|
||||||
Value = "CHILD",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Children = {"CHILD"},
|
Children = {"CHILD"},
|
||||||
@@ -349,4 +344,4 @@ return function()
|
|||||||
expect(update.id).to.equal("ROOT")
|
expect(update.id).to.equal("ROOT")
|
||||||
expect(update.changedProperties.Value).to.equal(virtualInstances["ROOT"].Properties.Value)
|
expect(update.changedProperties.Value).to.equal(virtualInstances["ROOT"].Properties.Value)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
local StudioService = game:GetService("StudioService")
|
local StudioService = game:GetService("StudioService")
|
||||||
|
local RunService = game:GetService("RunService")
|
||||||
|
|
||||||
local Log = require(script.Parent.Parent.Log)
|
local Log = require(script.Parent.Parent.Log)
|
||||||
local Fmt = require(script.Parent.Parent.Fmt)
|
local Fmt = require(script.Parent.Parent.Fmt)
|
||||||
local t = require(script.Parent.Parent.t)
|
local t = require(script.Parent.Parent.t)
|
||||||
|
|
||||||
|
local ChangeBatcher = require(script.Parent.ChangeBatcher)
|
||||||
local InstanceMap = require(script.Parent.InstanceMap)
|
local InstanceMap = require(script.Parent.InstanceMap)
|
||||||
local PatchSet = require(script.Parent.PatchSet)
|
local PatchSet = require(script.Parent.PatchSet)
|
||||||
local Reconciler = require(script.Parent.Reconciler)
|
local Reconciler = require(script.Parent.Reconciler)
|
||||||
@@ -55,10 +57,19 @@ function ServeSession.new(options)
|
|||||||
-- Declare self ahead of time to capture it in a closure
|
-- Declare self ahead of time to capture it in a closure
|
||||||
local self
|
local self
|
||||||
local function onInstanceChanged(instance, propertyName)
|
local function onInstanceChanged(instance, propertyName)
|
||||||
self:__onInstanceChanged(instance, propertyName)
|
if not self.__twoWaySync then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
self.__changeBatcher:add(instance, propertyName)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function onChangesFlushed(patch)
|
||||||
|
self.__apiContext:write(patch)
|
||||||
end
|
end
|
||||||
|
|
||||||
local instanceMap = InstanceMap.new(onInstanceChanged)
|
local instanceMap = InstanceMap.new(onInstanceChanged)
|
||||||
|
local changeBatcher = ChangeBatcher.new(instanceMap, onChangesFlushed)
|
||||||
local reconciler = Reconciler.new(instanceMap)
|
local reconciler = Reconciler.new(instanceMap)
|
||||||
|
|
||||||
local connections = {}
|
local connections = {}
|
||||||
@@ -81,6 +92,7 @@ function ServeSession.new(options)
|
|||||||
__twoWaySync = options.twoWaySync,
|
__twoWaySync = options.twoWaySync,
|
||||||
__reconciler = reconciler,
|
__reconciler = reconciler,
|
||||||
__instanceMap = instanceMap,
|
__instanceMap = instanceMap,
|
||||||
|
__changeBatcher = changeBatcher,
|
||||||
__statusChangedCallback = nil,
|
__statusChangedCallback = nil,
|
||||||
__connections = connections,
|
__connections = connections,
|
||||||
}
|
}
|
||||||
@@ -111,6 +123,7 @@ function ServeSession:start()
|
|||||||
self.__apiContext:connect()
|
self.__apiContext:connect()
|
||||||
:andThen(function(serverInfo)
|
:andThen(function(serverInfo)
|
||||||
self:__setStatus(Status.Connected, serverInfo.projectName)
|
self:__setStatus(Status.Connected, serverInfo.projectName)
|
||||||
|
self:__applyGameAndPlaceId(serverInfo)
|
||||||
|
|
||||||
local rootInstanceId = serverInfo.rootInstanceId
|
local rootInstanceId = serverInfo.rootInstanceId
|
||||||
|
|
||||||
@@ -128,6 +141,16 @@ function ServeSession:stop()
|
|||||||
self:__stopInternal()
|
self:__stopInternal()
|
||||||
end
|
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)
|
function ServeSession:__onActiveScriptChanged(activeScript)
|
||||||
if not self.__openScriptsExternally then
|
if not self.__openScriptsExternally then
|
||||||
Log.trace("Not opening script {} because feature not enabled.", activeScript)
|
Log.trace("Not opening script {} because feature not enabled.", activeScript)
|
||||||
@@ -150,64 +173,23 @@ function ServeSession:__onActiveScriptChanged(activeScript)
|
|||||||
|
|
||||||
Log.debug("Trying to open script {} externally...", activeScript)
|
Log.debug("Trying to open script {} externally...", activeScript)
|
||||||
|
|
||||||
-- Force-close the script inside Studio
|
-- Force-close the script inside Studio... with a small delay in the middle
|
||||||
local existingParent = activeScript.Parent
|
-- to prevent Studio from crashing.
|
||||||
activeScript.Parent = nil
|
spawn(function()
|
||||||
activeScript.Parent = existingParent
|
local existingParent = activeScript.Parent
|
||||||
|
activeScript.Parent = nil
|
||||||
|
|
||||||
|
for i = 1, 3 do
|
||||||
|
RunService.Heartbeat:Wait()
|
||||||
|
end
|
||||||
|
|
||||||
|
activeScript.Parent = existingParent
|
||||||
|
end)
|
||||||
|
|
||||||
-- Notify the Rojo server to open this script
|
-- Notify the Rojo server to open this script
|
||||||
self.__apiContext:open(scriptId)
|
self.__apiContext:open(scriptId)
|
||||||
end
|
end
|
||||||
|
|
||||||
function ServeSession:__onInstanceChanged(instance, propertyName)
|
|
||||||
if not self.__twoWaySync then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local instanceId = self.__instanceMap.fromInstances[instance]
|
|
||||||
|
|
||||||
if instanceId == nil then
|
|
||||||
Log.warn("Ignoring change for instance {:?} as it is unknown to Rojo", instance)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local remove = nil
|
|
||||||
|
|
||||||
local update = {
|
|
||||||
id = instanceId,
|
|
||||||
changedProperties = {},
|
|
||||||
}
|
|
||||||
|
|
||||||
if propertyName == "Name" then
|
|
||||||
update.changedName = instance.Name
|
|
||||||
elseif propertyName == "Parent" then
|
|
||||||
if instance.Parent == nil then
|
|
||||||
update = nil
|
|
||||||
remove = instanceId
|
|
||||||
else
|
|
||||||
Log.warn("Cannot sync non-nil Parent property changes yet")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
else
|
|
||||||
local success, encoded = self.__reconciler:encodeApiValue(instance[propertyName])
|
|
||||||
|
|
||||||
if not success then
|
|
||||||
Log.warn("Could not sync back property {:?}.{}", instance, propertyName)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
update.changedProperties[propertyName] = encoded
|
|
||||||
end
|
|
||||||
|
|
||||||
local patch = {
|
|
||||||
removed = {remove},
|
|
||||||
added = {},
|
|
||||||
updated = {update},
|
|
||||||
}
|
|
||||||
|
|
||||||
self.__apiContext:write(patch)
|
|
||||||
end
|
|
||||||
|
|
||||||
function ServeSession:__initialSync(rootInstanceId)
|
function ServeSession:__initialSync(rootInstanceId)
|
||||||
return self.__apiContext:read({ rootInstanceId })
|
return self.__apiContext:read({ rootInstanceId })
|
||||||
:andThen(function(readResponseBody)
|
:andThen(function(readResponseBody)
|
||||||
@@ -270,6 +252,7 @@ function ServeSession:__stopInternal(err)
|
|||||||
self:__setStatus(Status.Disconnected, err)
|
self:__setStatus(Status.Disconnected, err)
|
||||||
self.__apiContext:disconnect()
|
self.__apiContext:disconnect()
|
||||||
self.__instanceMap:stop()
|
self.__instanceMap:stop()
|
||||||
|
self.__changeBatcher:stop()
|
||||||
|
|
||||||
for _, connection in ipairs(self.__connections) do
|
for _, connection in ipairs(self.__connections) do
|
||||||
connection:Disconnect()
|
connection:Disconnect()
|
||||||
@@ -285,4 +268,4 @@ function ServeSession:__setStatus(status, detail)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return ServeSession
|
return ServeSession
|
||||||
|
|||||||
@@ -5,10 +5,7 @@ local strict = require(script.Parent.strict)
|
|||||||
|
|
||||||
local RbxId = t.string
|
local RbxId = t.string
|
||||||
|
|
||||||
local ApiValue = t.interface({
|
local ApiValue = t.keys(t.string)
|
||||||
Type = t.string,
|
|
||||||
Value = t.optional(t.any),
|
|
||||||
})
|
|
||||||
|
|
||||||
local ApiInstanceMetadata = t.interface({
|
local ApiInstanceMetadata = t.interface({
|
||||||
ignoreUnknownInstances = t.optional(t.boolean),
|
ignoreUnknownInstances = t.optional(t.boolean),
|
||||||
@@ -96,4 +93,4 @@ return strict("Types", {
|
|||||||
VirtualInstance = ApiInstance,
|
VirtualInstance = ApiInstance,
|
||||||
VirtualMetadata = ApiInstanceMetadata,
|
VirtualMetadata = ApiInstanceMetadata,
|
||||||
VirtualValue = ApiValue,
|
VirtualValue = ApiValue,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
"TestEZ": {
|
"TestEZ": {
|
||||||
"$path": "modules/testez/lib"
|
"$path": "modules/testez"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -25,4 +25,4 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
source: tests/tests/build.rs
|
||||||
|
expression: contents
|
||||||
|
|
||||||
|
---
|
||||||
|
<roblox version="4">
|
||||||
|
<Item class="Folder" referent="0">
|
||||||
|
<Properties>
|
||||||
|
<string name="Name">root</string>
|
||||||
|
</Properties>
|
||||||
|
<Item class="Folder" referent="1">
|
||||||
|
<Properties>
|
||||||
|
<string name="Name">folder</string>
|
||||||
|
</Properties>
|
||||||
|
</Item>
|
||||||
|
</Item>
|
||||||
|
</roblox>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
---
|
||||||
|
source: tests/tests/build.rs
|
||||||
|
expression: contents
|
||||||
|
|
||||||
|
---
|
||||||
|
<roblox version="4">
|
||||||
|
<Item class="Folder" referent="0">
|
||||||
|
<Properties>
|
||||||
|
<string name="Name">root</string>
|
||||||
|
</Properties>
|
||||||
|
<Item class="Folder" referent="1">
|
||||||
|
<Properties>
|
||||||
|
<string name="Name">folder</string>
|
||||||
|
</Properties>
|
||||||
|
<Item class="Folder" referent="2">
|
||||||
|
<Properties>
|
||||||
|
<string name="Name">child-projectname</string>
|
||||||
|
</Properties>
|
||||||
|
</Item>
|
||||||
|
</Item>
|
||||||
|
</Item>
|
||||||
|
</roblox>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
source: tests/tests/build.rs
|
||||||
|
expression: contents
|
||||||
|
|
||||||
|
---
|
||||||
|
<roblox version="4">
|
||||||
|
<Item class="Folder" referent="0">
|
||||||
|
<Properties>
|
||||||
|
<string name="Name">root</string>
|
||||||
|
</Properties>
|
||||||
|
</Item>
|
||||||
|
</roblox>
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
source: tests/tests/build.rs
|
source: tests/tests/build.rs
|
||||||
expression: contents
|
expression: contents
|
||||||
|
|
||||||
---
|
---
|
||||||
<roblox version="4">
|
<roblox version="4">
|
||||||
<Item class="Folder" referent="0">
|
<Item class="Folder" referent="0">
|
||||||
@@ -25,14 +26,12 @@ expression: contents
|
|||||||
<R22>1</R22>
|
<R22>1</R22>
|
||||||
</CoordinateFrame>
|
</CoordinateFrame>
|
||||||
<Ref name="PrimaryPart">null</Ref>
|
<Ref name="PrimaryPart">null</Ref>
|
||||||
<BinaryString name="Tags">
|
<BinaryString name="Tags"></BinaryString>
|
||||||
</BinaryString>
|
|
||||||
</Properties>
|
</Properties>
|
||||||
<Item class="StringValue" referent="2">
|
<Item class="StringValue" referent="2">
|
||||||
<Properties>
|
<Properties>
|
||||||
<string name="Name">Cool StringValue</string>
|
<string name="Name">Cool StringValue</string>
|
||||||
<BinaryString name="Tags">
|
<BinaryString name="Tags"></BinaryString>
|
||||||
</BinaryString>
|
|
||||||
<string name="Value">Did you know that BaseValue.Changed is different than Instance.Changed?</string>
|
<string name="Value">Did you know that BaseValue.Changed is different than Instance.Changed?</string>
|
||||||
</Properties>
|
</Properties>
|
||||||
</Item>
|
</Item>
|
||||||
|
|||||||
@@ -1,27 +1,25 @@
|
|||||||
---
|
---
|
||||||
source: tests/tests/build.rs
|
source: tests/tests/build.rs
|
||||||
expression: contents
|
expression: contents
|
||||||
|
|
||||||
---
|
---
|
||||||
<roblox version="4">
|
<roblox version="4">
|
||||||
<Item class="Folder" referent="0">
|
<Item class="Folder" referent="0">
|
||||||
<Properties>
|
<Properties>
|
||||||
<string name="Name">rbxmx_ref</string>
|
<string name="Name">rbxmx_ref</string>
|
||||||
<BinaryString name="Tags">
|
<BinaryString name="Tags"></BinaryString>
|
||||||
</BinaryString>
|
|
||||||
</Properties>
|
</Properties>
|
||||||
<Item class="StringValue" referent="1">
|
<Item class="StringValue" referent="1">
|
||||||
<Properties>
|
<Properties>
|
||||||
<string name="Name">Target</string>
|
<string name="Name">Target</string>
|
||||||
<BinaryString name="Tags">
|
<BinaryString name="Tags"></BinaryString>
|
||||||
</BinaryString>
|
|
||||||
<string name="Value">Pointed to by ObjectValue</string>
|
<string name="Value">Pointed to by ObjectValue</string>
|
||||||
</Properties>
|
</Properties>
|
||||||
</Item>
|
</Item>
|
||||||
<Item class="ObjectValue" referent="2">
|
<Item class="ObjectValue" referent="2">
|
||||||
<Properties>
|
<Properties>
|
||||||
<string name="Name">Pointer</string>
|
<string name="Name">Pointer</string>
|
||||||
<BinaryString name="Tags">
|
<BinaryString name="Tags"></BinaryString>
|
||||||
</BinaryString>
|
|
||||||
<Ref name="Value">1</Ref>
|
<Ref name="Value">1</Ref>
|
||||||
</Properties>
|
</Properties>
|
||||||
</Item>
|
</Item>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
source: tests/tests/build.rs
|
source: tests/tests/build.rs
|
||||||
expression: contents
|
expression: contents
|
||||||
|
|
||||||
---
|
---
|
||||||
<roblox version="4">
|
<roblox version="4">
|
||||||
<Item class="DataModel" referent="0">
|
<Item class="DataModel" referent="0">
|
||||||
@@ -31,11 +32,21 @@ expression: contents
|
|||||||
<Item class="Part" referent="4">
|
<Item class="Part" referent="4">
|
||||||
<Properties>
|
<Properties>
|
||||||
<string name="Name">Color</string>
|
<string name="Name">Color</string>
|
||||||
<Color3 name="Color3uint8">
|
<CoordinateFrame name="CFrame">
|
||||||
<R>0.5</R>
|
<X>1</X>
|
||||||
<G>0.25</G>
|
<Y>2</Y>
|
||||||
<B>0</B>
|
<Z>3</Z>
|
||||||
</Color3>
|
<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>
|
</Properties>
|
||||||
</Item>
|
</Item>
|
||||||
<Item class="NumberValue" referent="5">
|
<Item class="NumberValue" referent="5">
|
||||||
|
|||||||
@@ -0,0 +1,230 @@
|
|||||||
|
---
|
||||||
|
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>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"name": "root",
|
||||||
|
"tree": {
|
||||||
|
"$className": "Folder",
|
||||||
|
"folder": {
|
||||||
|
"$path": "folder"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "child-projectname",
|
||||||
|
"tree": {
|
||||||
|
"$className": "Folder"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"name": "root",
|
||||||
|
"tree": {
|
||||||
|
"$className": "Folder",
|
||||||
|
"folder": {
|
||||||
|
"$path": "folder"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "child-projectname",
|
||||||
|
"tree": {
|
||||||
|
"$className": "Folder"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "root",
|
||||||
|
"tree": {
|
||||||
|
"$className": "Folder"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,7 +14,13 @@
|
|||||||
"Color": {
|
"Color": {
|
||||||
"$className": "Part",
|
"$className": "Part",
|
||||||
"$properties": {
|
"$properties": {
|
||||||
"Color": [0.5, 0.25, 0]
|
"Color": [0.5, 0.25, 0],
|
||||||
|
"CFrame": [
|
||||||
|
1, 2, 3,
|
||||||
|
0, 1, 0,
|
||||||
|
0, 0, 1,
|
||||||
|
1, 0, 0
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "weldconstraint",
|
||||||
|
"tree": {
|
||||||
|
"$path": "two-parts-welded.rbxmx"
|
||||||
|
}
|
||||||
|
}
|
||||||
224
rojo-test/build-tests/weldconstraint/two-parts-welded.rbxmx
Normal file
224
rojo-test/build-tests/weldconstraint/two-parts-welded.rbxmx
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
<roblox xmlns:xmime="http://www.w3.org/2005/05/xmlmime" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.roblox.com/roblox.xsd" version="4">
|
||||||
|
<Meta name="ExplicitAutoJoints">true</Meta>
|
||||||
|
<External>null</External>
|
||||||
|
<External>nil</External>
|
||||||
|
<Item class="Folder" referent="RBX1959D8B589424CFD943B349BB8DB0A3B">
|
||||||
|
<Properties>
|
||||||
|
<BinaryString name="AttributesSerialize"></BinaryString>
|
||||||
|
<string name="Name">Folder</string>
|
||||||
|
<int64 name="SourceAssetId">-1</int64>
|
||||||
|
<BinaryString name="Tags"></BinaryString>
|
||||||
|
</Properties>
|
||||||
|
<Item class="Part" referent="RBX15D09A13EACB4A6D96E75739B60CB129">
|
||||||
|
<Properties>
|
||||||
|
<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">4288914085</Color3uint8>
|
||||||
|
<PhysicalProperties name="CustomPhysicalProperties">
|
||||||
|
<CustomPhysics>false</CustomPhysics>
|
||||||
|
</PhysicalProperties>
|
||||||
|
<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>
|
||||||
|
<string name="Name">A</string>
|
||||||
|
<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>
|
||||||
|
<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>
|
||||||
|
<token name="formFactorRaw">1</token>
|
||||||
|
<token name="shape">1</token>
|
||||||
|
<Vector3 name="size">
|
||||||
|
<X>4</X>
|
||||||
|
<Y>1</Y>
|
||||||
|
<Z>2</Z>
|
||||||
|
</Vector3>
|
||||||
|
</Properties>
|
||||||
|
<Item class="WeldConstraint" referent="RBXD0337E67C9F1411681C2FEC8CA324E6F">
|
||||||
|
<Properties>
|
||||||
|
<BinaryString name="AttributesSerialize"></BinaryString>
|
||||||
|
<CoordinateFrame name="CFrame0">
|
||||||
|
<X>7</X>
|
||||||
|
<Y>1.01327896e-06</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>
|
||||||
|
<string name="Name">WeldConstraint</string>
|
||||||
|
<Ref name="Part0Internal">RBX15D09A13EACB4A6D96E75739B60CB129</Ref>
|
||||||
|
<Ref name="Part1Internal">RBX308EE5932F7A492685067C0B84AA3DAF</Ref>
|
||||||
|
<int64 name="SourceAssetId">-1</int64>
|
||||||
|
<int name="State">3</int>
|
||||||
|
<BinaryString name="Tags"></BinaryString>
|
||||||
|
</Properties>
|
||||||
|
</Item>
|
||||||
|
</Item>
|
||||||
|
<Item class="Part" referent="RBX308EE5932F7A492685067C0B84AA3DAF">
|
||||||
|
<Properties>
|
||||||
|
<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.500001013</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">4288914085</Color3uint8>
|
||||||
|
<PhysicalProperties name="CustomPhysicalProperties">
|
||||||
|
<CustomPhysics>false</CustomPhysics>
|
||||||
|
</PhysicalProperties>
|
||||||
|
<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>
|
||||||
|
<string name="Name">B</string>
|
||||||
|
<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>
|
||||||
|
<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>
|
||||||
|
<token name="formFactorRaw">1</token>
|
||||||
|
<token name="shape">1</token>
|
||||||
|
<Vector3 name="size">
|
||||||
|
<X>4</X>
|
||||||
|
<Y>1</Y>
|
||||||
|
<Z>2</Z>
|
||||||
|
</Vector3>
|
||||||
|
</Properties>
|
||||||
|
</Item>
|
||||||
|
</Item>
|
||||||
|
</roblox>
|
||||||
@@ -4,6 +4,8 @@ expression: redactions.redacted_yaml(info)
|
|||||||
|
|
||||||
---
|
---
|
||||||
expectedPlaceIds: ~
|
expectedPlaceIds: ~
|
||||||
|
gameId: ~
|
||||||
|
placeId: ~
|
||||||
projectName: add_folder
|
projectName: add_folder
|
||||||
protocolVersion: 4
|
protocolVersion: 4
|
||||||
rootInstanceId: id-2
|
rootInstanceId: id-2
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
source: tests/tests/serve.rs
|
source: tests/tests/serve.rs
|
||||||
expression: "read_response.intern_and_redact(&mut redactions, root_id)"
|
expression: "read_response.intern_and_redact(&mut redactions, root_id)"
|
||||||
|
|
||||||
---
|
---
|
||||||
instances:
|
instances:
|
||||||
id-2:
|
id-2:
|
||||||
@@ -13,7 +14,7 @@ instances:
|
|||||||
Parent: "00000000000000000000000000000000"
|
Parent: "00000000000000000000000000000000"
|
||||||
Properties:
|
Properties:
|
||||||
Source:
|
Source:
|
||||||
Type: String
|
String: "-- Edited contents"
|
||||||
Value: "-- Edited contents"
|
|
||||||
messageCursor: 1
|
messageCursor: 1
|
||||||
sessionId: id-1
|
sessionId: id-1
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
source: tests/tests/serve.rs
|
source: tests/tests/serve.rs
|
||||||
expression: "read_response.intern_and_redact(&mut redactions, root_id)"
|
expression: "read_response.intern_and_redact(&mut redactions, root_id)"
|
||||||
|
|
||||||
---
|
---
|
||||||
instances:
|
instances:
|
||||||
id-2:
|
id-2:
|
||||||
@@ -13,7 +14,7 @@ instances:
|
|||||||
Parent: "00000000000000000000000000000000"
|
Parent: "00000000000000000000000000000000"
|
||||||
Properties:
|
Properties:
|
||||||
Source:
|
Source:
|
||||||
Type: String
|
String: "-- Original contents"
|
||||||
Value: "-- Original contents"
|
|
||||||
messageCursor: 0
|
messageCursor: 0
|
||||||
sessionId: id-1
|
sessionId: id-1
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ expression: redactions.redacted_yaml(info)
|
|||||||
|
|
||||||
---
|
---
|
||||||
expectedPlaceIds: ~
|
expectedPlaceIds: ~
|
||||||
|
gameId: ~
|
||||||
|
placeId: ~
|
||||||
projectName: edit_init
|
projectName: edit_init
|
||||||
protocolVersion: 4
|
protocolVersion: 4
|
||||||
rootInstanceId: id-2
|
rootInstanceId: id-2
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
source: tests/tests/serve.rs
|
source: tests/tests/serve.rs
|
||||||
expression: "subscribe_response.intern_and_redact(&mut redactions, ())"
|
expression: "subscribe_response.intern_and_redact(&mut redactions, ())"
|
||||||
|
|
||||||
---
|
---
|
||||||
messageCursor: 1
|
messageCursor: 1
|
||||||
messages:
|
messages:
|
||||||
@@ -12,7 +13,7 @@ messages:
|
|||||||
changedName: ~
|
changedName: ~
|
||||||
changedProperties:
|
changedProperties:
|
||||||
Source:
|
Source:
|
||||||
Type: String
|
String: "-- Edited contents"
|
||||||
Value: "-- Edited contents"
|
|
||||||
id: id-2
|
id: id-2
|
||||||
sessionId: id-1
|
sessionId: id-1
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ expression: redactions.redacted_yaml(info)
|
|||||||
|
|
||||||
---
|
---
|
||||||
expectedPlaceIds: ~
|
expectedPlaceIds: ~
|
||||||
|
gameId: ~
|
||||||
|
placeId: ~
|
||||||
projectName: empty
|
projectName: empty
|
||||||
protocolVersion: 4
|
protocolVersion: 4
|
||||||
rootInstanceId: id-2
|
rootInstanceId: id-2
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
---
|
||||||
|
source: tests/tests/serve.rs
|
||||||
|
expression: "read_response.intern_and_redact(&mut redactions, root_id)"
|
||||||
|
|
||||||
|
---
|
||||||
|
instances:
|
||||||
|
id-2:
|
||||||
|
Children:
|
||||||
|
- id-3
|
||||||
|
ClassName: Folder
|
||||||
|
Id: id-2
|
||||||
|
Metadata:
|
||||||
|
ignoreUnknownInstances: false
|
||||||
|
Name: empty_folder
|
||||||
|
Parent: "00000000000000000000000000000000"
|
||||||
|
Properties: {}
|
||||||
|
id-3:
|
||||||
|
Children: []
|
||||||
|
ClassName: Model
|
||||||
|
Id: id-3
|
||||||
|
Metadata:
|
||||||
|
ignoreUnknownInstances: false
|
||||||
|
Name: test
|
||||||
|
Parent: id-2
|
||||||
|
Properties: {}
|
||||||
|
messageCursor: 1
|
||||||
|
sessionId: id-1
|
||||||
|
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
source: tests/tests/serve.rs
|
||||||
|
expression: "read_response.intern_and_redact(&mut redactions, root_id)"
|
||||||
|
|
||||||
|
---
|
||||||
|
instances:
|
||||||
|
id-2:
|
||||||
|
Children: []
|
||||||
|
ClassName: Folder
|
||||||
|
Id: id-2
|
||||||
|
Metadata:
|
||||||
|
ignoreUnknownInstances: false
|
||||||
|
Name: empty_folder
|
||||||
|
Parent: "00000000000000000000000000000000"
|
||||||
|
Properties: {}
|
||||||
|
messageCursor: 0
|
||||||
|
sessionId: id-1
|
||||||
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
source: tests/tests/serve.rs
|
||||||
|
expression: redactions.redacted_yaml(info)
|
||||||
|
|
||||||
|
---
|
||||||
|
expectedPlaceIds: ~
|
||||||
|
gameId: ~
|
||||||
|
placeId: ~
|
||||||
|
projectName: empty_folder
|
||||||
|
protocolVersion: 4
|
||||||
|
rootInstanceId: id-2
|
||||||
|
serverVersion: "[server-version]"
|
||||||
|
sessionId: id-1
|
||||||
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
source: tests/tests/serve.rs
|
||||||
|
expression: "subscribe_response.intern_and_redact(&mut redactions, ())"
|
||||||
|
|
||||||
|
---
|
||||||
|
messageCursor: 1
|
||||||
|
messages:
|
||||||
|
- added:
|
||||||
|
id-3:
|
||||||
|
Children: []
|
||||||
|
ClassName: Model
|
||||||
|
Id: id-3
|
||||||
|
Metadata:
|
||||||
|
ignoreUnknownInstances: false
|
||||||
|
Name: test
|
||||||
|
Parent: id-2
|
||||||
|
Properties: {}
|
||||||
|
removed: []
|
||||||
|
updated: []
|
||||||
|
sessionId: id-1
|
||||||
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
source: tests/tests/serve.rs
|
source: tests/tests/serve.rs
|
||||||
expression: "read_response.intern_and_redact(&mut redactions, root_id)"
|
expression: "read_response.intern_and_redact(&mut redactions, root_id)"
|
||||||
|
|
||||||
---
|
---
|
||||||
instances:
|
instances:
|
||||||
id-10:
|
id-10:
|
||||||
@@ -13,8 +14,7 @@ instances:
|
|||||||
Parent: id-3
|
Parent: id-3
|
||||||
Properties:
|
Properties:
|
||||||
Value:
|
Value:
|
||||||
Type: String
|
String: "File #6"
|
||||||
Value: "File #6"
|
|
||||||
id-11:
|
id-11:
|
||||||
Children: []
|
Children: []
|
||||||
ClassName: StringValue
|
ClassName: StringValue
|
||||||
@@ -25,8 +25,7 @@ instances:
|
|||||||
Parent: id-3
|
Parent: id-3
|
||||||
Properties:
|
Properties:
|
||||||
Value:
|
Value:
|
||||||
Type: String
|
String: "File #7"
|
||||||
Value: "File #7"
|
|
||||||
id-12:
|
id-12:
|
||||||
Children: []
|
Children: []
|
||||||
ClassName: StringValue
|
ClassName: StringValue
|
||||||
@@ -37,8 +36,7 @@ instances:
|
|||||||
Parent: id-3
|
Parent: id-3
|
||||||
Properties:
|
Properties:
|
||||||
Value:
|
Value:
|
||||||
Type: String
|
String: "File #8"
|
||||||
Value: "File #8"
|
|
||||||
id-13:
|
id-13:
|
||||||
Children: []
|
Children: []
|
||||||
ClassName: StringValue
|
ClassName: StringValue
|
||||||
@@ -49,8 +47,7 @@ instances:
|
|||||||
Parent: id-3
|
Parent: id-3
|
||||||
Properties:
|
Properties:
|
||||||
Value:
|
Value:
|
||||||
Type: String
|
String: "File #9"
|
||||||
Value: "File #9"
|
|
||||||
id-2:
|
id-2:
|
||||||
Children:
|
Children:
|
||||||
- id-3
|
- id-3
|
||||||
@@ -90,8 +87,7 @@ instances:
|
|||||||
Parent: id-3
|
Parent: id-3
|
||||||
Properties:
|
Properties:
|
||||||
Value:
|
Value:
|
||||||
Type: String
|
String: "File #0"
|
||||||
Value: "File #0"
|
|
||||||
id-5:
|
id-5:
|
||||||
Children: []
|
Children: []
|
||||||
ClassName: StringValue
|
ClassName: StringValue
|
||||||
@@ -102,8 +98,7 @@ instances:
|
|||||||
Parent: id-3
|
Parent: id-3
|
||||||
Properties:
|
Properties:
|
||||||
Value:
|
Value:
|
||||||
Type: String
|
String: "File #1"
|
||||||
Value: "File #1"
|
|
||||||
id-6:
|
id-6:
|
||||||
Children: []
|
Children: []
|
||||||
ClassName: StringValue
|
ClassName: StringValue
|
||||||
@@ -114,8 +109,7 @@ instances:
|
|||||||
Parent: id-3
|
Parent: id-3
|
||||||
Properties:
|
Properties:
|
||||||
Value:
|
Value:
|
||||||
Type: String
|
String: "File #2"
|
||||||
Value: "File #2"
|
|
||||||
id-7:
|
id-7:
|
||||||
Children: []
|
Children: []
|
||||||
ClassName: StringValue
|
ClassName: StringValue
|
||||||
@@ -126,8 +120,7 @@ instances:
|
|||||||
Parent: id-3
|
Parent: id-3
|
||||||
Properties:
|
Properties:
|
||||||
Value:
|
Value:
|
||||||
Type: String
|
String: "File #3"
|
||||||
Value: "File #3"
|
|
||||||
id-8:
|
id-8:
|
||||||
Children: []
|
Children: []
|
||||||
ClassName: StringValue
|
ClassName: StringValue
|
||||||
@@ -138,8 +131,7 @@ instances:
|
|||||||
Parent: id-3
|
Parent: id-3
|
||||||
Properties:
|
Properties:
|
||||||
Value:
|
Value:
|
||||||
Type: String
|
String: "File #4"
|
||||||
Value: "File #4"
|
|
||||||
id-9:
|
id-9:
|
||||||
Children: []
|
Children: []
|
||||||
ClassName: StringValue
|
ClassName: StringValue
|
||||||
@@ -150,7 +142,7 @@ instances:
|
|||||||
Parent: id-3
|
Parent: id-3
|
||||||
Properties:
|
Properties:
|
||||||
Value:
|
Value:
|
||||||
Type: String
|
String: "File #5"
|
||||||
Value: "File #5"
|
|
||||||
messageCursor: 1
|
messageCursor: 1
|
||||||
sessionId: id-1
|
sessionId: id-1
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ expression: redactions.redacted_yaml(info)
|
|||||||
|
|
||||||
---
|
---
|
||||||
expectedPlaceIds: ~
|
expectedPlaceIds: ~
|
||||||
|
gameId: ~
|
||||||
|
placeId: ~
|
||||||
projectName: move_folder_of_stuff
|
projectName: move_folder_of_stuff
|
||||||
protocolVersion: 4
|
protocolVersion: 4
|
||||||
rootInstanceId: id-2
|
rootInstanceId: id-2
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
source: tests/tests/serve.rs
|
source: tests/tests/serve.rs
|
||||||
expression: "subscribe_response.intern_and_redact(&mut redactions, ())"
|
expression: "subscribe_response.intern_and_redact(&mut redactions, ())"
|
||||||
|
|
||||||
---
|
---
|
||||||
messageCursor: 1
|
messageCursor: 1
|
||||||
messages:
|
messages:
|
||||||
@@ -15,8 +16,7 @@ messages:
|
|||||||
Parent: id-3
|
Parent: id-3
|
||||||
Properties:
|
Properties:
|
||||||
Value:
|
Value:
|
||||||
Type: String
|
String: "File #6"
|
||||||
Value: "File #6"
|
|
||||||
id-11:
|
id-11:
|
||||||
Children: []
|
Children: []
|
||||||
ClassName: StringValue
|
ClassName: StringValue
|
||||||
@@ -27,8 +27,7 @@ messages:
|
|||||||
Parent: id-3
|
Parent: id-3
|
||||||
Properties:
|
Properties:
|
||||||
Value:
|
Value:
|
||||||
Type: String
|
String: "File #7"
|
||||||
Value: "File #7"
|
|
||||||
id-12:
|
id-12:
|
||||||
Children: []
|
Children: []
|
||||||
ClassName: StringValue
|
ClassName: StringValue
|
||||||
@@ -39,8 +38,7 @@ messages:
|
|||||||
Parent: id-3
|
Parent: id-3
|
||||||
Properties:
|
Properties:
|
||||||
Value:
|
Value:
|
||||||
Type: String
|
String: "File #8"
|
||||||
Value: "File #8"
|
|
||||||
id-13:
|
id-13:
|
||||||
Children: []
|
Children: []
|
||||||
ClassName: StringValue
|
ClassName: StringValue
|
||||||
@@ -51,8 +49,7 @@ messages:
|
|||||||
Parent: id-3
|
Parent: id-3
|
||||||
Properties:
|
Properties:
|
||||||
Value:
|
Value:
|
||||||
Type: String
|
String: "File #9"
|
||||||
Value: "File #9"
|
|
||||||
id-3:
|
id-3:
|
||||||
Children:
|
Children:
|
||||||
- id-4
|
- id-4
|
||||||
@@ -82,8 +79,7 @@ messages:
|
|||||||
Parent: id-3
|
Parent: id-3
|
||||||
Properties:
|
Properties:
|
||||||
Value:
|
Value:
|
||||||
Type: String
|
String: "File #0"
|
||||||
Value: "File #0"
|
|
||||||
id-5:
|
id-5:
|
||||||
Children: []
|
Children: []
|
||||||
ClassName: StringValue
|
ClassName: StringValue
|
||||||
@@ -94,8 +90,7 @@ messages:
|
|||||||
Parent: id-3
|
Parent: id-3
|
||||||
Properties:
|
Properties:
|
||||||
Value:
|
Value:
|
||||||
Type: String
|
String: "File #1"
|
||||||
Value: "File #1"
|
|
||||||
id-6:
|
id-6:
|
||||||
Children: []
|
Children: []
|
||||||
ClassName: StringValue
|
ClassName: StringValue
|
||||||
@@ -106,8 +101,7 @@ messages:
|
|||||||
Parent: id-3
|
Parent: id-3
|
||||||
Properties:
|
Properties:
|
||||||
Value:
|
Value:
|
||||||
Type: String
|
String: "File #2"
|
||||||
Value: "File #2"
|
|
||||||
id-7:
|
id-7:
|
||||||
Children: []
|
Children: []
|
||||||
ClassName: StringValue
|
ClassName: StringValue
|
||||||
@@ -118,8 +112,7 @@ messages:
|
|||||||
Parent: id-3
|
Parent: id-3
|
||||||
Properties:
|
Properties:
|
||||||
Value:
|
Value:
|
||||||
Type: String
|
String: "File #3"
|
||||||
Value: "File #3"
|
|
||||||
id-8:
|
id-8:
|
||||||
Children: []
|
Children: []
|
||||||
ClassName: StringValue
|
ClassName: StringValue
|
||||||
@@ -130,8 +123,7 @@ messages:
|
|||||||
Parent: id-3
|
Parent: id-3
|
||||||
Properties:
|
Properties:
|
||||||
Value:
|
Value:
|
||||||
Type: String
|
String: "File #4"
|
||||||
Value: "File #4"
|
|
||||||
id-9:
|
id-9:
|
||||||
Children: []
|
Children: []
|
||||||
ClassName: StringValue
|
ClassName: StringValue
|
||||||
@@ -142,8 +134,8 @@ messages:
|
|||||||
Parent: id-3
|
Parent: id-3
|
||||||
Properties:
|
Properties:
|
||||||
Value:
|
Value:
|
||||||
Type: String
|
String: "File #5"
|
||||||
Value: "File #5"
|
|
||||||
removed: []
|
removed: []
|
||||||
updated: []
|
updated: []
|
||||||
sessionId: id-1
|
sessionId: id-1
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
source: tests/tests/serve.rs
|
source: tests/tests/serve.rs
|
||||||
expression: "read_response.intern_and_redact(&mut redactions, root_id)"
|
expression: "read_response.intern_and_redact(&mut redactions, root_id)"
|
||||||
|
|
||||||
---
|
---
|
||||||
instances:
|
instances:
|
||||||
id-2:
|
id-2:
|
||||||
@@ -23,7 +24,7 @@ instances:
|
|||||||
Parent: id-2
|
Parent: id-2
|
||||||
Properties:
|
Properties:
|
||||||
Value:
|
Value:
|
||||||
Type: String
|
String: This file will be removed!
|
||||||
Value: This file will be removed!
|
|
||||||
messageCursor: 0
|
messageCursor: 0
|
||||||
sessionId: id-1
|
sessionId: id-1
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ expression: redactions.redacted_yaml(info)
|
|||||||
|
|
||||||
---
|
---
|
||||||
expectedPlaceIds: ~
|
expectedPlaceIds: ~
|
||||||
|
gameId: ~
|
||||||
|
placeId: ~
|
||||||
projectName: remove_file
|
projectName: remove_file
|
||||||
protocolVersion: 4
|
protocolVersion: 4
|
||||||
rootInstanceId: id-2
|
rootInstanceId: id-2
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
source: tests/tests/serve.rs
|
source: tests/tests/serve.rs
|
||||||
expression: "read_response.intern_and_redact(&mut redactions, root_id)"
|
expression: "read_response.intern_and_redact(&mut redactions, root_id)"
|
||||||
|
|
||||||
---
|
---
|
||||||
instances:
|
instances:
|
||||||
id-2:
|
id-2:
|
||||||
@@ -24,8 +25,7 @@ instances:
|
|||||||
Parent: id-2
|
Parent: id-2
|
||||||
Properties:
|
Properties:
|
||||||
Source:
|
Source:
|
||||||
Type: String
|
String: "-- Hello, from bar!"
|
||||||
Value: "-- Hello, from bar!"
|
|
||||||
id-4:
|
id-4:
|
||||||
Children: []
|
Children: []
|
||||||
ClassName: ModuleScript
|
ClassName: ModuleScript
|
||||||
@@ -36,7 +36,7 @@ instances:
|
|||||||
Parent: id-2
|
Parent: id-2
|
||||||
Properties:
|
Properties:
|
||||||
Source:
|
Source:
|
||||||
Type: String
|
String: Updated foo!
|
||||||
Value: Updated foo!
|
|
||||||
messageCursor: 1
|
messageCursor: 1
|
||||||
sessionId: id-1
|
sessionId: id-1
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
source: tests/tests/serve.rs
|
source: tests/tests/serve.rs
|
||||||
expression: "read_response.intern_and_redact(&mut redactions, root_id)"
|
expression: "read_response.intern_and_redact(&mut redactions, root_id)"
|
||||||
|
|
||||||
---
|
---
|
||||||
instances:
|
instances:
|
||||||
id-2:
|
id-2:
|
||||||
@@ -24,8 +25,7 @@ instances:
|
|||||||
Parent: id-2
|
Parent: id-2
|
||||||
Properties:
|
Properties:
|
||||||
Source:
|
Source:
|
||||||
Type: String
|
String: "-- Hello, from bar!"
|
||||||
Value: "-- Hello, from bar!"
|
|
||||||
id-4:
|
id-4:
|
||||||
Children: []
|
Children: []
|
||||||
ClassName: ModuleScript
|
ClassName: ModuleScript
|
||||||
@@ -36,7 +36,7 @@ instances:
|
|||||||
Parent: id-2
|
Parent: id-2
|
||||||
Properties:
|
Properties:
|
||||||
Source:
|
Source:
|
||||||
Type: String
|
String: "-- Hello, from foo!"
|
||||||
Value: "-- Hello, from foo!"
|
|
||||||
messageCursor: 0
|
messageCursor: 0
|
||||||
sessionId: id-1
|
sessionId: id-1
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ expression: redactions.redacted_yaml(info)
|
|||||||
|
|
||||||
---
|
---
|
||||||
expectedPlaceIds: ~
|
expectedPlaceIds: ~
|
||||||
|
gameId: ~
|
||||||
|
placeId: ~
|
||||||
projectName: scripts
|
projectName: scripts
|
||||||
protocolVersion: 4
|
protocolVersion: 4
|
||||||
rootInstanceId: id-2
|
rootInstanceId: id-2
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
---
|
---
|
||||||
source: tests/tests/serve.rs
|
source: tests/tests/serve.rs
|
||||||
expression: "subscribe_response.intern_and_redact(&mut redactions, ())"
|
expression: "subscribe_response.intern_and_redact(&mut redactions, ())"
|
||||||
|
|
||||||
---
|
---
|
||||||
messageCursor: 1
|
messageCursor: 1
|
||||||
messages:
|
messages:
|
||||||
@@ -12,7 +13,7 @@ messages:
|
|||||||
changedName: ~
|
changedName: ~
|
||||||
changedProperties:
|
changedProperties:
|
||||||
Source:
|
Source:
|
||||||
Type: String
|
String: Updated foo!
|
||||||
Value: Updated foo!
|
|
||||||
id: id-4
|
id: id-4
|
||||||
sessionId: id-1
|
sessionId: id-1
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "rbx_dom_lua",
|
"name": "empty_folder",
|
||||||
"tree": {
|
"tree": {
|
||||||
"$path": "src"
|
"$path": "src"
|
||||||
}
|
}
|
||||||
@@ -16,6 +16,9 @@ use crate::{
|
|||||||
snapshot_middleware::{snapshot_from_vfs, snapshot_project_node},
|
snapshot_middleware::{snapshot_from_vfs, snapshot_project_node},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Processes file change events, updates the DOM, and sends those updates
|
||||||
|
/// through a channel for other stuff to consume.
|
||||||
|
///
|
||||||
/// Owns the connection between Rojo's VFS and its DOM by holding onto another
|
/// Owns the connection between Rojo's VFS and its DOM by holding onto another
|
||||||
/// thread that processes messages.
|
/// thread that processes messages.
|
||||||
///
|
///
|
||||||
|
|||||||
146
src/cli/build.rs
146
src/cli/build.rs
@@ -1,24 +1,88 @@
|
|||||||
use std::{
|
use std::{
|
||||||
fs::File,
|
|
||||||
io::{BufWriter, Write},
|
io::{BufWriter, Write},
|
||||||
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use anyhow::Context;
|
||||||
|
use fs_err::File;
|
||||||
use memofs::Vfs;
|
use memofs::Vfs;
|
||||||
use thiserror::Error;
|
use structopt::StructOpt;
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
|
|
||||||
use crate::{cli::BuildCommand, serve_session::ServeSession, snapshot::RojoTree};
|
use crate::serve_session::ServeSession;
|
||||||
|
|
||||||
|
use super::resolve_path;
|
||||||
|
|
||||||
|
const UNKNOWN_OUTPUT_KIND_ERR: &str = "Could not detect what kind of file to build. \
|
||||||
|
Expected output file to end in .rbxl, .rbxlx, .rbxm, or .rbxmx.";
|
||||||
|
|
||||||
|
/// Generates a model or place file from the Rojo project.
|
||||||
|
#[derive(Debug, StructOpt)]
|
||||||
|
pub struct BuildCommand {
|
||||||
|
/// Path to the project to serve. Defaults to the current directory.
|
||||||
|
#[structopt(default_value = "")]
|
||||||
|
pub project: PathBuf,
|
||||||
|
|
||||||
|
/// Where to output the result.
|
||||||
|
///
|
||||||
|
/// Should end in .rbxm, .rbxl, .rbxmx, or .rbxlx.
|
||||||
|
#[structopt(long, short)]
|
||||||
|
pub output: PathBuf,
|
||||||
|
|
||||||
|
/// Whether to automatically rebuild when any input files change.
|
||||||
|
#[structopt(long)]
|
||||||
|
pub watch: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BuildCommand {
|
||||||
|
pub fn run(self) -> anyhow::Result<()> {
|
||||||
|
let project_path = resolve_path(&self.project);
|
||||||
|
|
||||||
|
let output_kind = detect_output_kind(&self.output).context(UNKNOWN_OUTPUT_KIND_ERR)?;
|
||||||
|
|
||||||
|
log::trace!("Constructing in-memory filesystem");
|
||||||
|
let vfs = Vfs::new_default();
|
||||||
|
vfs.set_watch_enabled(self.watch);
|
||||||
|
|
||||||
|
let session = ServeSession::new(vfs, &project_path)?;
|
||||||
|
let mut cursor = session.message_queue().cursor();
|
||||||
|
|
||||||
|
write_model(&session, &self.output, output_kind)?;
|
||||||
|
|
||||||
|
if self.watch {
|
||||||
|
let rt = Runtime::new().unwrap();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let receiver = session.message_queue().subscribe(cursor);
|
||||||
|
let (new_cursor, _patch_set) = rt.block_on(receiver).unwrap();
|
||||||
|
cursor = new_cursor;
|
||||||
|
|
||||||
|
write_model(&session, &self.output, output_kind)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The different kinds of output that Rojo can build to.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
enum OutputKind {
|
enum OutputKind {
|
||||||
|
/// An XML model file.
|
||||||
Rbxmx,
|
Rbxmx,
|
||||||
|
|
||||||
|
/// An XML place file.
|
||||||
Rbxlx,
|
Rbxlx,
|
||||||
|
|
||||||
|
/// A binary model file.
|
||||||
Rbxm,
|
Rbxm,
|
||||||
|
|
||||||
|
/// A binary place file.
|
||||||
Rbxl,
|
Rbxl,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn detect_output_kind(options: &BuildCommand) -> Option<OutputKind> {
|
fn detect_output_kind(output: &Path) -> Option<OutputKind> {
|
||||||
let extension = options.output.extension()?.to_str()?;
|
let extension = output.extension()?.to_str()?;
|
||||||
|
|
||||||
match extension {
|
match extension {
|
||||||
"rbxlx" => Some(OutputKind::Rbxlx),
|
"rbxlx" => Some(OutputKind::Rbxlx),
|
||||||
@@ -29,57 +93,33 @@ fn detect_output_kind(options: &BuildCommand) -> Option<OutputKind> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
|
||||||
enum Error {
|
|
||||||
#[error("Could not detect what kind of file to build. Expected output file to end in .rbxl, .rbxlx, .rbxm, or .rbxmx.")]
|
|
||||||
UnknownOutputKind,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn xml_encode_config() -> rbx_xml::EncodeOptions {
|
fn xml_encode_config() -> rbx_xml::EncodeOptions {
|
||||||
rbx_xml::EncodeOptions::new().property_behavior(rbx_xml::EncodePropertyBehavior::WriteUnknown)
|
rbx_xml::EncodeOptions::new().property_behavior(rbx_xml::EncodePropertyBehavior::WriteUnknown)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(options: BuildCommand) -> Result<(), anyhow::Error> {
|
fn write_model(
|
||||||
log::trace!("Constructing in-memory filesystem");
|
session: &ServeSession,
|
||||||
|
output: &Path,
|
||||||
let vfs = Vfs::new_default();
|
output_kind: OutputKind,
|
||||||
vfs.set_watch_enabled(options.watch);
|
) -> anyhow::Result<()> {
|
||||||
|
println!("Building project '{}'", session.project_name());
|
||||||
let session = ServeSession::new(vfs, &options.absolute_project())?;
|
|
||||||
let mut cursor = session.message_queue().cursor();
|
|
||||||
|
|
||||||
{
|
|
||||||
let tree = session.tree();
|
|
||||||
write_model(&tree, &options)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.watch {
|
|
||||||
let mut rt = Runtime::new().unwrap();
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let receiver = session.message_queue().subscribe(cursor);
|
|
||||||
let (new_cursor, _patch_set) = rt.block_on(receiver).unwrap();
|
|
||||||
cursor = new_cursor;
|
|
||||||
|
|
||||||
let tree = session.tree();
|
|
||||||
write_model(&tree, &options)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_model(tree: &RojoTree, options: &BuildCommand) -> Result<(), anyhow::Error> {
|
|
||||||
let output_kind = detect_output_kind(&options).ok_or(Error::UnknownOutputKind)?;
|
|
||||||
log::debug!("Hoping to generate file of type {:?}", output_kind);
|
|
||||||
|
|
||||||
|
let tree = session.tree();
|
||||||
let root_id = tree.get_root_id();
|
let root_id = tree.get_root_id();
|
||||||
|
|
||||||
log::trace!("Opening output file for write");
|
log::trace!("Opening output file for write");
|
||||||
let file = File::create(&options.output)?;
|
let mut file = BufWriter::new(File::create(output)?);
|
||||||
let mut file = BufWriter::new(file);
|
|
||||||
|
|
||||||
match output_kind {
|
match output_kind {
|
||||||
|
OutputKind::Rbxm => {
|
||||||
|
rbx_binary::to_writer(&mut file, tree.inner(), &[root_id])?;
|
||||||
|
}
|
||||||
|
OutputKind::Rbxl => {
|
||||||
|
let root_instance = tree.get_instance(root_id).unwrap();
|
||||||
|
let top_level_ids = root_instance.children();
|
||||||
|
|
||||||
|
rbx_binary::to_writer(&mut file, tree.inner(), top_level_ids)?;
|
||||||
|
}
|
||||||
OutputKind::Rbxmx => {
|
OutputKind::Rbxmx => {
|
||||||
// Model files include the root instance of the tree and all its
|
// Model files include the root instance of the tree and all its
|
||||||
// descendants.
|
// descendants.
|
||||||
@@ -95,25 +135,15 @@ fn write_model(tree: &RojoTree, options: &BuildCommand) -> Result<(), anyhow::Er
|
|||||||
|
|
||||||
rbx_xml::to_writer(&mut file, tree.inner(), top_level_ids, xml_encode_config())?;
|
rbx_xml::to_writer(&mut file, tree.inner(), top_level_ids, xml_encode_config())?;
|
||||||
}
|
}
|
||||||
OutputKind::Rbxm => {
|
|
||||||
rbx_binary::to_writer_default(&mut file, tree.inner(), &[root_id])?;
|
|
||||||
}
|
|
||||||
OutputKind::Rbxl => {
|
|
||||||
let root_instance = tree.get_instance(root_id).unwrap();
|
|
||||||
let top_level_ids = root_instance.children();
|
|
||||||
|
|
||||||
rbx_binary::to_writer_default(&mut file, tree.inner(), top_level_ids)?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
file.flush()?;
|
file.flush()?;
|
||||||
|
|
||||||
let filename = options
|
let filename = output
|
||||||
.output
|
|
||||||
.file_name()
|
.file_name()
|
||||||
.and_then(|name| name.to_str())
|
.and_then(|name| name.to_str())
|
||||||
.unwrap_or("<invalid utf-8>");
|
.unwrap_or("<invalid utf-8>");
|
||||||
log::info!("Built project to {}", filename);
|
println!("Built project to {}", filename);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,12 @@
|
|||||||
pub fn doc() -> Result<(), anyhow::Error> {
|
use structopt::StructOpt;
|
||||||
opener::open("https://rojo.space/docs")?;
|
|
||||||
Ok(())
|
/// Open Rojo's documentation in your browser.
|
||||||
|
#[derive(Debug, StructOpt)]
|
||||||
|
pub struct DocCommand {}
|
||||||
|
|
||||||
|
impl DocCommand {
|
||||||
|
pub fn run(self) -> anyhow::Result<()> {
|
||||||
|
opener::open("https://rojo.space/docs")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
29
src/cli/fmt_project.rs
Normal file
29
src/cli/fmt_project.rs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use anyhow::Context;
|
||||||
|
use structopt::StructOpt;
|
||||||
|
|
||||||
|
use crate::project::Project;
|
||||||
|
|
||||||
|
/// Reformat a Rojo project using the standard JSON formatting rules.
|
||||||
|
#[derive(Debug, StructOpt)]
|
||||||
|
pub struct FmtProjectCommand {
|
||||||
|
/// Path to the project to format. Defaults to the current directory.
|
||||||
|
#[structopt(default_value = "")]
|
||||||
|
pub project: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FmtProjectCommand {
|
||||||
|
pub fn run(self) -> anyhow::Result<()> {
|
||||||
|
let project = Project::load_fuzzy(&self.project)?
|
||||||
|
.context("A project file is required to run 'rojo fmt-project'")?;
|
||||||
|
|
||||||
|
let serialized = serde_json::to_string_pretty(&project)
|
||||||
|
.context("could not re-encode project file as JSON")?;
|
||||||
|
|
||||||
|
fs_err::write(&project.file_location, &serialized)
|
||||||
|
.context("could not write back to project file")?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
111
src/cli/init.rs
111
src/cli/init.rs
@@ -1,13 +1,14 @@
|
|||||||
use std::{
|
use std::io::{self, Write};
|
||||||
fs::{self, OpenOptions},
|
use std::path::{Path, PathBuf};
|
||||||
io::{self, Write},
|
use std::process::{Command, Stdio};
|
||||||
path::Path,
|
use std::str::FromStr;
|
||||||
process::{Command, Stdio},
|
|
||||||
};
|
|
||||||
|
|
||||||
use thiserror::Error;
|
use anyhow::{bail, format_err};
|
||||||
|
use fs_err as fs;
|
||||||
|
use fs_err::OpenOptions;
|
||||||
|
use structopt::StructOpt;
|
||||||
|
|
||||||
use crate::cli::{InitCommand, InitKind};
|
use super::resolve_path;
|
||||||
|
|
||||||
static MODEL_PROJECT: &str =
|
static MODEL_PROJECT: &str =
|
||||||
include_str!("../../assets/default-model-project/default.project.json");
|
include_str!("../../assets/default-model-project/default.project.json");
|
||||||
@@ -20,37 +21,71 @@ static PLACE_PROJECT: &str =
|
|||||||
static PLACE_README: &str = include_str!("../../assets/default-place-project/README.md");
|
static PLACE_README: &str = include_str!("../../assets/default-place-project/README.md");
|
||||||
static PLACE_GIT_IGNORE: &str = include_str!("../../assets/default-place-project/gitignore.txt");
|
static PLACE_GIT_IGNORE: &str = include_str!("../../assets/default-place-project/gitignore.txt");
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
/// Initializes a new Rojo project.
|
||||||
enum Error {
|
#[derive(Debug, StructOpt)]
|
||||||
#[error("A project file named default.project.json already exists in this folder")]
|
pub struct InitCommand {
|
||||||
AlreadyExists,
|
/// Path to the place to create the project. Defaults to the current directory.
|
||||||
|
#[structopt(default_value = "")]
|
||||||
|
pub path: PathBuf,
|
||||||
|
|
||||||
#[error("git init failed")]
|
/// The kind of project to create, 'place' or 'model'. Defaults to place.
|
||||||
GitInit,
|
#[structopt(long, default_value = "place")]
|
||||||
|
pub kind: InitKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(options: InitCommand) -> Result<(), anyhow::Error> {
|
impl InitCommand {
|
||||||
let base_path = options.absolute_path();
|
pub fn run(self) -> anyhow::Result<()> {
|
||||||
fs::create_dir_all(&base_path)?;
|
let base_path = resolve_path(&self.path);
|
||||||
|
fs::create_dir_all(&base_path)?;
|
||||||
|
|
||||||
let canonical = fs::canonicalize(&base_path)?;
|
let canonical = fs::canonicalize(&base_path)?;
|
||||||
let project_name = canonical
|
let project_name = canonical
|
||||||
.file_name()
|
.file_name()
|
||||||
.and_then(|name| name.to_str())
|
.and_then(|name| name.to_str())
|
||||||
.unwrap_or("new-project");
|
.unwrap_or("new-project");
|
||||||
|
|
||||||
let project_params = ProjectParams {
|
let project_params = ProjectParams {
|
||||||
name: project_name.to_owned(),
|
name: project_name.to_owned(),
|
||||||
};
|
};
|
||||||
|
|
||||||
match options.kind {
|
match self.kind {
|
||||||
InitKind::Place => init_place(&base_path, project_params),
|
InitKind::Place => init_place(&base_path, project_params)?,
|
||||||
InitKind::Model => init_model(&base_path, project_params),
|
InitKind::Model => init_model(&base_path, project_params)?,
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Created project successfully.");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_place(base_path: &Path, project_params: ProjectParams) -> Result<(), anyhow::Error> {
|
/// The templates we support for initializing a Rojo project.
|
||||||
eprintln!("Creating new place project '{}'", project_params.name);
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum InitKind {
|
||||||
|
/// A place that contains a baseplate.
|
||||||
|
Place,
|
||||||
|
|
||||||
|
/// An empty model, suitable for a library or plugin.
|
||||||
|
Model,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for InitKind {
|
||||||
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
|
fn from_str(source: &str) -> Result<Self, Self::Err> {
|
||||||
|
match source {
|
||||||
|
"place" => Ok(InitKind::Place),
|
||||||
|
"model" => Ok(InitKind::Model),
|
||||||
|
_ => Err(format_err!(
|
||||||
|
"Invalid init kind '{}'. Valid kinds are: place, model",
|
||||||
|
source
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_place(base_path: &Path, project_params: ProjectParams) -> anyhow::Result<()> {
|
||||||
|
println!("Creating new place project '{}'", project_params.name);
|
||||||
|
|
||||||
let project_file = project_params.render_template(PLACE_PROJECT);
|
let project_file = project_params.render_template(PLACE_PROJECT);
|
||||||
try_create_project(base_path, &project_file)?;
|
try_create_project(base_path, &project_file)?;
|
||||||
@@ -88,13 +123,11 @@ fn init_place(base_path: &Path, project_params: ProjectParams) -> Result<(), any
|
|||||||
let git_ignore = project_params.render_template(PLACE_GIT_IGNORE);
|
let git_ignore = project_params.render_template(PLACE_GIT_IGNORE);
|
||||||
try_git_init(base_path, &git_ignore)?;
|
try_git_init(base_path, &git_ignore)?;
|
||||||
|
|
||||||
eprintln!("Created project successfully.");
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_model(base_path: &Path, project_params: ProjectParams) -> Result<(), anyhow::Error> {
|
fn init_model(base_path: &Path, project_params: ProjectParams) -> anyhow::Result<()> {
|
||||||
eprintln!("Creating new model project '{}'", project_params.name);
|
println!("Creating new model project '{}'", project_params.name);
|
||||||
|
|
||||||
let project_file = project_params.render_template(MODEL_PROJECT);
|
let project_file = project_params.render_template(MODEL_PROJECT);
|
||||||
try_create_project(base_path, &project_file)?;
|
try_create_project(base_path, &project_file)?;
|
||||||
@@ -111,8 +144,6 @@ fn init_model(base_path: &Path, project_params: ProjectParams) -> Result<(), any
|
|||||||
let git_ignore = project_params.render_template(MODEL_GIT_IGNORE);
|
let git_ignore = project_params.render_template(MODEL_GIT_IGNORE);
|
||||||
try_git_init(base_path, &git_ignore)?;
|
try_git_init(base_path, &git_ignore)?;
|
||||||
|
|
||||||
eprintln!("Created project successfully.");
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +169,7 @@ fn try_git_init(path: &Path, git_ignore: &str) -> Result<(), anyhow::Error> {
|
|||||||
let status = Command::new("git").arg("init").current_dir(path).status()?;
|
let status = Command::new("git").arg("init").current_dir(path).status()?;
|
||||||
|
|
||||||
if !status.success() {
|
if !status.success() {
|
||||||
return Err(Error::GitInit.into());
|
bail!("git init failed: status code {:?}", status.code());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,13 +226,15 @@ fn try_create_project(base_path: &Path, contents: &str) -> Result<(), anyhow::Er
|
|||||||
let file_res = OpenOptions::new()
|
let file_res = OpenOptions::new()
|
||||||
.write(true)
|
.write(true)
|
||||||
.create_new(true)
|
.create_new(true)
|
||||||
.open(project_path);
|
.open(&project_path);
|
||||||
|
|
||||||
let mut file = match file_res {
|
let mut file = match file_res {
|
||||||
Ok(file) => file,
|
Ok(file) => file,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
return match err.kind() {
|
return match err.kind() {
|
||||||
io::ErrorKind::AlreadyExists => Err(Error::AlreadyExists.into()),
|
io::ErrorKind::AlreadyExists => {
|
||||||
|
bail!("Project file already exists: {}", project_path.display())
|
||||||
|
}
|
||||||
_ => Err(err.into()),
|
_ => Err(err.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
238
src/cli/mod.rs
238
src/cli/mod.rs
@@ -2,29 +2,24 @@
|
|||||||
|
|
||||||
mod build;
|
mod build;
|
||||||
mod doc;
|
mod doc;
|
||||||
|
mod fmt_project;
|
||||||
mod init;
|
mod init;
|
||||||
mod plugin;
|
mod plugin;
|
||||||
mod serve;
|
mod serve;
|
||||||
mod upload;
|
mod upload;
|
||||||
|
|
||||||
use std::{
|
use std::{borrow::Cow, env, path::Path, str::FromStr};
|
||||||
borrow::Cow,
|
|
||||||
env,
|
|
||||||
error::Error,
|
|
||||||
fmt,
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
str::FromStr,
|
|
||||||
};
|
|
||||||
|
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
pub use self::build::*;
|
pub use self::build::BuildCommand;
|
||||||
pub use self::doc::*;
|
pub use self::doc::DocCommand;
|
||||||
pub use self::init::*;
|
pub use self::fmt_project::FmtProjectCommand;
|
||||||
pub use self::plugin::*;
|
pub use self::init::{InitCommand, InitKind};
|
||||||
pub use self::serve::*;
|
pub use self::plugin::{PluginCommand, PluginSubcommand};
|
||||||
pub use self::upload::*;
|
pub use self::serve::ServeCommand;
|
||||||
|
pub use self::upload::UploadCommand;
|
||||||
|
|
||||||
/// Command line options that Rojo accepts, defined using the structopt crate.
|
/// Command line options that Rojo accepts, defined using the structopt crate.
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Debug, StructOpt)]
|
||||||
@@ -38,6 +33,20 @@ pub struct Options {
|
|||||||
pub subcommand: Subcommand,
|
pub subcommand: Subcommand,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Options {
|
||||||
|
pub fn run(self) -> anyhow::Result<()> {
|
||||||
|
match self.subcommand {
|
||||||
|
Subcommand::Init(subcommand) => subcommand.run(),
|
||||||
|
Subcommand::Serve(subcommand) => subcommand.run(self.global),
|
||||||
|
Subcommand::Build(subcommand) => subcommand.run(),
|
||||||
|
Subcommand::Upload(subcommand) => subcommand.run(),
|
||||||
|
Subcommand::FmtProject(subcommand) => subcommand.run(),
|
||||||
|
Subcommand::Doc(subcommand) => subcommand.run(),
|
||||||
|
Subcommand::Plugin(subcommand) => subcommand.run(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Debug, StructOpt)]
|
||||||
pub struct GlobalOptions {
|
pub struct GlobalOptions {
|
||||||
/// Sets verbosity level. Can be specified multiple times.
|
/// Sets verbosity level. Can be specified multiple times.
|
||||||
@@ -99,214 +108,19 @@ pub struct ColorChoiceParseError {
|
|||||||
|
|
||||||
#[derive(Debug, StructOpt)]
|
#[derive(Debug, StructOpt)]
|
||||||
pub enum Subcommand {
|
pub enum Subcommand {
|
||||||
/// Creates a new Rojo project.
|
|
||||||
Init(InitCommand),
|
Init(InitCommand),
|
||||||
|
|
||||||
/// Serves the project's files for use with the Rojo Studio plugin.
|
|
||||||
Serve(ServeCommand),
|
Serve(ServeCommand),
|
||||||
|
|
||||||
/// Generates a model or place file from the project.
|
|
||||||
Build(BuildCommand),
|
Build(BuildCommand),
|
||||||
|
|
||||||
/// Generates a place or model file out of the project and uploads it to Roblox.
|
|
||||||
Upload(UploadCommand),
|
Upload(UploadCommand),
|
||||||
|
FmtProject(FmtProjectCommand),
|
||||||
/// Open Rojo's documentation in your browser.
|
Doc(DocCommand),
|
||||||
Doc,
|
|
||||||
|
|
||||||
/// Manages Rojo's Roblox Studio plugin.
|
|
||||||
Plugin(PluginCommand),
|
Plugin(PluginCommand),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initializes a new Rojo project.
|
pub(super) fn resolve_path(path: &Path) -> Cow<'_, Path> {
|
||||||
#[derive(Debug, StructOpt)]
|
|
||||||
pub struct InitCommand {
|
|
||||||
/// Path to the place to create the project. Defaults to the current directory.
|
|
||||||
#[structopt(default_value = "")]
|
|
||||||
pub path: PathBuf,
|
|
||||||
|
|
||||||
/// The kind of project to create, 'place' or 'model'. Defaults to place.
|
|
||||||
#[structopt(long, default_value = "place")]
|
|
||||||
pub kind: InitKind,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InitCommand {
|
|
||||||
pub fn absolute_path(&self) -> Cow<'_, Path> {
|
|
||||||
resolve_path(&self.path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The templates we support for initializing a Rojo project.
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub enum InitKind {
|
|
||||||
/// A place that matches what File -> New does in Roblox Studio.
|
|
||||||
Place,
|
|
||||||
|
|
||||||
/// An empty model, suitable for a library or plugin.
|
|
||||||
Model,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for InitKind {
|
|
||||||
type Err = InitKindParseError;
|
|
||||||
|
|
||||||
fn from_str(source: &str) -> Result<Self, Self::Err> {
|
|
||||||
match source {
|
|
||||||
"place" => Ok(InitKind::Place),
|
|
||||||
"model" => Ok(InitKind::Model),
|
|
||||||
_ => Err(InitKindParseError {
|
|
||||||
attempted: source.to_owned(),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Error type for failing to parse an `InitKind`.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct InitKindParseError {
|
|
||||||
attempted: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for InitKindParseError {}
|
|
||||||
|
|
||||||
impl fmt::Display for InitKindParseError {
|
|
||||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(
|
|
||||||
formatter,
|
|
||||||
"Invalid init kind '{}'. Valid kinds are: place, model",
|
|
||||||
self.attempted
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Expose a Rojo project through a web server that can communicate with the
|
|
||||||
/// Rojo Roblox Studio plugin, or be visited by the user in the browser.
|
|
||||||
#[derive(Debug, StructOpt)]
|
|
||||||
pub struct ServeCommand {
|
|
||||||
/// Path to the project to serve. Defaults to the current directory.
|
|
||||||
#[structopt(default_value = "")]
|
|
||||||
pub project: PathBuf,
|
|
||||||
|
|
||||||
/// The port to listen on. Defaults to the project's preference, or 34872 if
|
|
||||||
/// it has none.
|
|
||||||
#[structopt(long)]
|
|
||||||
pub port: Option<u16>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ServeCommand {
|
|
||||||
pub fn absolute_project(&self) -> Cow<'_, Path> {
|
|
||||||
resolve_path(&self.project)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Build a Rojo project into a file.
|
|
||||||
#[derive(Debug, StructOpt)]
|
|
||||||
pub struct BuildCommand {
|
|
||||||
/// Path to the project to serve. Defaults to the current directory.
|
|
||||||
#[structopt(default_value = "")]
|
|
||||||
pub project: PathBuf,
|
|
||||||
|
|
||||||
/// Where to output the result.
|
|
||||||
#[structopt(long, short)]
|
|
||||||
pub output: PathBuf,
|
|
||||||
|
|
||||||
/// Whether to automatically rebuild when any input files change.
|
|
||||||
#[structopt(long)]
|
|
||||||
pub watch: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BuildCommand {
|
|
||||||
pub fn absolute_project(&self) -> Cow<'_, Path> {
|
|
||||||
resolve_path(&self.project)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Build and upload a Rojo project to Roblox.com.
|
|
||||||
#[derive(Debug, StructOpt)]
|
|
||||||
pub struct UploadCommand {
|
|
||||||
/// Path to the project to upload. Defaults to the current directory.
|
|
||||||
#[structopt(default_value = "")]
|
|
||||||
pub project: PathBuf,
|
|
||||||
|
|
||||||
/// Authenication cookie to use. If not specified, Rojo will attempt to find one from the system automatically.
|
|
||||||
#[structopt(long)]
|
|
||||||
pub cookie: Option<String>,
|
|
||||||
|
|
||||||
/// Asset ID to upload to.
|
|
||||||
#[structopt(long = "asset_id")]
|
|
||||||
pub asset_id: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UploadCommand {
|
|
||||||
pub fn absolute_project(&self) -> Cow<'_, Path> {
|
|
||||||
resolve_path(&self.project)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The kind of asset to upload to the website. Affects what endpoints Rojo uses
|
|
||||||
/// and changes how the asset is built.
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub enum UploadKind {
|
|
||||||
/// Upload to a place.
|
|
||||||
Place,
|
|
||||||
|
|
||||||
/// Upload to a model-like asset, like a Model, Plugin, or Package.
|
|
||||||
Model,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for UploadKind {
|
|
||||||
type Err = UploadKindParseError;
|
|
||||||
|
|
||||||
fn from_str(source: &str) -> Result<Self, Self::Err> {
|
|
||||||
match source {
|
|
||||||
"place" => Ok(UploadKind::Place),
|
|
||||||
"model" => Ok(UploadKind::Model),
|
|
||||||
_ => Err(UploadKindParseError {
|
|
||||||
attempted: source.to_owned(),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Error type for failing to parse an `UploadKind`.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct UploadKindParseError {
|
|
||||||
attempted: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for UploadKindParseError {}
|
|
||||||
|
|
||||||
impl fmt::Display for UploadKindParseError {
|
|
||||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(
|
|
||||||
formatter,
|
|
||||||
"Invalid upload kind '{}'. Valid kinds are: place, model",
|
|
||||||
self.attempted
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resolve_path(path: &Path) -> Cow<'_, Path> {
|
|
||||||
if path.is_absolute() {
|
if path.is_absolute() {
|
||||||
Cow::Borrowed(path)
|
Cow::Borrowed(path)
|
||||||
} else {
|
} else {
|
||||||
Cow::Owned(env::current_dir().unwrap().join(path))
|
Cow::Owned(env::current_dir().unwrap().join(path))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, StructOpt)]
|
|
||||||
pub enum PluginSubcommand {
|
|
||||||
/// Install the plugin in Roblox Studio's plugins folder. If the plugin is
|
|
||||||
/// already installed, installing it again will overwrite the current plugin
|
|
||||||
/// file.
|
|
||||||
Install,
|
|
||||||
|
|
||||||
/// Removes the plugin if it is installed.
|
|
||||||
Uninstall,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Install Rojo's plugin.
|
|
||||||
#[derive(Debug, StructOpt)]
|
|
||||||
pub struct PluginCommand {
|
|
||||||
#[structopt(subcommand)]
|
|
||||||
subcommand: PluginSubcommand,
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,26 +3,50 @@ use std::{
|
|||||||
io::BufWriter,
|
io::BufWriter,
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use memofs::{InMemoryFs, Vfs, VfsSnapshot};
|
use memofs::{InMemoryFs, Vfs, VfsSnapshot};
|
||||||
use roblox_install::RobloxStudio;
|
use roblox_install::RobloxStudio;
|
||||||
|
use structopt::StructOpt;
|
||||||
|
|
||||||
use crate::{
|
use crate::serve_session::ServeSession;
|
||||||
cli::{PluginCommand, PluginSubcommand},
|
|
||||||
serve_session::ServeSession,
|
|
||||||
};
|
|
||||||
|
|
||||||
static PLUGIN_BINCODE: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/plugin.bincode"));
|
static PLUGIN_BINCODE: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/plugin.bincode"));
|
||||||
static PLUGIN_FILE_NAME: &str = "RojoManagedPlugin.rbxm";
|
static PLUGIN_FILE_NAME: &str = "RojoManagedPlugin.rbxm";
|
||||||
|
|
||||||
pub fn plugin(options: PluginCommand) -> Result<()> {
|
/// Install Rojo's plugin.
|
||||||
match options.subcommand {
|
#[derive(Debug, StructOpt)]
|
||||||
PluginSubcommand::Install => install_plugin(),
|
pub struct PluginCommand {
|
||||||
PluginSubcommand::Uninstall => uninstall_plugin(),
|
#[structopt(subcommand)]
|
||||||
|
subcommand: PluginSubcommand,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Manages Rojo's Roblox Studio plugin.
|
||||||
|
#[derive(Debug, StructOpt)]
|
||||||
|
pub enum PluginSubcommand {
|
||||||
|
/// Install the plugin in Roblox Studio's plugins folder. If the plugin is
|
||||||
|
/// already installed, installing it again will overwrite the current plugin
|
||||||
|
/// file.
|
||||||
|
Install,
|
||||||
|
|
||||||
|
/// Removes the plugin if it is installed.
|
||||||
|
Uninstall,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PluginCommand {
|
||||||
|
pub fn run(self) -> anyhow::Result<()> {
|
||||||
|
self.subcommand.run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn install_plugin() -> Result<()> {
|
impl PluginSubcommand {
|
||||||
|
pub fn run(self) -> anyhow::Result<()> {
|
||||||
|
match self {
|
||||||
|
PluginSubcommand::Install => install_plugin(),
|
||||||
|
PluginSubcommand::Uninstall => uninstall_plugin(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn install_plugin() -> anyhow::Result<()> {
|
||||||
let plugin_snapshot: VfsSnapshot = bincode::deserialize(PLUGIN_BINCODE)
|
let plugin_snapshot: VfsSnapshot = bincode::deserialize(PLUGIN_BINCODE)
|
||||||
.expect("Rojo's plugin was not properly packed into Rojo's binary");
|
.expect("Rojo's plugin was not properly packed into Rojo's binary");
|
||||||
|
|
||||||
@@ -49,12 +73,12 @@ pub fn install_plugin() -> Result<()> {
|
|||||||
let tree = session.tree();
|
let tree = session.tree();
|
||||||
let root_id = tree.get_root_id();
|
let root_id = tree.get_root_id();
|
||||||
|
|
||||||
rbx_binary::to_writer_default(&mut file, tree.inner(), &[root_id])?;
|
rbx_binary::to_writer(&mut file, tree.inner(), &[root_id])?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn uninstall_plugin() -> Result<()> {
|
fn uninstall_plugin() -> anyhow::Result<()> {
|
||||||
let studio = RobloxStudio::locate()?;
|
let studio = RobloxStudio::locate()?;
|
||||||
|
|
||||||
let plugin_path = studio.plugins_path().join(PLUGIN_FILE_NAME);
|
let plugin_path = studio.plugins_path().join(PLUGIN_FILE_NAME);
|
||||||
|
|||||||
@@ -1,51 +1,82 @@
|
|||||||
use std::{
|
use std::{
|
||||||
io::{self, Write},
|
io::{self, Write},
|
||||||
|
net::{IpAddr, Ipv4Addr},
|
||||||
|
path::PathBuf,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use memofs::Vfs;
|
use memofs::Vfs;
|
||||||
|
use structopt::StructOpt;
|
||||||
use termcolor::{BufferWriter, Color, ColorChoice, ColorSpec, WriteColor};
|
use termcolor::{BufferWriter, Color, ColorChoice, ColorSpec, WriteColor};
|
||||||
|
|
||||||
use crate::{
|
use crate::{serve_session::ServeSession, web::LiveServer};
|
||||||
cli::{GlobalOptions, ServeCommand},
|
|
||||||
serve_session::ServeSession,
|
|
||||||
web::LiveServer,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
use super::{resolve_path, GlobalOptions};
|
||||||
|
|
||||||
|
const DEFAULT_BIND_ADDRESS: Ipv4Addr = Ipv4Addr::new(127, 0, 0, 1);
|
||||||
const DEFAULT_PORT: u16 = 34872;
|
const DEFAULT_PORT: u16 = 34872;
|
||||||
|
|
||||||
pub fn serve(global: GlobalOptions, options: ServeCommand) -> Result<()> {
|
/// Expose a Rojo project to the Rojo Studio plugin.
|
||||||
let vfs = Vfs::new_default();
|
#[derive(Debug, StructOpt)]
|
||||||
|
pub struct ServeCommand {
|
||||||
|
/// Path to the project to serve. Defaults to the current directory.
|
||||||
|
#[structopt(default_value = "")]
|
||||||
|
pub project: PathBuf,
|
||||||
|
|
||||||
let session = Arc::new(ServeSession::new(vfs, &options.absolute_project())?);
|
/// The IP address to listen on. Defaults to `127.0.0.1`.
|
||||||
|
#[structopt(long)]
|
||||||
|
pub address: Option<IpAddr>,
|
||||||
|
|
||||||
let port = options
|
/// The port to listen on. Defaults to the project's preference, or `34872` if
|
||||||
.port
|
/// it has none.
|
||||||
.or_else(|| session.project_port())
|
#[structopt(long)]
|
||||||
.unwrap_or(DEFAULT_PORT);
|
pub port: Option<u16>,
|
||||||
|
|
||||||
let server = LiveServer::new(session);
|
|
||||||
|
|
||||||
let _ = show_start_message(port, global.color.into());
|
|
||||||
server.start(port);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show_start_message(port: u16, color: ColorChoice) -> io::Result<()> {
|
impl ServeCommand {
|
||||||
|
pub fn run(self, global: GlobalOptions) -> anyhow::Result<()> {
|
||||||
|
let project_path = resolve_path(&self.project);
|
||||||
|
|
||||||
|
let vfs = Vfs::new_default();
|
||||||
|
|
||||||
|
let session = Arc::new(ServeSession::new(vfs, &project_path)?);
|
||||||
|
|
||||||
|
let ip = self.address.unwrap_or(DEFAULT_BIND_ADDRESS.into());
|
||||||
|
|
||||||
|
let port = self
|
||||||
|
.port
|
||||||
|
.or_else(|| session.project_port())
|
||||||
|
.unwrap_or(DEFAULT_PORT);
|
||||||
|
|
||||||
|
let server = LiveServer::new(session);
|
||||||
|
|
||||||
|
let _ = show_start_message(ip, port, global.color.into());
|
||||||
|
server.start((ip, port).into());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_start_message(bind_address: IpAddr, port: u16, color: ColorChoice) -> io::Result<()> {
|
||||||
|
let mut green = ColorSpec::new();
|
||||||
|
green.set_fg(Some(Color::Green)).set_bold(true);
|
||||||
|
|
||||||
let writer = BufferWriter::stdout(color);
|
let writer = BufferWriter::stdout(color);
|
||||||
let mut buffer = writer.buffer();
|
let mut buffer = writer.buffer();
|
||||||
|
|
||||||
writeln!(&mut buffer, "Rojo server listening:")?;
|
writeln!(&mut buffer, "Rojo server listening:")?;
|
||||||
|
|
||||||
write!(&mut buffer, " Address: ")?;
|
write!(&mut buffer, " Address: ")?;
|
||||||
buffer.set_color(ColorSpec::new().set_fg(Some(Color::Green)).set_bold(true))?;
|
buffer.set_color(&green)?;
|
||||||
writeln!(&mut buffer, "localhost")?;
|
if bind_address.is_loopback() {
|
||||||
|
writeln!(&mut buffer, "localhost")?;
|
||||||
|
} else {
|
||||||
|
writeln!(&mut buffer, "{}", bind_address)?;
|
||||||
|
}
|
||||||
|
|
||||||
buffer.set_color(&ColorSpec::new())?;
|
buffer.set_color(&ColorSpec::new())?;
|
||||||
write!(&mut buffer, " Port: ")?;
|
write!(&mut buffer, " Port: ")?;
|
||||||
buffer.set_color(ColorSpec::new().set_fg(Some(Color::Green)).set_bold(true))?;
|
buffer.set_color(&green)?;
|
||||||
writeln!(&mut buffer, "{}", port)?;
|
writeln!(&mut buffer, "{}", port)?;
|
||||||
|
|
||||||
writeln!(&mut buffer)?;
|
writeln!(&mut buffer)?;
|
||||||
@@ -53,7 +84,7 @@ fn show_start_message(port: u16, color: ColorChoice) -> io::Result<()> {
|
|||||||
buffer.set_color(&ColorSpec::new())?;
|
buffer.set_color(&ColorSpec::new())?;
|
||||||
write!(&mut buffer, "Visit ")?;
|
write!(&mut buffer, "Visit ")?;
|
||||||
|
|
||||||
buffer.set_color(ColorSpec::new().set_fg(Some(Color::Green)).set_bold(true))?;
|
buffer.set_color(&green)?;
|
||||||
write!(&mut buffer, "http://localhost:{}/", port)?;
|
write!(&mut buffer, "http://localhost:{}/", port)?;
|
||||||
|
|
||||||
buffer.set_color(&ColorSpec::new())?;
|
buffer.set_color(&ColorSpec::new())?;
|
||||||
|
|||||||
@@ -1,46 +1,87 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use anyhow::{bail, format_err, Context};
|
||||||
use memofs::Vfs;
|
use memofs::Vfs;
|
||||||
use reqwest::{
|
use reqwest::{
|
||||||
header::{ACCEPT, CONTENT_TYPE, COOKIE, USER_AGENT},
|
header::{ACCEPT, CONTENT_TYPE, COOKIE, USER_AGENT},
|
||||||
StatusCode,
|
StatusCode,
|
||||||
};
|
};
|
||||||
use thiserror::Error;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
use crate::{auth_cookie::get_auth_cookie, cli::UploadCommand, serve_session::ServeSession};
|
use crate::{auth_cookie::get_auth_cookie, serve_session::ServeSession};
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
use super::resolve_path;
|
||||||
enum Error {
|
|
||||||
#[error("Rojo could not find your Roblox auth cookie. Please pass one via --cookie.")]
|
|
||||||
NeedAuthCookie,
|
|
||||||
|
|
||||||
#[error("The Roblox API returned an unexpected error: {body}")]
|
/// Builds the project and uploads it to Roblox.
|
||||||
RobloxApi { body: String },
|
#[derive(Debug, StructOpt)]
|
||||||
|
pub struct UploadCommand {
|
||||||
|
/// Path to the project to upload. Defaults to the current directory.
|
||||||
|
#[structopt(default_value = "")]
|
||||||
|
pub project: PathBuf,
|
||||||
|
|
||||||
|
/// Authenication cookie to use. If not specified, Rojo will attempt to find one from the system automatically.
|
||||||
|
#[structopt(long)]
|
||||||
|
pub cookie: Option<String>,
|
||||||
|
|
||||||
|
/// Asset ID to upload to.
|
||||||
|
#[structopt(long = "asset_id")]
|
||||||
|
pub asset_id: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn upload(options: UploadCommand) -> Result<(), anyhow::Error> {
|
impl UploadCommand {
|
||||||
let cookie = options
|
pub fn run(self) -> Result<(), anyhow::Error> {
|
||||||
.cookie
|
let project_path = resolve_path(&self.project);
|
||||||
.clone()
|
|
||||||
.or_else(get_auth_cookie)
|
|
||||||
.ok_or(Error::NeedAuthCookie)?;
|
|
||||||
|
|
||||||
let vfs = Vfs::new_default();
|
let cookie = self.cookie.or_else(get_auth_cookie).context(
|
||||||
|
"Rojo could not find your Roblox auth cookie. Please pass one via --cookie.",
|
||||||
|
)?;
|
||||||
|
|
||||||
let session = ServeSession::new(vfs, &options.absolute_project())?;
|
let vfs = Vfs::new_default();
|
||||||
|
|
||||||
let tree = session.tree();
|
let session = ServeSession::new(vfs, project_path)?;
|
||||||
let inner_tree = tree.inner();
|
|
||||||
let root = inner_tree.root();
|
|
||||||
|
|
||||||
let encode_ids = match root.class.as_str() {
|
let tree = session.tree();
|
||||||
"DataModel" => root.children().to_vec(),
|
let inner_tree = tree.inner();
|
||||||
_ => vec![root.referent()],
|
let root = inner_tree.root();
|
||||||
};
|
|
||||||
|
|
||||||
let mut buffer = Vec::new();
|
let encode_ids = match root.class.as_str() {
|
||||||
|
"DataModel" => root.children().to_vec(),
|
||||||
|
_ => vec![root.referent()],
|
||||||
|
};
|
||||||
|
|
||||||
log::trace!("Encoding binary model");
|
let mut buffer = Vec::new();
|
||||||
rbx_binary::to_writer_default(&mut buffer, tree.inner(), &encode_ids)?;
|
|
||||||
do_upload(buffer, options.asset_id, &cookie)
|
log::trace!("Encoding binary model");
|
||||||
|
rbx_binary::to_writer(&mut buffer, tree.inner(), &encode_ids)?;
|
||||||
|
do_upload(buffer, self.asset_id, &cookie)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The kind of asset to upload to the website. Affects what endpoints Rojo uses
|
||||||
|
/// and changes how the asset is built.
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
enum UploadKind {
|
||||||
|
/// Upload to a place.
|
||||||
|
Place,
|
||||||
|
|
||||||
|
/// Upload to a model-like asset, like a Model, Plugin, or Package.
|
||||||
|
Model,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for UploadKind {
|
||||||
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
|
fn from_str(source: &str) -> Result<Self, Self::Err> {
|
||||||
|
match source {
|
||||||
|
"place" => Ok(UploadKind::Place),
|
||||||
|
"model" => Ok(UploadKind::Model),
|
||||||
|
attempted => Err(format_err!(
|
||||||
|
"Invalid upload kind '{}'. Valid kinds are: place, model",
|
||||||
|
attempted
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_upload(buffer: Vec<u8>, asset_id: u64, cookie: &str) -> anyhow::Result<()> {
|
fn do_upload(buffer: Vec<u8>, asset_id: u64, cookie: &str) -> anyhow::Result<()> {
|
||||||
@@ -76,10 +117,10 @@ fn do_upload(buffer: Vec<u8>, asset_id: u64, cookie: &str) -> anyhow::Result<()>
|
|||||||
|
|
||||||
let status = response.status();
|
let status = response.status();
|
||||||
if !status.is_success() {
|
if !status.is_success() {
|
||||||
return Err(Error::RobloxApi {
|
bail!(
|
||||||
body: response.text()?,
|
"The Roblox API returned an unexpected error: {}",
|
||||||
}
|
response.text()?
|
||||||
.into());
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
//! Defines module for defining a small Lua AST for simple codegen.
|
//! Defines module for defining a small Lua AST for simple codegen. Rojo uses
|
||||||
|
//! this module to convert JSON into generated Lua code.
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fmt::{self, Write},
|
fmt::{self, Write},
|
||||||
|
|||||||
@@ -3,20 +3,7 @@ use std::{env, panic, process};
|
|||||||
use backtrace::Backtrace;
|
use backtrace::Backtrace;
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
use librojo::cli::{self, GlobalOptions, Options, Subcommand};
|
use librojo::cli::Options;
|
||||||
|
|
||||||
fn run(global: GlobalOptions, subcommand: Subcommand) -> anyhow::Result<()> {
|
|
||||||
match subcommand {
|
|
||||||
Subcommand::Init(init_options) => cli::init(init_options)?,
|
|
||||||
Subcommand::Serve(serve_options) => cli::serve(global, serve_options)?,
|
|
||||||
Subcommand::Build(build_options) => cli::build(build_options)?,
|
|
||||||
Subcommand::Upload(upload_options) => cli::upload(upload_options)?,
|
|
||||||
Subcommand::Doc => cli::doc()?,
|
|
||||||
Subcommand::Plugin(plugin_options) => cli::plugin(plugin_options)?,
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
panic::set_hook(Box::new(|panic_info| {
|
panic::set_hook(Box::new(|panic_info| {
|
||||||
@@ -81,7 +68,7 @@ fn main() {
|
|||||||
.write_style(options.global.color.into())
|
.write_style(options.global.color.into())
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
if let Err(err) = run(options.global, options.subcommand) {
|
if let Err(err) = options.run() {
|
||||||
log::error!("{:?}", err);
|
log::error!("{:?}", err);
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
}
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user