mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-04-20 12:45:05 +00:00
Compare commits
54 Commits
v0.5.0-alp
...
v0.5.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
90661b7743 | ||
|
|
d07571ea7e | ||
|
|
fbf29e336f | ||
|
|
09a0a803a1 | ||
|
|
dd0327ba85 | ||
|
|
d900887d97 | ||
|
|
2a0efe70a5 | ||
|
|
ce09e57315 | ||
|
|
91023c5239 | ||
|
|
714fb10fac | ||
|
|
aa3e43207f | ||
|
|
e045989d39 | ||
|
|
ad5695210d | ||
|
|
4dab6e5008 | ||
|
|
522f26cf4e | ||
|
|
3eca4bc439 | ||
|
|
b374f67b52 | ||
|
|
c68277be2c | ||
|
|
bb8a3e82e6 | ||
|
|
b511d4ba53 | ||
|
|
fd997d4bda | ||
|
|
21d04a9f85 | ||
|
|
dcb5c12197 | ||
|
|
125e8766c5 | ||
|
|
7bce1f6df4 | ||
|
|
8f66fb6fef | ||
|
|
711e009e6d | ||
|
|
212fe31cb3 | ||
|
|
a3dc4fa001 | ||
|
|
ff53113358 | ||
|
|
94cbe15b54 | ||
|
|
90516e035d | ||
|
|
c77c754f6d | ||
|
|
288c52a2cd | ||
|
|
f0fa7326dd | ||
|
|
f29b0f2f26 | ||
|
|
5dcac24f99 | ||
|
|
1eb11ac377 | ||
|
|
2e89cdcfad | ||
|
|
1b0beccd3d | ||
|
|
abb5a72fc4 | ||
|
|
bf706f7586 | ||
|
|
4459663510 | ||
|
|
68a34dc28b | ||
|
|
ba1826587c | ||
|
|
2e7a8d50b0 | ||
|
|
2a4ca21050 | ||
|
|
0ed6c57c7f | ||
|
|
983d44947e | ||
|
|
5bd88dc82f | ||
|
|
51bbab803f | ||
|
|
a587ba4558 | ||
|
|
075b6cca30 | ||
|
|
4c263bbb3e |
143
CHANGELOG.md
143
CHANGELOG.md
@@ -1,9 +1,26 @@
|
||||
# Rojo Changelog
|
||||
|
||||
## [Unreleased]
|
||||
## [0.5.0 Alpha 12](https://github.com/rojo-rbx/rojo/releases/tag/v0.5.0-alpha.12) (July 2, 2019)
|
||||
* Added `.meta.json` files
|
||||
* `init.meta.json` files replace `init.model.json` files from Rojo 0.4.x ([#183](https://github.com/rojo-rbx/rojo/pull/183))
|
||||
* Other `.meta.json` files allow attaching extra data to other files ([#189](https://github.com/rojo-rbx/rojo/pull/189))
|
||||
* Added support for infinite and NaN values in types like `Vector2` when building models and places.
|
||||
* These types aren't supported for live-syncing yet due to limitations around JSON encoding.
|
||||
* Added support for using `SharedString` values when building XML models and places.
|
||||
* Added support for live-syncing `CollectionService` tags.
|
||||
* Added a warning when building binary place files, since they're still experimental and have bugs.
|
||||
* Added a warning when trying to use Rojo 0.5.x with a Rojo 0.4.x-only project.
|
||||
* Added a warning when a Rojo project contains keys that start with `$`, which are reserved names. ([#191](https://github.com/rojo-rbx/rojo/issues/191))
|
||||
* Rojo now throws an error if unknown keys are found most files.
|
||||
* Added an icon to the plugin's toolbar button
|
||||
* Changed the plugin to use a docking widget for all UI.
|
||||
* Changed the plugin to ignore unknown properties when live-syncing.
|
||||
* Rojo's approach to this problem might change later, like with a strict model mode ([#190](https://github.com/rojo-rbx/rojo/issues/190)) or another approach.
|
||||
* Upgraded to reflection database from client release 388.
|
||||
* Updated Rojo's branding to shift the color palette to make it work better on dark backgrounds
|
||||
|
||||
## [0.5.0 Alpha 11](https://github.com/LPGhatguy/rojo/releases/tag/v0.5.0-alpha.11) (May 29, 2019)
|
||||
* Added support for implicit property values in JSON model files ([#154](https://github.com/LPGhatguy/rojo/pull/154))
|
||||
## [0.5.0 Alpha 11](https://github.com/rojo-rbx/rojo/releases/tag/v0.5.0-alpha.11) (May 29, 2019)
|
||||
* Added support for implicit property values in JSON model files ([#154](https://github.com/rojo-rbx/rojo/pull/154))
|
||||
* `Content` propertyes can now be specified in projects and model files as regular string literals.
|
||||
* Added support for `BrickColor` properties.
|
||||
* Added support for properties added in client release 384, like `Lighting.Technology` being set to `"ShadowMap"`.
|
||||
@@ -14,16 +31,16 @@
|
||||
* Plugin should now be able to live-sync more properties, and ignore ones it can't, like `Lighting.Technology`.
|
||||
|
||||
## 0.5.0 Alpha 10
|
||||
* This release was a dud due to [issue #176](https://github.com/LPGhatguy/rojo/issues/176) and was rolled back.
|
||||
* This release was a dud due to [issue #176](https://github.com/rojo-rbx/rojo/issues/176) and was rolled back.
|
||||
|
||||
## [0.5.0 Alpha 9](https://github.com/LPGhatguy/rojo/releases/tag/v0.5.0-alpha.9) (April 4, 2019)
|
||||
## [0.5.0 Alpha 9](https://github.com/rojo-rbx/rojo/releases/tag/v0.5.0-alpha.9) (April 4, 2019)
|
||||
* Changed `rojo build` to use buffered I/O, which can make it up to 2x faster in some cases.
|
||||
* Building [*Road Not Taken*](https://github.com/LPGhatguy/roads) to an `rbxlx` file dropped from 150ms to 70ms on my machine
|
||||
* Fixed `LocalizationTable` instances being made from `csv` files incorrectly interpreting empty rows and columns. ([#149](https://github.com/LPGhatguy/rojo/pull/149))
|
||||
* Fixed CSV files with entries that parse as numbers causing Rojo to panic. ([#152](https://github.com/LPGhatguy/rojo/pull/152))
|
||||
* Building [*Road Not Taken*](https://github.com/rojo-rbx/roads) to an `rbxlx` file dropped from 150ms to 70ms on my machine
|
||||
* Fixed `LocalizationTable` instances being made from `csv` files incorrectly interpreting empty rows and columns. ([#149](https://github.com/rojo-rbx/rojo/pull/149))
|
||||
* Fixed CSV files with entries that parse as numbers causing Rojo to panic. ([#152](https://github.com/rojo-rbx/rojo/pull/152))
|
||||
* Improved error messages when malformed CSV files are found in a Rojo project.
|
||||
|
||||
## [0.5.0 Alpha 8](https://github.com/LPGhatguy/rojo/releases/tag/v0.5.0-alpha.8) (March 29, 2019)
|
||||
## [0.5.0 Alpha 8](https://github.com/rojo-rbx/rojo/releases/tag/v0.5.0-alpha.8) (March 29, 2019)
|
||||
* Added support for a bunch of new types when dealing with XML model/place files:
|
||||
* `ColorSequence`
|
||||
* `Float64`
|
||||
@@ -34,13 +51,13 @@
|
||||
* `Ray`
|
||||
* `Rect`
|
||||
* `Ref`
|
||||
* Improved server instance ordering behavior when files are added during a live session ([#135](https://github.com/LPGhatguy/rojo/pull/135))
|
||||
* Improved server instance ordering behavior when files are added during a live session ([#135](https://github.com/rojo-rbx/rojo/pull/135))
|
||||
* Fixed error being thrown when trying to unload the Rojo plugin.
|
||||
* Added partial fix for [issue #141](https://github.com/LPGhatguy/rojo/issues/141) for `Lighting.Technology`, which should restore live sync functionality for the default project file.
|
||||
* Added partial fix for [issue #141](https://github.com/rojo-rbx/rojo/issues/141) for `Lighting.Technology`, which should restore live sync functionality for the default project file.
|
||||
|
||||
## [0.5.0 Alpha 6](https://github.com/LPGhatguy/rojo/releases/tag/v0.5.0-alpha.6) (March 19, 2019)
|
||||
## [0.5.0 Alpha 6](https://github.com/rojo-rbx/rojo/releases/tag/v0.5.0-alpha.6) (March 19, 2019)
|
||||
* Fixed `rojo init` giving unexpected results by upgrading to `rbx_dom_weak` 1.1.0
|
||||
* Fixed live server not responding when the Rojo plugin is connected ([#133](https://github.com/LPGhatguy/rojo/issues/133))
|
||||
* Fixed live server not responding when the Rojo plugin is connected ([#133](https://github.com/rojo-rbx/rojo/issues/133))
|
||||
* Updated default place file:
|
||||
* Improved default properties to be closer to Studio's built-in 'Baseplate' template
|
||||
* Added a baseplate to the project file (Thanks, [@AmaranthineCodices](https://github.com/AmaranthineCodices/)!)
|
||||
@@ -48,40 +65,40 @@
|
||||
* Fixed some cases where the Rojo plugin would leave around objects that it knows should be deleted
|
||||
* Updated plugin to correctly listen to `Plugin.Unloading` when installing or uninstalling new plugins
|
||||
|
||||
## [0.5.0 Alpha 5](https://github.com/LPGhatguy/rojo/releases/tag/v0.5.0-alpha.5) (March 1, 2019)
|
||||
## [0.5.0 Alpha 5](https://github.com/rojo-rbx/rojo/releases/tag/v0.5.0-alpha.5) (March 1, 2019)
|
||||
* Upgraded core dependencies, which improves compatibility for lots of instance types
|
||||
* Upgraded from `rbx_tree` 0.2.0 to `rbx_dom_weak` 1.0.0
|
||||
* Upgraded from `rbx_xml` 0.2.0 to `rbx_xml` 0.4.0
|
||||
* Upgraded from `rbx_binary` 0.2.0 to `rbx_binary` 0.4.0
|
||||
* Added support for non-primitive types in the Rojo plugin.
|
||||
* Types like `Color3` and `CFrame` can now be updated live!
|
||||
* Fixed plugin assets flashing in on first load ([#121](https://github.com/LPGhatguy/rojo/issues/121))
|
||||
* Fixed plugin assets flashing in on first load ([#121](https://github.com/rojo-rbx/rojo/issues/121))
|
||||
* Changed Rojo's HTTP server from Rouille to Hyper, which reduced the release size by around a megabyte.
|
||||
* Added property type inference to projects, which makes specifying services a lot easier ([#130](https://github.com/LPGhatguy/rojo/pull/130))
|
||||
* Added property type inference to projects, which makes specifying services a lot easier ([#130](https://github.com/rojo-rbx/rojo/pull/130))
|
||||
* Made error messages from invalid and missing files more user-friendly
|
||||
|
||||
## [0.5.0 Alpha 4](https://github.com/LPGhatguy/rojo/releases/tag/v0.5.0-alpha.4) (February 8, 2019)
|
||||
* Added support for nested partitions ([#102](https://github.com/LPGhatguy/rojo/issues/102))
|
||||
* Added support for 'transmuting' partitions ([#112](https://github.com/LPGhatguy/rojo/issues/112))
|
||||
* Added support for aliasing filesystem paths ([#105](https://github.com/LPGhatguy/rojo/issues/105))
|
||||
* Changed Windows builds to statically link the CRT ([#89](https://github.com/LPGhatguy/rojo/issues/89))
|
||||
## [0.5.0 Alpha 4](https://github.com/rojo-rbx/rojo/releases/tag/v0.5.0-alpha.4) (February 8, 2019)
|
||||
* Added support for nested partitions ([#102](https://github.com/rojo-rbx/rojo/issues/102))
|
||||
* Added support for 'transmuting' partitions ([#112](https://github.com/rojo-rbx/rojo/issues/112))
|
||||
* Added support for aliasing filesystem paths ([#105](https://github.com/rojo-rbx/rojo/issues/105))
|
||||
* Changed Windows builds to statically link the CRT ([#89](https://github.com/rojo-rbx/rojo/issues/89))
|
||||
|
||||
## [0.5.0 Alpha 3](https://github.com/LPGhatguy/rojo/releases/tag/v0.5.0-alpha.3) (February 1, 2019)
|
||||
* Changed default project file name from `roblox-project.json` to `default.project.json` ([#120](https://github.com/LPGhatguy/rojo/pull/120))
|
||||
## [0.5.0 Alpha 3](https://github.com/rojo-rbx/rojo/releases/tag/v0.5.0-alpha.3) (February 1, 2019)
|
||||
* Changed default project file name from `roblox-project.json` to `default.project.json` ([#120](https://github.com/rojo-rbx/rojo/pull/120))
|
||||
* The old file name will still be supported until 0.5.0 is fully released.
|
||||
* Added warning when loading project files that don't end in `.project.json`
|
||||
* This new extension enables Rojo to distinguish project files from random JSON files, which is necessary to support nested projects.
|
||||
* Added new (empty) diagnostic page served from the server
|
||||
* Added better error messages for when a file is missing that's referenced by a Rojo project
|
||||
* Added support for visualization endpoints returning GraphViz source when Dot is not available
|
||||
* Fixed an in-memory filesystem regression introduced recently ([#119](https://github.com/LPGhatguy/rojo/pull/119))
|
||||
* Fixed an in-memory filesystem regression introduced recently ([#119](https://github.com/rojo-rbx/rojo/pull/119))
|
||||
|
||||
## [0.5.0 Alpha 2](https://github.com/LPGhatguy/rojo/releases/tag/v0.5.0-alpha.2) (January 28, 2019)
|
||||
## [0.5.0 Alpha 2](https://github.com/rojo-rbx/rojo/releases/tag/v0.5.0-alpha.2) (January 28, 2019)
|
||||
* Added support for `.model.json` files, compatible with 0.4.x
|
||||
* Fixed in-memory filesystem not handling out-of-order filesystem change events
|
||||
* Fixed long-polling error caused by a promise mixup ([#110](https://github.com/LPGhatguy/rojo/issues/110))
|
||||
* Fixed long-polling error caused by a promise mixup ([#110](https://github.com/rojo-rbx/rojo/issues/110))
|
||||
|
||||
## [0.5.0 Alpha 1](https://github.com/LPGhatguy/rojo/releases/tag/v0.5.0-alpha.1) (January 25, 2019)
|
||||
## [0.5.0 Alpha 1](https://github.com/rojo-rbx/rojo/releases/tag/v0.5.0-alpha.1) (January 25, 2019)
|
||||
* Changed plugin UI to be way prettier
|
||||
* Thanks to [Reselim](https://github.com/Reselim) for the design!
|
||||
* Changed plugin error messages to be a little more useful
|
||||
@@ -89,7 +106,7 @@
|
||||
* Fixed bug where bad server responses could cause the plugin to be in a bad state
|
||||
* Upgraded to rbx\_tree, rbx\_xml, and rbx\_binary 0.2.0, which dramatically expands the kinds of properties that Rojo can handle, especially in XML.
|
||||
|
||||
## [0.5.0 Alpha 0](https://github.com/LPGhatguy/rojo/releases/tag/v0.5.0-alpha.0) (January 14, 2019)
|
||||
## [0.5.0 Alpha 0](https://github.com/rojo-rbx/rojo/releases/tag/v0.5.0-alpha.0) (January 14, 2019)
|
||||
* "Epiphany" rewrite, in progress since the beginning of time
|
||||
* New live sync protocol
|
||||
* Uses HTTP long polling to reduce request count and improve responsiveness
|
||||
@@ -114,105 +131,105 @@
|
||||
* Multiple places can be specified, like when building a multi-place game
|
||||
* Added support for specifying properties on services in project files
|
||||
|
||||
## [0.4.13](https://github.com/LPGhatguy/rojo/releases/tag/v0.4.13) (November 12, 2018)
|
||||
## [0.4.13](https://github.com/rojo-rbx/rojo/releases/tag/v0.4.13) (November 12, 2018)
|
||||
* When `rojo.json` points to a file or directory that does not exist, Rojo now issues a warning instead of throwing an error and exiting
|
||||
|
||||
## [0.4.12](https://github.com/LPGhatguy/rojo/releases/tag/v0.4.12) (June 21, 2018)
|
||||
* Fixed obscure assertion failure when renaming or deleting files ([#78](https://github.com/LPGhatguy/rojo/issues/78))
|
||||
* Added a `PluginAction` for the sync in command, which should help with some automation scripts ([#80](https://github.com/LPGhatguy/rojo/pull/80))
|
||||
## [0.4.12](https://github.com/rojo-rbx/rojo/releases/tag/v0.4.12) (June 21, 2018)
|
||||
* Fixed obscure assertion failure when renaming or deleting files ([#78](https://github.com/rojo-rbx/rojo/issues/78))
|
||||
* Added a `PluginAction` for the sync in command, which should help with some automation scripts ([#80](https://github.com/rojo-rbx/rojo/pull/80))
|
||||
|
||||
## [0.4.11](https://github.com/LPGhatguy/rojo/releases/tag/v0.4.11) (June 10, 2018)
|
||||
## [0.4.11](https://github.com/rojo-rbx/rojo/releases/tag/v0.4.11) (June 10, 2018)
|
||||
* Defensively insert existing instances into RouteMap; should fix most duplication cases when syncing into existing trees.
|
||||
* Fixed incorrect synchronization from `Plugin:_pull` that would cause polling to create issues
|
||||
* Fixed incorrect file routes being assigned to `init.lua` and `init.model.json` files
|
||||
* Untangled route handling-internals slightly
|
||||
|
||||
## [0.4.10](https://github.com/LPGhatguy/rojo/releases/tag/v0.4.10) (June 2, 2018)
|
||||
* Added support for `init.model.json` files, which enable versioning `Tool` instances (among other things) with Rojo. ([#66](https://github.com/LPGhatguy/rojo/issues/66))
|
||||
## [0.4.10](https://github.com/rojo-rbx/rojo/releases/tag/v0.4.10) (June 2, 2018)
|
||||
* Added support for `init.model.json` files, which enable versioning `Tool` instances (among other things) with Rojo. ([#66](https://github.com/rojo-rbx/rojo/issues/66))
|
||||
* Fixed obscure error when syncing into an invalid service.
|
||||
* Fixed multiple sync processes occurring when a server ID mismatch is detected.
|
||||
|
||||
## [0.4.9](https://github.com/LPGhatguy/rojo/releases/tag/v0.4.9) (May 26, 2018)
|
||||
* Fixed warning when renaming or removing files that would sometimes corrupt the instance cache ([#72](https://github.com/LPGhatguy/rojo/pull/72))
|
||||
## [0.4.9](https://github.com/rojo-rbx/rojo/releases/tag/v0.4.9) (May 26, 2018)
|
||||
* Fixed warning when renaming or removing files that would sometimes corrupt the instance cache ([#72](https://github.com/rojo-rbx/rojo/pull/72))
|
||||
* JSON models are no longer as strict -- `Children` and `Properties` are now optional.
|
||||
|
||||
## [0.4.8](https://github.com/LPGhatguy/rojo/releases/tag/v0.4.8) (May 26, 2018)
|
||||
## [0.4.8](https://github.com/rojo-rbx/rojo/releases/tag/v0.4.8) (May 26, 2018)
|
||||
* Hotfix to prevent errors from being thrown when objects managed by Rojo are deleted
|
||||
|
||||
## [0.4.7](https://github.com/LPGhatguy/rojo/releases/tag/v0.4.7) (May 25, 2018)
|
||||
* Added icons to the Rojo plugin, made by [@Vorlias](https://github.com/Vorlias)! ([#70](https://github.com/LPGhatguy/rojo/pull/70))
|
||||
* Server will now issue a warning if no partitions are specified in `rojo serve` ([#40](https://github.com/LPGhatguy/rojo/issues/40))
|
||||
## [0.4.7](https://github.com/rojo-rbx/rojo/releases/tag/v0.4.7) (May 25, 2018)
|
||||
* Added icons to the Rojo plugin, made by [@Vorlias](https://github.com/Vorlias)! ([#70](https://github.com/rojo-rbx/rojo/pull/70))
|
||||
* Server will now issue a warning if no partitions are specified in `rojo serve` ([#40](https://github.com/rojo-rbx/rojo/issues/40))
|
||||
|
||||
## [0.4.6](https://github.com/LPGhatguy/rojo/releases/tag/v0.4.6) (May 21, 2018)
|
||||
* Rojo handles being restarted by Roblox Studio more gracefully ([#67](https://github.com/LPGhatguy/rojo/issues/67))
|
||||
## [0.4.6](https://github.com/rojo-rbx/rojo/releases/tag/v0.4.6) (May 21, 2018)
|
||||
* Rojo handles being restarted by Roblox Studio more gracefully ([#67](https://github.com/rojo-rbx/rojo/issues/67))
|
||||
* Folders should no longer get collapsed when syncing occurs.
|
||||
* **Significant** robustness improvements with regards to caching.
|
||||
* **This should catch all existing script duplication bugs.**
|
||||
* If there are any bugs with script duplication or caching in the future, restarting the Rojo server process will fix them for that session.
|
||||
* Fixed message in plugin not being prefixed with `Rojo: `.
|
||||
|
||||
## [0.4.5](https://github.com/LPGhatguy/rojo/releases/tag/v0.4.5) (May 1, 2018)
|
||||
## [0.4.5](https://github.com/rojo-rbx/rojo/releases/tag/v0.4.5) (May 1, 2018)
|
||||
* Rojo messages are now prefixed with `Rojo: ` to make them stand out in the output more.
|
||||
* Fixed server to notice file changes *much* more quickly. (200ms vs 1000ms)
|
||||
* Server now lists name of project when starting up.
|
||||
* Rojo now throws an error if no project file is found. ([#63](https://github.com/LPGhatguy/rojo/issues/63))
|
||||
* Fixed multiple sync operations occuring at the same time. ([#61](https://github.com/LPGhatguy/rojo/issues/61))
|
||||
* Partitions targeting files directly now work as expected. ([#57](https://github.com/LPGhatguy/rojo/issues/57))
|
||||
* Rojo now throws an error if no project file is found. ([#63](https://github.com/rojo-rbx/rojo/issues/63))
|
||||
* Fixed multiple sync operations occuring at the same time. ([#61](https://github.com/rojo-rbx/rojo/issues/61))
|
||||
* Partitions targeting files directly now work as expected. ([#57](https://github.com/rojo-rbx/rojo/issues/57))
|
||||
|
||||
## [0.4.4](https://github.com/LPGhatguy/rojo/releases/tag/v0.4.4) (April 7, 2018)
|
||||
## [0.4.4](https://github.com/rojo-rbx/rojo/releases/tag/v0.4.4) (April 7, 2018)
|
||||
* Fix small regression introduced in 0.4.3
|
||||
|
||||
## [0.4.3](https://github.com/LPGhatguy/rojo/releases/tag/v0.4.3) (April 7, 2018)
|
||||
* Plugin now automatically selects `HttpService` if it determines that HTTP isn't enabled ([#58](https://github.com/LPGhatguy/rojo/pull/58))
|
||||
## [0.4.3](https://github.com/rojo-rbx/rojo/releases/tag/v0.4.3) (April 7, 2018)
|
||||
* Plugin now automatically selects `HttpService` if it determines that HTTP isn't enabled ([#58](https://github.com/rojo-rbx/rojo/pull/58))
|
||||
* Plugin now has much more robust handling and will wipe all state when the server changes.
|
||||
* This should fix issues that would otherwise be solved by restarting Roblox Studio.
|
||||
|
||||
## [0.4.2](https://github.com/LPGhatguy/rojo/releases/tag/v0.4.2) (April 4, 2018)
|
||||
## [0.4.2](https://github.com/rojo-rbx/rojo/releases/tag/v0.4.2) (April 4, 2018)
|
||||
* Fixed final case of duplicated instance insertion, caused by reconciled instances not being inserted into `RouteMap`.
|
||||
* The reconciler is still not a perfect solution, especially if script instances get moved around without being destroyed. I don't think this can be fixed before a big refactor.
|
||||
|
||||
## [0.4.1](https://github.com/LPGhatguy/rojo/releases/tag/v0.4.1) (April 1, 2018)
|
||||
## [0.4.1](https://github.com/rojo-rbx/rojo/releases/tag/v0.4.1) (April 1, 2018)
|
||||
* Merged plugin repository into main Rojo repository for easier tracking.
|
||||
* Improved `RouteMap` object tracking; this should fix some cases of duplicated instances being synced into the tree.
|
||||
|
||||
## [0.4.0](https://github.com/LPGhatguy/rojo/releases/tag/v0.4.0) (March 27, 2018)
|
||||
## [0.4.0](https://github.com/rojo-rbx/rojo/releases/tag/v0.4.0) (March 27, 2018)
|
||||
* Protocol version 1, which shifts more responsibility onto the server
|
||||
* This is a **major breaking** change!
|
||||
* The server now has a content of 'filter plugins', which transform data at various stages in the pipeline
|
||||
* The server now exposes Roblox instance objects instead of file contents, which lines up with how `rojo pack` will work, and paves the way for more robust syncing.
|
||||
* Added `*.model.json` files, which let you embed small Roblox objects into your Rojo tree.
|
||||
* Improved error messages in some cases ([#46](https://github.com/LPGhatguy/rojo/issues/46))
|
||||
* Improved error messages in some cases ([#46](https://github.com/rojo-rbx/rojo/issues/46))
|
||||
|
||||
## [0.3.2](https://github.com/LPGhatguy/rojo/releases/tag/v0.3.2) (December 20, 2017)
|
||||
## [0.3.2](https://github.com/rojo-rbx/rojo/releases/tag/v0.3.2) (December 20, 2017)
|
||||
* Fixed `rojo serve` failing to correctly construct an absolute root path when passed as an argument
|
||||
* Fixed intense CPU usage when running `rojo serve`
|
||||
|
||||
## [0.3.1](https://github.com/LPGhatguy/rojo/releases/tag/v0.3.1) (December 14, 2017)
|
||||
## [0.3.1](https://github.com/rojo-rbx/rojo/releases/tag/v0.3.1) (December 14, 2017)
|
||||
* Improved error reporting when invalid JSON is found in a `rojo.json` project
|
||||
* These messages are passed on from Serde
|
||||
|
||||
## [0.3.0](https://github.com/LPGhatguy/rojo/releases/tag/v0.3.0) (December 12, 2017)
|
||||
## [0.3.0](https://github.com/rojo-rbx/rojo/releases/tag/v0.3.0) (December 12, 2017)
|
||||
* Factored out the plugin into a separate repository
|
||||
* Fixed server when using a file as a partition
|
||||
* Previously, trailing slashes were put on the end of a partition even if the read request was an empty string. This broke file reading on Windows when a partition pointed to a file instead of a directory!
|
||||
* Started running automatic tests on Travis CI (#9)
|
||||
|
||||
## [0.2.3](https://github.com/LPGhatguy/rojo/releases/tag/v0.2.3) (December 4, 2017)
|
||||
## [0.2.3](https://github.com/rojo-rbx/rojo/releases/tag/v0.2.3) (December 4, 2017)
|
||||
* Plugin only release
|
||||
* Tightened `init` file rules to only match script files
|
||||
* Previously, Rojo would sometimes pick up the wrong file when syncing
|
||||
|
||||
## [0.2.2](https://github.com/LPGhatguy/rojo/releases/tag/v0.2.2) (December 1, 2017)
|
||||
## [0.2.2](https://github.com/rojo-rbx/rojo/releases/tag/v0.2.2) (December 1, 2017)
|
||||
* Plugin only release
|
||||
* Fixed broken reconciliation behavior with `init` files
|
||||
|
||||
## [0.2.1](https://github.com/LPGhatguy/rojo/releases/tag/v0.2.1) (December 1, 2017)
|
||||
## [0.2.1](https://github.com/rojo-rbx/rojo/releases/tag/v0.2.1) (December 1, 2017)
|
||||
* Plugin only release
|
||||
* Changes default port to 8000
|
||||
|
||||
## [0.2.0](https://github.com/LPGhatguy/rojo/releases/tag/v0.2.0) (December 1, 2017)
|
||||
## [0.2.0](https://github.com/rojo-rbx/rojo/releases/tag/v0.2.0) (December 1, 2017)
|
||||
* Support for `init.lua` like rbxfs and rbxpacker
|
||||
* More robust syncing with a new reconciler
|
||||
|
||||
## [0.1.0](https://github.com/LPGhatguy/rojo/releases/tag/v0.1.0) (November 29, 2017)
|
||||
* Initial release, functionally very similar to [rbxfs](https://github.com/LPGhatguy/rbxfs)
|
||||
## [0.1.0](https://github.com/rojo-rbx/rojo/releases/tag/v0.1.0) (November 29, 2017)
|
||||
* Initial release, functionally very similar to [rbxfs](https://github.com/rojo-rbx/rbxfs)
|
||||
568
Cargo.lock
generated
568
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
58
README.md
58
README.md
@@ -1,68 +1,56 @@
|
||||
<div align="center">
|
||||
<img src="assets/rojo-logo.png" alt="Rojo" height="217" />
|
||||
<a href="https://rojo.space">
|
||||
<img src="assets/rojo-logo.png" alt="Rojo" height="217" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div> </div>
|
||||
|
||||
<div align="center">
|
||||
<a href="https://travis-ci.org/LPGhatguy/rojo">
|
||||
<img src="https://api.travis-ci.org/LPGhatguy/rojo.svg?branch=master" alt="Travis-CI Build Status" />
|
||||
<a href="https://travis-ci.org/rojo-rbx/rojo">
|
||||
<img src="https://api.travis-ci.org/rojo-rbx/rojo.svg?branch=master" alt="Travis-CI Build Status" />
|
||||
</a>
|
||||
<a href="https://crates.io/crates/rojo">
|
||||
<img src="https://img.shields.io/crates/v/rojo.svg?label=version" alt="Latest server version" />
|
||||
</a>
|
||||
<a href="https://lpghatguy.github.io/rojo/0.4.x">
|
||||
<img src="https://img.shields.io/badge/docs-0.4.x-brightgreen.svg" alt="Rojo Documentation" />
|
||||
<a href="https://rojo.space/docs/0.4.x">
|
||||
<img src="https://img.shields.io/badge/docs-0.4.x-brightgreen.svg" alt="Rojo 0.4.x Documentation" />
|
||||
</a>
|
||||
<a href="https://lpghatguy.github.io/rojo/0.5.x">
|
||||
<img src="https://img.shields.io/badge/docs-0.5.x-brightgreen.svg" alt="Rojo Documentation" />
|
||||
<a href="https://rojo.space/docs/0.5.x">
|
||||
<img src="https://img.shields.io/badge/docs-0.5.x-brightgreen.svg" alt="Rojo 0.5.x Documentation" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
**Rojo** is a flexible multi-tool designed for creating robust Roblox projects.
|
||||
**Rojo** is a tool designed to enable Roblox developers to use professional-grade software engineering tools.
|
||||
|
||||
It lets Roblox developers use industry-leading tools like Git and VS Code, and crucial utilities like Luacheck.
|
||||
With Rojo, it's possible to use industry-leading tools like **Visual Studio Code** and **Git**.
|
||||
|
||||
Rojo is designed for **power users** who want to use the **best tools available** for building games, libraries, and plugins.
|
||||
Rojo is designed for power users who want to use the best tools available for building games, libraries, and plugins.
|
||||
|
||||
## Features
|
||||
Rojo lets you:
|
||||
Rojo enables:
|
||||
|
||||
* Work on scripts from the filesystem, in your favorite editor
|
||||
* Version your place, model, or plugin using Git or another VCS
|
||||
* Sync `rbxmx` and `rbxm` models into your game in real time
|
||||
* Package and deploy your project to Roblox.com from the command line
|
||||
* Working on scripts and models from the filesystem, in your favorite editor
|
||||
* Versioning your game, library, or plugin using Git or another VCS
|
||||
* Streaming `rbxmx` and `rbxm` models into your game in real time
|
||||
* Packaging and deploying your project to Roblox.com from the command line
|
||||
|
||||
Soon, Rojo will be able to:
|
||||
|
||||
* Automatically convert your existing game to work with Rojo
|
||||
* Sync instances from Roblox Studio to the filesystem
|
||||
* Compile MoonScript and other custom things for your project
|
||||
* Automatically manage your assets on Roblox.com, like images and sounds
|
||||
* Import custom instances like MoonScript code
|
||||
|
||||
## [Documentation](https://lpghatguy.github.io/rojo)
|
||||
You can also view the documentation by browsing the [docs](https://github.com/LPGhatguy/rojo/tree/master/docs) folder of the repository, but because it uses a number of Markdown extensions, it may not be very readable.
|
||||
|
||||
## Inspiration and Alternatives
|
||||
There are lots of other tools that sync scripts into Roblox or provide other tools for working with Roblox places.
|
||||
|
||||
Here are a few, if you're looking for alternatives or supplements to Rojo:
|
||||
|
||||
* [rbxmk by Anaminus](https://github.com/anaminus/rbxmk)
|
||||
* [Rofresh by Osyris](https://github.com/osyrisrblx/rofresh)
|
||||
* [RbxRefresh by Osyris](https://github.com/osyrisrblx/RbxRefresh)
|
||||
* [Studio Bridge by Vocksel](https://github.com/vocksel/studio-bridge)
|
||||
* [Elixir by Vocksel](https://github.com/vocksel/elixir)
|
||||
* [RbxSync by evaera](https://github.com/evaera/RbxSync)
|
||||
* [CodeSync by MemoryPenguin](https://github.com/MemoryPenguin/CodeSync)
|
||||
* [rbx-exteditor by MemoryPenguin](https://github.com/MemoryPenguin/rbx-exteditor)
|
||||
|
||||
If you use a plugin that _isn't_ Rojo for syncing code, open an issue and let me know why! I'd like Rojo to be the end-all tool so that people stop reinventing solutions to this problem.
|
||||
## [Documentation](https://rojo.space/docs/latest)
|
||||
If you find any mistakes, feel free to make changes in the [docs](https://github.com/rojo-rbx/rojo/tree/master/docs) folder of this repository and submit a pull request!
|
||||
|
||||
## Contributing
|
||||
Pull requests are welcome!
|
||||
|
||||
Rojo supports Rust 1.32 and newer. Any changes to the minimum required compiler version require a _minor_ version bump.
|
||||
Rojo supports Rust 1.32 and newer.
|
||||
|
||||
## License
|
||||
Rojo is available under the terms of the Mozilla Public License, Version 2.0. See [LICENSE.txt](LICENSE.txt) for details.
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 42 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 43 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 375 B |
Binary file not shown.
|
Before Width: | Height: | Size: 382 B |
Binary file not shown.
|
Before Width: | Height: | Size: 430 B |
@@ -1,35 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Kludged documentation generator to support multiple versions.
|
||||
# Make sure the `site` folder is a checkout of this repository's `gh-pages`
|
||||
# branch.
|
||||
|
||||
set -e
|
||||
|
||||
REMOTE=$(git remote get-url origin)
|
||||
CHECKOUT="$(mktemp -d)"
|
||||
OUTPUT="$(pwd)/site"
|
||||
|
||||
if [ -d site ]
|
||||
then
|
||||
cd site
|
||||
git pull
|
||||
else
|
||||
git clone "$REMOTE" site
|
||||
cd site
|
||||
git checkout gh-pages
|
||||
fi
|
||||
|
||||
git clone "$REMOTE" "$CHECKOUT"
|
||||
cd "$CHECKOUT"
|
||||
|
||||
echo "Building master"
|
||||
git checkout master
|
||||
mkdocs build --site-dir "$OUTPUT"
|
||||
|
||||
echo "Building 0.5.x"
|
||||
mkdocs build --site-dir "$OUTPUT/0.5.x"
|
||||
|
||||
echo "Building 0.4.x"
|
||||
git checkout v0.4.x
|
||||
mkdocs build --site-dir "$OUTPUT/0.4.x"
|
||||
@@ -1,68 +0,0 @@
|
||||
[TOC]
|
||||
|
||||
## Creating the Rojo Project
|
||||
|
||||
To use Rojo to build a place, you'll need to create a new project file, which tells Rojo how your project is structured on-disk and in Roblox.
|
||||
|
||||
Create a new folder, then run `rojo init` inside that folder to initialize an empty project.
|
||||
|
||||
```sh
|
||||
mkdir my-new-project
|
||||
cd my-new-project
|
||||
|
||||
rojo init
|
||||
```
|
||||
|
||||
Rojo will make a small project file in your directory, named `default.project.json`. It'll make sure that any code in the directory `src` will get put into `ReplicatedStorage.Source`.
|
||||
|
||||
Speaking of, let's make sure we create a directory named `src`, and maybe a Lua file inside of it:
|
||||
|
||||
```sh
|
||||
mkdir src
|
||||
echo 'print("Hello, world!")' > src/hello.lua
|
||||
```
|
||||
|
||||
## Building Your Place
|
||||
Now that we have a project, one thing we can do is build a Roblox place file for our project. This is a great way to get started with a project quickly with no fuss.
|
||||
|
||||
All we have to do is call `rojo build`:
|
||||
|
||||
```sh
|
||||
rojo build -o MyNewProject.rbxlx
|
||||
```
|
||||
|
||||
If you open `MyNewProject.rbxlx` in Roblox Studio now, you should see a `Folder` containing a `ModuleScript` under `ReplicatedStorage`!
|
||||
|
||||
!!! info
|
||||
To generate a binary place file instead, use `rbxl`. Note that support for binary model/place files (`rbxm` and `rbxl`) is very limited in Rojo presently.
|
||||
|
||||
## Live-Syncing into Studio
|
||||
Building a place file is great for the initial build, but for actively working on your place, you'll want something quicker.
|
||||
|
||||
In Roblox Studio, make sure the Rojo plugin is installed. If you need it, check out [the installation guide](installation) to learn how to install it.
|
||||
|
||||
To expose your project to the plugin, you'll need to _serve_ it from the command line:
|
||||
|
||||
```sh
|
||||
rojo serve
|
||||
```
|
||||
|
||||
This will start up a web server that tells Roblox Studio what instances are in your project and sends notifications if any of them change.
|
||||
|
||||
Note the port number, then switch into Roblox Studio and press the Rojo **Connect** button in the plugins tab. Type in the port number, if necessary, and press **Start**.
|
||||
|
||||
If everything went well, you should now be able to change files in the `src` directory and watch them sync into Roblox Studio in real time!
|
||||
|
||||
## Uploading Your Place
|
||||
Aimed at teams that want serious levels of automation, Rojo can upload places to Roblox.com automatically.
|
||||
|
||||
You'll need an existing place on Roblox.com as well as the `.ROBLOSECURITY` cookie of an account that has write access to that place.
|
||||
|
||||
!!! warning
|
||||
It's recommended that you set up a Roblox account dedicated to deploying your place instead of your personal account in case your security cookie is compromised.
|
||||
|
||||
Generating and uploading your place file is as simple as:
|
||||
|
||||
```sh
|
||||
rojo upload --asset_id [PLACE ID] --cookie "[SECURITY COOKIE]"
|
||||
```
|
||||
@@ -1,3 +1,13 @@
|
||||
.md-typeset__table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.feature-image img {
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.codehilite {
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
11
docs/guide/existing-game.md
Normal file
11
docs/guide/existing-game.md
Normal file
@@ -0,0 +1,11 @@
|
||||
**This page is under construction!**
|
||||
|
||||
## Summary
|
||||
* Tools to port existing games are in progress!
|
||||
* [rbxlx-to-rojo](https://github.com/rojo/rbxlx-to-rojo)
|
||||
* `rojo export` ([issue #208](https://github.com/rojo-rbx/rojo/issues/208))
|
||||
* Can port as much or as little of your game as you like
|
||||
* Rojo can manage just a slice of your game!
|
||||
* Some Roblox idioms aren't very well supported
|
||||
* Redundant copies of scripts don't work well with files
|
||||
* Having only a couple places with scripts simplifies your project dramatically!
|
||||
@@ -1,7 +1,8 @@
|
||||
This is this installation guide for Rojo **0.5.x**.
|
||||
|
||||
[TOC]
|
||||
|
||||
## Overview
|
||||
|
||||
Rojo has two components:
|
||||
|
||||
* The command line interface (CLI)
|
||||
@@ -12,6 +13,9 @@ Rojo has two components:
|
||||
|
||||
The plugin will show errors in the Roblox Studio output window if there is a version mismatch.
|
||||
|
||||
## Visual Studio Code Extension
|
||||
If you use Visual Studio Code, you can install [the Rojo VS Code extension](https://marketplace.visualstudio.com/items?itemName=evaera.vscode-rojo), which will install both halves of Rojo for you. It even has a nifty UI to sync files and start/stop the Rojo server!
|
||||
|
||||
## Installing the CLI
|
||||
|
||||
### Installing from GitHub
|
||||
@@ -25,7 +29,7 @@ If you have Rust installed, the easiest way to get Rojo is with Cargo!
|
||||
To install the latest 0.5.0 alpha, use:
|
||||
|
||||
```sh
|
||||
cargo install rojo --version 0.5.0-alpha.11
|
||||
cargo install rojo --version 0.5.0-alpha.12
|
||||
```
|
||||
|
||||
## Installing the Plugin
|
||||
@@ -35,11 +39,8 @@ The Rojo Roblox Studio plugin is available from Rojo's [GitHub Releases page](ht
|
||||
|
||||
Download the attached `rbxm` file and put it into your Roblox Studio plugins folder. You can find that folder by pressing **Plugins Folder** from your Plugins toolbar in Roblox Studio:
|
||||
|
||||

|
||||

|
||||
{: align="center" }
|
||||
|
||||
### Installing from Roblox.com
|
||||
Visit [Rojo's Roblox.com Plugin page](https://www.roblox.com/library/1997686364/Rojo-0-5-0-alpha-3) in Roblox Studio and press **Install**.
|
||||
|
||||
## Visual Studio Code Extension
|
||||
If you use Visual Studio Code, you can install [Evaera's unofficial Rojo extension](https://marketplace.visualstudio.com/items?itemName=evaera.vscode-rojo), which will install both halves of Rojo for you. It even has a nifty UI to sync files and start/stop the Rojo server!
|
||||
Visit [Rojo's Roblox.com Plugin page](https://www.roblox.com/library/1997686364/Rojo-0-5-0-alpha-3) in Roblox Studio and press **Install**.
|
||||
@@ -55,4 +55,9 @@ All other values are considered children, where the key is the instance's name,
|
||||
## Migrating Unknown Files
|
||||
If you used Rojo to sync in files as `StringValue` objects, you'll need to make sure those files end with the `txt` extension to preserve this in Rojo 0.5.x.
|
||||
|
||||
Unknown files are now ignored in Rojo instead of being converted to `StringValue` objects.
|
||||
Unknown files are now ignored in Rojo instead of being converted to `StringValue` objects.
|
||||
|
||||
## Migrating `init.model.json` files
|
||||
In Rojo 0.4.x, it's possible to create a file named `init.model.json` that lets you describe a model that becomes the container for all of the other files in the folder, just like `init.lua`.
|
||||
|
||||
In Rojo 0.5.x, this feature has been replaced with `init.meta.json` files. See [Sync Details](../reference/sync-details) for more information about these new files.
|
||||
90
docs/guide/new-game.md
Normal file
90
docs/guide/new-game.md
Normal file
@@ -0,0 +1,90 @@
|
||||
[TOC]
|
||||
|
||||
## Creating the Rojo Project
|
||||
To use Rojo to build a game, you'll need to create a new project file, which tells Rojo how to turn your files into a Roblox place.
|
||||
|
||||
First, create a new folder to contain the files for your game and open up a new terminal inside of it, like cmd.exe or Bash.
|
||||
|
||||
It's convenient to make the folder from the command line:
|
||||
|
||||
```sh
|
||||
mkdir my-new-project
|
||||
cd my-new-project
|
||||
```
|
||||
|
||||
Inside the folder, initialize a new Rojo project:
|
||||
|
||||
```sh
|
||||
rojo init
|
||||
```
|
||||
|
||||
Rojo will make a small project file in your directory, named `default.project.json`. It matches the "Baseplate" template from Roblox Studio, except that it'll take any files you put in a folder called `src` and put it into `ReplicatedStorage.Source`.
|
||||
|
||||
Speaking of files, make sure to create a directory named `src` in this folder, or Rojo will be upset about missing files!
|
||||
|
||||
```sh
|
||||
mkdir src
|
||||
```
|
||||
|
||||
Let's also add a Lua file, `hello.lua` to the `src` folder, so that we can make this project our own.
|
||||
|
||||
```sh
|
||||
echo 'return "Hello, Rojo!"' > src/hello.lua
|
||||
```
|
||||
|
||||
## Building Your Place
|
||||
Now that we have a project, one thing we can do is build a Roblox place file for our project. This is a great way to get started with a project quickly with no fuss.
|
||||
|
||||
All we have to do is call `rojo build`:
|
||||
|
||||
```sh
|
||||
rojo build -o MyNewProject.rbxlx
|
||||
```
|
||||
|
||||
If you open `MyNewProject.rbxlx` in Roblox Studio now, you should see a `Folder` containing a `ModuleScript` under `ReplicatedStorage`!
|
||||
|
||||
!!! info
|
||||
To generate a binary place file instead, use `rbxl`. Note that support for binary model/place files (`rbxm` and `rbxl`) is very limited in Rojo presently.
|
||||
|
||||
## Live-Syncing into Studio
|
||||
Building a place file is great for starting to work on a game, but for active iteration, you'll want something faster.
|
||||
|
||||
In Roblox Studio, make sure the Rojo plugin is installed. If you need it, check out [the installation guide](installation) to learn how to install it.
|
||||
|
||||
To expose your project to the plugin, you'll need to start a new **live sync session** from the command line:
|
||||
|
||||
```sh
|
||||
rojo serve
|
||||
```
|
||||
|
||||
You should see output like this in your terminal:
|
||||
|
||||
```sh
|
||||
$ rojo serve
|
||||
Rojo server listening on port 34872
|
||||
```
|
||||
|
||||
Switch into Roblox Studio and press the **Connect** button on the Rojo plugin toolbar. A dialog should appear:
|
||||
|
||||

|
||||
{: class="feature-image" align="center" }
|
||||
|
||||
If the port number doesn't match the output from the command line, change it, and then press **Connect**.
|
||||
|
||||
If all went well, you should now be able to change files in the `src` directory and watch them sync into Roblox Studio in real time!
|
||||
|
||||
## Uploading Your Place
|
||||
Aimed at teams that want serious levels of automation, Rojo can upload places to Roblox.com automatically.
|
||||
|
||||
You'll need an existing game on Roblox.com as well as the `.ROBLOSECURITY` cookie of an account that has write access to that game.
|
||||
|
||||
!!! warning
|
||||
It's recommended that you set up a Roblox account dedicated to deploying your game instead of your personal account in case your security cookie is compromised.
|
||||
|
||||
Generating and publishing your game is as simple as:
|
||||
|
||||
```sh
|
||||
rojo upload --asset_id [PLACE ID] --cookie "[SECURITY COOKIE]"
|
||||
```
|
||||
|
||||
An example project is available on GitHub that deploys to Roblox.com from GitHub and Travis-CI automatically: [https://github.com/LPGhatguy/roads](https://github.com/LPGhatguy/roads)
|
||||
BIN
docs/images/connection-dialog.png
Normal file
BIN
docs/images/connection-dialog.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
@@ -2,10 +2,10 @@ This is the documentation home for Rojo 0.5.x.
|
||||
|
||||
Available versions of these docs:
|
||||
|
||||
* [Latest version (currently 0.5.x)](https://lpghatguy.github.io/rojo)
|
||||
* [0.5.x](https://lpghatguy.github.io/rojo/0.5.x)
|
||||
* [0.4.x](https://lpghatguy.github.io/rojo/0.4.x)
|
||||
* [Latest version (currently 0.5.x)](https://rojo.space/docs/latest)
|
||||
* [0.5.x](https://rojo.space/docs/0.5.x)
|
||||
* [0.4.x](https://rojo.space/docs/0.4.x)
|
||||
|
||||
**Rojo** is a flexible multi-tool designed for creating robust Roblox projects.
|
||||
|
||||
This documentation is a continual work in progress. If you find any issues, please file an issue on [Rojo's issue tracker](https://github.com/LPGhatguy/rojo/issues)!
|
||||
This documentation is a continual work in progress. If you find any issues, please file an issue on [Rojo's issue tracker](https://github.com/rojo-rbx/rojo/issues)!
|
||||
37
docs/reference/full-vs-partial.md
Normal file
37
docs/reference/full-vs-partial.md
Normal file
@@ -0,0 +1,37 @@
|
||||
Rojo is designed to be adopted incrementally. How much of your project Rojo manages is up to you!
|
||||
|
||||
There are two primary categories of ways to use Rojo: *Fully Managed*, where everything is managed by Rojo, and *Partially Managed*, where Rojo only manages a slice of your project.
|
||||
|
||||
## Fully Managed
|
||||
In a fully managed game project, Rojo controls every instance. A fully managed Rojo project can be built from scratch using `rojo build`.
|
||||
|
||||
Fully managed projects are most practical for libraries, plugins, and simple games.
|
||||
|
||||
Rojo's goal is to make it practical and easy for _every_ project to be fully managed, but we're not quite there yet!
|
||||
|
||||
### Pros
|
||||
* Fully reproducible builds from scratch
|
||||
* Everything checked into version control
|
||||
|
||||
### Cons
|
||||
* Without two-way sync, models have to be saved manually
|
||||
* This can be done with the 'Save to File...' menu in Roblox Studio
|
||||
* This will be solved by Two-Way Sync ([issue #164](https://github.com/LPGhatguy/rojo/issues/164))
|
||||
* Rojo can't manage everything yet
|
||||
* Refs are currently broken ([issue #142](https://github.com/LPGhatguy/rojo/issues/142))
|
||||
|
||||
## Partially Managed
|
||||
In a partially managed project, Rojo only handles a slice of the game. This could be as small as a couple scripts, or as large as everything except `Workspace`!
|
||||
|
||||
The rest of the place's content can be versioned using Team Create or checked into source control.
|
||||
|
||||
Partially managed projects are most practical for complicated games, or games that are migrating to use Rojo.
|
||||
|
||||
### Pros
|
||||
* Easier to adopt gradually
|
||||
* Integrates with Team Create
|
||||
|
||||
### Cons
|
||||
* Not everything is in version control, which makes merges tougher
|
||||
* Rojo can't live-sync instances like Terrain, MeshPart, or CSG operations yet
|
||||
* Will be fixed with plugin escalation ([issue #169](https://github.com/LPGhatguy/rojo/issues/169))
|
||||
@@ -24,19 +24,70 @@ Instance Descriptions correspond one-to-one with the actual Roblox Instances in
|
||||
|
||||
All other fields in an Instance Description are turned into instances whose name is the key. These values should also be Instance Descriptions!
|
||||
|
||||
Instance Descriptions are fairly verbose and strict. In the future, it'll be possible for Rojo to infer class names for known services like `Workspace`.
|
||||
Instance Descriptions are fairly verbose and strict. In the future, it'll be possible for Rojo to [infer class names for known services like `Workspace`](https://github.com/LPGhatguy/rojo/issues/179).
|
||||
|
||||
## Instance Property Value
|
||||
The shape of Instance Property Values is defined by the [rbx_tree](https://github.com/LPGhatguy/rbx-tree) library, so it uses slightly different conventions than the rest of Rojo.
|
||||
There are two kinds of property values on instances, **implicit** and **explicit**.
|
||||
|
||||
In the vast majority of cases, you should be able to use **implicit** property values. To use them, just use a value that's the same shape as the type that the property has:
|
||||
|
||||
```json
|
||||
"MyPart": {
|
||||
"$className": "Part",
|
||||
"$properties": {
|
||||
"Size": [3, 5, 3],
|
||||
"Color": [0.5, 0, 0.5],
|
||||
"Anchored": true,
|
||||
"Material": "Granite"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`Vector3` and `Color3` properties can just be arrays of numbers, as can types like `Vector2`, `CFrame`, and more!
|
||||
|
||||
Enums can be set to a string containing the enum variant. Rojo will raise an error if the string isn't a valid variant for the enum.
|
||||
|
||||
There are some cases where this syntax for assigning properties _doesn't_ work. In these cases, Rojo requires you to use the **explicit** property syntax.
|
||||
|
||||
Some reasons why you might need to use an **explicit** property:
|
||||
|
||||
* Using exotic property types like `BinaryString`
|
||||
* Using properties added to Roblox recently that Rojo doesn't know about yet
|
||||
|
||||
The shape of explicit property values is defined by the [rbx-dom](https://github.com/LPGhatguy/rbx-dom) library, so it uses slightly different conventions than the rest of Rojo.
|
||||
|
||||
Each value should be an object with the following required fields:
|
||||
|
||||
* `Type`: The type of property to represent.
|
||||
* [Supported types can be found here](https://github.com/LPGhatguy/rbx-tree#property-type-coverage).
|
||||
* `Value`: The value of the property.
|
||||
* The shape of this field depends on which property type is being used. `Vector3` and `Color3` values are both represented as a list of numbers, for example.
|
||||
* The shape of this field depends on which property type is being used. `Vector3` and `Color3` values are both represented as a list of numbers, while `BinaryString` expects a base64-encoded string, for example.
|
||||
|
||||
Instance Property Values are intentionally very strict. Rojo will eventually be able to infer types for you!
|
||||
Here's the same object, but with explicit properties:
|
||||
|
||||
```json
|
||||
"MyPart": {
|
||||
"$className": "Part",
|
||||
"$properties": {
|
||||
"Size": {
|
||||
"Type": "Vector3",
|
||||
"Value": [3, 5, 3]
|
||||
},
|
||||
"Color": {
|
||||
"Type": "Color3",
|
||||
"Value": [0.5, 0, 0.5]
|
||||
},
|
||||
"Anchored": {
|
||||
"Type": "Bool",
|
||||
"Value": true
|
||||
},
|
||||
"Material": {
|
||||
"Type": "Enum",
|
||||
"Value": 832
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Example Projects
|
||||
This project bundles up everything in the `src` directory. It'd be suitable for making a plugin or model:
|
||||
@@ -61,10 +112,7 @@ This project describes the layout you might use if you were making the next hit
|
||||
"HttpService": {
|
||||
"$className": "HttpService",
|
||||
"$properties": {
|
||||
"HttpEnabled": {
|
||||
"Type": "Bool",
|
||||
"Value": true
|
||||
}
|
||||
"HttpEnabled": true
|
||||
}
|
||||
},
|
||||
|
||||
@@ -85,10 +133,7 @@ This project describes the layout you might use if you were making the next hit
|
||||
"Workspace": {
|
||||
"$className": "Workspace",
|
||||
"$properties": {
|
||||
"Gravity": {
|
||||
"Type": "Float32",
|
||||
"Value": 67.3
|
||||
}
|
||||
"Gravity": 67.3
|
||||
},
|
||||
|
||||
"Terrain": {
|
||||
144
docs/reference/sync-details.md
Normal file
144
docs/reference/sync-details.md
Normal file
@@ -0,0 +1,144 @@
|
||||
This page aims to describe how Rojo turns files on the filesystem into Roblox objects.
|
||||
|
||||
[TOC]
|
||||
|
||||
## Overview
|
||||
| File Name | Instance Type |
|
||||
| -------------- | ------------------------- |
|
||||
| any directory | `Folder` |
|
||||
| `*.server.lua` | `Script` |
|
||||
| `*.client.lua` | `LocalScript` |
|
||||
| `*.lua` | `ModuleScript` |
|
||||
| `*.csv` | `LocalizationTable` |
|
||||
| `*.txt` | `StringValue` |
|
||||
| `*.model.json` | Any |
|
||||
| `*.rbxm` | Any |
|
||||
| `*.rbxmx` | Any |
|
||||
| `*.meta.json` | Modifies another instance |
|
||||
|
||||
## Limitations
|
||||
Not all property types can be synced by Rojo in real-time due to limitations of the Roblox Studio plugin API. In these cases, you can usually generate a place file and open it when you start working on a project.
|
||||
|
||||
Some common cases you might hit are:
|
||||
|
||||
* Binary data (Terrain, CSG, CollectionService tags)
|
||||
* `MeshPart.MeshId`
|
||||
* `HttpService.HttpEnabled`
|
||||
|
||||
For a list of all property types that Rojo can reason about, both when live-syncing and when building place files, look at [rbx-dom's type coverage chart](https://github.com/rojo-rbx/rbx-dom#property-type-coverage).
|
||||
|
||||
This limitation may be solved by [issue #205](https://github.com/rojo-rbx/rojo/issues/205) in the future.
|
||||
|
||||
## Folders
|
||||
Any directory on the filesystem will turn into a `Folder` instance unless it contains an 'init' script, described below.
|
||||
|
||||
## Scripts
|
||||
The default script type in Rojo projects is `ModuleScript`, since most scripts in well-structued Roblox projects will be modules.
|
||||
|
||||
If a directory contains a file named `init.server.lua`, `init.client.lua`, or `init.lua`, that folder will be transformed into a `*Script` instance with the contents of the 'init' file. This can be used to create scripts inside of scripts.
|
||||
|
||||
For example, these files:
|
||||
|
||||

|
||||
{: align="center" }
|
||||
|
||||
Will turn into these instances in Roblox:
|
||||
|
||||

|
||||
{: align="center" }
|
||||
|
||||
## Localization Tables
|
||||
Any CSV files are transformed into `LocalizationTable` instances. Rojo expects these files to follow the same format that Roblox does when importing and exporting localization information.
|
||||
|
||||
## Plain Text Files
|
||||
Plain text files (`.txt`) files are transformed into `StringValue` instances. This is useful for bringing in text data that can be read by scripts at runtime.
|
||||
|
||||
## JSON Models
|
||||
Files ending in `.model.json` can be used to describe simple models. They're designed to be hand-written and are useful for instances like `RemoteEvent`.
|
||||
|
||||
A JSON model describing a folder containing a `Part` and a `RemoteEvent` could be described as:
|
||||
|
||||
```json
|
||||
{
|
||||
"Name": "My Cool Model",
|
||||
"ClassName": "Folder",
|
||||
"Children": [
|
||||
{
|
||||
"Name": "RootPart",
|
||||
"ClassName": "Part",
|
||||
"Properties": {
|
||||
"Size": {
|
||||
"Type": "Vector3",
|
||||
"Value": [4, 4, 4]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "SendMoney",
|
||||
"ClassName": "RemoteEvent"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
It would turn into instances in this shape:
|
||||
|
||||

|
||||
{: align="center" }
|
||||
|
||||
## Binary and XML Models
|
||||
Rojo supports both binary (`.rbxm`) and XML (`.rbxmx`) models generated by Roblox Studio or another tool.
|
||||
|
||||
Support for the `rbxmx` is very good, while support for `rbxm` is still very early, buggy, and lacking features.
|
||||
|
||||
For a rundown of supported types, check out [rbx-dom's type coverage chart](https://github.com/rojo-rbx/rbx-dom#property-type-coverage).
|
||||
|
||||
## Meta Files
|
||||
New in Rojo 0.5.0-alpha.12 are meta files, named `.meta.json`.
|
||||
|
||||
Meta files allow attaching extra Rojo data to models defined in other formats, like Roblox's `rbxm` and `rbxmx` model formats, or even Lua scripts.
|
||||
|
||||
This can be used to set Rojo-specific settings like `ignoreUnknownInstances`, or can be used to set properties like `Disabled` on a script.
|
||||
|
||||
Meta files can contain:
|
||||
|
||||
* `className`: Changes the `className` of a containing `Folder` into something else.
|
||||
* Usable only in `init.meta.json` files
|
||||
* `properties`: A map of properties to set on the instance, just like projects
|
||||
* Usable on anything except `.rbxmx`, `.rbxm`, and `.model.json` files, which already have properties
|
||||
* `ignoreUnknownInstances`: Works just like `$ignoreUnknownInstances` in project files
|
||||
|
||||
### Meta Files to set Rojo metadata
|
||||
Sometimes it's useful to apply properties like `ignoreUnknownInstances` on instances that are defined on the filesystem instead of within the project itself.
|
||||
|
||||
### Meta Files for Disabled Scripts
|
||||
Meta files can be used to set properties on `Script` instances, like `Disabled`.
|
||||
|
||||
If your project had `foo.server.lua` and you wanted to make sure it would be disabled, you could create a `foo.meta.json` next to it with:
|
||||
|
||||
```json
|
||||
{
|
||||
"properties": {
|
||||
"Disabled": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Meta Files for Tools
|
||||
If you wanted to represent a tool containing a script and a model for its handle, create a directory with an `init.meta.json` file in it:
|
||||
|
||||
```json
|
||||
{
|
||||
"className": "Tool",
|
||||
"properties": {
|
||||
"Grip": [
|
||||
0, 0, 0,
|
||||
1, 0, 0,
|
||||
0, 1, 0,
|
||||
0, 0, 1,
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Instead of a `Folder` instance, you'll end up with a `Tool` instance with the `Grip` property set!
|
||||
23
docs/rojo-alternatives.md
Normal file
23
docs/rojo-alternatives.md
Normal file
@@ -0,0 +1,23 @@
|
||||
There are a number of existing plugins for Roblox that move code from the filesystem into Roblox.
|
||||
|
||||
Besides Rojo, you might consider:
|
||||
|
||||
* [rbxmk by Anaminus](https://github.com/anaminus/rbxmk)
|
||||
* [Rofresh by Osyris](https://github.com/osyrisrblx/rofresh)
|
||||
* [RbxRefresh by Osyris](https://github.com/osyrisrblx/RbxRefresh)
|
||||
* [Studio Bridge by Vocksel](https://github.com/vocksel/studio-bridge)
|
||||
* [Elixir by Vocksel](https://github.com/vocksel/elixir)
|
||||
* [RbxSync by evaera](https://github.com/evaera/RbxSync)
|
||||
* [CodeSync by MemoryPenguin](https://github.com/MemoryPenguin/CodeSync)
|
||||
* [rbx-exteditor by MemoryPenguin](https://github.com/MemoryPenguin/rbx-exteditor)
|
||||
|
||||
So why did I build Rojo?
|
||||
|
||||
Each of these tools solves what is essentially the same problem from a few different angles. The goal of Rojo is to take all of the lessons and ideas learned from these projects and build a tool that can solve this problem for good.
|
||||
|
||||
Additionally:
|
||||
|
||||
* I think that this tool needs to be built in a compiled language without a runtime, for easy distribution and good performance.
|
||||
* I think that the conventions promoted by other sync plugins (`.module.lua` for modules, as well a single sync point) are sub-optimal.
|
||||
* I think that I have a good enough understanding of the problem to build something robust.
|
||||
* I think that Rojo should be able to do more than just sync code.
|
||||
@@ -1,91 +0,0 @@
|
||||
This page aims to describe how Rojo turns files on the filesystem into Roblox objects.
|
||||
|
||||
[TOC]
|
||||
|
||||
## Overview
|
||||
| File Name | Instance Type |
|
||||
| -------------- | ------------------- |
|
||||
| any directory | `Folder` |
|
||||
| `*.server.lua` | `Script` |
|
||||
| `*.client.lua` | `LocalScript` |
|
||||
| `*.lua` | `ModuleScript` |
|
||||
| `*.csv` | `LocalizationTable` |
|
||||
| `*.txt` | `StringValue` |
|
||||
| `*.model.json` | Any |
|
||||
| `*.rbxm` | Any |
|
||||
| `*.rbxmx` | Any |
|
||||
|
||||
## Limitations
|
||||
Not all property types can be synced by Rojo in real-time due to limitations of the Roblox Studio plugin API. In these cases, you can usually generate a place file and open it when you start working on a project.
|
||||
|
||||
Some common cases you might hit are:
|
||||
|
||||
* Binary data (Terrain, CSG, CollectionService tags)
|
||||
* `MeshPart.MeshId`
|
||||
* `HttpService.HttpEnabled`
|
||||
|
||||
For a list of all property types that Rojo can reason about, both when live-syncing and when building place files, look at [rbx_tree's type coverage chart](https://github.com/LPGhatguy/rbx-tree#property-type-coverage).
|
||||
|
||||
## Folders
|
||||
Any directory on the filesystem will turn into a `Folder` instance unless it contains an 'init' script, described below.
|
||||
|
||||
## Scripts
|
||||
The default script type in Rojo projects is `ModuleScript`, since most scripts in well-structued Roblox projects will be modules.
|
||||
|
||||
If a directory contains a file named `init.server.lua`, `init.client.lua`, or `init.lua`, that folder will be transformed into a `*Script` instance with the contents of the 'init' file. This can be used to create scripts inside of scripts.
|
||||
|
||||
For example, these files:
|
||||
|
||||

|
||||
{: align="center" }
|
||||
|
||||
Will turn into these instances in Roblox:
|
||||
|
||||

|
||||
{: align="center" }
|
||||
|
||||
## Localization Tables
|
||||
Any CSV files are transformed into `LocalizationTable` instances. Rojo expects these files to follow the same format that Roblox does when importing and exporting localization information.
|
||||
|
||||
## Plain Text Files
|
||||
Plain text files (`.txt`) files are transformed into `StringValue` instances. This is useful for bringing in text data that can be read by scripts at runtime.
|
||||
|
||||
## JSON Models
|
||||
Files ending in `.model.json` can be used to describe simple models. They're designed to be hand-written and are useful for instances like `RemoteEvent`.
|
||||
|
||||
A JSON model describing a folder containing a `Part` and a `RemoteEvent` could be described as:
|
||||
|
||||
```json
|
||||
{
|
||||
"Name": "My Cool Model",
|
||||
"ClassName": "Folder",
|
||||
"Children": [
|
||||
{
|
||||
"Name": "RootPart",
|
||||
"ClassName": "Part",
|
||||
"Properties": {
|
||||
"Size": {
|
||||
"Type": "Vector3",
|
||||
"Value": [4, 4, 4]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "SendMoney",
|
||||
"ClassName": "RemoteEvent"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
It would turn into instances in this shape:
|
||||
|
||||

|
||||
{: align="center" }
|
||||
|
||||
## Binary and XML Models
|
||||
Rojo supports both binary (`.rbxm`) and XML (`.rbxmx`) models generated by Roblox Studio or another tool.
|
||||
|
||||
Support for the `rbxmx` is very good, while support for `rbxm` is still very early, buggy, and lacking features.
|
||||
|
||||
For a rundown of supported types, check out [rbx-dom's type coverage chart](https://github.com/LPGhatguy/rbx-dom#property-type-coverage).
|
||||
@@ -1,23 +1,39 @@
|
||||
There are a number of existing plugins for Roblox that move code from the filesystem into Roblox.
|
||||
Adding a tool like Rojo to your Roblox workflow can be daunting, but it comes with some key advantages.
|
||||
|
||||
Besides Rojo, you might consider:
|
||||
[TOC]
|
||||
|
||||
* [rbxmk by Anaminus](https://github.com/anaminus/rbxmk)
|
||||
* [Rofresh by Osyris](https://github.com/osyrisrblx/rofresh)
|
||||
* [RbxRefresh by Osyris](https://github.com/osyrisrblx/RbxRefresh)
|
||||
* [Studio Bridge by Vocksel](https://github.com/vocksel/studio-bridge)
|
||||
* [Elixir by Vocksel](https://github.com/vocksel/elixir)
|
||||
* [RbxSync by evaera](https://github.com/evaera/RbxSync)
|
||||
* [CodeSync by MemoryPenguin](https://github.com/MemoryPenguin/CodeSync)
|
||||
* [rbx-exteditor by MemoryPenguin](https://github.com/MemoryPenguin/rbx-exteditor)
|
||||
## External Text Editors
|
||||
Rojo opens the door to use the absolute best text editors in the world and their rich plugin ecosystems.
|
||||
|
||||
So why did I build Rojo?
|
||||
Some very popular editors include [Visual Studio Code](https://code.visualstudio.com) and [Sublime Text](https://www.sublimetext.com).
|
||||
|
||||
Each of these tools solves what is essentially the same problem from a few different angles. The goal of Rojo is to take all of the lessons and ideas learned from these projects and build a tool that can solve this problem for good.
|
||||
These advanced text editors have features like multi-cursor editing, goto symbol, multi-file regex find and replace, bookmarks and much more.
|
||||
|
||||
Additionally:
|
||||
Many Rojo VS Code users also use extensions like:
|
||||
|
||||
* I think that this tool needs to be built in a compiled language without a runtime, for easy distribution and good performance.
|
||||
* I think that the conventions promoted by other sync plugins (`.module.lua` for modules, as well a single sync point) are sub-optimal.
|
||||
* I think that I have a good enough understanding of the problem to build something robust.
|
||||
* I think that Rojo should be able to do more than just sync code.
|
||||
* [vscode-rbxlua](https://marketplace.visualstudio.com/items?itemName=AmaranthineCodices.vscode-rbxlua)
|
||||
* [Roblox Lua Autocompletes](https://marketplace.visualstudio.com/items?itemName=Kampfkarren.roblox-lua-autofills)
|
||||
* [TabNine](https://tabnine.com)
|
||||
|
||||
## Version Control
|
||||
By building your game (or just the scripts) as individual files on the filesystem, it becomes easy to start using professional-grade version control tools like [Git](https://git-scm.com) and [GitHub](https://github.com).
|
||||
|
||||
Hundreds of thousands of companies and individual developers use Git to version their software projects. With Rojo, Roblox developers can take advantage of the best collaboration tool around.
|
||||
|
||||
Using a repository hosting service like GitHub or GitLab brings powerful features to Roblox developers like code reviews and issue tracking that professional engineers can't live without.
|
||||
|
||||
## TypeScript
|
||||
TypeScript enables static type safety, which helps prevent typos and adds unparalleled autocompletion. It also brings features like arrow functions, object destructuring, functional programming methods, and more!
|
||||
|
||||
With Rojo, you can use [roblox-ts](https://roblox-ts.github.io) to compile TypeScript to Lua and take advantage of a huge ecosystem of TypeScript tooling.
|
||||
|
||||
It's also possible to use other languages that compile to Lua like [MoonScript](https://moonscript.org) and [Haxe](https://haxe.org).
|
||||
|
||||
## Other Tools
|
||||
There are decades of excellent tools available that operate on files. With Rojo, it's possible to take advantage of any of them!
|
||||
|
||||
Popular tools include:
|
||||
|
||||
* [luacheck](https://github.com/mpeterv/luacheck), a static analysis tool to help you write better Lua
|
||||
* [ripgrep](https://github.com/BurntSushi/ripgrep), an extremely fast code search tool
|
||||
* [Tokei](https://github.com/XAMPPRocky/tokei), a tool for statistics like lines of code
|
||||
19
mkdocs.yml
19
mkdocs.yml
@@ -1,6 +1,6 @@
|
||||
site_name: Rojo Documentation
|
||||
repo_name: LPGhatguy/rojo
|
||||
repo_url: https://github.com/LPGhatguy/rojo
|
||||
repo_name: rojo-rbx/rojo
|
||||
repo_url: https://github.com/rojo-rbx/rojo
|
||||
|
||||
theme:
|
||||
name: material
|
||||
@@ -11,11 +11,16 @@ theme:
|
||||
nav:
|
||||
- Home: index.md
|
||||
- Why Rojo?: why-rojo.md
|
||||
- Installation: installation.md
|
||||
- Creating a Place with Rojo: creating-a-place.md
|
||||
- Migrating from 0.4.x to 0.5.x: migrating-to-epiphany.md
|
||||
- Project Format: project-format.md
|
||||
- Sync Details: sync-details.md
|
||||
- Guide:
|
||||
- Installation: guide/installation.md
|
||||
- Creating a Game with Rojo: guide/new-game.md
|
||||
- Porting an Existing Game to Rojo: guide/existing-game.md
|
||||
- Migrating from 0.4.x to 0.5.x: guide/migrating-to-epiphany.md
|
||||
- Reference:
|
||||
- Fully vs Partially Managed Rojo: reference/full-vs-partial.md
|
||||
- Project Format: reference/project-format.md
|
||||
- Sync Details: reference/sync-details.md
|
||||
- Rojo Alternatives: rojo-alternatives.md
|
||||
- Rojo Internals:
|
||||
- Internals Overview: internals/overview.md
|
||||
|
||||
|
||||
Submodule plugin/modules/rbx-dom updated: ac129152f2...d7be961eb3
Submodule plugin/modules/roact updated: 2fb60ea648...b1db3f82a2
@@ -15,7 +15,8 @@ local Assets = {
|
||||
},
|
||||
},
|
||||
Images = {
|
||||
Logo = "rbxassetid://2773210620",
|
||||
Logo = "rbxassetid://3405346157",
|
||||
Icon = "rbxassetid://3405341609",
|
||||
},
|
||||
StartSession = "",
|
||||
SessionActive = "",
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
local Roact = require(script:FindFirstAncestor("Rojo").Roact)
|
||||
local Rojo = script:FindFirstAncestor("Rojo")
|
||||
local Plugin = Rojo.Plugin
|
||||
|
||||
local Plugin = script:FindFirstAncestor("Plugin")
|
||||
local Roact = require(Rojo.Roact)
|
||||
|
||||
local Assets = require(Plugin.Assets)
|
||||
local Session = require(Plugin.Session)
|
||||
local Config = require(Plugin.Config)
|
||||
local Version = require(Plugin.Version)
|
||||
local Logging = require(Plugin.Logging)
|
||||
local DevSettings = require(Plugin.DevSettings)
|
||||
local Logging = require(Plugin.Logging)
|
||||
local Session = require(Plugin.Session)
|
||||
local Version = require(Plugin.Version)
|
||||
local preloadAssets = require(Plugin.preloadAssets)
|
||||
|
||||
local ConnectPanel = require(Plugin.Components.ConnectPanel)
|
||||
@@ -54,8 +55,6 @@ end
|
||||
local SessionStatus = {
|
||||
Disconnected = "Disconnected",
|
||||
Connected = "Connected",
|
||||
ConfiguringSession = "ConfiguringSession",
|
||||
-- TODO: Error?
|
||||
}
|
||||
|
||||
setmetatable(SessionStatus, {
|
||||
@@ -71,17 +70,45 @@ function App:init()
|
||||
sessionStatus = SessionStatus.Disconnected,
|
||||
})
|
||||
|
||||
self.connectButton = nil
|
||||
self.signals = {}
|
||||
self.currentSession = nil
|
||||
|
||||
self.displayedVersion = DevSettings:isEnabled()
|
||||
and Config.codename
|
||||
or Version.display(Config.version)
|
||||
|
||||
local toolbar = self.props.plugin:CreateToolbar("Rojo " .. self.displayedVersion)
|
||||
|
||||
self.toggleButton = toolbar:CreateButton(
|
||||
"Rojo",
|
||||
"Show or hide the Rojo panel",
|
||||
Assets.Images.Icon)
|
||||
self.toggleButton.ClickableWhenViewportHidden = true
|
||||
self.toggleButton.Click:Connect(function()
|
||||
self.dockWidget.Enabled = not self.dockWidget.Enabled
|
||||
end)
|
||||
|
||||
local widgetInfo = DockWidgetPluginGuiInfo.new(
|
||||
Enum.InitialDockState.Right,
|
||||
false, -- Initially enabled state
|
||||
false, -- Whether to override the widget's previous state
|
||||
360, 190, -- Floating size
|
||||
360, 190 -- Minimum size
|
||||
)
|
||||
|
||||
self.dockWidget = self.props.plugin:CreateDockWidgetPluginGui("Rojo-0.5.x", widgetInfo)
|
||||
self.dockWidget.Name = "Rojo " .. self.displayedVersion
|
||||
self.dockWidget.Title = "Rojo " .. self.displayedVersion
|
||||
self.dockWidget.AutoLocalize = false
|
||||
self.dockWidget.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
|
||||
|
||||
self.signals.dockWidgetEnabled = self.dockWidget:GetPropertyChangedSignal("Enabled"):Connect(function()
|
||||
self.toggleButton:SetActive(self.dockWidget.Enabled)
|
||||
end)
|
||||
end
|
||||
|
||||
function App:render()
|
||||
-- FIXME: https://github.com/Roblox/roact/issues/209
|
||||
local children = {}
|
||||
local children
|
||||
|
||||
if self.state.sessionStatus == SessionStatus.Connected then
|
||||
children = {
|
||||
@@ -99,7 +126,7 @@ function App:render()
|
||||
end,
|
||||
}),
|
||||
}
|
||||
elseif self.state.sessionStatus == SessionStatus.ConfiguringSession then
|
||||
elseif self.state.sessionStatus == SessionStatus.Disconnected then
|
||||
children = {
|
||||
ConnectPanel = e(ConnectPanel, {
|
||||
startSession = function(address, port)
|
||||
@@ -136,50 +163,15 @@ function App:render()
|
||||
}
|
||||
end
|
||||
|
||||
return e("ScreenGui", {
|
||||
AutoLocalize = false,
|
||||
ZIndexBehavior = Enum.ZIndexBehavior.Sibling,
|
||||
return Roact.createElement(Roact.Portal, {
|
||||
target = self.dockWidget,
|
||||
}, children)
|
||||
end
|
||||
|
||||
function App:didMount()
|
||||
Logging.trace("Rojo %s initializing", self.displayedVersion)
|
||||
|
||||
local toolbar = self.props.plugin:CreateToolbar("Rojo " .. self.displayedVersion)
|
||||
|
||||
self.connectButton = toolbar:CreateButton(
|
||||
"Connect",
|
||||
"Connect to a running Rojo session",
|
||||
Assets.StartSession)
|
||||
self.connectButton.ClickableWhenViewportHidden = false
|
||||
self.connectButton.Click:Connect(function()
|
||||
checkUpgrade(self.props.plugin)
|
||||
|
||||
if self.state.sessionStatus == SessionStatus.Connected then
|
||||
Logging.trace("Disconnecting session")
|
||||
|
||||
self.currentSession:disconnect()
|
||||
self.currentSession = nil
|
||||
self:setState({
|
||||
sessionStatus = SessionStatus.Disconnected,
|
||||
})
|
||||
|
||||
Logging.trace("Session terminated by user")
|
||||
elseif self.state.sessionStatus == SessionStatus.Disconnected then
|
||||
Logging.trace("Starting session configuration")
|
||||
|
||||
self:setState({
|
||||
sessionStatus = SessionStatus.ConfiguringSession,
|
||||
})
|
||||
elseif self.state.sessionStatus == SessionStatus.ConfiguringSession then
|
||||
Logging.trace("Canceling session configuration")
|
||||
|
||||
self:setState({
|
||||
sessionStatus = SessionStatus.Disconnected,
|
||||
})
|
||||
end
|
||||
end)
|
||||
|
||||
checkUpgrade(self.props.plugin)
|
||||
preloadAssets()
|
||||
end
|
||||
|
||||
@@ -188,18 +180,9 @@ function App:willUnmount()
|
||||
self.currentSession:disconnect()
|
||||
self.currentSession = nil
|
||||
end
|
||||
end
|
||||
|
||||
function App:didUpdate()
|
||||
local connectActive = self.state.sessionStatus == SessionStatus.ConfiguringSession
|
||||
or self.state.sessionStatus == SessionStatus.Connected
|
||||
|
||||
self.connectButton:SetActive(connectActive)
|
||||
|
||||
if self.state.sessionStatus == SessionStatus.Connected then
|
||||
self.connectButton.Icon = Assets.SessionActive
|
||||
else
|
||||
self.connectButton.Icon = Assets.StartSession
|
||||
for _, signal in pairs(self.signals) do
|
||||
signal:Disconnect()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -4,39 +4,19 @@ local Plugin = Rojo.Plugin
|
||||
local Roact = require(Rojo.Roact)
|
||||
|
||||
local Config = require(Plugin.Config)
|
||||
local Version = require(Plugin.Version)
|
||||
local Assets = require(Plugin.Assets)
|
||||
local Theme = require(Plugin.Theme)
|
||||
local joinBindings = require(Plugin.joinBindings)
|
||||
|
||||
local Panel = require(Plugin.Components.Panel)
|
||||
local FitList = require(Plugin.Components.FitList)
|
||||
local FitText = require(Plugin.Components.FitText)
|
||||
local FormButton = require(Plugin.Components.FormButton)
|
||||
local FormTextInput = require(Plugin.Components.FormTextInput)
|
||||
|
||||
local RoundBox = Assets.Slices.RoundBox
|
||||
|
||||
local e = Roact.createElement
|
||||
|
||||
local ConnectPanel = Roact.Component:extend("ConnectPanel")
|
||||
|
||||
function ConnectPanel:init()
|
||||
self.footerSize, self.setFooterSize = Roact.createBinding(Vector2.new())
|
||||
self.footerVersionSize, self.setFooterVersionSize = Roact.createBinding(Vector2.new())
|
||||
|
||||
-- This is constructed in init because 'joinBindings' is a hack and we'd
|
||||
-- leak memory constructing it every render. When this kind of feature lands
|
||||
-- in Roact properly, we can do this inline in render without fear.
|
||||
self.footerRestSize = joinBindings(
|
||||
{
|
||||
self.footerSize,
|
||||
self.footerVersionSize,
|
||||
},
|
||||
function(container, other)
|
||||
return UDim2.new(0, container.X - other.X - 16, 0, 32)
|
||||
end
|
||||
)
|
||||
|
||||
self:setState({
|
||||
address = "",
|
||||
port = "",
|
||||
@@ -45,24 +25,14 @@ end
|
||||
|
||||
function ConnectPanel:render()
|
||||
local startSession = self.props.startSession
|
||||
local cancel = self.props.cancel
|
||||
|
||||
return e(FitList, {
|
||||
containerKind = "ImageLabel",
|
||||
containerProps = {
|
||||
Image = RoundBox.asset,
|
||||
ImageRectOffset = RoundBox.offset,
|
||||
ImageRectSize = RoundBox.size,
|
||||
SliceCenter = RoundBox.center,
|
||||
ScaleType = Enum.ScaleType.Slice,
|
||||
BackgroundTransparency = 1,
|
||||
Position = UDim2.new(0.5, 0, 0.5, 0),
|
||||
AnchorPoint = Vector2.new(0.5, 0.5),
|
||||
},
|
||||
layoutProps = {
|
||||
return e(Panel, nil, {
|
||||
Layout = e("UIListLayout", {
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
HorizontalAlignment = Enum.HorizontalAlignment.Center,
|
||||
},
|
||||
}, {
|
||||
VerticalAlignment = Enum.VerticalAlignment.Center,
|
||||
}),
|
||||
|
||||
Inputs = e(FitList, {
|
||||
containerProps = {
|
||||
BackgroundTransparency = 1,
|
||||
@@ -96,7 +66,7 @@ function ConnectPanel:render()
|
||||
Font = Theme.TitleFont,
|
||||
TextSize = 20,
|
||||
Text = "Address",
|
||||
TextColor3 = Theme.AccentColor,
|
||||
TextColor3 = Theme.PrimaryColor,
|
||||
}),
|
||||
|
||||
Input = e(FormTextInput, {
|
||||
@@ -129,7 +99,7 @@ function ConnectPanel:render()
|
||||
Font = Theme.TitleFont,
|
||||
TextSize = 20,
|
||||
Text = "Port",
|
||||
TextColor3 = Theme.AccentColor,
|
||||
TextColor3 = Theme.PrimaryColor,
|
||||
}),
|
||||
|
||||
Input = e(FormTextInput, {
|
||||
@@ -165,17 +135,6 @@ function ConnectPanel:render()
|
||||
PaddingRight = UDim.new(0, 24),
|
||||
},
|
||||
}, {
|
||||
e(FormButton, {
|
||||
layoutOrder = 1,
|
||||
text = "Cancel",
|
||||
onClick = function()
|
||||
if cancel ~= nil then
|
||||
cancel()
|
||||
end
|
||||
end,
|
||||
secondary = true,
|
||||
}),
|
||||
|
||||
e(FormButton, {
|
||||
layoutOrder = 2,
|
||||
text = "Connect",
|
||||
@@ -196,65 +155,6 @@ function ConnectPanel:render()
|
||||
end,
|
||||
}),
|
||||
}),
|
||||
|
||||
Footer = e(FitList, {
|
||||
fitAxes = "Y",
|
||||
containerKind = "ImageLabel",
|
||||
containerProps = {
|
||||
Image = RoundBox.asset,
|
||||
ImageRectOffset = RoundBox.offset + Vector2.new(0, RoundBox.size.Y / 2),
|
||||
ImageRectSize = RoundBox.size * Vector2.new(1, 0.5),
|
||||
SliceCenter = RoundBox.center,
|
||||
ScaleType = Enum.ScaleType.Slice,
|
||||
ImageColor3 = Theme.SecondaryColor,
|
||||
Size = UDim2.new(1, 0, 0, 0),
|
||||
LayoutOrder = 3,
|
||||
BackgroundTransparency = 1,
|
||||
|
||||
[Roact.Change.AbsoluteSize] = function(rbx)
|
||||
self.setFooterSize(rbx.AbsoluteSize)
|
||||
end,
|
||||
},
|
||||
layoutProps = {
|
||||
FillDirection = Enum.FillDirection.Horizontal,
|
||||
HorizontalAlignment = Enum.HorizontalAlignment.Center,
|
||||
VerticalAlignment = Enum.VerticalAlignment.Center,
|
||||
},
|
||||
paddingProps = {
|
||||
PaddingTop = UDim.new(0, 4),
|
||||
PaddingBottom = UDim.new(0, 4),
|
||||
PaddingLeft = UDim.new(0, 8),
|
||||
PaddingRight = UDim.new(0, 8),
|
||||
},
|
||||
}, {
|
||||
LogoContainer = e("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
|
||||
Size = self.footerRestSize,
|
||||
}, {
|
||||
Logo = e("ImageLabel", {
|
||||
Image = Assets.Images.Logo,
|
||||
Size = UDim2.new(0, 80, 0, 40),
|
||||
ScaleType = Enum.ScaleType.Fit,
|
||||
BackgroundTransparency = 1,
|
||||
Position = UDim2.new(0, 0, 1, -10),
|
||||
AnchorPoint = Vector2.new(0, 1),
|
||||
}),
|
||||
}),
|
||||
|
||||
Version = e(FitText, {
|
||||
Font = Theme.TitleFont,
|
||||
TextSize = 18,
|
||||
Text = Version.display(Config.version),
|
||||
TextXAlignment = Enum.TextXAlignment.Right,
|
||||
TextColor3 = Theme.LightTextColor,
|
||||
BackgroundTransparency = 1,
|
||||
|
||||
[Roact.Change.AbsoluteSize] = function(rbx)
|
||||
self.setFooterVersionSize(rbx.AbsoluteSize)
|
||||
end,
|
||||
}),
|
||||
}),
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
@@ -3,63 +3,42 @@ local Roact = require(script:FindFirstAncestor("Rojo").Roact)
|
||||
local Plugin = script:FindFirstAncestor("Plugin")
|
||||
|
||||
local Theme = require(Plugin.Theme)
|
||||
local Assets = require(Plugin.Assets)
|
||||
|
||||
local FitList = require(Plugin.Components.FitList)
|
||||
local Panel = require(Plugin.Components.Panel)
|
||||
local FitText = require(Plugin.Components.FitText)
|
||||
local FormButton = require(Plugin.Components.FormButton)
|
||||
|
||||
local e = Roact.createElement
|
||||
|
||||
local RoundBox = Assets.Slices.RoundBox
|
||||
local WhiteCross = Assets.Sprites.WhiteCross
|
||||
local ConnectionActivePanel = Roact.Component:extend("ConnectionActivePanel")
|
||||
|
||||
local function ConnectionActivePanel(props)
|
||||
local stopSession = props.stopSession
|
||||
function ConnectionActivePanel:render()
|
||||
local stopSession = self.props.stopSession
|
||||
|
||||
return e(FitList, {
|
||||
containerKind = "ImageLabel",
|
||||
containerProps = {
|
||||
Image = RoundBox.asset,
|
||||
ImageRectOffset = RoundBox.offset + Vector2.new(0, RoundBox.size.Y / 2),
|
||||
ImageRectSize = RoundBox.size * Vector2.new(1, 0.5),
|
||||
SliceCenter = Rect.new(4, 4, 4, 4),
|
||||
ScaleType = Enum.ScaleType.Slice,
|
||||
BackgroundTransparency = 1,
|
||||
Position = UDim2.new(0.5, 0, 0, 0),
|
||||
AnchorPoint = Vector2.new(0.5, 0),
|
||||
},
|
||||
layoutProps = {
|
||||
FillDirection = Enum.FillDirection.Horizontal,
|
||||
return e(Panel, nil, {
|
||||
Layout = Roact.createElement("UIListLayout", {
|
||||
HorizontalAlignment = Enum.HorizontalAlignment.Center,
|
||||
VerticalAlignment = Enum.VerticalAlignment.Center,
|
||||
},
|
||||
}, {
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
Padding = UDim.new(0, 8),
|
||||
}),
|
||||
|
||||
Text = e(FitText, {
|
||||
Padding = Vector2.new(12, 6),
|
||||
Font = Theme.ButtonFont,
|
||||
TextSize = 18,
|
||||
Text = "Rojo Connected",
|
||||
Text = "Connected to Live-Sync Server",
|
||||
TextColor3 = Theme.PrimaryColor,
|
||||
BackgroundTransparency = 1,
|
||||
}),
|
||||
|
||||
CloseContainer = e("ImageButton", {
|
||||
Size = UDim2.new(0, 30, 0, 30),
|
||||
BackgroundTransparency = 1,
|
||||
|
||||
[Roact.Event.Activated] = function()
|
||||
DisconnectButton = e(FormButton, {
|
||||
layoutOrder = 2,
|
||||
text = "Disconnect",
|
||||
secondary = true,
|
||||
onClick = function()
|
||||
stopSession()
|
||||
end,
|
||||
}, {
|
||||
CloseImage = e("ImageLabel", {
|
||||
Size = UDim2.new(0, 16, 0, 16),
|
||||
Position = UDim2.new(0.5, 0, 0.5, 0),
|
||||
AnchorPoint = Vector2.new(0.5, 0.5),
|
||||
Image = WhiteCross.asset,
|
||||
ImageRectOffset = WhiteCross.offset,
|
||||
ImageRectSize = WhiteCross.size,
|
||||
ImageColor3 = Theme.PrimaryColor,
|
||||
BackgroundTransparency = 1,
|
||||
}),
|
||||
}),
|
||||
})
|
||||
end
|
||||
|
||||
@@ -57,8 +57,8 @@ function FormTextInput:render()
|
||||
TextSize = TEXT_SIZE,
|
||||
Text = value,
|
||||
PlaceholderText = shownPlaceholder,
|
||||
PlaceholderColor3 = Theme.AccentLightColor,
|
||||
TextColor3 = Theme.AccentColor,
|
||||
PlaceholderColor3 = Theme.LightTextColor,
|
||||
TextColor3 = Theme.PrimaryColor,
|
||||
|
||||
[Roact.Change.Text] = function(rbx)
|
||||
onValueChange(rbx.Text)
|
||||
|
||||
34
plugin/src/Components/Panel.lua
Normal file
34
plugin/src/Components/Panel.lua
Normal file
@@ -0,0 +1,34 @@
|
||||
local Roact = require(script:FindFirstAncestor("Rojo").Roact)
|
||||
|
||||
local Plugin = script:FindFirstAncestor("Plugin")
|
||||
|
||||
local RojoFooter = require(Plugin.Components.RojoFooter)
|
||||
|
||||
local e = Roact.createElement
|
||||
|
||||
local Panel = Roact.Component:extend("Panel")
|
||||
|
||||
function Panel:init()
|
||||
self.footerSize, self.setFooterSize = Roact.createBinding(Vector2.new())
|
||||
end
|
||||
|
||||
function Panel:render()
|
||||
return e("Frame", {
|
||||
Size = UDim2.new(1, 0, 1, 0),
|
||||
BackgroundTransparency = 1,
|
||||
}, {
|
||||
Layout = Roact.createElement("UIListLayout", {
|
||||
HorizontalAlignment = Enum.HorizontalAlignment.Center,
|
||||
SortOrder = Enum.SortOrder.LayoutOrder,
|
||||
}),
|
||||
|
||||
Body = e("Frame", {
|
||||
Size = UDim2.new(0, 360, 1, -32),
|
||||
BackgroundTransparency = 1,
|
||||
}, self.props[Roact.Children]),
|
||||
|
||||
Footer = e(RojoFooter),
|
||||
})
|
||||
end
|
||||
|
||||
return Panel
|
||||
69
plugin/src/Components/RojoFooter.lua
Normal file
69
plugin/src/Components/RojoFooter.lua
Normal file
@@ -0,0 +1,69 @@
|
||||
local Rojo = script:FindFirstAncestor("Rojo")
|
||||
local Plugin = Rojo.Plugin
|
||||
|
||||
local Roact = require(Rojo.Roact)
|
||||
|
||||
local Config = require(Plugin.Config)
|
||||
local Version = require(Plugin.Version)
|
||||
local Assets = require(Plugin.Assets)
|
||||
local Theme = require(Plugin.Theme)
|
||||
|
||||
local FitText = require(Plugin.Components.FitText)
|
||||
|
||||
local e = Roact.createElement
|
||||
|
||||
local RojoFooter = Roact.Component:extend("RojoFooter")
|
||||
|
||||
function RojoFooter:init()
|
||||
self.footerSize, self.setFooterSize = Roact.createBinding(Vector2.new())
|
||||
self.footerVersionSize, self.setFooterVersionSize = Roact.createBinding(Vector2.new())
|
||||
end
|
||||
|
||||
function RojoFooter:render()
|
||||
return e("Frame", {
|
||||
LayoutOrder = 3,
|
||||
Size = UDim2.new(1, 0, 0, 32),
|
||||
BackgroundColor3 = Theme.SecondaryColor,
|
||||
BorderSizePixel = 0,
|
||||
}, {
|
||||
Padding = e("UIPadding", {
|
||||
PaddingTop = UDim.new(0, 4),
|
||||
PaddingBottom = UDim.new(0, 4),
|
||||
PaddingLeft = UDim.new(0, 8),
|
||||
PaddingRight = UDim.new(0, 8),
|
||||
}),
|
||||
|
||||
LogoContainer = e("Frame", {
|
||||
BackgroundTransparency = 1,
|
||||
|
||||
Size = UDim2.new(0, 0, 0, 32),
|
||||
}, {
|
||||
Logo = e("ImageLabel", {
|
||||
Image = Assets.Images.Logo,
|
||||
Size = UDim2.new(0, 80, 0, 40),
|
||||
ScaleType = Enum.ScaleType.Fit,
|
||||
BackgroundTransparency = 1,
|
||||
Position = UDim2.new(0, 0, 1, -10),
|
||||
AnchorPoint = Vector2.new(0, 1),
|
||||
}),
|
||||
}),
|
||||
|
||||
Version = e("TextLabel", {
|
||||
Position = UDim2.new(1, 0, 0, 0),
|
||||
Size = UDim2.new(0, 0, 1, 0),
|
||||
AnchorPoint = Vector2.new(1, 0),
|
||||
Font = Theme.TitleFont,
|
||||
TextSize = 18,
|
||||
Text = Version.display(Config.version),
|
||||
TextXAlignment = Enum.TextXAlignment.Right,
|
||||
TextColor3 = Theme.LightTextColor,
|
||||
BackgroundTransparency = 1,
|
||||
|
||||
[Roact.Change.AbsoluteSize] = function(rbx)
|
||||
self.setFooterVersionSize(rbx.AbsoluteSize)
|
||||
end,
|
||||
}),
|
||||
})
|
||||
end
|
||||
|
||||
return RojoFooter
|
||||
@@ -1,6 +1,6 @@
|
||||
return {
|
||||
codename = "Epiphany",
|
||||
version = {0, 5, 0, "-alpha.11"},
|
||||
version = {0, 5, 0, "-alpha.12"},
|
||||
expectedServerVersionString = "0.5.0 or newer",
|
||||
protocolVersion = 2,
|
||||
defaultHost = "localhost",
|
||||
|
||||
@@ -4,11 +4,11 @@ local Theme = {
|
||||
TitleFont = Enum.Font.GothamBold,
|
||||
MainFont = Enum.Font.Gotham,
|
||||
|
||||
AccentColor = Color3.fromRGB(136, 0, 27),
|
||||
AccentLightColor = Color3.fromRGB(210, 145, 157),
|
||||
PrimaryColor = Color3.fromRGB(20, 20, 20),
|
||||
AccentColor = Color3.fromRGB(225, 56, 53),
|
||||
AccentLightColor = Color3.fromRGB(255, 146, 145),
|
||||
PrimaryColor = Color3.fromRGB(64, 64, 64),
|
||||
SecondaryColor = Color3.fromRGB(235, 235, 235),
|
||||
LightTextColor = Color3.fromRGB(140, 140, 140),
|
||||
LightTextColor = Color3.fromRGB(160, 160, 160),
|
||||
}
|
||||
|
||||
setmetatable(Theme, {
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
--[[
|
||||
joinBindings is a crazy hack that allows combining multiple Roact bindings
|
||||
in the same spirit as `map`.
|
||||
|
||||
It's implemented in terms of Roact internals that will probably break at
|
||||
some point; please don't do that or use this module in your own code!
|
||||
]]
|
||||
|
||||
local Binding = require(script:FindFirstAncestor("Rojo").Roact.Binding)
|
||||
|
||||
local function evaluate(fun, bindings)
|
||||
local input = {}
|
||||
|
||||
for index, binding in ipairs(bindings) do
|
||||
input[index] = binding:getValue()
|
||||
end
|
||||
|
||||
return fun(unpack(input, 1, #bindings))
|
||||
end
|
||||
|
||||
local function joinBindings(bindings, joinFunction)
|
||||
local initialValue = evaluate(joinFunction, bindings)
|
||||
local binding, setValue = Binding.create(initialValue)
|
||||
|
||||
for _, binding in ipairs(bindings) do
|
||||
Binding.subscribe(binding, function()
|
||||
setValue(evaluate(joinFunction, bindings))
|
||||
end)
|
||||
end
|
||||
|
||||
return binding
|
||||
end
|
||||
|
||||
return joinBindings
|
||||
@@ -7,20 +7,6 @@ local function rojoValueToRobloxValue(value)
|
||||
return nil
|
||||
end
|
||||
|
||||
-- TODO: Remove this once rbx_dom_weak and rbx_dom_lua agree on encoding
|
||||
if value.Type == "BinaryString" then
|
||||
local actualValue = ""
|
||||
|
||||
for i = 1, #value.Value do
|
||||
actualValue = actualValue .. string.char(i)
|
||||
end
|
||||
|
||||
value = {
|
||||
Type = "BinaryString",
|
||||
Value = actualValue,
|
||||
}
|
||||
end
|
||||
|
||||
local success, decodedValue = RbxDom.EncodedValue.decode(value)
|
||||
|
||||
if not success then
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
local RbxDom = require(script:FindFirstAncestor("Rojo").RbxDom)
|
||||
|
||||
local Logging = require(script.Parent.Logging)
|
||||
|
||||
--[[
|
||||
Attempts to set a property on the given instance.
|
||||
]]
|
||||
local function setCanonicalProperty(instance, key, value)
|
||||
if not RbxDom.CanonicalProperty.isScriptable(instance.ClassName, key) then
|
||||
return false
|
||||
local function setCanonicalProperty(instance, propertyName, value)
|
||||
local descriptor = RbxDom.findCanonicalPropertyDescriptor(instance.ClassName, propertyName)
|
||||
|
||||
-- We can skip unknown properties; they're not likely reflected to Lua.
|
||||
--
|
||||
-- A good example of a property like this is `Model.ModelInPrimary`, which
|
||||
-- is serialized but not reflected to Lua.
|
||||
if descriptor == nil then
|
||||
return false, "unknown property"
|
||||
end
|
||||
|
||||
-- If we don't have permissions to access this value at all, we can skip it.
|
||||
local readSuccess, existingValue = RbxDom.CanonicalProperty.read(instance, key)
|
||||
if descriptor.scriptability == "None" or descriptor.scriptability == "Read" then
|
||||
return false, "unwritable property"
|
||||
end
|
||||
|
||||
if not readSuccess then
|
||||
-- An error will be thrown if there was a permission issue or if the
|
||||
-- property doesn't exist. In the latter case, we should tell the user
|
||||
-- because it's probably their fault.
|
||||
if existingValue:find("lacking permission") then
|
||||
Logging.trace("Permission error reading property %s on class %s", tostring(key), instance.ClassName)
|
||||
return false
|
||||
else
|
||||
error(("Invalid property %s on class %s: %s"):format(tostring(key), instance.ClassName, existingValue), 2)
|
||||
local success, err = descriptor:write(instance, value)
|
||||
|
||||
if not success then
|
||||
-- If we don't have permission to write a property, we just silently
|
||||
-- ignore it.
|
||||
if err.kind == RbxDom.Error.Kind.Roblox and err.extra:find("lacking permission") then
|
||||
return false, "permission error"
|
||||
end
|
||||
end
|
||||
|
||||
local writeSuccess, err = RbxDom.CanonicalProperty.write(instance, key, value)
|
||||
|
||||
if not writeSuccess then
|
||||
error(("Cannot set property %s on class %s: %s"):format(tostring(key), instance.ClassName, err), 2)
|
||||
local message = ("Invalid property %s.%s: %s"):format(descriptor.className, descriptor.name, tostring(err))
|
||||
error(message, 2)
|
||||
end
|
||||
|
||||
return true
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
[package]
|
||||
name = "rojo"
|
||||
version = "0.5.0-alpha.11"
|
||||
version = "0.5.0-alpha.12"
|
||||
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
|
||||
description = "A tool to create robust Roblox projects"
|
||||
description = "Enables professional-grade development tools for Roblox developers"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/LPGhatguy/rojo"
|
||||
repository = "https://github.com/rojo-rbx/rojo"
|
||||
edition = "2018"
|
||||
|
||||
[features]
|
||||
@@ -29,10 +29,10 @@ hyper = "0.12"
|
||||
log = "0.4"
|
||||
maplit = "1.0.1"
|
||||
notify = "4.0"
|
||||
rbx_binary = "0.4.0"
|
||||
rbx_dom_weak = "1.7.0"
|
||||
rbx_xml = "0.9.0"
|
||||
rbx_reflection = "3.0.384"
|
||||
rbx_binary = "0.4.1"
|
||||
rbx_dom_weak = "1.8.0"
|
||||
rbx_xml = "0.10.0"
|
||||
rbx_reflection = "3.1.388"
|
||||
regex = "1.0"
|
||||
reqwest = "0.9.5"
|
||||
rlua = "0.16"
|
||||
|
||||
@@ -9,7 +9,7 @@ use failure::Fail;
|
||||
|
||||
use crate::{
|
||||
imfs::{Imfs, FsError},
|
||||
project::{Project, ProjectLoadFuzzyError},
|
||||
project::{Project, ProjectLoadError},
|
||||
rbx_session::construct_oneoff_tree,
|
||||
rbx_snapshot::SnapshotError,
|
||||
};
|
||||
@@ -47,7 +47,7 @@ pub enum BuildError {
|
||||
UnknownOutputKind,
|
||||
|
||||
#[fail(display = "Project load error: {}", _0)]
|
||||
ProjectLoadError(#[fail(cause)] ProjectLoadFuzzyError),
|
||||
ProjectLoadError(#[fail(cause)] ProjectLoadError),
|
||||
|
||||
#[fail(display = "IO error: {}", _0)]
|
||||
IoError(#[fail(cause)] io::Error),
|
||||
@@ -66,7 +66,7 @@ pub enum BuildError {
|
||||
}
|
||||
|
||||
impl_from!(BuildError {
|
||||
ProjectLoadFuzzyError => ProjectLoadError,
|
||||
ProjectLoadError => ProjectLoadError,
|
||||
io::Error => IoError,
|
||||
rbx_xml::EncodeError => XmlModelEncodeError,
|
||||
rbx_binary::EncodeError => BinaryModelEncodeError,
|
||||
@@ -89,7 +89,6 @@ pub fn build(options: &BuildOptions) -> Result<(), BuildError> {
|
||||
info!("Looking for project at {}", options.fuzzy_project_path.display());
|
||||
|
||||
let project = Project::load_fuzzy(&options.fuzzy_project_path)?;
|
||||
project.check_compatibility();
|
||||
|
||||
info!("Found project at {}", project.file_location.display());
|
||||
info!("Using project {:#?}", project);
|
||||
@@ -120,6 +119,10 @@ pub fn build(options: &BuildOptions) -> Result<(), BuildError> {
|
||||
rbx_binary::encode(&tree, &[root_id], &mut file)?;
|
||||
},
|
||||
OutputKind::Rbxl => {
|
||||
log::warn!("Support for building binary places (rbxl) is still experimental.");
|
||||
log::warn!("Using the XML place format (rbxlx) is recommended instead.");
|
||||
log::warn!("For more info, see https://github.com/LPGhatguy/rojo/issues/180");
|
||||
|
||||
let root_id = tree.get_root_id();
|
||||
let top_level_ids = tree.get_instance(root_id).unwrap().get_children_ids();
|
||||
rbx_binary::encode(&tree, top_level_ids, &mut file)?;
|
||||
|
||||
@@ -7,7 +7,7 @@ use log::info;
|
||||
use failure::Fail;
|
||||
|
||||
use crate::{
|
||||
project::{Project, ProjectLoadFuzzyError},
|
||||
project::{Project, ProjectLoadError},
|
||||
web::LiveServer,
|
||||
imfs::FsError,
|
||||
live_session::{LiveSession, LiveSessionError},
|
||||
@@ -24,7 +24,7 @@ pub struct ServeOptions {
|
||||
#[derive(Debug, Fail)]
|
||||
pub enum ServeError {
|
||||
#[fail(display = "Project load error: {}", _0)]
|
||||
ProjectLoadError(#[fail(cause)] ProjectLoadFuzzyError),
|
||||
ProjectLoadError(#[fail(cause)] ProjectLoadError),
|
||||
|
||||
#[fail(display = "{}", _0)]
|
||||
FsError(#[fail(cause)] FsError),
|
||||
@@ -34,7 +34,7 @@ pub enum ServeError {
|
||||
}
|
||||
|
||||
impl_from!(ServeError {
|
||||
ProjectLoadFuzzyError => ProjectLoadError,
|
||||
ProjectLoadError => ProjectLoadError,
|
||||
FsError => FsError,
|
||||
LiveSessionError => LiveSessionError,
|
||||
});
|
||||
@@ -43,7 +43,6 @@ pub fn serve(options: &ServeOptions) -> Result<(), ServeError> {
|
||||
info!("Looking for project at {}", options.fuzzy_project_path.display());
|
||||
|
||||
let project = Arc::new(Project::load_fuzzy(&options.fuzzy_project_path)?);
|
||||
project.check_compatibility();
|
||||
|
||||
info!("Found project at {}", project.file_location.display());
|
||||
info!("Using project {:#?}", project);
|
||||
|
||||
@@ -10,7 +10,7 @@ use reqwest::header::{ACCEPT, USER_AGENT, CONTENT_TYPE, COOKIE};
|
||||
|
||||
use crate::{
|
||||
imfs::{Imfs, FsError},
|
||||
project::{Project, ProjectLoadFuzzyError},
|
||||
project::{Project, ProjectLoadError},
|
||||
rbx_session::construct_oneoff_tree,
|
||||
rbx_snapshot::SnapshotError,
|
||||
};
|
||||
@@ -24,7 +24,7 @@ pub enum UploadError {
|
||||
InvalidKind(String),
|
||||
|
||||
#[fail(display = "Project load error: {}", _0)]
|
||||
ProjectLoadError(#[fail(cause)] ProjectLoadFuzzyError),
|
||||
ProjectLoadError(#[fail(cause)] ProjectLoadError),
|
||||
|
||||
#[fail(display = "IO error: {}", _0)]
|
||||
IoError(#[fail(cause)] io::Error),
|
||||
@@ -43,7 +43,7 @@ pub enum UploadError {
|
||||
}
|
||||
|
||||
impl_from!(UploadError {
|
||||
ProjectLoadFuzzyError => ProjectLoadError,
|
||||
ProjectLoadError => ProjectLoadError,
|
||||
io::Error => IoError,
|
||||
reqwest::Error => HttpError,
|
||||
rbx_xml::EncodeError => XmlModelEncodeError,
|
||||
@@ -65,7 +65,6 @@ pub fn upload(options: &UploadOptions) -> Result<(), UploadError> {
|
||||
info!("Looking for project at {}", options.fuzzy_project_path.display());
|
||||
|
||||
let project = Project::load_fuzzy(&options.fuzzy_project_path)?;
|
||||
project.check_compatibility();
|
||||
|
||||
info!("Found project at {}", project.file_location.display());
|
||||
info!("Using project {:#?}", project);
|
||||
|
||||
@@ -20,7 +20,7 @@ pub static COMPAT_PROJECT_FILENAME: &'static str = "roblox-project.json";
|
||||
/// want to do things like transforming paths to be absolute before handing them
|
||||
/// off to the rest of Rojo, we use this intermediate struct.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(deny_unknown_fields, rename_all = "camelCase")]
|
||||
struct SourceProject {
|
||||
name: String,
|
||||
tree: SourceProjectNode,
|
||||
@@ -88,7 +88,6 @@ fn serialize_unresolved_minimal<S>(unresolved: &UnresolvedRbxValue, serializer:
|
||||
RbxValue::Color3 { value } => value.serialize(serializer),
|
||||
RbxValue::Color3uint8 { value } => value.serialize(serializer),
|
||||
RbxValue::Content { value } => value.serialize(serializer),
|
||||
RbxValue::Enum { value } => value.serialize(serializer),
|
||||
RbxValue::Float32 { value } => value.serialize(serializer),
|
||||
RbxValue::Int32 { value } => value.serialize(serializer),
|
||||
RbxValue::String { value } => value.serialize(serializer),
|
||||
@@ -198,34 +197,37 @@ impl SourcePlugin {
|
||||
}
|
||||
}
|
||||
|
||||
/// Error returned by Project::load_exact
|
||||
#[derive(Debug, Fail)]
|
||||
pub enum ProjectLoadExactError {
|
||||
#[fail(display = "IO error: {}", _0)]
|
||||
IoError(#[fail(cause)] io::Error),
|
||||
|
||||
#[fail(display = "JSON error: {}", _0)]
|
||||
JsonError(#[fail(cause)] serde_json::Error),
|
||||
}
|
||||
|
||||
/// Error returned by Project::load_fuzzy
|
||||
#[derive(Debug, Fail)]
|
||||
pub enum ProjectLoadFuzzyError {
|
||||
#[fail(display = "Project not found")]
|
||||
pub enum ProjectLoadError {
|
||||
NotFound,
|
||||
|
||||
#[fail(display = "IO error: {}", _0)]
|
||||
IoError(#[fail(cause)] io::Error),
|
||||
Io {
|
||||
#[fail(cause)]
|
||||
inner: io::Error,
|
||||
path: PathBuf,
|
||||
},
|
||||
|
||||
#[fail(display = "JSON error: {}", _0)]
|
||||
JsonError(#[fail(cause)] serde_json::Error),
|
||||
Json {
|
||||
#[fail(cause)]
|
||||
inner: serde_json::Error,
|
||||
path: PathBuf,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<ProjectLoadExactError> for ProjectLoadFuzzyError {
|
||||
fn from(error: ProjectLoadExactError) -> ProjectLoadFuzzyError {
|
||||
match error {
|
||||
ProjectLoadExactError::IoError(inner) => ProjectLoadFuzzyError::IoError(inner),
|
||||
ProjectLoadExactError::JsonError(inner) => ProjectLoadFuzzyError::JsonError(inner),
|
||||
impl fmt::Display for ProjectLoadError {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
use self::ProjectLoadError::*;
|
||||
|
||||
match self {
|
||||
NotFound => {
|
||||
write!(formatter, "Project file not found")
|
||||
}
|
||||
Io { inner, path } => {
|
||||
write!(formatter, "I/O error: {} in path {}", inner, path.display())
|
||||
}
|
||||
Json { inner, path } => {
|
||||
write!(formatter, "JSON error: {} in path {}", inner, path.display())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -272,6 +274,17 @@ pub struct ProjectNode {
|
||||
}
|
||||
|
||||
impl ProjectNode {
|
||||
fn validate_reserved_names(&self) {
|
||||
for (name, child) in &self.children {
|
||||
if name.starts_with('$') {
|
||||
warn!("Keys starting with '$' are reserved by Rojo to ensure forward compatibility.");
|
||||
warn!("This project uses the key '{}', which should be renamed.", name);
|
||||
}
|
||||
|
||||
child.validate_reserved_names();
|
||||
}
|
||||
}
|
||||
|
||||
fn to_source_node(&self, project_file_location: &Path) -> SourceProjectNode {
|
||||
let children = self.children.iter()
|
||||
.map(|(key, value)| (key.clone(), value.to_source_node(project_file_location)))
|
||||
@@ -334,11 +347,17 @@ pub struct Project {
|
||||
|
||||
impl Project {
|
||||
pub fn init_place(project_fuzzy_path: &Path) -> Result<PathBuf, ProjectInitError> {
|
||||
let project_path = Project::init_pick_path(project_fuzzy_path)?;
|
||||
let project_path = Project::pick_path_for_init(project_fuzzy_path)?;
|
||||
|
||||
let project_name = if project_fuzzy_path == project_path {
|
||||
project_fuzzy_path.parent().unwrap().file_name().unwrap().to_str().unwrap()
|
||||
project_fuzzy_path
|
||||
.parent().expect("Path did not have a parent directory")
|
||||
.file_name().expect("Path did not have a file name")
|
||||
.to_str().expect("Path had invalid Unicode")
|
||||
} else {
|
||||
project_fuzzy_path.file_name().unwrap().to_str().unwrap()
|
||||
project_fuzzy_path
|
||||
.file_name().expect("Path did not have a file name")
|
||||
.to_str().expect("Path had invalid Unicode")
|
||||
};
|
||||
|
||||
let mut project = Project::load_from_str(DEFAULT_PLACE, &project_path)
|
||||
@@ -353,14 +372,22 @@ impl Project {
|
||||
}
|
||||
|
||||
pub fn init_model(project_fuzzy_path: &Path) -> Result<PathBuf, ProjectInitError> {
|
||||
let project_path = Project::init_pick_path(project_fuzzy_path)?;
|
||||
let project_folder_path = project_path.parent().unwrap();
|
||||
let project_path = Project::pick_path_for_init(project_fuzzy_path)?;
|
||||
|
||||
let project_name = if project_fuzzy_path == project_path {
|
||||
project_fuzzy_path.parent().unwrap().file_name().unwrap().to_str().unwrap()
|
||||
project_fuzzy_path
|
||||
.parent().expect("Path did not have a parent directory")
|
||||
.file_name().expect("Path did not have a file name")
|
||||
.to_str().expect("Path had invalid Unicode")
|
||||
} else {
|
||||
project_fuzzy_path.file_name().unwrap().to_str().unwrap()
|
||||
project_fuzzy_path
|
||||
.file_name().expect("Path did not have a file name")
|
||||
.to_str().expect("Path had invalid Unicode")
|
||||
};
|
||||
|
||||
let project_folder_path = project_path
|
||||
.parent().expect("Path did not have a parent directory");
|
||||
|
||||
let tree = ProjectNode {
|
||||
path: Some(project_folder_path.join("src")),
|
||||
..Default::default()
|
||||
@@ -381,7 +408,7 @@ impl Project {
|
||||
Ok(project_path)
|
||||
}
|
||||
|
||||
fn init_pick_path(project_fuzzy_path: &Path) -> Result<PathBuf, ProjectInitError> {
|
||||
fn pick_path_for_init(project_fuzzy_path: &Path) -> Result<PathBuf, ProjectInitError> {
|
||||
let is_exact = project_fuzzy_path.extension().is_some();
|
||||
|
||||
let project_path = if is_exact {
|
||||
@@ -401,7 +428,7 @@ impl Project {
|
||||
Ok(project_path)
|
||||
}
|
||||
|
||||
pub fn locate(start_location: &Path) -> Option<PathBuf> {
|
||||
fn locate(start_location: &Path) -> Option<PathBuf> {
|
||||
// TODO: Check for specific error kinds, convert 'not found' to Result.
|
||||
let location_metadata = fs::metadata(start_location).ok()?;
|
||||
|
||||
@@ -438,21 +465,35 @@ impl Project {
|
||||
Ok(parsed.into_project(project_file_location))
|
||||
}
|
||||
|
||||
pub fn load_fuzzy(fuzzy_project_location: &Path) -> Result<Project, ProjectLoadFuzzyError> {
|
||||
let project_path = Self::locate(fuzzy_project_location)
|
||||
.ok_or(ProjectLoadFuzzyError::NotFound)?;
|
||||
|
||||
Self::load_exact(&project_path).map_err(From::from)
|
||||
pub fn load_fuzzy(fuzzy_project_location: &Path) -> Result<Project, ProjectLoadError> {
|
||||
if let Some(project_path) = Self::locate(fuzzy_project_location) {
|
||||
Self::load_exact(&project_path)
|
||||
} else {
|
||||
Project::warn_if_4x_project_present(fuzzy_project_location);
|
||||
Err(ProjectLoadError::NotFound)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_exact(project_file_location: &Path) -> Result<Project, ProjectLoadExactError> {
|
||||
pub fn load_exact(project_file_location: &Path) -> Result<Project, ProjectLoadError> {
|
||||
let contents = fs::read_to_string(project_file_location)
|
||||
.map_err(ProjectLoadExactError::IoError)?;
|
||||
.map_err(|error| match error.kind() {
|
||||
io::ErrorKind::NotFound => ProjectLoadError::NotFound,
|
||||
_ => ProjectLoadError::Io {
|
||||
inner: error,
|
||||
path: project_file_location.to_path_buf(),
|
||||
}
|
||||
})?;
|
||||
|
||||
let parsed: SourceProject = serde_json::from_str(&contents)
|
||||
.map_err(ProjectLoadExactError::JsonError)?;
|
||||
.map_err(|error| ProjectLoadError::Json {
|
||||
inner: error,
|
||||
path: project_file_location.to_path_buf(),
|
||||
})?;
|
||||
|
||||
Ok(parsed.into_project(project_file_location))
|
||||
let project = parsed.into_project(project_file_location);
|
||||
project.check_compatibility();
|
||||
|
||||
Ok(project)
|
||||
}
|
||||
|
||||
pub fn save(&self) -> Result<(), ProjectSaveError> {
|
||||
@@ -468,10 +509,10 @@ impl Project {
|
||||
|
||||
/// Checks if there are any compatibility issues with this project file and
|
||||
/// warns the user if there are any.
|
||||
pub fn check_compatibility(&self) {
|
||||
fn check_compatibility(&self) {
|
||||
let file_name = self.file_location
|
||||
.file_name().unwrap()
|
||||
.to_str().expect("Project file path was not valid Unicode!");
|
||||
.file_name().expect("Project file path did not have a file name")
|
||||
.to_str().expect("Project file path was not valid Unicode");
|
||||
|
||||
if file_name == COMPAT_PROJECT_FILENAME {
|
||||
warn!("Rojo's default project file name changed in 0.5.0-alpha3.");
|
||||
@@ -483,6 +524,23 @@ impl Project {
|
||||
warn!(".project.json extension. This helps Rojo differentiate project files from");
|
||||
warn!("other JSON files!");
|
||||
}
|
||||
|
||||
self.tree.validate_reserved_names();
|
||||
}
|
||||
|
||||
/// Issues a warning if no Rojo 0.5.x project is found, but there's a legacy
|
||||
/// 0.4.x project in the directory.
|
||||
fn warn_if_4x_project_present(folder: &Path) {
|
||||
let file_path = folder.join("rojo.json");
|
||||
|
||||
if fs::metadata(file_path).is_ok() {
|
||||
warn!("No Rojo 0.5 project file was found, but a Rojo 0.4 project was.");
|
||||
warn!("Rojo 0.5.x uses 'default.project.json' files");
|
||||
warn!("Rojo 0.5.x uses 'rojo.json' files");
|
||||
warn!("");
|
||||
warn!("For help upgrading, see:");
|
||||
warn!("https://lpghatguy.github.io/rojo/guide/migrating-to-epiphany/");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn folder_location(&self) -> &Path {
|
||||
|
||||
@@ -105,6 +105,21 @@ pub enum SnapshotError {
|
||||
path: PathBuf,
|
||||
},
|
||||
|
||||
ExtraMetadataError {
|
||||
#[fail(cause)]
|
||||
inner: serde_json::Error,
|
||||
path: PathBuf,
|
||||
},
|
||||
|
||||
InvalidMetadataModelField {
|
||||
field_name: String,
|
||||
path: PathBuf,
|
||||
},
|
||||
|
||||
MetadataClassNameNonInit {
|
||||
path: PathBuf,
|
||||
},
|
||||
|
||||
XmlModelDecodeError {
|
||||
#[fail(cause)]
|
||||
inner: rbx_xml::DecodeError,
|
||||
@@ -152,6 +167,17 @@ impl fmt::Display for SnapshotError {
|
||||
SnapshotError::JsonModelDecodeError { inner, path } => {
|
||||
write!(output, "Malformed .model.json model: {} in path {}", inner, path.display())
|
||||
},
|
||||
SnapshotError::ExtraMetadataError { inner, path } => {
|
||||
write!(output, "Malformed init.meta.json: {} in path {}", inner, path.display())
|
||||
},
|
||||
SnapshotError::InvalidMetadataModelField { field_name, path } => {
|
||||
writeln!(output, "The field '{}' cannot be specified on .meta.json files attached to models.", field_name)?;
|
||||
writeln!(output, "Model path: {}", path.display())
|
||||
},
|
||||
SnapshotError::MetadataClassNameNonInit { path } => {
|
||||
writeln!(output, "The field 'className' cannot be specified on .meta.json files besides init.meta.json")?;
|
||||
writeln!(output, "Model path: {}", path.display())
|
||||
},
|
||||
SnapshotError::XmlModelDecodeError { inner, path } => {
|
||||
write!(output, "Malformed rbxmx model: {} in path {}", inner, path.display())
|
||||
},
|
||||
@@ -285,7 +311,7 @@ fn snapshot_imfs_item<'source>(
|
||||
instance_name: Option<Cow<'source, str>>,
|
||||
) -> SnapshotResult<'source> {
|
||||
match item {
|
||||
ImfsItem::File(file) => snapshot_imfs_file(context, file, instance_name),
|
||||
ImfsItem::File(file) => snapshot_imfs_file(context, imfs, file, instance_name),
|
||||
ImfsItem::Directory(directory) => snapshot_imfs_directory(context, imfs, directory, instance_name),
|
||||
}
|
||||
}
|
||||
@@ -327,6 +353,10 @@ fn snapshot_imfs_directory<'source>(
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(meta) = ExtraMetadata::locate(&imfs, &directory.path.join("init"))? {
|
||||
meta.apply(&mut snapshot)?;
|
||||
}
|
||||
|
||||
snapshot.metadata.source_path = Some(directory.path.to_owned());
|
||||
|
||||
for child_path in &directory.children {
|
||||
@@ -334,25 +364,105 @@ fn snapshot_imfs_directory<'source>(
|
||||
.file_name().expect("Couldn't extract file name")
|
||||
.to_str().expect("Couldn't convert file name to UTF-8");
|
||||
|
||||
if child_name.ends_with(".meta.json") {
|
||||
// meta.json files don't turn into instances themselves, they just
|
||||
// modify other instances.
|
||||
continue;
|
||||
}
|
||||
|
||||
match child_name {
|
||||
INIT_MODULE_NAME | INIT_SERVER_NAME | INIT_CLIENT_NAME => {
|
||||
// The existence of files with these names modifies the
|
||||
// parent instance and is handled above, so we can skip
|
||||
// them here.
|
||||
},
|
||||
_ => {
|
||||
if let Some(child) = snapshot_imfs_path(context, imfs, child_path, None)? {
|
||||
snapshot.children.push(child);
|
||||
}
|
||||
},
|
||||
continue;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if let Some(child) = snapshot_imfs_path(context, imfs, child_path, None)? {
|
||||
snapshot.children.push(child);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Some(snapshot))
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
||||
struct ExtraMetadata {
|
||||
class_name: Option<String>,
|
||||
ignore_unknown_instances: Option<bool>,
|
||||
|
||||
#[serde(default = "HashMap::new")]
|
||||
properties: HashMap<String, UnresolvedRbxValue>,
|
||||
}
|
||||
|
||||
impl ExtraMetadata {
|
||||
fn apply(self, snapshot: &mut RbxSnapshotInstance) -> Result<(), SnapshotError> {
|
||||
if let Some(meta_class) = self.class_name {
|
||||
snapshot.class_name = Cow::Owned(meta_class);
|
||||
}
|
||||
|
||||
if let Some(meta_ignore_instances) = self.ignore_unknown_instances {
|
||||
snapshot.metadata.ignore_unknown_instances = meta_ignore_instances;
|
||||
}
|
||||
|
||||
for (key, value) in self.properties {
|
||||
let resolved_value = try_resolve_value(&snapshot.class_name, &key, &value)?;
|
||||
snapshot.properties.insert(key, resolved_value);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn locate(imfs: &Imfs, path: &Path) -> Result<Option<ExtraMetadata>, SnapshotError> {
|
||||
match imfs.get(&path.with_extension("meta.json")) {
|
||||
Some(ImfsItem::File(file)) => {
|
||||
let meta: ExtraMetadata = serde_json::from_slice(&file.contents)
|
||||
.map_err(|inner| SnapshotError::ExtraMetadataError {
|
||||
inner,
|
||||
path: file.path.to_path_buf(),
|
||||
})?;
|
||||
|
||||
Ok(Some(meta))
|
||||
}
|
||||
_ => Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_for_non_init(&self, path: &Path) -> Result<(), SnapshotError> {
|
||||
if self.class_name.is_some() {
|
||||
return Err(SnapshotError::MetadataClassNameNonInit {
|
||||
path: path.to_owned(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_for_model(&self, path: &Path) -> Result<(), SnapshotError> {
|
||||
if self.class_name.is_some() {
|
||||
return Err(SnapshotError::InvalidMetadataModelField {
|
||||
field_name: "className".to_owned(),
|
||||
path: path.to_owned(),
|
||||
});
|
||||
}
|
||||
|
||||
if !self.properties.is_empty() {
|
||||
return Err(SnapshotError::InvalidMetadataModelField {
|
||||
field_name: "properties".to_owned(),
|
||||
path: path.to_owned(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn snapshot_imfs_file<'source>(
|
||||
context: &SnapshotContext,
|
||||
imfs: &'source Imfs,
|
||||
file: &'source ImfsFile,
|
||||
instance_name: Option<Cow<'source, str>>,
|
||||
) -> SnapshotResult<'source> {
|
||||
@@ -360,11 +470,11 @@ fn snapshot_imfs_file<'source>(
|
||||
.map(|v| v.to_str().expect("Could not convert extension to UTF-8"));
|
||||
|
||||
let mut maybe_snapshot = match extension {
|
||||
Some("lua") => snapshot_lua_file(file)?,
|
||||
Some("csv") => snapshot_csv_file(file)?,
|
||||
Some("txt") => snapshot_txt_file(file)?,
|
||||
Some("rbxmx") => snapshot_xml_model_file(file)?,
|
||||
Some("rbxm") => snapshot_binary_model_file(file)?,
|
||||
Some("lua") => snapshot_lua_file(file, imfs)?,
|
||||
Some("csv") => snapshot_csv_file(file, imfs)?,
|
||||
Some("txt") => snapshot_txt_file(file, imfs)?,
|
||||
Some("rbxmx") => snapshot_xml_model_file(file, imfs)?,
|
||||
Some("rbxm") => snapshot_binary_model_file(file, imfs)?,
|
||||
Some("json") => {
|
||||
let file_stem = file.path
|
||||
.file_stem().expect("Could not extract file stem")
|
||||
@@ -379,7 +489,7 @@ fn snapshot_imfs_file<'source>(
|
||||
Some(_) | None => None,
|
||||
};
|
||||
|
||||
if let Some(snapshot) = maybe_snapshot.as_mut() {
|
||||
if let Some(mut snapshot) = maybe_snapshot.as_mut() {
|
||||
// Carefully preserve name from project manifest if present.
|
||||
if let Some(snapshot_name) = instance_name {
|
||||
snapshot.name = snapshot_name;
|
||||
@@ -407,6 +517,7 @@ fn snapshot_imfs_file<'source>(
|
||||
|
||||
fn snapshot_lua_file<'source>(
|
||||
file: &'source ImfsFile,
|
||||
imfs: &'source Imfs,
|
||||
) -> SnapshotResult<'source> {
|
||||
let file_stem = file.path
|
||||
.file_stem().expect("Could not extract file stem")
|
||||
@@ -426,7 +537,7 @@ fn snapshot_lua_file<'source>(
|
||||
path: file.path.to_path_buf(),
|
||||
})?;
|
||||
|
||||
Ok(Some(RbxSnapshotInstance {
|
||||
let mut snapshot = RbxSnapshotInstance {
|
||||
name: Cow::Borrowed(instance_name),
|
||||
class_name: Cow::Borrowed(class_name),
|
||||
properties: hashmap! {
|
||||
@@ -440,7 +551,14 @@ fn snapshot_lua_file<'source>(
|
||||
ignore_unknown_instances: false,
|
||||
project_definition: None,
|
||||
},
|
||||
}))
|
||||
};
|
||||
|
||||
if let Some(meta) = ExtraMetadata::locate(&imfs, &file.path.with_file_name(instance_name))? {
|
||||
meta.validate_for_non_init(&file.path)?;
|
||||
meta.apply(&mut snapshot)?;
|
||||
}
|
||||
|
||||
Ok(Some(snapshot))
|
||||
}
|
||||
|
||||
fn match_trailing<'a>(input: &'a str, trailer: &str) -> Option<&'a str> {
|
||||
@@ -454,6 +572,7 @@ fn match_trailing<'a>(input: &'a str, trailer: &str) -> Option<&'a str> {
|
||||
|
||||
fn snapshot_txt_file<'source>(
|
||||
file: &'source ImfsFile,
|
||||
imfs: &'source Imfs,
|
||||
) -> SnapshotResult<'source> {
|
||||
let instance_name = file.path
|
||||
.file_stem().expect("Could not extract file stem")
|
||||
@@ -465,7 +584,7 @@ fn snapshot_txt_file<'source>(
|
||||
path: file.path.to_path_buf(),
|
||||
})?;
|
||||
|
||||
Ok(Some(RbxSnapshotInstance {
|
||||
let mut snapshot = RbxSnapshotInstance {
|
||||
name: Cow::Borrowed(instance_name),
|
||||
class_name: Cow::Borrowed("StringValue"),
|
||||
properties: hashmap! {
|
||||
@@ -479,11 +598,19 @@ fn snapshot_txt_file<'source>(
|
||||
ignore_unknown_instances: false,
|
||||
project_definition: None,
|
||||
},
|
||||
}))
|
||||
};
|
||||
|
||||
if let Some(meta) = ExtraMetadata::locate(&imfs, &file.path)? {
|
||||
meta.validate_for_non_init(&file.path)?;
|
||||
meta.apply(&mut snapshot)?;
|
||||
}
|
||||
|
||||
Ok(Some(snapshot))
|
||||
}
|
||||
|
||||
fn snapshot_csv_file<'source>(
|
||||
file: &'source ImfsFile,
|
||||
imfs: &'source Imfs,
|
||||
) -> SnapshotResult<'source> {
|
||||
/// Struct that holds any valid row from a Roblox CSV translation table.
|
||||
///
|
||||
@@ -570,7 +697,7 @@ fn snapshot_csv_file<'source>(
|
||||
let table_contents = serde_json::to_string(&entries)
|
||||
.expect("Could not encode JSON for localization table");
|
||||
|
||||
Ok(Some(RbxSnapshotInstance {
|
||||
let mut snapshot = RbxSnapshotInstance {
|
||||
name: Cow::Borrowed(instance_name),
|
||||
class_name: Cow::Borrowed("LocalizationTable"),
|
||||
properties: hashmap! {
|
||||
@@ -584,7 +711,14 @@ fn snapshot_csv_file<'source>(
|
||||
ignore_unknown_instances: false,
|
||||
project_definition: None,
|
||||
},
|
||||
}))
|
||||
};
|
||||
|
||||
if let Some(meta) = ExtraMetadata::locate(&imfs, &file.path)? {
|
||||
meta.validate_for_non_init(&file.path)?;
|
||||
meta.apply(&mut snapshot)?;
|
||||
}
|
||||
|
||||
Ok(Some(snapshot))
|
||||
}
|
||||
|
||||
fn snapshot_json_model_file<'source>(
|
||||
@@ -648,6 +782,7 @@ impl JsonModelInstance {
|
||||
|
||||
fn snapshot_xml_model_file<'source>(
|
||||
file: &'source ImfsFile,
|
||||
imfs: &'source Imfs,
|
||||
) -> SnapshotResult<'source> {
|
||||
let instance_name = file.path
|
||||
.file_stem().expect("Could not extract file stem")
|
||||
@@ -670,6 +805,13 @@ fn snapshot_xml_model_file<'source>(
|
||||
1 => {
|
||||
let mut snapshot = snapshot_from_tree(&temp_tree, children[0]).unwrap();
|
||||
snapshot.name = Cow::Borrowed(instance_name);
|
||||
snapshot.metadata.source_path = Some(file.path.clone());
|
||||
|
||||
if let Some(meta) = ExtraMetadata::locate(&imfs, &file.path)? {
|
||||
meta.validate_for_model(&file.path)?;
|
||||
meta.apply(&mut snapshot)?;
|
||||
}
|
||||
|
||||
Ok(Some(snapshot))
|
||||
},
|
||||
_ => panic!("Rojo doesn't have support for model files with multiple roots yet"),
|
||||
@@ -678,6 +820,7 @@ fn snapshot_xml_model_file<'source>(
|
||||
|
||||
fn snapshot_binary_model_file<'source>(
|
||||
file: &'source ImfsFile,
|
||||
imfs: &'source Imfs,
|
||||
) -> SnapshotResult<'source> {
|
||||
let instance_name = file.path
|
||||
.file_stem().expect("Could not extract file stem")
|
||||
@@ -704,6 +847,13 @@ fn snapshot_binary_model_file<'source>(
|
||||
1 => {
|
||||
let mut snapshot = snapshot_from_tree(&temp_tree, children[0]).unwrap();
|
||||
snapshot.name = Cow::Borrowed(instance_name);
|
||||
snapshot.metadata.source_path = Some(file.path.clone());
|
||||
|
||||
if let Some(meta) = ExtraMetadata::locate(&imfs, &file.path)? {
|
||||
meta.validate_for_model(&file.path)?;
|
||||
meta.apply(&mut snapshot)?;
|
||||
}
|
||||
|
||||
Ok(Some(snapshot))
|
||||
},
|
||||
_ => panic!("Rojo doesn't have support for model files with multiple roots yet"),
|
||||
|
||||
55
server/tests/malformed_projects.rs
Normal file
55
server/tests/malformed_projects.rs
Normal file
@@ -0,0 +1,55 @@
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use librojo::{
|
||||
live_session::LiveSession,
|
||||
project::Project,
|
||||
};
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref TEST_PROJECTS_ROOT: PathBuf = {
|
||||
Path::new(env!("CARGO_MANIFEST_DIR")).join("../test-projects")
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bad_json_model() {
|
||||
let project = Project::load_fuzzy(&TEST_PROJECTS_ROOT.join("bad_json_model"))
|
||||
.expect("Project file didn't load");
|
||||
|
||||
if LiveSession::new(Arc::new(project)).is_ok() {
|
||||
panic!("Project should not have succeeded");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bad_meta_lua_classname() {
|
||||
let project = Project::load_fuzzy(&TEST_PROJECTS_ROOT.join("bad_meta_lua_classname"))
|
||||
.expect("Project file didn't load");
|
||||
|
||||
if LiveSession::new(Arc::new(project)).is_ok() {
|
||||
panic!("Project should not have succeeded");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bad_meta_rbxmx_properties() {
|
||||
let project = Project::load_fuzzy(&TEST_PROJECTS_ROOT.join("bad_meta_rbxmx_properties"))
|
||||
.expect("Project file didn't load");
|
||||
|
||||
if LiveSession::new(Arc::new(project)).is_ok() {
|
||||
panic!("Project should not have succeeded");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bad_missing_files() {
|
||||
let project = Project::load_fuzzy(&TEST_PROJECTS_ROOT.join("bad_missing_files"))
|
||||
.expect("Project file didn't load");
|
||||
|
||||
if LiveSession::new(Arc::new(project)).is_ok() {
|
||||
panic!("Project should not have succeeded");
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,7 @@ generate_snapshot_tests!(
|
||||
empty,
|
||||
json_model,
|
||||
localization,
|
||||
meta_files,
|
||||
multi_partition_game,
|
||||
nested_partitions,
|
||||
single_partition_game,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "malformed-stuff",
|
||||
"name": "bad_json_model",
|
||||
"tree": {
|
||||
"$path": "src"
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "bad_meta_lua_classname",
|
||||
"tree": {
|
||||
"$path": "src"
|
||||
}
|
||||
}
|
||||
1
test-projects/bad_meta_lua_classname/src/foo.lua
Normal file
1
test-projects/bad_meta_lua_classname/src/foo.lua
Normal file
@@ -0,0 +1 @@
|
||||
-- foo.lua
|
||||
3
test-projects/bad_meta_lua_classname/src/foo.meta.json
Normal file
3
test-projects/bad_meta_lua_classname/src/foo.meta.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"className": "Script"
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "bad_meta_rbxmx_properties",
|
||||
"tree": {
|
||||
"$path": "src"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"properties": {
|
||||
"x": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<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="RBXFC73D3B1B4524E729A1563A276CBC702">
|
||||
<Properties>
|
||||
<string name="Name">Folder</string>
|
||||
<BinaryString name="Tags"></BinaryString>
|
||||
</Properties>
|
||||
</Item>
|
||||
</roblox>
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "missing-files",
|
||||
"name": "bad_missing_files",
|
||||
"tree": {
|
||||
"$path": "does-not-exist"
|
||||
}
|
||||
4
test-projects/legacy-0.4.x/rojo.json
Normal file
4
test-projects/legacy-0.4.x/rojo.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"name": "legacy",
|
||||
"parittions": {}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "legacy-0.5.x-reserved-names",
|
||||
"tree": {
|
||||
"$className": "Folder",
|
||||
"$warn-about-me": {
|
||||
"$className": "Folder"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "legacy-0.5.x-reserved-names",
|
||||
"tree": {
|
||||
"$className": "Folder"
|
||||
},
|
||||
"some field Rojo doesn't have": true
|
||||
}
|
||||
6
test-projects/legacy-old-0.5.x/roblox-project.json
Normal file
6
test-projects/legacy-old-0.5.x/roblox-project.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "Foo",
|
||||
"tree": {
|
||||
"$className": "Folder"
|
||||
}
|
||||
}
|
||||
6
test-projects/meta_files/default.project.json
Normal file
6
test-projects/meta_files/default.project.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "test-model",
|
||||
"tree": {
|
||||
"$path": "src"
|
||||
}
|
||||
}
|
||||
129
test-projects/meta_files/expected-snapshot.json
Normal file
129
test-projects/meta_files/expected-snapshot.json
Normal file
@@ -0,0 +1,129 @@
|
||||
{
|
||||
"name": "test-model",
|
||||
"class_name": "Tool",
|
||||
"properties": {
|
||||
"Enabled": {
|
||||
"Type": "Bool",
|
||||
"Value": true
|
||||
}
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"name": "A",
|
||||
"class_name": "Folder",
|
||||
"properties": {},
|
||||
"children": [],
|
||||
"metadata": {
|
||||
"ignore_unknown_instances": true,
|
||||
"source_path": "src/A",
|
||||
"project_definition": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "DisableMe",
|
||||
"class_name": "Script",
|
||||
"properties": {
|
||||
"Disabled": {
|
||||
"Type": "Bool",
|
||||
"Value": true
|
||||
},
|
||||
"Source": {
|
||||
"Type": "String",
|
||||
"Value": ""
|
||||
}
|
||||
},
|
||||
"children": [],
|
||||
"metadata": {
|
||||
"ignore_unknown_instances": true,
|
||||
"source_path": "src/DisableMe.server.lua",
|
||||
"project_definition": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "LocalizationTable",
|
||||
"class_name": "LocalizationTable",
|
||||
"properties": {
|
||||
"Contents": {
|
||||
"Type": "String",
|
||||
"Value": "[{\"key\":\"Doge\",\"example\":\"A funny dog\",\"source\":\"Perro!\",\"values\":{\"en\":\"Doge!\"}}]"
|
||||
},
|
||||
"SourceLocaleId": {
|
||||
"Type": "String",
|
||||
"Value": "es"
|
||||
}
|
||||
},
|
||||
"children": [],
|
||||
"metadata": {
|
||||
"ignore_unknown_instances": false,
|
||||
"source_path": "src/LocalizationTable.csv",
|
||||
"project_definition": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "RobloxInstance",
|
||||
"class_name": "Folder",
|
||||
"properties": {
|
||||
"Tags": {
|
||||
"Type": "BinaryString",
|
||||
"Value": ""
|
||||
}
|
||||
},
|
||||
"children": [],
|
||||
"metadata": {
|
||||
"ignore_unknown_instances": true,
|
||||
"source_path": "src/RobloxInstance.rbxmx",
|
||||
"project_definition": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Script",
|
||||
"class_name": "Script",
|
||||
"properties": {
|
||||
"Source": {
|
||||
"Type": "String",
|
||||
"Value": "print(\"Hello, world\")"
|
||||
},
|
||||
"Disabled": {
|
||||
"Type": "Bool",
|
||||
"Value": true
|
||||
}
|
||||
},
|
||||
"children": [],
|
||||
"metadata": {
|
||||
"ignore_unknown_instances": false,
|
||||
"source_path": "src/Script",
|
||||
"project_definition": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "StringValue",
|
||||
"class_name": "StringValue",
|
||||
"properties": {
|
||||
"Value": {
|
||||
"Type": "String",
|
||||
"Value": "I'm supposed to put funny text here, aren't I? Oh well."
|
||||
}
|
||||
},
|
||||
"children": [],
|
||||
"metadata": {
|
||||
"ignore_unknown_instances": true,
|
||||
"source_path": "src/StringValue.txt",
|
||||
"project_definition": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"ignore_unknown_instances": false,
|
||||
"source_path": "src",
|
||||
"project_definition": [
|
||||
"test-model",
|
||||
{
|
||||
"class_name": null,
|
||||
"children": {},
|
||||
"properties": {},
|
||||
"ignore_unknown_instances": null,
|
||||
"path": "src"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
3
test-projects/meta_files/src/A/init.meta.json
Normal file
3
test-projects/meta_files/src/A/init.meta.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"ignoreUnknownInstances": true
|
||||
}
|
||||
6
test-projects/meta_files/src/DisableMe.meta.json
Normal file
6
test-projects/meta_files/src/DisableMe.meta.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"ignoreUnknownInstances": true,
|
||||
"properties": {
|
||||
"Disabled": true
|
||||
}
|
||||
}
|
||||
0
test-projects/meta_files/src/DisableMe.server.lua
Normal file
0
test-projects/meta_files/src/DisableMe.server.lua
Normal file
2
test-projects/meta_files/src/LocalizationTable.csv
Normal file
2
test-projects/meta_files/src/LocalizationTable.csv
Normal file
@@ -0,0 +1,2 @@
|
||||
Key,Source,Context,Example,en
|
||||
Doge,Perro!,,A funny dog,Doge!
|
||||
|
5
test-projects/meta_files/src/LocalizationTable.meta.json
Normal file
5
test-projects/meta_files/src/LocalizationTable.meta.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"properties": {
|
||||
"SourceLocaleId": "es"
|
||||
}
|
||||
}
|
||||
3
test-projects/meta_files/src/RobloxInstance.meta.json
Normal file
3
test-projects/meta_files/src/RobloxInstance.meta.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"ignoreUnknownInstances": true
|
||||
}
|
||||
11
test-projects/meta_files/src/RobloxInstance.rbxmx
Normal file
11
test-projects/meta_files/src/RobloxInstance.rbxmx
Normal file
@@ -0,0 +1,11 @@
|
||||
<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="RBXFC73D3B1B4524E729A1563A276CBC702">
|
||||
<Properties>
|
||||
<string name="Name">Folder</string>
|
||||
<BinaryString name="Tags"></BinaryString>
|
||||
</Properties>
|
||||
</Item>
|
||||
</roblox>
|
||||
5
test-projects/meta_files/src/Script/init.meta.json
Normal file
5
test-projects/meta_files/src/Script/init.meta.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"properties": {
|
||||
"Disabled": true
|
||||
}
|
||||
}
|
||||
1
test-projects/meta_files/src/Script/init.server.lua
Normal file
1
test-projects/meta_files/src/Script/init.server.lua
Normal file
@@ -0,0 +1 @@
|
||||
print("Hello, world")
|
||||
3
test-projects/meta_files/src/StringValue.meta.json
Normal file
3
test-projects/meta_files/src/StringValue.meta.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"ignoreUnknownInstances": true
|
||||
}
|
||||
1
test-projects/meta_files/src/StringValue.txt
Normal file
1
test-projects/meta_files/src/StringValue.txt
Normal file
@@ -0,0 +1 @@
|
||||
I'm supposed to put funny text here, aren't I? Oh well.
|
||||
6
test-projects/meta_files/src/init.meta.json
Normal file
6
test-projects/meta_files/src/init.meta.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"className": "Tool",
|
||||
"properties": {
|
||||
"Enabled": true
|
||||
}
|
||||
}
|
||||
6
test-projects/unions/default.project.json
Normal file
6
test-projects/unions/default.project.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "unions",
|
||||
"tree": {
|
||||
"$path": "src"
|
||||
}
|
||||
}
|
||||
357
test-projects/unions/src/two-parts.rbxmx
Normal file
357
test-projects/unions/src/two-parts.rbxmx
Normal file
@@ -0,0 +1,357 @@
|
||||
<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="UnionOperation" referent="RBX352E50AE6BA54B49BA84369F24DECC02">
|
||||
<Properties>
|
||||
<bool name="Anchored">false</bool>
|
||||
<Content name="AssetId"><null></null></Content>
|
||||
<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>-5.5</X>
|
||||
<Y>1.00000095</Y>
|
||||
<Z>-10.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="CastShadow">true</bool>
|
||||
<BinaryString name="ChildData"><![CDATA[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
|
||||
b3g+]]></BinaryString>
|
||||
<int name="CollisionGroupId">0</int>
|
||||
<Color3uint8 name="Color3uint8">4288914085</Color3uint8>
|
||||
<PhysicalProperties name="CustomPhysicalProperties">
|
||||
<CustomPhysics>false</CustomPhysics>
|
||||
</PhysicalProperties>
|
||||
<token name="FormFactor">3</token>
|
||||
<float name="FrontParamA">-0.5</float>
|
||||
<float name="FrontParamB">0.5</float>
|
||||
<token name="FrontSurface">0</token>
|
||||
<token name="FrontSurfaceInput">0</token>
|
||||
<Vector3 name="InitialSize">
|
||||
<X>5</X>
|
||||
<Y>2</Y>
|
||||
<Z>3</Z>
|
||||
</Vector3>
|
||||
<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>
|
||||
<BinaryString name="MeshData"><![CDATA[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
|
||||
BRpgN20dUiswTyRZZ1MEelYublhRIDAE]]></BinaryString>
|
||||
<string name="Name">Union</string>
|
||||
<SharedString name="PhysicalConfigData">MpkZJVsLUKCPlqSHCAWUqw==</SharedString>
|
||||
<BinaryString name="PhysicsData"></BinaryString>
|
||||
<float name="Reflectance">0</float>
|
||||
<token name="RenderFidelity">1</token>
|
||||
<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>
|
||||
<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>
|
||||
<bool name="UsePartColor">false</bool>
|
||||
<Vector3 name="Velocity">
|
||||
<X>0</X>
|
||||
<Y>0</Y>
|
||||
<Z>0</Z>
|
||||
</Vector3>
|
||||
<Vector3 name="size">
|
||||
<X>5</X>
|
||||
<Y>2</Y>
|
||||
<Z>3</Z>
|
||||
</Vector3>
|
||||
</Properties>
|
||||
</Item>
|
||||
<SharedStrings>
|
||||
<SharedString md5="MpkZJVsLUKCPlqSHCAWUqw==">Q1NHUEhTBgAAAI4mRUFT5Hi8ReB1vToHcL0i+nRBg+FvwAg5dcDGrAZCL4ppwKxM7kEQAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAIA/DAAAAAQAAAAAACBAAAAAAAAA
|
||||
QD8AAPA/AAAAAAAAQD8AACBAAAAAAAAAAAAAAPA/AAAAAAAAAAAMAAAAAAAAAAEAAAADAAAA
|
||||
AAAAAAMAAAACAAAAAAAAAAIAAAADAAAAAAAAAAMAAAABAAAAEAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAQAAAAAAAAAAAAAAAAAAAAAACAPwwAAAAEAAAAAAAAAAAAAAAAAEA/AAAAAAAAAAAAAMA/
|
||||
AACgPwAAAAAAAEA/AACgPwAAAAAAAMA/DAAAAAAAAAABAAAAAwAAAAAAAAADAAAAAgAAAAAA
|
||||
AAACAAAAAwAAAAAAAAADAAAAAQAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAA
|
||||
AAAAAAAAgD8PAAAABAAAAAAAoL8AAAAAAADAPzP/v78AAIAzAADAPwAAwL8AAAAAAADAPwAA
|
||||
oL8AAAAAAABAPwAAwL8AAAAAAABAPxIAAAAAAAAAAQAAAAIAAAAAAAAAAgAAAAQAAAAAAAAA
|
||||
BAAAAAMAAAAAAAAAAwAAAAEAAAABAAAAAwAAAAQAAAABAAAABAAAAAIAAAAQAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAIA/DwAAAAQAAAAAAPA/AAAAAAAAAL8AAPA/
|
||||
AAAAAAAAAAAAACBAAACAMwAAAAAAACBAAAAAAAAAAL/NAABAAAAAAAAAAAASAAAAAAAAAAEA
|
||||
AAACAAAAAAAAAAIAAAADAAAAAAAAAAMAAAAEAAAAAAAAAAQAAAABAAAAAQAAAAQAAAACAAAA
|
||||
AgAAAAQAAAADAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAACAPw8A
|
||||
AAAEAAAAAACgvwAAAAAAAEA/AACgvwAAAAAAAMA//vufvwAAgDMAAMA/AAAAAAAAAAAAAEA/
|
||||
AAAAAAAAAAAAAMA/EgAAAAAAAAABAAAAAgAAAAAAAAACAAAAAwAAAAAAAAADAAAABAAAAAAA
|
||||
AAAEAAAAAQAAAAEAAAAEAAAAAgAAAAIAAAAEAAAAAwAAABAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
EAAAAAAAAAAAAAAAAAAAAAAAgD8PAAAABAAAAAAAoD8AAAAAAABAPwAAoD8AAAAAAADAPwAA
|
||||
IEAAAIAzMPu/PwAAIEAAAAAAAABAPwAAIEAAAAAAAADAPxIAAAAAAAAAAQAAAAIAAAAAAAAA
|
||||
AgAAAAMAAAAAAAAAAwAAAAQAAAAAAAAABAAAAAEAAAABAAAABAAAAAIAAAACAAAABAAAAAMA
|
||||
AAAQAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAIA/GAAAAAQAAAAAAKC/
|
||||
AACAPwAAAAAAAMC/AACAPwAAAAAAAMC/AAAAPwAAAAAAAMC/AEDNOABATbgAAKC/AEDNOAAA
|
||||
AL8AAKC/AACAPwAAAL8AAMC/AACAPwAAAL8AAMC/AEDNOAAAAL8kAAAAAAAAAAEAAAACAAAA
|
||||
AAAAAAIAAAADAAAAAAAAAAMAAAAEAAAAAAAAAAQAAAAFAAAAAAAAAAUAAAAGAAAAAAAAAAYA
|
||||
AAABAAAAAQAAAAYAAAAHAAAAAQAAAAcAAAADAAAAAQAAAAMAAAACAAAAAwAAAAcAAAAEAAAA
|
||||
BAAAAAcAAAAGAAAABAAAAAYAAAAFAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAA
|
||||
AAAAAAAAAACAPxgAAAAEAAAAAADAPwAAAAAAAEC/AACgPwAAAAAAAEC/AADAP///f78AAEC/
|
||||
AADAPwAAAAAAAMC/AACgPwAAAAAAAMC/AACgP///f78AAEC/AADAP///f78AAMC/AACgP///
|
||||
f78AAMC/JAAAAAAAAAABAAAABQAAAAAAAAAFAAAAAgAAAAAAAAACAAAABgAAAAAAAAAGAAAA
|
||||
AwAAAAAAAAADAAAABAAAAAAAAAAEAAAAAQAAAAEAAAAEAAAABwAAAAEAAAAHAAAABQAAAAIA
|
||||
AAAFAAAABwAAAAIAAAAHAAAABgAAAAMAAAAGAAAABwAAAAMAAAAHAAAABAAAABAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAgD8YAAAABAAAAAAAID8AAAAAAABAPwAA
|
||||
AAAAAAAAAABAPwAAID///3+/lvn/PgAAID8AAAAAlvn/PgAAAAAAAAAAlvn/PgAAAAD//3+/
|
||||
lvn/PgAAID///3+/AAAAAAAAAAD//3+/AAAAACQAAAAAAAAAAQAAAAUAAAAAAAAABQAAAAIA
|
||||
AAAAAAAAAgAAAAYAAAAAAAAABgAAAAMAAAAAAAAAAwAAAAQAAAAAAAAABAAAAAEAAAABAAAA
|
||||
BAAAAAcAAAABAAAABwAAAAUAAAACAAAABQAAAAcAAAACAAAABwAAAAYAAAADAAAABgAAAAcA
|
||||
AAADAAAABwAAAAQAAAAQAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAIA/
|
||||
GAAAAAQAAAAAACC/AAAAAAAAQD8AAKC/AAAAAAAAQD8AACC///9/v5b5/z4AACC/AAAAAJb5
|
||||
/z4AAKC/AAAAAJb5/z4AAKC///9/v5b5/z4AACC///9/vwAAAAAAAKC///9/vwAAAAAkAAAA
|
||||
AAAAAAEAAAAFAAAAAAAAAAUAAAACAAAAAAAAAAIAAAAGAAAAAAAAAAYAAAADAAAAAAAAAAMA
|
||||
AAAEAAAAAAAAAAQAAAABAAAAAQAAAAQAAAAHAAAAAQAAAAcAAAAFAAAAAgAAAAUAAAAHAAAA
|
||||
AgAAAAcAAAAGAAAAAwAAAAYAAAAHAAAAAwAAAAcAAAAEAAAAEAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAQAAAAAAAAAAAAAAAAAAAAAACAPxgAAAAEAAAAAAAAAAAAAAAAAEA/AAAgvwAAAAAAAEA/
|
||||
AAAAAP//f7+W+f8+AAAAAAAAAACW+f8+AAAgvwAAAACW+f8+AAAgv///f7+W+f8+AAAAAP//
|
||||
f78AAAAAAAAgv///f78AAAAAJAAAAAAAAAABAAAABQAAAAAAAAAFAAAAAgAAAAAAAAACAAAA
|
||||
BgAAAAAAAAAGAAAAAwAAAAAAAAADAAAABAAAAAAAAAAEAAAAAQAAAAEAAAAEAAAABwAAAAEA
|
||||
AAAHAAAABQAAAAIAAAAFAAAABwAAAAIAAAAHAAAABgAAAAMAAAAGAAAABwAAAAMAAAAHAAAA
|
||||
BAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAgD8YAAAABAAAAAAA
|
||||
oD8AAAAAAABAPwAAID8AAAAAAABAPwAAoD///3+/lvn/PgAAoD8AAAAAlvn/PgAAID8AAAAA
|
||||
lvn/PgAAID///3+/lvn/PgAAoD///3+/AAAAAAAAID///3+/AAAAACQAAAAAAAAAAQAAAAUA
|
||||
AAAAAAAABQAAAAIAAAAAAAAAAgAAAAYAAAAAAAAABgAAAAMAAAAAAAAAAwAAAAQAAAAAAAAA
|
||||
BAAAAAEAAAABAAAABAAAAAcAAAABAAAABwAAAAUAAAACAAAABQAAAAcAAAACAAAABwAAAAYA
|
||||
AAADAAAABgAAAAcAAAADAAAABwAAAAQAAAAQAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAA
|
||||
AAAAAAAAAAAAAIA/GwAAAAQAAAAAAPA/AAAAAAAAQD8AAKA/AAAAAAAAQD81A8A///9/v5b5
|
||||
/z4AAPA/AAAAAAAAAAAAAKA/AAAAAJb5/z4AAKA///9/v5b5/z41A8A///9/vwAAAAA1A8A/
|
||||
AAAAAAAAAAAAAKA///9/vwAAAAAqAAAAAAAAAAEAAAAFAAAAAAAAAAUAAAACAAAAAAAAAAIA
|
||||
AAAGAAAAAAAAAAYAAAADAAAAAAAAAAMAAAAHAAAAAAAAAAcAAAAEAAAAAAAAAAQAAAABAAAA
|
||||
AQAAAAQAAAAIAAAAAQAAAAgAAAAFAAAAAgAAAAUAAAAIAAAAAgAAAAgAAAAGAAAAAwAAAAYA
|
||||
AAAIAAAAAwAAAAgAAAAHAAAABAAAAAcAAAAIAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAA
|
||||
AAAAAAAAAAAAAAAAAACAPyEAAAAEAAAAAADwPwAgzbgAAAAANQPAPwAAgDOW+f++NQPAPwAg
|
||||
zbgAAAAANQPAP///f78AAAAAAADwPwAgzbiW+f++NQPAPwAgzbgAAEC/AACgPwAgzbgAAEC/
|
||||
AACgPwAgzbiW+f++AACgP///f78AAAAANQPAP///f78AAEC/AACgP///f78AAEC/NgAAAAAA
|
||||
AAABAAAAAgAAAAAAAAACAAAACAAAAAAAAAAIAAAAAwAAAAAAAAADAAAACQAAAAAAAAAJAAAA
|
||||
BAAAAAAAAAAEAAAAAQAAAAEAAAAEAAAABQAAAAEAAAAFAAAABgAAAAEAAAAGAAAABwAAAAEA
|
||||
AAAHAAAAAgAAAAIAAAAHAAAACAAAAAMAAAAIAAAACgAAAAMAAAAKAAAACQAAAAQAAAAJAAAA
|
||||
BQAAAAUAAAAJAAAACgAAAAUAAAAKAAAABgAAAAYAAAAKAAAACAAAAAYAAAAIAAAABwAAABAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAgD8YAAAABAAAAAAAIMD//3+/
|
||||
AAAAAAAAIMD//3+/AAAAPwAAIMAAAAAAAAAAAAAA8L///3+/AAAAAAAA8L///3+/AAAAPwAA
|
||||
IMAAAAAAAAAAPwAA8L8AAAAAAAAAAAAA8L8AAAAAAAAAPyQAAAAAAAAAAQAAAAUAAAAAAAAA
|
||||
BQAAAAIAAAAAAAAAAgAAAAYAAAAAAAAABgAAAAMAAAAAAAAAAwAAAAQAAAAAAAAABAAAAAEA
|
||||
AAABAAAABAAAAAcAAAABAAAABwAAAAUAAAACAAAABQAAAAcAAAACAAAABwAAAAYAAAADAAAA
|
||||
BgAAAAcAAAADAAAABwAAAAQAAAAQAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAA
|
||||
AAAAAIA/FQAAAAQAAAAAAMC/AEDNOAAAAAAAAKC/AACAPwAAAAAAAKC/AEDNOAAAwD8AAMC/
|
||||
AEDNOAAAwD8AAMC/AACAPwAAAAAAAKC/AACAPwAAwD8AAMC/AACAPwAAwD8eAAAAAAAAAAEA
|
||||
AAACAAAAAAAAAAIAAAADAAAAAAAAAAMAAAAGAAAAAAAAAAYAAAAEAAAAAAAAAAQAAAABAAAA
|
||||
AQAAAAQAAAAGAAAAAQAAAAYAAAAFAAAAAQAAAAUAAAACAAAAAgAAAAUAAAAGAAAAAgAAAAYA
|
||||
AAADAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAACAPxIAAAAEAAAA
|
||||
AACgPwAAgD8AAAAAAACgPwAAgD8AAAC/AAAAAAAAgD8AAAAAAACgPwBAzTgAAAC/AAAAAAAA
|
||||
gD8AAAC/AAAAAABAzTgAAAC/GAAAAAAAAAABAAAABAAAAAAAAAAEAAAAAgAAAAAAAAACAAAA
|
||||
BQAAAAAAAAAFAAAAAwAAAAAAAAADAAAAAQAAAAEAAAADAAAABQAAAAEAAAAFAAAABAAAAAIA
|
||||
AAAEAAAABQAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAgD8SAAAA
|
||||
BAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAvwAAoL8AAIA/AAAAAAAAAAAAQM04AAAAvwAA
|
||||
oL8AAIA/AAAAvwAAoL8AQM04AAAAvxgAAAAAAAAAAQAAAAQAAAAAAAAABAAAAAIAAAAAAAAA
|
||||
AgAAAAUAAAAAAAAABQAAAAMAAAAAAAAAAwAAAAEAAAABAAAAAwAAAAUAAAABAAAABQAAAAQA
|
||||
AAACAAAABAAAAAUAAAAQAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAIA/
|
||||
IQAAAAQAAAAAAKC/ACDNuAAAQD81A8C/AACAM5b5/z41A8C/ACDNuAAAQD8AAKC///9/v5b5
|
||||
/z4AAKC/ACDNuJb5/z41A8C/ACDNuAAAAAAAAPC/ACDNuAAAAAAAAPC/ACDNuJb5/z4AAPC/
|
||||
//9/v5b5/z4AAKC///9/vwAAAAAAAPC///9/vwAAAAA2AAAAAAAAAAEAAAACAAAAAAAAAAIA
|
||||
AAAIAAAAAAAAAAgAAAADAAAAAAAAAAMAAAAJAAAAAAAAAAkAAAAEAAAAAAAAAAQAAAABAAAA
|
||||
AQAAAAQAAAAFAAAAAQAAAAUAAAAGAAAAAQAAAAYAAAAHAAAAAQAAAAcAAAACAAAAAgAAAAcA
|
||||
AAAIAAAAAwAAAAgAAAAKAAAAAwAAAAoAAAAJAAAABAAAAAkAAAAFAAAABQAAAAkAAAAKAAAA
|
||||
BQAAAAoAAAAGAAAABgAAAAoAAAAIAAAABgAAAAgAAAAHAAAAEAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAQAAAAAAAAAAAAAAAAAAAAAACAPxUAAAAEAAAAAAAgQAAAgD8AAAAAAAAgQAAAgD8AAAC/
|
||||
AACgPwAAgD8AAAAAAAAgQABAzTgAAAAAAAAgQABAzTgAAAC/AACgPwAAgD8AAAC/AACgPwBA
|
||||
zTgAAAC/HgAAAAAAAAABAAAABQAAAAAAAAAFAAAAAgAAAAAAAAACAAAAAwAAAAAAAAADAAAA
|
||||
BAAAAAAAAAAEAAAAAQAAAAEAAAAEAAAABgAAAAEAAAAGAAAABQAAAAIAAAAFAAAABgAAAAIA
|
||||
AAAGAAAAAwAAAAMAAAAGAAAABAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAA
|
||||
AAAAAAAAgD8VAAAABAAAAAAAoD8AAIA/AADAPwAAoD8AQM04AADAPwAAoD8AAIA/AAAAAAAA
|
||||
AAAAAIA/AADAPwBQADkAQM04AADAPwAAAAAAAIA/AAAAAAAAAAAAAMA+AADAPx4AAAAAAAAA
|
||||
AQAAAAIAAAAAAAAAAgAAAAUAAAAAAAAABQAAAAMAAAAAAAAAAwAAAAYAAAAAAAAABgAAAAQA
|
||||
AAAAAAAABAAAAAEAAAABAAAABAAAAAUAAAABAAAABQAAAAIAAAADAAAABQAAAAYAAAAEAAAA
|
||||
BgAAAAUAAAAQAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAIA/EgAAAAQA
|
||||
AAAAAAAAAACAPwAAwD8AAAAAAEDNOAAAwD8AAAAAAACAPwAAAAAAAKC/AACAPwAAwD8AAKC/
|
||||
AEDNOAAAwD8AAKC/AACAPwAAAAAYAAAAAAAAAAEAAAACAAAAAAAAAAIAAAAFAAAAAAAAAAUA
|
||||
AAADAAAAAAAAAAMAAAAEAAAAAAAAAAQAAAABAAAAAQAAAAQAAAAFAAAAAQAAAAUAAAACAAAA
|
||||
AwAAAAUAAAAEAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAACAPxgA
|
||||
AAAEAAAAAAAAAP//f78AAAAAAAAAAAAAgDNqBgC/AACgv///f78AAAAAAAAAAP//f78AAMC/
|
||||
AACgvwAgzbhqBgC/AAAAAAAgzbgAAMC/AACgvwAgzbgAAMC/AACgv///f78AAMC/JAAAAAAA
|
||||
AAABAAAAAgAAAAAAAAACAAAABwAAAAAAAAAHAAAAAwAAAAAAAAADAAAABQAAAAAAAAAFAAAA
|
||||
AQAAAAEAAAAEAAAAAgAAAAEAAAAFAAAABgAAAAEAAAAGAAAABAAAAAIAAAAEAAAABgAAAAIA
|
||||
AAAGAAAABwAAAAMAAAAHAAAABgAAAAMAAAAGAAAABQAAABAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
EAAAAAAAAAAAAAAAAAAAAAAAgD8bAAAABAAAAAAAoD///3+/AAAAAAAAAAD//3+/AAAAAAAA
|
||||
oD///3+/AADAvwAAoD8AAIAzagYAv/77nz8AAIAzagYAvwAAAAAAIM24agYAvwAAAAD//3+/
|
||||
AADAvwAAoD8AIM24AADAvwAAAAAAIM24AADAvyoAAAAAAAAAAQAAAAYAAAAAAAAABgAAAAIA
|
||||
AAAAAAAAAgAAAAcAAAAAAAAABwAAAAMAAAAAAAAAAwAAAAQAAAAAAAAABAAAAAEAAAABAAAA
|
||||
BAAAAAUAAAABAAAABQAAAAgAAAABAAAACAAAAAYAAAACAAAABgAAAAgAAAACAAAACAAAAAcA
|
||||
AAADAAAABwAAAAgAAAADAAAACAAAAAQAAAAEAAAACAAAAAUAAAAQAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAABAAAAAAAAAAAAAAAAAAAAAAAIA/GAAAAAQAAAAAACBAAACAPwAAwD8AACBAAEDNOAAA
|
||||
wD8AACBAAACAPwAAAAAAAKA/AACAPwAAwD8CBKA/AEDNOAAAwD8AACBAAEDNOAAAAAAAAKA/
|
||||
AACAPwAAAAAAAKA/AAAAPwAAwD8kAAAAAAAAAAEAAAAFAAAAAAAAAAUAAAACAAAAAAAAAAIA
|
||||
AAAGAAAAAAAAAAYAAAADAAAAAAAAAAMAAAAHAAAAAAAAAAcAAAAEAAAAAAAAAAQAAAABAAAA
|
||||
AQAAAAQAAAAFAAAAAgAAAAUAAAAGAAAAAwAAAAYAAAAHAAAABAAAAAcAAAAGAAAABAAAAAYA
|
||||
AAAFAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAACAPxsAAAAEAAAA
|
||||
AACgv///f78AAAAANQPAvwAgzbgAAAAAAAAgwP//f78AAAAAAACgv///f78AAMC/AACgvwAA
|
||||
gDNqBgC/AAAgwAAgzbgAAAAAAAAgwP//f78AAMC/AACgvwAgzbgAAMC/AAAgwAAgzbgAAMC/
|
||||
KgAAAAAAAAABAAAABQAAAAAAAAAFAAAAAgAAAAAAAAACAAAABgAAAAAAAAAGAAAAAwAAAAAA
|
||||
AAADAAAABwAAAAAAAAAHAAAABAAAAAAAAAAEAAAAAQAAAAEAAAAEAAAABQAAAAIAAAAFAAAA
|
||||
CAAAAAIAAAAIAAAABgAAAAMAAAAGAAAACAAAAAMAAAAIAAAABwAAAAQAAAAHAAAACAAAAAQA
|
||||
AAAIAAAABQAAAA==</SharedString>
|
||||
</SharedStrings>
|
||||
</roblox>
|
||||
Reference in New Issue
Block a user