Compare commits

..

43 Commits

Author SHA1 Message Date
Lucien Greathouse
420627d892 0.5.0-alpha.11 2019-05-29 14:07:15 -07:00
Lucien Greathouse
ce3a409997 Undo 0.5.0-alpha.10 release due to regression 2019-05-29 13:38:36 -07:00
Lucien Greathouse
0f9f1782ae 0.5.0-alpha.10 2019-05-29 13:24:06 -07:00
Lucien Greathouse
d4704a02c5 Upgrade dependencies 2019-05-29 13:15:22 -07:00
Lucien Greathouse
9ca2ed2c93 plugin: upgrade Roact 2019-05-16 18:45:35 -07:00
Lucien Greathouse
ae12ffdefb Work around Roact bug 2019-05-16 18:45:00 -07:00
Lucien Greathouse
1e13097126 plugin: update rbx-dom 2019-05-16 18:33:37 -07:00
Lucien Greathouse
9b8a6b1168 Add terrain test project 2019-05-16 18:03:11 -07:00
Lucien Greathouse
8f6dda5cd3 Use rbx_xml 0.9.0's config to read unknown properties 2019-05-16 17:58:32 -07:00
Lucien Greathouse
91780f236e Update dependencies 2019-05-16 17:58:19 -07:00
Lucien Greathouse
f16474815c plugin: update rbx-dom 2019-05-15 11:19:05 -07:00
Lucien Greathouse
a8ff6d7e6e Update dependencies 2019-05-14 18:23:01 -07:00
Lucien Greathouse
8395782a2e Use Display instead of Debug for rbx_xml errors now 2019-05-14 17:55:18 -07:00
Lucien Greathouse
28ea625b01 Plugin: Port reconciler to use rbx_dom_lua 2019-05-14 14:22:55 -07:00
Lucien Greathouse
efc569f6ed Plugin: Update rbx-dom 2019-05-14 14:22:44 -07:00
Lucien Greathouse
d377e10771 Update rbx-dom 2019-05-13 17:35:55 -07:00
Lucien Greathouse
fef85877e6 Add safeguards against accidentally committing model or place files 2019-05-13 17:29:41 -07:00
Lucien Greathouse
19135bfaf4 Add RbxDom library as piece of plugin 2019-05-13 17:29:41 -07:00
Lucien Greathouse
5a147fccc2 Add rbx-dom as Git submodule to plugin 2019-05-13 17:29:41 -07:00
Lucien Greathouse
20976814ba Upgrade a bunch of small dependencies 2019-05-12 12:57:59 -07:00
Lucien Greathouse
27e2612fc9 Upgrade rbx_dom_weak, rbx_reflection, and rbx_xml 2019-05-12 12:57:24 -07:00
Lucien Greathouse
3ea432ef2d Fix up docs on model/place files a little 2019-05-09 13:29:03 -07:00
Lucien Greathouse
fe6acbc1e3 Clean up repo cruft 2019-05-04 21:01:10 -07:00
Lucien Greathouse
379b162e64 Fix dependency paths changing.
Roact 1.0 changed from lib to src!
t changed from lib/t.lua to lib/init.lua, so we just use lib
2019-05-04 19:33:08 -07:00
Lucien Greathouse
84832955dd Upgrade to Roact 1.0 and latest t 2019-05-04 00:05:45 -07:00
Lucien Greathouse
34b99a51c3 Relax debug assert in IMFS, since paths can alias now 2019-04-30 23:06:59 -07:00
Lucien Greathouse
fb5245e2af Update dependencies 2019-04-22 18:26:28 -07:00
Diego Alpízar
ff0a830e0c Minor typo fix (#156)
Fix repeated "available available"
2019-04-06 23:38:29 -07:00
eryn L. K
a365f071a4 Update installation.md (#155) 2019-04-05 17:20:53 -07:00
Lucien Greathouse
f290e7b5b2 Support implicit values in JSON models (#154)
* Support implicit values in JSON models

* Update Changelog
2019-04-05 15:17:58 -07:00
Lucien Greathouse
83a0ae673c 0.5.0-alpha.9 2019-04-04 21:20:00 -07:00
Lucien Greathouse
7de646c290 Upgrade dependencies 2019-04-04 18:35:18 -07:00
Lucien Greathouse
5d681a72ac Rewrite CSV conversion to dodge Serde (#152)
* Rewrite CSV conversion to dodge Serde

* Update CHANGELOG
2019-04-04 18:21:55 -07:00
Lucien Greathouse
d725970e6e Fix handling of CSV files with empty columns and rows (#149)
* Fix #147

* Add localization test project, fix empty rows in general

* Fill out 'normal' CSV in localization test project

* Update Changelog
2019-04-04 13:16:10 -07:00
Lucien Greathouse
54b82760cd Switch 'rojo build' to use BufWriter, magic performance increase 2019-04-01 18:02:46 -07:00
Lucien Greathouse
77f79fa913 0.5.0-alpha.8 2019-03-29 17:36:43 -07:00
Lucien Greathouse
6db714a2b1 Special-case Lighting.Technology in setCanonicalProperty, temporary fix 2019-03-29 17:25:57 -07:00
Lucien Greathouse
913ac7c9f5 Update dependencies 2019-03-28 15:44:56 -07:00
Lucien Greathouse
eecbfd29e7 Update dependencies, adding a bunch of new features 2019-03-27 13:31:12 -07:00
Lucien Greathouse
41025225b2 Rewrite message queue with oneshot futures (#139) 2019-03-27 13:27:50 -07:00
Lucien Greathouse
07c7b28c03 Fix plugin unloading 2019-03-21 22:35:30 -07:00
Lucien Greathouse
3faf3d2a56 Update Changelog for #135 2019-03-20 10:42:18 -07:00
Lucien Greathouse
be094d5b7c Make snapshot application communicative (#135)
* Add children sorting to snapshot_reconciler

* Update snapshot tests to include stable children order

* Bump dependencies, which should make this PR work
2019-03-20 10:39:53 -07:00
57 changed files with 1936 additions and 1176 deletions

6
.gitignore vendored
View File

@@ -2,4 +2,8 @@
/target
/scratch-project
**/*.rs.bk
/server/failed-snapshots/
/server/failed-snapshots/
/*.rbxm
/*.rbxmx
/*.rbxl
/*.rbxlx

5
.gitmodules vendored
View File

@@ -12,4 +12,7 @@
url = https://github.com/LPGhatguy/roblox-lua-promise.git
[submodule "plugin/modules/t"]
path = plugin/modules/t
url = https://github.com/osyrisrblx/t.git
url = https://github.com/osyrisrblx/t.git
[submodule "plugin/modules/rbx-dom"]
path = plugin/modules/rbx-dom
url = http://github.com/LPGhatguy/rbx-dom

View File

@@ -2,6 +2,42 @@
## [Unreleased]
## [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))
* `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"`.
* Improved performance when working with XML models and places
* Fixed serializing empty `Content` properties as XML
* Fixed serializing infinite and NaN floating point properties in XML
* Improved compatibility with XML models
* 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.
## [0.5.0 Alpha 9](https://github.com/LPGhatguy/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))
* 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)
* Added support for a bunch of new types when dealing with XML model/place files:
* `ColorSequence`
* `Float64`
* `Int64`
* `NumberRange`
* `NumberSequence`
* `PhysicalProperties`
* `Ray`
* `Rect`
* `Ref`
* Improved server instance ordering behavior when files are added during a live session ([#135](https://github.com/LPGhatguy/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.
## [0.5.0 Alpha 6](https://github.com/LPGhatguy/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))

893
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,7 @@
[workspace]
members = [
"server",
"rojo-e2e",
]
]
[profile.dev]
opt-level = 1

View File

@@ -1,39 +0,0 @@
digraph G {
graph [
ranksep = "0.7",
nodesep = "1.0",
];
node [
fontname = "Hack",
shape = "record",
];
roblox_studio -> plugin [dir = "both"];
plugin -> web_server [style = "dashed", dir = "both"];
web_server -> session;
session -> rbx_session;
session -> fs_watcher;
session -> message_queue;
fs_watcher -> imfs [weight = "10"];
fs_watcher -> rbx_session [constraint = "false"];
imfs -> fs;
rbx_session -> imfs;
rbx_session -> middlewares [weight = "10"];
rbx_session -> message_queue [constraint = "false"];
plugin [label = "Studio Plugin"];
roblox_studio [label = "Roblox Studio"];
fs [label = "Filesystem"];
fs_watcher [label = "Filesystem Watcher"];
session [label = "Session"];
web_server [label = "Web API"];
imfs [label = "In-Memory Filesystem"];
rbx_session [label = "RbxSession"];
message_queue [label = "MessageQueue"];
middlewares [label = "Middlewares"];
}

View File

@@ -28,13 +28,13 @@ Now that we have a project, one thing we can do is build a Roblox place file for
All we have to do is call `rojo build`:
```sh
rojo build -o MyNewProject.rbxl
rojo build -o MyNewProject.rbxlx
```
If you open `MyNewProject.rbxl` in Roblox Studio now, you should see a `Folder` containing a `ModuleScript` under `ReplicatedStorage`!
If you open `MyNewProject.rbxlx` in Roblox Studio now, you should see a `Folder` containing a `ModuleScript` under `ReplicatedStorage`!
!!! info
To generate an XML place file instead, like if you're checking the place file into version control, just use `rbxlx` as the extension on the output file instead.
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.

View File

@@ -25,13 +25,13 @@ 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.6
cargo install rojo --version 0.5.0-alpha.11
```
## Installing the Plugin
### Installing from GitHub
The Rojo Roblox Studio plugin is available available from Rojo's [GitHub Releases page](https://github.com/LPGhatguy/rojo/releases).
The Rojo Roblox Studio plugin is available from Rojo's [GitHub Releases page](https://github.com/LPGhatguy/rojo/releases).
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:
@@ -42,4 +42,4 @@ Download the attached `rbxm` file and put it into your Roblox Studio plugins fol
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 on Windows, 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 add partitions and start/stop the Rojo server!
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!

View File

@@ -86,6 +86,6 @@ It would turn into instances in this shape:
## Binary and XML Models
Rojo supports both binary (`.rbxm`) and XML (`.rbxmx`) models generated by Roblox Studio or another tool.
Not all property types are supported for all formats!
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_tree's type coverage chart](https://github.com/LPGhatguy/rbx-tree#property-type-coverage).
For a rundown of supported types, check out [rbx-dom's type coverage chart](https://github.com/LPGhatguy/rbx-dom#property-type-coverage).

View File

@@ -6,13 +6,16 @@
"$path": "src"
},
"Roact": {
"$path": "modules/roact/lib"
"$path": "modules/roact/src"
},
"Promise": {
"$path": "modules/promise/lib"
},
"t": {
"$path": "modules/t/lib/t.lua"
"$path": "modules/t/lib"
},
"RbxDom": {
"$path": "modules/rbx-dom/rbx_dom_lua/src"
}
}
}

View File

@@ -13,13 +13,13 @@
"$path": "src"
},
"Roact": {
"$path": "modules/roact/lib"
"$path": "modules/roact/src"
},
"Promise": {
"$path": "modules/promise/lib"
},
"t": {
"$path": "modules/t/lib/t.lua"
"$path": "modules/t/lib"
}
},
"TestEZ": {

View File

@@ -1,34 +0,0 @@
{
"name": "rojo",
"servePort": 8000,
"partitions": {
"plugin": {
"path": "src",
"target": "ReplicatedStorage.Rojo.Plugin"
},
"modules/roact": {
"path": "modules/roact/lib",
"target": "ReplicatedStorage.Rojo.Roact"
},
"modules/rodux": {
"path": "modules/rodux/lib",
"target": "ReplicatedStorage.Rojo.Rodux"
},
"modules/roact-rodux": {
"path": "modules/roact-rodux/lib",
"target": "ReplicatedStorage.Rojo.RoactRodux"
},
"modules/promise": {
"path": "modules/promise/lib",
"target": "ReplicatedStorage.Rojo.Promise"
},
"modules/testez": {
"path": "modules/testez/lib",
"target": "ReplicatedStorage.TestEZ"
},
"tests": {
"path": "testBootstrap.server.lua",
"target": "TestService.testBootstrap"
}
}
}

View File

@@ -80,7 +80,8 @@ function App:init()
end
function App:render()
local children
-- FIXME: https://github.com/Roblox/roact/issues/209
local children = {}
if self.state.sessionStatus == SessionStatus.Connected then
children = {

View File

@@ -1,6 +1,6 @@
return {
codename = "Epiphany",
version = {0, 5, 0, "-alpha.6"},
version = {0, 5, 0, "-alpha.11"},
expectedServerVersionString = "0.5.0 or newer",
protocolVersion = 2,
defaultHost = "localhost",

View File

@@ -6,6 +6,12 @@ local setCanonicalProperty = require(script.Parent.setCanonicalProperty)
local rojoValueToRobloxValue = require(script.Parent.rojoValueToRobloxValue)
local Types = require(script.Parent.Types)
local function setParent(instance, newParent)
pcall(function()
instance.Parent = newParent
end)
end
local Reconciler = {}
Reconciler.__index = Reconciler
@@ -117,7 +123,7 @@ function Reconciler:reconcile(virtualInstancesById, id, instance)
-- Some instances, like services, don't like having their Parent
-- property poked, even if we're setting it to the same value.
setCanonicalProperty(instance, "Parent", parent)
setParent(instance, parent)
end
return instance
@@ -154,7 +160,7 @@ function Reconciler:__reify(virtualInstancesById, id, parent)
self:__reify(virtualInstancesById, childId, instance)
end
setCanonicalProperty(instance, "Parent", parent)
setParent(instance, parent)
self.instanceMap:insert(id, instance)
return instance

View File

@@ -10,8 +10,8 @@ local app = Roact.createElement(App, {
plugin = plugin,
})
Roact.mount(app, game:GetService("CoreGui"), "Rojo UI")
local tree = Roact.mount(app, game:GetService("CoreGui"), "Rojo UI")
plugin.Unloading:Connect(function()
Roact.unmount(app)
Roact.unmount(tree)
end)

View File

@@ -1,38 +1,33 @@
local primitiveTypes = {
Bool = true,
Enum = true,
Float32 = true,
Float64 = true,
Int32 = true,
Int64 = true,
String = true,
}
local directConstructors = {
CFrame = CFrame.new,
Color3 = Color3.new,
Color3uint8 = Color3.fromRGB,
Rect = Rect.new,
UDim = UDim.new,
UDim2 = UDim2.new,
Vector2 = Vector2.new,
Vector2int16 = Vector2int16.new,
Vector3 = Vector3.new,
Vector3int16 = Vector3int16.new,
}
local RbxDom = require(script:FindFirstAncestor("Rojo").RbxDom)
local function rojoValueToRobloxValue(value)
if primitiveTypes[value.Type] then
return value.Value
-- TODO: Manually decode this value by looking up its GUID The Rojo server
-- doesn't give us valid ref values yet, so this isn't important yet.
if value.Type == "Ref" then
return nil
end
local constructor = directConstructors[value.Type]
if constructor ~= nil then
return constructor(unpack(value.Value))
-- 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 errorMessage = ("The Rojo plugin doesn't know how to handle values of type %q yet!"):format(tostring(value.Type))
error(errorMessage)
local success, decodedValue = RbxDom.EncodedValue.decode(value)
if not success then
error(decodedValue, 2)
end
return decodedValue
end
return rojoValueToRobloxValue

View File

@@ -1,28 +1,17 @@
local RbxDom = require(script:FindFirstAncestor("Rojo").RbxDom)
local Logging = require(script.Parent.Logging)
--[[
Attempts to set a property on the given instance.
This method deals in terms of what Rojo calls 'canonical properties', which
don't necessarily exist either in serialization or in Lua-reflected APIs,
but may be present in the API dump.
Ideally, canonical properties map 1:1 with properties we can assign, but in
some cases like LocalizationTable contents and CollectionService tags, we
have to read/write properties a little differently.
]]
local function setCanonicalProperty(instance, key, value)
-- The 'Contents' property of LocalizationTable isn't directly exposed, but
-- has corresponding (deprecated) getters and setters.
if instance.ClassName == "LocalizationTable" and key == "Contents" then
instance:SetContents(value)
return
if not RbxDom.CanonicalProperty.isScriptable(instance.ClassName, key) then
return false
end
-- If we don't have permissions to access this value at all, we can skip it.
local readSuccess, existingValue = pcall(function()
return instance[key]
end)
local readSuccess, existingValue = RbxDom.CanonicalProperty.read(instance, key)
if not readSuccess then
-- An error will be thrown if there was a permission issue or if the
@@ -30,17 +19,13 @@ local function setCanonicalProperty(instance, key, value)
-- 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
return false
else
error(("Invalid property %s on class %s: %s"):format(tostring(key), instance.ClassName, existingValue), 2)
end
end
local writeSuccess, err = pcall(function()
if existingValue ~= value then
instance[key] = value
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)

View File

@@ -1,6 +0,0 @@
[package]
name = "rojo-e2e"
version = "0.1.0"
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
[dependencies]

View File

@@ -1,2 +0,0 @@
# Rojo End-to-End
This is a WIP test runner designed for Rojo. It will eventually start up the Rojo server and plugin and test functionality end-to-end.

View File

@@ -1,32 +0,0 @@
use std::{
path::Path,
process::Command,
thread,
time::Duration,
};
fn main() {
let plugin_path = Path::new("../plugin");
let server_path = Path::new("../server");
let tests_path = Path::new("../tests");
let server = Command::new("cargo")
.args(&["run", "--", "serve", "../test-projects/empty"])
.current_dir(server_path)
.spawn();
thread::sleep(Duration::from_millis(1000));
// TODO: Wait for server to start responding on the right port
let test_client = Command::new("lua")
.args(&["runTest.lua", "tests/empty.lua"])
.current_dir(plugin_path)
.spawn();
thread::sleep(Duration::from_millis(300));
// TODO: Collect output from the client for success/failure?
println!("Dying!");
}

View File

@@ -1,6 +1,6 @@
[package]
name = "rojo"
version = "0.5.0-alpha.6"
version = "0.5.0-alpha.11"
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
description = "A tool to create robust Roblox projects"
license = "MIT"
@@ -30,15 +30,14 @@ log = "0.4"
maplit = "1.0.1"
notify = "4.0"
rbx_binary = "0.4.0"
rbx_dom_weak = "1.2.0"
rbx_xml = "0.5.0"
rbx_reflection = "2.0.374"
rbx_dom_weak = "1.7.0"
rbx_xml = "0.9.0"
rbx_reflection = "3.0.384"
regex = "1.0"
reqwest = "0.9.5"
rlua = "0.16"
ritz = "0.1.0"
serde = "1.0"
serde_derive = "1.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
uuid = { version = "0.7", features = ["v4", "serde"] }

View File

@@ -1,7 +1,7 @@
use std::{
path::PathBuf,
fs::File,
io,
io::{self, Write, BufWriter},
};
use log::info;
@@ -74,6 +74,11 @@ impl_from!(BuildError {
SnapshotError => SnapshotError,
});
fn xml_encode_config() -> rbx_xml::EncodeOptions {
rbx_xml::EncodeOptions::new()
.property_behavior(rbx_xml::EncodePropertyBehavior::WriteUnknown)
}
pub fn build(options: &BuildOptions) -> Result<(), BuildError> {
let output_kind = options.output_kind
.or_else(|| detect_output_kind(options))
@@ -92,7 +97,7 @@ pub fn build(options: &BuildOptions) -> Result<(), BuildError> {
let mut imfs = Imfs::new();
imfs.add_roots_from_project(&project)?;
let tree = construct_oneoff_tree(&project, &imfs)?;
let mut file = File::create(&options.output_file)?;
let mut file = BufWriter::new(File::create(&options.output_file)?);
match output_kind {
OutputKind::Rbxmx => {
@@ -100,7 +105,7 @@ pub fn build(options: &BuildOptions) -> Result<(), BuildError> {
// descendants.
let root_id = tree.get_root_id();
rbx_xml::encode(&tree, &[root_id], &mut file)?;
rbx_xml::to_writer(&mut file, &tree, &[root_id], xml_encode_config())?;
},
OutputKind::Rbxlx => {
// Place files don't contain an entry for the DataModel, but our
@@ -108,7 +113,7 @@ pub fn build(options: &BuildOptions) -> Result<(), BuildError> {
let root_id = tree.get_root_id();
let top_level_ids = tree.get_instance(root_id).unwrap().get_children_ids();
rbx_xml::encode(&tree, top_level_ids, &mut file)?;
rbx_xml::to_writer(&mut file, &tree, top_level_ids, xml_encode_config())?;
},
OutputKind::Rbxm => {
let root_id = tree.get_root_id();
@@ -121,5 +126,7 @@ pub fn build(options: &BuildOptions) -> Result<(), BuildError> {
},
}
file.flush()?;
Ok(())
}

View File

@@ -80,10 +80,10 @@ pub fn upload(options: &UploadOptions) -> Result<(), UploadError> {
match options.kind {
Some("place") | None => {
let top_level_ids = tree.get_instance(root_id).unwrap().get_children_ids();
rbx_xml::encode(&tree, top_level_ids, &mut contents)?;
rbx_xml::to_writer_default(&mut contents, &tree, top_level_ids)?;
},
Some("model") => {
rbx_xml::encode(&tree, &[root_id], &mut contents)?;
rbx_xml::to_writer_default(&mut contents, &tree, &[root_id])?;
},
Some(invalid) => return Err(UploadError::InvalidKind(invalid.to_owned())),
}

View File

@@ -8,7 +8,7 @@ use std::{
};
use failure::Fail;
use serde_derive::{Serialize, Deserialize};
use serde::{Serialize, Deserialize};
use crate::project::{Project, ProjectNode};
@@ -89,11 +89,13 @@ impl Imfs {
pub fn add_root(&mut self, path: &Path) -> Result<(), FsError> {
debug_assert!(path.is_absolute());
debug_assert!(!self.is_within_roots(path));
self.roots.insert(path.to_path_buf());
if !self.is_within_roots(path) {
self.roots.insert(path.to_path_buf());
self.descend_and_read_from_disk(path)?;
}
self.descend_and_read_from_disk(path)
Ok(())
}
pub fn remove_root(&mut self, path: &Path) {

View File

@@ -1,23 +1,28 @@
use std::{
collections::HashMap,
mem,
sync::{
atomic::{AtomicUsize, Ordering},
RwLock,
Mutex,
},
};
use futures::sync::mpsc;
use futures::sync::oneshot;
/// A unique identifier, not guaranteed to be generated in any order.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ListenerId(usize);
struct Listener<T> {
sender: oneshot::Sender<(u32, Vec<T>)>,
cursor: u32,
}
/// Generate a new ID, which has no defined ordering.
pub fn get_listener_id() -> ListenerId {
static LAST_ID: AtomicUsize = AtomicUsize::new(0);
fn fire_listener_if_ready<T: Clone>(messages: &[T], listener: Listener<T>) -> Result<(), Listener<T>> {
let current_cursor = messages.len() as u32;
ListenerId(LAST_ID.fetch_add(1, Ordering::SeqCst))
if listener.cursor < current_cursor {
let new_messages = messages[(listener.cursor as usize)..].to_vec();
let _ = listener.sender.send((current_cursor, new_messages));
Ok(())
} else {
Err(listener)
}
}
/// A message queue with persistent history that can be subscribed to.
@@ -26,42 +31,53 @@ pub fn get_listener_id() -> ListenerId {
#[derive(Default)]
pub struct MessageQueue<T> {
messages: RwLock<Vec<T>>,
message_listeners: Mutex<HashMap<ListenerId, mpsc::Sender<()>>>,
message_listeners: Mutex<Vec<Listener<T>>>,
}
impl<T: Clone> MessageQueue<T> {
pub fn new() -> MessageQueue<T> {
MessageQueue {
messages: RwLock::new(Vec::new()),
message_listeners: Mutex::new(HashMap::new()),
message_listeners: Mutex::new(Vec::new()),
}
}
pub fn push_messages(&self, new_messages: &[T]) {
let mut message_listeners = self.message_listeners.lock().unwrap();
let mut messages = self.messages.write().unwrap();
messages.extend_from_slice(new_messages);
{
let mut messages = self.messages.write().unwrap();
messages.extend_from_slice(new_messages);
let mut remaining_listeners = Vec::new();
for listener in message_listeners.drain(..) {
match fire_listener_if_ready(&messages, listener) {
Ok(_) => {}
Err(listener) => remaining_listeners.push(listener)
}
}
for listener in message_listeners.values_mut() {
listener.try_send(()).unwrap();
}
// Without this annotation, Rust gets confused since the first argument
// is a MutexGuard, but the second is a Vec.
mem::replace::<Vec<_>>(&mut message_listeners, remaining_listeners);
}
pub fn subscribe(&self, sender: mpsc::Sender<()>) -> ListenerId {
let id = get_listener_id();
pub fn subscribe(&self, cursor: u32, sender: oneshot::Sender<(u32, Vec<T>)>) {
let listener = {
let listener = Listener {
sender,
cursor,
};
let messages = self.messages.read().unwrap();
match fire_listener_if_ready(&messages, listener) {
Ok(_) => return,
Err(listener) => listener
}
};
let mut message_listeners = self.message_listeners.lock().unwrap();
message_listeners.insert(id, sender);
id
}
pub fn unsubscribe(&self, id: ListenerId) {
let mut message_listeners = self.message_listeners.lock().unwrap();
message_listeners.remove(&id);
message_listeners.push(listener);
}
pub fn get_message_cursor(&self) -> u32 {

View File

@@ -3,7 +3,7 @@ use std::{
collections::{HashMap, HashSet},
};
use serde_derive::Serialize;
use serde::Serialize;
use log::warn;
#[derive(Debug, Serialize)]

View File

@@ -10,7 +10,7 @@
//!
//! ```
//! # use std::path::PathBuf;
//! # use serde_derive::{Serialize, Deserialize};
//! # use serde::{Serialize, Deserialize};
//!
//! #[derive(Serialize, Deserialize)]
//! struct Mine {

View File

@@ -9,8 +9,7 @@ use std::{
use log::warn;
use failure::Fail;
use rbx_dom_weak::{UnresolvedRbxValue, RbxValue};
use serde_derive::{Serialize, Deserialize};
use serde::{Serialize, Serializer};
use serde::{Serialize, Serializer, Deserialize};
static DEFAULT_PLACE: &'static str = include_str!("../assets/place.project.json");

View File

@@ -7,7 +7,7 @@ use std::{
};
use rlua::Lua;
use serde_derive::{Serialize, Deserialize};
use serde::{Serialize, Deserialize};
use log::{info, trace, error};
use rbx_dom_weak::{RbxTree, RbxId};

View File

@@ -13,8 +13,8 @@ use rlua::Lua;
use failure::Fail;
use log::info;
use maplit::hashmap;
use rbx_dom_weak::{RbxTree, RbxValue, RbxInstanceProperties};
use serde_derive::{Serialize, Deserialize};
use rbx_dom_weak::{RbxTree, RbxValue, RbxInstanceProperties, UnresolvedRbxValue};
use serde::{Serialize, Deserialize};
use rbx_reflection::{try_resolve_value, ValueResolveError};
use crate::{
@@ -106,6 +106,7 @@ pub enum SnapshotError {
},
XmlModelDecodeError {
#[fail(cause)]
inner: rbx_xml::DecodeError,
path: PathBuf,
},
@@ -115,6 +116,12 @@ pub enum SnapshotError {
path: PathBuf,
},
CsvDecodeError {
#[fail(cause)]
inner: csv::Error,
path: PathBuf,
},
ProjectNodeUnusable,
ProjectNodeInvalidTransmute {
@@ -146,11 +153,14 @@ impl fmt::Display for SnapshotError {
write!(output, "Malformed .model.json model: {} in path {}", inner, path.display())
},
SnapshotError::XmlModelDecodeError { inner, path } => {
write!(output, "Malformed rbxmx model: {:?} in path {}", inner, path.display())
write!(output, "Malformed rbxmx model: {} in path {}", inner, path.display())
},
SnapshotError::BinaryModelDecodeError { inner, path } => {
write!(output, "Malformed rbxm model: {:?} in path {}", inner, path.display())
},
SnapshotError::CsvDecodeError { inner, path } => {
write!(output, "Malformed csv file: {} in path {}", inner, path.display())
},
SnapshotError::ProjectNodeUnusable => {
write!(output, "Rojo project nodes must specify either $path or $className.")
},
@@ -475,16 +485,87 @@ fn snapshot_txt_file<'source>(
fn snapshot_csv_file<'source>(
file: &'source ImfsFile,
) -> SnapshotResult<'source> {
/// Struct that holds any valid row from a Roblox CSV translation table.
///
/// We manually deserialize into this table from CSV, but let JSON handle
/// serializing.
#[derive(Debug, Default, Serialize)]
#[serde(rename_all = "camelCase")]
struct LocalizationEntry<'a> {
#[serde(skip_serializing_if = "Option::is_none")]
key: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
context: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
example: Option<&'a str>,
#[serde(skip_serializing_if = "Option::is_none")]
source: Option<&'a str>,
values: HashMap<&'a str, &'a str>,
}
let instance_name = file.path
.file_stem().expect("Could not extract file stem")
.to_str().expect("Could not convert path to UTF-8");
let entries: Vec<LocalizationEntryJson> = csv::Reader::from_reader(file.contents.as_slice())
.deserialize()
// TODO: Propagate error upward instead of panicking
.map(|result| result.expect("Malformed localization table found!"))
.map(LocalizationEntryCsv::to_json)
.collect();
// Normally, we'd be able to let the csv crate construct our struct for us.
//
// However, because of a limitation with Serde's 'flatten' feature, it's not
// possible presently to losslessly collect extra string values while using
// csv+Serde.
//
// https://github.com/BurntSushi/rust-csv/issues/151
let mut reader = csv::Reader::from_reader(file.contents.as_slice());
let headers = reader.headers()
.map_err(|inner| SnapshotError::CsvDecodeError {
inner,
path: file.path.to_path_buf(),
})?
.clone();
let mut records = Vec::new();
for record in reader.into_records() {
let record = record
.map_err(|inner| SnapshotError::CsvDecodeError {
inner,
path: file.path.to_path_buf(),
})?;
records.push(record);
}
let mut entries = Vec::new();
for record in &records {
let mut entry = LocalizationEntry::default();
for (header, value) in headers.iter().zip(record.into_iter()) {
if header.is_empty() || value.is_empty() {
continue;
}
match header {
"Key" => entry.key = Some(value),
"Source" => entry.source = Some(value),
"Context" => entry.context = Some(value),
"Example" => entry.example = Some(value),
_ => {
entry.values.insert(header, value);
}
}
}
if entry.key.is_none() && entry.source.is_none() {
continue;
}
entries.push(entry);
}
let table_contents = serde_json::to_string(&entries)
.expect("Could not encode JSON for localization table");
@@ -506,39 +587,6 @@ fn snapshot_csv_file<'source>(
}))
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
struct LocalizationEntryCsv {
key: String,
context: String,
example: String,
source: String,
#[serde(flatten)]
values: HashMap<String, String>,
}
impl LocalizationEntryCsv {
fn to_json(self) -> LocalizationEntryJson {
LocalizationEntryJson {
key: self.key,
context: self.context,
example: self.example,
source: self.source,
values: self.values,
}
}
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
struct LocalizationEntryJson {
key: String,
context: String,
example: String,
source: String,
values: HashMap<String, String>,
}
fn snapshot_json_model_file<'source>(
file: &'source ImfsFile,
) -> SnapshotResult<'source> {
@@ -554,7 +602,7 @@ fn snapshot_json_model_file<'source>(
path: file.path.to_owned(),
})?;
let mut snapshot = json_instance.into_snapshot();
let mut snapshot = json_instance.into_snapshot()?;
snapshot.metadata.source_path = Some(file.path.to_owned());
Ok(Some(snapshot))
@@ -570,23 +618,31 @@ struct JsonModelInstance {
children: Vec<JsonModelInstance>,
#[serde(default = "HashMap::new", skip_serializing_if = "HashMap::is_empty")]
properties: HashMap<String, RbxValue>,
properties: HashMap<String, UnresolvedRbxValue>,
}
impl JsonModelInstance {
fn into_snapshot(mut self) -> RbxSnapshotInstance<'static> {
let children = self.children
.drain(..)
.map(JsonModelInstance::into_snapshot)
.collect();
fn into_snapshot(self) -> Result<RbxSnapshotInstance<'static>, SnapshotError> {
let mut children = Vec::with_capacity(self.children.len());
RbxSnapshotInstance {
for child in self.children {
children.push(child.into_snapshot()?);
}
let mut properties = HashMap::with_capacity(self.properties.len());
for (key, value) in self.properties {
let resolved_value = try_resolve_value(&self.class_name, &key, &value)?;
properties.insert(key, resolved_value);
}
Ok(RbxSnapshotInstance {
name: Cow::Owned(self.name),
class_name: Cow::Owned(self.class_name),
properties: self.properties,
properties,
children,
metadata: Default::default(),
}
})
}
}
@@ -597,20 +653,16 @@ fn snapshot_xml_model_file<'source>(
.file_stem().expect("Could not extract file stem")
.to_str().expect("Could not convert path to UTF-8");
let mut temp_tree = RbxTree::new(RbxInstanceProperties {
name: "Temp".to_owned(),
class_name: "Folder".to_owned(),
properties: HashMap::new(),
});
let options = rbx_xml::DecodeOptions::new()
.property_behavior(rbx_xml::DecodePropertyBehavior::ReadUnknown);
let root_id = temp_tree.get_root_id();
rbx_xml::decode(&mut temp_tree, root_id, file.contents.as_slice())
let temp_tree = rbx_xml::from_reader(file.contents.as_slice(), options)
.map_err(|inner| SnapshotError::XmlModelDecodeError {
inner,
path: file.path.clone(),
})?;
let root_instance = temp_tree.get_instance(root_id).unwrap();
let root_instance = temp_tree.get_instance(temp_tree.get_root_id()).unwrap();
let children = root_instance.get_children_ids();
match children.len() {

View File

@@ -1,4 +1,4 @@
use serde_derive::{Serialize, Deserialize};
use serde::{Serialize, Deserialize};
use uuid::Uuid;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]

View File

@@ -11,7 +11,7 @@ use std::{
};
use rbx_dom_weak::{RbxTree, RbxId, RbxInstanceProperties, RbxValue};
use serde_derive::{Serialize, Deserialize};
use serde::{Serialize, Deserialize};
use crate::{
path_map::PathMap,
@@ -153,7 +153,7 @@ pub fn reify_subtree(
instance_per_path: &mut PathMap<HashSet<RbxId>>,
metadata_per_instance: &mut HashMap<RbxId, MetadataPerInstance>,
changes: &mut InstanceChanges,
) {
) -> RbxId {
let instance = reify_core(snapshot);
let id = tree.insert_instance(instance, parent_id);
@@ -164,6 +164,8 @@ pub fn reify_subtree(
for child in &snapshot.children {
reify_subtree(child, tree, id, instance_per_path, metadata_per_instance, changes);
}
id
}
fn reify_metadata(
@@ -222,6 +224,9 @@ fn reify_core(snapshot: &RbxSnapshotInstance) -> RbxInstanceProperties {
instance
}
/// Updates the given instance to match the properties defined on the snapshot.
///
/// Returns whether any changes were applied.
fn reconcile_instance_properties(instance: &mut RbxInstanceProperties, snapshot: &RbxSnapshotInstance) -> bool {
let mut has_diffs = false;
@@ -279,6 +284,8 @@ fn reconcile_instance_properties(instance: &mut RbxInstanceProperties, snapshot:
has_diffs
}
/// Updates the children of the instance in the `RbxTree` to match the children
/// of the `RbxSnapshotInstance`. Order will be updated to match.
fn reconcile_instance_children(
tree: &mut RbxTree,
id: RbxId,
@@ -287,12 +294,21 @@ fn reconcile_instance_children(
metadata_per_instance: &mut HashMap<RbxId, MetadataPerInstance>,
changes: &mut InstanceChanges,
) {
let mut visited_snapshot_indices = HashSet::new();
let mut children_to_update: Vec<(RbxId, &RbxSnapshotInstance)> = Vec::new();
let mut children_to_add: Vec<&RbxSnapshotInstance> = Vec::new();
// These lists are kept so that we can apply all the changes we figure out
let mut children_to_maybe_update: Vec<(RbxId, &RbxSnapshotInstance)> = Vec::new();
let mut children_to_add: Vec<(usize, &RbxSnapshotInstance)> = Vec::new();
let mut children_to_remove: Vec<RbxId> = Vec::new();
// This map is used once we're done mutating children to sort them according
// to the order specified in the snapshot. Without it, a snapshot with a new
// child prepended will cause the RbxTree instance to have out-of-order
// children and would make Rojo non-deterministic.
let mut ids_to_snapshot_indices = HashMap::new();
// Since we have to enumerate the children of both the RbxTree instance and
// our snapshot, we keep a set of the snapshot children we've seen.
let mut visited_snapshot_indices = vec![false; snapshot.children.len()];
let children_ids = tree.get_instance(id).unwrap().get_children_ids();
// Find all instances that were removed or updated, which we derive by
@@ -303,7 +319,7 @@ fn reconcile_instance_children(
// Locate a matching snapshot for this instance
let mut matching_snapshot = None;
for (snapshot_index, child_snapshot) in snapshot.children.iter().enumerate() {
if visited_snapshot_indices.contains(&snapshot_index) {
if visited_snapshot_indices[snapshot_index] {
continue;
}
@@ -311,7 +327,8 @@ fn reconcile_instance_children(
// similar. This heuristic is similar to React's reconciliation
// strategy.
if child_snapshot.name == child_instance.name {
visited_snapshot_indices.insert(snapshot_index);
ids_to_snapshot_indices.insert(child_id, snapshot_index);
visited_snapshot_indices[snapshot_index] = true;
matching_snapshot = Some(child_snapshot);
break;
}
@@ -319,26 +336,23 @@ fn reconcile_instance_children(
match matching_snapshot {
Some(child_snapshot) => {
children_to_update.push((child_instance.get_id(), child_snapshot));
},
children_to_maybe_update.push((child_instance.get_id(), child_snapshot));
}
None => {
children_to_remove.push(child_instance.get_id());
},
}
}
}
// Find all instancs that were added, which is just the snapshots we didn't
// match up to existing instances above.
for (snapshot_index, child_snapshot) in snapshot.children.iter().enumerate() {
if !visited_snapshot_indices.contains(&snapshot_index) {
children_to_add.push(child_snapshot);
if !visited_snapshot_indices[snapshot_index] {
children_to_add.push((snapshot_index, child_snapshot));
}
}
for child_snapshot in &children_to_add {
reify_subtree(child_snapshot, tree, id, instance_per_path, metadata_per_instance, changes);
}
// Apply all of our removals we gathered from our diff
for child_id in &children_to_remove {
if let Some(subtree) = tree.remove_instance(*child_id) {
for id in subtree.iter_all_ids() {
@@ -348,7 +362,18 @@ fn reconcile_instance_children(
}
}
for (child_id, child_snapshot) in &children_to_update {
// Apply all of our children additions
for (snapshot_index, child_snapshot) in &children_to_add {
let id = reify_subtree(child_snapshot, tree, id, instance_per_path, metadata_per_instance, changes);
ids_to_snapshot_indices.insert(id, *snapshot_index);
}
// Apply any updates that might have updates
for (child_id, child_snapshot) in &children_to_maybe_update {
reconcile_subtree(tree, *child_id, child_snapshot, instance_per_path, metadata_per_instance, changes);
}
// Apply the sort mapping defined by ids_to_snapshot_indices above
let instance = tree.get_instance_mut(id).unwrap();
instance.sort_children_unstable_by_key(|id| ids_to_snapshot_indices.get(&id).unwrap());
}

View File

@@ -7,7 +7,11 @@ use std::{
sync::Arc,
};
use futures::{future, Future, stream::Stream, sync::mpsc};
use futures::{
future::{self, IntoFuture},
Future,
sync::oneshot,
};
use hyper::{
service::Service,
header,
@@ -17,7 +21,7 @@ use hyper::{
Request,
Response,
};
use serde_derive::{Serialize, Deserialize};
use serde::{Serialize, Deserialize};
use rbx_dom_weak::{RbxId, RbxInstance};
use crate::{
@@ -168,30 +172,13 @@ impl ApiService {
};
let message_queue = Arc::clone(&self.live_session.message_queue);
// Did the client miss any messages since the last subscribe?
{
let (new_cursor, new_messages) = message_queue.get_messages_since(cursor);
if !new_messages.is_empty() {
return Box::new(future::ok(response_json(&SubscribeResponse {
session_id: self.live_session.session_id(),
messages: Cow::Borrowed(&new_messages),
message_cursor: new_cursor,
})));
}
}
let (tx, rx) = mpsc::channel(1024);
let sender_id = message_queue.subscribe(tx);
let session_id = self.live_session.session_id();
let (tx, rx) = oneshot::channel();
message_queue.subscribe(cursor, tx);
let result = rx.into_future()
.and_then(move |_| {
message_queue.unsubscribe(sender_id);
let (new_cursor, new_messages) = message_queue.get_messages_since(cursor);
.and_then(move |(new_cursor, new_messages)| {
Box::new(future::ok(response_json(SubscribeResponse {
session_id: session_id,
messages: Cow::Owned(new_messages),

View File

@@ -11,10 +11,7 @@ use librojo::{
use test_util::tree::trees_equal;
// TODO: Snapshot application isn't communicative right now with the current
// snapshot reconciler. In practice this mostly isn't a problem, but presents
// a problem trying to rely on determinism to make snapshot tests.
// #[test]
#[test]
fn patch_communicativity() {
let base_tree = RbxTree::new(RbxInstanceProperties {
name: "DataModel".into(),

View File

@@ -33,6 +33,8 @@ macro_rules! generate_snapshot_tests {
generate_snapshot_tests!(
empty,
json_model,
localization,
multi_partition_game,
nested_partitions,
single_partition_game,

View File

@@ -20,7 +20,7 @@ use std::{
};
use log::error;
use serde_derive::{Serialize, Deserialize};
use serde::{Serialize, Deserialize};
use rbx_dom_weak::{RbxId, RbxTree};
use librojo::{

View File

@@ -0,0 +1,6 @@
{
"name": "json_model",
"tree": {
"$path": "src"
}
}

View File

@@ -0,0 +1,76 @@
{
"name": "json_model",
"class_name": "Folder",
"properties": {},
"children": [
{
"name": "children",
"class_name": "Folder",
"properties": {},
"children": [
{
"name": "The Child",
"class_name": "StringValue",
"properties": {},
"children": [],
"metadata": {
"ignore_unknown_instances": false,
"source_path": null,
"project_definition": null
}
}
],
"metadata": {
"ignore_unknown_instances": false,
"source_path": "src/children.model.json",
"project_definition": null
}
},
{
"name": "explicit",
"class_name": "StringValue",
"properties": {
"Value": {
"Type": "String",
"Value": "Hello, world!"
}
},
"children": [],
"metadata": {
"ignore_unknown_instances": false,
"source_path": "src/explicit.model.json",
"project_definition": null
}
},
{
"name": "implicit",
"class_name": "StringValue",
"properties": {
"Value": {
"Type": "String",
"Value": "What's happenin', Earth?"
}
},
"children": [],
"metadata": {
"ignore_unknown_instances": false,
"source_path": "src/implicit.model.json",
"project_definition": null
}
}
],
"metadata": {
"ignore_unknown_instances": false,
"source_path": "src",
"project_definition": [
"json_model",
{
"class_name": null,
"children": {},
"properties": {},
"ignore_unknown_instances": null,
"path": "src"
}
]
}
}

View File

@@ -0,0 +1,10 @@
{
"Name": "children",
"ClassName": "Folder",
"Children": [
{
"Name": "The Child",
"ClassName": "StringValue"
}
]
}

View File

@@ -0,0 +1,11 @@
{
"Name": "explicit",
"ClassName": "StringValue",
"Properties": {
"Value": {
"Type": "String",
"Value": "Hello, world!"
}
},
"Children": []
}

View File

@@ -0,0 +1,7 @@
{
"Name": "implicit",
"ClassName": "StringValue",
"Properties": {
"Value": "What's happenin', Earth?"
}
}

View File

@@ -0,0 +1,6 @@
{
"name": "localization",
"tree": {
"$path": "src"
}
}

View File

@@ -0,0 +1,69 @@
{
"name": "localization",
"class_name": "Folder",
"properties": {},
"children": [
{
"name": "empty-column-bug-147",
"class_name": "LocalizationTable",
"properties": {
"Contents": {
"Type": "String",
"Value": "[{\"key\":\"Language.Name\",\"source\":\"English\",\"values\":{}},{\"key\":\"Language.Region\",\"source\":\"United States\",\"values\":{}},{\"key\":\"Label.Thickness\",\"source\":\"Thickness\",\"values\":{}},{\"key\":\"Label.Opacity\",\"source\":\"Opacity\",\"values\":{}},{\"key\":\"Toolbar.Undo\",\"source\":\"Undo\",\"values\":{}},{\"key\":\"Toolbar.Redo\",\"source\":\"Redo\",\"values\":{}},{\"key\":\"Toolbar.Camera\",\"source\":\"Top-down camera\",\"values\":{}},{\"key\":\"Toolbar.Saves\",\"source\":\"Saved drawings\",\"values\":{}},{\"key\":\"Toolbar.Preferences\",\"source\":\"Settings\",\"values\":{}},{\"key\":\"Toolbar.Mode.Vector\",\"source\":\"Vector mode\",\"values\":{}},{\"key\":\"Toolbar.Mode.Pixel\",\"source\":\"Pixel mode\",\"values\":{}}]"
}
},
"children": [],
"metadata": {
"ignore_unknown_instances": false,
"source_path": "src/empty-column-bug-147.csv",
"project_definition": null
}
},
{
"name": "integers-bug-145",
"class_name": "LocalizationTable",
"properties": {
"Contents": {
"Type": "String",
"Value": "[{\"key\":\"Count\",\"example\":\"A number demonstrating issue 145\",\"source\":\"3\",\"values\":{\"es\":\"7\"}}]"
}
},
"children": [],
"metadata": {
"ignore_unknown_instances": false,
"source_path": "src/integers-bug-145.csv",
"project_definition": null
}
},
{
"name": "normal",
"class_name": "LocalizationTable",
"properties": {
"Contents": {
"Type": "String",
"Value": "[{\"key\":\"Ack\",\"example\":\"An exclamation of despair\",\"source\":\"Ack!\",\"values\":{\"es\":\"¡Ay!\"}}]"
}
},
"children": [],
"metadata": {
"ignore_unknown_instances": false,
"source_path": "src/normal.csv",
"project_definition": null
}
}
],
"metadata": {
"ignore_unknown_instances": false,
"source_path": "src",
"project_definition": [
"localization",
{
"class_name": null,
"children": {},
"properties": {},
"ignore_unknown_instances": null,
"path": "src"
}
]
}
}

View File

@@ -0,0 +1,17 @@
,Key,Source,Context,Example
,,,,
Metadata,Language.Name,English,,
,Language.Region,United States,,
,,,,
Options,Label.Thickness,Thickness,,
,Label.Opacity,Opacity,,
,,,,
Toolbar,Toolbar.Undo,Undo,,
,Toolbar.Redo,Redo,,
,,,,
,Toolbar.Camera,Top-down camera,,
,Toolbar.Saves,Saved drawings,,
,Toolbar.Preferences,Settings,,
,,,,
,Toolbar.Mode.Vector,Vector mode,,
,Toolbar.Mode.Pixel,Pixel mode,,
1 Key Source Context Example
2
3 Metadata Language.Name English
4 Language.Region United States
5
6 Options Label.Thickness Thickness
7 Label.Opacity Opacity
8
9 Toolbar Toolbar.Undo Undo
10 Toolbar.Redo Redo
11
12 Toolbar.Camera Top-down camera
13 Toolbar.Saves Saved drawings
14 Toolbar.Preferences Settings
15
16 Toolbar.Mode.Vector Vector mode
17 Toolbar.Mode.Pixel Pixel mode

View File

@@ -0,0 +1,2 @@
Key,Source,Context,Example,es
Count,3,,A number demonstrating issue 145,7
1 Key Source Context Example es
2 Count 3 A number demonstrating issue 145 7

View File

@@ -0,0 +1,2 @@
Key,Source,Context,Example,es
Ack,Ack!,,An exclamation of despair,¡Ay!
1 Key Source Context Example es
2 Ack Ack! An exclamation of despair ¡Ay!

View File

@@ -1,6 +1,6 @@
{
"instances": {
"8d44bb30-db3c-4366-a6c5-633bd1441885": {
"00f207b1-fc18-4088-a45e-caf8cd98f5dd": {
"Name": "main",
"ClassName": "ModuleScript",
"Properties": {
@@ -9,69 +9,32 @@
"Value": "-- hello, from a/main.lua"
}
},
"Id": "8d44bb30-db3c-4366-a6c5-633bd1441885",
"Id": "00f207b1-fc18-4088-a45e-caf8cd98f5dd",
"Children": [],
"Parent": "1aafa29b-bdca-40a0-a677-7ead327b84ce"
"Parent": "14fed1a3-ba97-46a6-ae93-ac26bd9471df"
},
"b1c9928c-bf11-427f-90eb-b672c811d859": {
"14fed1a3-ba97-46a6-ae93-ac26bd9471df": {
"Name": "Ack",
"ClassName": "Folder",
"Properties": {},
"Id": "14fed1a3-ba97-46a6-ae93-ac26bd9471df",
"Children": [
"c55fd55c-258e-4a93-a63a-ea243038c9b9",
"00f207b1-fc18-4088-a45e-caf8cd98f5dd"
],
"Parent": "99eefe5f-ef74-49e6-8a8b-c833e00ca56b"
},
"c910510c-37a8-4fd8-ae41-01169ccb739c": {
"Name": "Bar",
"ClassName": "Folder",
"Properties": {},
"Id": "b1c9928c-bf11-427f-90eb-b672c811d859",
"Id": "c910510c-37a8-4fd8-ae41-01169ccb739c",
"Children": [
"8d690a2a-e987-4c86-b9ac-18e6d3a98503"
"71a95983-c856-4cf2-aee6-bd8a523e80e4"
],
"Parent": "9f141826-14c2-492b-b360-2558712f0c08"
"Parent": "99eefe5f-ef74-49e6-8a8b-c833e00ca56b"
},
"645ba594-4482-441f-9f41-5bb9c444405b": {
"Name": "multi_partition_game",
"ClassName": "DataModel",
"Properties": {},
"Id": "645ba594-4482-441f-9f41-5bb9c444405b",
"Children": [
"b1298bcc-e370-44a6-9ef4-fbefa290124c",
"9f141826-14c2-492b-b360-2558712f0c08"
],
"Parent": null
},
"9f141826-14c2-492b-b360-2558712f0c08": {
"Name": "ReplicatedStorage",
"ClassName": "ReplicatedStorage",
"Properties": {},
"Id": "9f141826-14c2-492b-b360-2558712f0c08",
"Children": [
"1aafa29b-bdca-40a0-a677-7ead327b84ce",
"b1c9928c-bf11-427f-90eb-b672c811d859"
],
"Parent": "645ba594-4482-441f-9f41-5bb9c444405b"
},
"8d690a2a-e987-4c86-b9ac-18e6d3a98503": {
"Name": "something",
"ClassName": "ModuleScript",
"Properties": {
"Source": {
"Type": "String",
"Value": "-- b/something.lua"
}
},
"Id": "8d690a2a-e987-4c86-b9ac-18e6d3a98503",
"Children": [],
"Parent": "b1c9928c-bf11-427f-90eb-b672c811d859"
},
"b1298bcc-e370-44a6-9ef4-fbefa290124c": {
"Name": "HttpService",
"ClassName": "HttpService",
"Properties": {
"HttpEnabled": {
"Type": "Bool",
"Value": true
}
},
"Id": "b1298bcc-e370-44a6-9ef4-fbefa290124c",
"Children": [],
"Parent": "645ba594-4482-441f-9f41-5bb9c444405b"
},
"54f2f276-964f-4c60-87d8-5fb2209c97c9": {
"c55fd55c-258e-4a93-a63a-ea243038c9b9": {
"Name": "foo",
"ClassName": "StringValue",
"Properties": {
@@ -80,25 +43,153 @@
"Value": "Hello world, from a/foo.txt"
}
},
"Id": "54f2f276-964f-4c60-87d8-5fb2209c97c9",
"Id": "c55fd55c-258e-4a93-a63a-ea243038c9b9",
"Children": [],
"Parent": "1aafa29b-bdca-40a0-a677-7ead327b84ce"
"Parent": "14fed1a3-ba97-46a6-ae93-ac26bd9471df"
},
"1aafa29b-bdca-40a0-a677-7ead327b84ce": {
"Name": "Ack",
"ClassName": "Folder",
"71a95983-c856-4cf2-aee6-bd8a523e80e4": {
"Name": "something",
"ClassName": "ModuleScript",
"Properties": {
"Source": {
"Type": "String",
"Value": "-- b/something.lua"
}
},
"Id": "71a95983-c856-4cf2-aee6-bd8a523e80e4",
"Children": [],
"Parent": "c910510c-37a8-4fd8-ae41-01169ccb739c"
},
"3b5af13f-c997-4009-915c-0810b0e83032": {
"Name": "multi_partition_game",
"ClassName": "DataModel",
"Properties": {},
"Id": "1aafa29b-bdca-40a0-a677-7ead327b84ce",
"Id": "3b5af13f-c997-4009-915c-0810b0e83032",
"Children": [
"54f2f276-964f-4c60-87d8-5fb2209c97c9",
"8d44bb30-db3c-4366-a6c5-633bd1441885"
"bf8e2d4f-33a0-42a0-8168-1b62d6ac050c",
"99eefe5f-ef74-49e6-8a8b-c833e00ca56b"
],
"Parent": "9f141826-14c2-492b-b360-2558712f0c08"
"Parent": null
},
"bf8e2d4f-33a0-42a0-8168-1b62d6ac050c": {
"Name": "HttpService",
"ClassName": "HttpService",
"Properties": {
"HttpEnabled": {
"Type": "Bool",
"Value": true
}
},
"Id": "bf8e2d4f-33a0-42a0-8168-1b62d6ac050c",
"Children": [],
"Parent": "3b5af13f-c997-4009-915c-0810b0e83032"
},
"99eefe5f-ef74-49e6-8a8b-c833e00ca56b": {
"Name": "ReplicatedStorage",
"ClassName": "ReplicatedStorage",
"Properties": {},
"Id": "99eefe5f-ef74-49e6-8a8b-c833e00ca56b",
"Children": [
"14fed1a3-ba97-46a6-ae93-ac26bd9471df",
"c910510c-37a8-4fd8-ae41-01169ccb739c"
],
"Parent": "3b5af13f-c997-4009-915c-0810b0e83032"
}
},
"root_id": "645ba594-4482-441f-9f41-5bb9c444405b",
"root_id": "3b5af13f-c997-4009-915c-0810b0e83032",
"metadata": {
"645ba594-4482-441f-9f41-5bb9c444405b": {
"00f207b1-fc18-4088-a45e-caf8cd98f5dd": {
"ignore_unknown_instances": false,
"source_path": "a/main.lua",
"project_definition": null
},
"bf8e2d4f-33a0-42a0-8168-1b62d6ac050c": {
"ignore_unknown_instances": true,
"source_path": null,
"project_definition": [
"HttpService",
{
"class_name": "HttpService",
"children": {},
"properties": {
"HttpEnabled": {
"Type": "Bool",
"Value": true
}
},
"ignore_unknown_instances": null,
"path": null
}
]
},
"14fed1a3-ba97-46a6-ae93-ac26bd9471df": {
"ignore_unknown_instances": false,
"source_path": "a",
"project_definition": [
"Ack",
{
"class_name": null,
"children": {},
"properties": {},
"ignore_unknown_instances": null,
"path": "a"
}
]
},
"c55fd55c-258e-4a93-a63a-ea243038c9b9": {
"ignore_unknown_instances": false,
"source_path": "a/foo.txt",
"project_definition": null
},
"71a95983-c856-4cf2-aee6-bd8a523e80e4": {
"ignore_unknown_instances": false,
"source_path": "b/something.lua",
"project_definition": null
},
"c910510c-37a8-4fd8-ae41-01169ccb739c": {
"ignore_unknown_instances": false,
"source_path": "b",
"project_definition": [
"Bar",
{
"class_name": null,
"children": {},
"properties": {},
"ignore_unknown_instances": null,
"path": "b"
}
]
},
"99eefe5f-ef74-49e6-8a8b-c833e00ca56b": {
"ignore_unknown_instances": true,
"source_path": null,
"project_definition": [
"ReplicatedStorage",
{
"class_name": "ReplicatedStorage",
"children": {
"Ack": {
"class_name": null,
"children": {},
"properties": {},
"ignore_unknown_instances": null,
"path": "a"
},
"Bar": {
"class_name": null,
"children": {},
"properties": {},
"ignore_unknown_instances": null,
"path": "b"
}
},
"properties": {},
"ignore_unknown_instances": null,
"path": null
}
]
},
"3b5af13f-c997-4009-915c-0810b0e83032": {
"ignore_unknown_instances": true,
"source_path": null,
"project_definition": [
@@ -146,97 +237,6 @@
"path": null
}
]
},
"1aafa29b-bdca-40a0-a677-7ead327b84ce": {
"ignore_unknown_instances": false,
"source_path": "a",
"project_definition": [
"Ack",
{
"class_name": null,
"children": {},
"properties": {},
"ignore_unknown_instances": null,
"path": "a"
}
]
},
"b1c9928c-bf11-427f-90eb-b672c811d859": {
"ignore_unknown_instances": false,
"source_path": "b",
"project_definition": [
"Bar",
{
"class_name": null,
"children": {},
"properties": {},
"ignore_unknown_instances": null,
"path": "b"
}
]
},
"54f2f276-964f-4c60-87d8-5fb2209c97c9": {
"ignore_unknown_instances": false,
"source_path": "a/foo.txt",
"project_definition": null
},
"9f141826-14c2-492b-b360-2558712f0c08": {
"ignore_unknown_instances": true,
"source_path": null,
"project_definition": [
"ReplicatedStorage",
{
"class_name": "ReplicatedStorage",
"children": {
"Ack": {
"class_name": null,
"children": {},
"properties": {},
"ignore_unknown_instances": null,
"path": "a"
},
"Bar": {
"class_name": null,
"children": {},
"properties": {},
"ignore_unknown_instances": null,
"path": "b"
}
},
"properties": {},
"ignore_unknown_instances": null,
"path": null
}
]
},
"8d44bb30-db3c-4366-a6c5-633bd1441885": {
"ignore_unknown_instances": false,
"source_path": "a/main.lua",
"project_definition": null
},
"b1298bcc-e370-44a6-9ef4-fbefa290124c": {
"ignore_unknown_instances": true,
"source_path": null,
"project_definition": [
"HttpService",
{
"class_name": "HttpService",
"children": {},
"properties": {
"HttpEnabled": {
"Type": "Bool",
"Value": true
}
},
"ignore_unknown_instances": null,
"path": null
}
]
},
"8d690a2a-e987-4c86-b9ac-18e6d3a98503": {
"ignore_unknown_instances": false,
"source_path": "b/something.lua",
"project_definition": null
}
}
}

View File

@@ -1,6 +1,14 @@
{
"instances": {
"8d44bb30-db3c-4366-a6c5-633bd1441885": {
"b48b369f-5706-4029-9fa6-90651a4910ea": {
"Name": "added",
"ClassName": "Folder",
"Properties": {},
"Id": "b48b369f-5706-4029-9fa6-90651a4910ea",
"Children": [],
"Parent": "14fed1a3-ba97-46a6-ae93-ac26bd9471df"
},
"00f207b1-fc18-4088-a45e-caf8cd98f5dd": {
"Name": "main",
"ClassName": "ModuleScript",
"Properties": {
@@ -9,77 +17,33 @@
"Value": "-- hello, from a/main.lua"
}
},
"Id": "8d44bb30-db3c-4366-a6c5-633bd1441885",
"Id": "00f207b1-fc18-4088-a45e-caf8cd98f5dd",
"Children": [],
"Parent": "1aafa29b-bdca-40a0-a677-7ead327b84ce"
"Parent": "14fed1a3-ba97-46a6-ae93-ac26bd9471df"
},
"b1c9928c-bf11-427f-90eb-b672c811d859": {
"14fed1a3-ba97-46a6-ae93-ac26bd9471df": {
"Name": "Ack",
"ClassName": "Folder",
"Properties": {},
"Id": "14fed1a3-ba97-46a6-ae93-ac26bd9471df",
"Children": [
"b48b369f-5706-4029-9fa6-90651a4910ea",
"c55fd55c-258e-4a93-a63a-ea243038c9b9",
"00f207b1-fc18-4088-a45e-caf8cd98f5dd"
],
"Parent": "99eefe5f-ef74-49e6-8a8b-c833e00ca56b"
},
"c910510c-37a8-4fd8-ae41-01169ccb739c": {
"Name": "Bar",
"ClassName": "Folder",
"Properties": {},
"Id": "b1c9928c-bf11-427f-90eb-b672c811d859",
"Id": "c910510c-37a8-4fd8-ae41-01169ccb739c",
"Children": [
"8d690a2a-e987-4c86-b9ac-18e6d3a98503"
"71a95983-c856-4cf2-aee6-bd8a523e80e4"
],
"Parent": "9f141826-14c2-492b-b360-2558712f0c08"
"Parent": "99eefe5f-ef74-49e6-8a8b-c833e00ca56b"
},
"645ba594-4482-441f-9f41-5bb9c444405b": {
"Name": "multi_partition_game",
"ClassName": "DataModel",
"Properties": {},
"Id": "645ba594-4482-441f-9f41-5bb9c444405b",
"Children": [
"b1298bcc-e370-44a6-9ef4-fbefa290124c",
"9f141826-14c2-492b-b360-2558712f0c08"
],
"Parent": null
},
"9f141826-14c2-492b-b360-2558712f0c08": {
"Name": "ReplicatedStorage",
"ClassName": "ReplicatedStorage",
"Properties": {},
"Id": "9f141826-14c2-492b-b360-2558712f0c08",
"Children": [
"1aafa29b-bdca-40a0-a677-7ead327b84ce",
"b1c9928c-bf11-427f-90eb-b672c811d859"
],
"Parent": "645ba594-4482-441f-9f41-5bb9c444405b"
},
"8d690a2a-e987-4c86-b9ac-18e6d3a98503": {
"Name": "something",
"ClassName": "ModuleScript",
"Properties": {
"Source": {
"Type": "String",
"Value": "-- b/something.lua"
}
},
"Id": "8d690a2a-e987-4c86-b9ac-18e6d3a98503",
"Children": [],
"Parent": "b1c9928c-bf11-427f-90eb-b672c811d859"
},
"b1298bcc-e370-44a6-9ef4-fbefa290124c": {
"Name": "HttpService",
"ClassName": "HttpService",
"Properties": {
"HttpEnabled": {
"Type": "Bool",
"Value": true
}
},
"Id": "b1298bcc-e370-44a6-9ef4-fbefa290124c",
"Children": [],
"Parent": "645ba594-4482-441f-9f41-5bb9c444405b"
},
"46353305-8818-48fe-94fd-80cf0c5d974c": {
"Name": "added",
"ClassName": "Folder",
"Properties": {},
"Id": "46353305-8818-48fe-94fd-80cf0c5d974c",
"Children": [],
"Parent": "1aafa29b-bdca-40a0-a677-7ead327b84ce"
},
"54f2f276-964f-4c60-87d8-5fb2209c97c9": {
"c55fd55c-258e-4a93-a63a-ea243038c9b9": {
"Name": "foo",
"ClassName": "StringValue",
"Properties": {
@@ -88,50 +52,67 @@
"Value": "Hello world, from a/foo.txt"
}
},
"Id": "54f2f276-964f-4c60-87d8-5fb2209c97c9",
"Id": "c55fd55c-258e-4a93-a63a-ea243038c9b9",
"Children": [],
"Parent": "1aafa29b-bdca-40a0-a677-7ead327b84ce"
"Parent": "14fed1a3-ba97-46a6-ae93-ac26bd9471df"
},
"1aafa29b-bdca-40a0-a677-7ead327b84ce": {
"Name": "Ack",
"ClassName": "Folder",
"71a95983-c856-4cf2-aee6-bd8a523e80e4": {
"Name": "something",
"ClassName": "ModuleScript",
"Properties": {
"Source": {
"Type": "String",
"Value": "-- b/something.lua"
}
},
"Id": "71a95983-c856-4cf2-aee6-bd8a523e80e4",
"Children": [],
"Parent": "c910510c-37a8-4fd8-ae41-01169ccb739c"
},
"3b5af13f-c997-4009-915c-0810b0e83032": {
"Name": "multi_partition_game",
"ClassName": "DataModel",
"Properties": {},
"Id": "1aafa29b-bdca-40a0-a677-7ead327b84ce",
"Id": "3b5af13f-c997-4009-915c-0810b0e83032",
"Children": [
"54f2f276-964f-4c60-87d8-5fb2209c97c9",
"8d44bb30-db3c-4366-a6c5-633bd1441885",
"46353305-8818-48fe-94fd-80cf0c5d974c"
"bf8e2d4f-33a0-42a0-8168-1b62d6ac050c",
"99eefe5f-ef74-49e6-8a8b-c833e00ca56b"
],
"Parent": "9f141826-14c2-492b-b360-2558712f0c08"
"Parent": null
},
"bf8e2d4f-33a0-42a0-8168-1b62d6ac050c": {
"Name": "HttpService",
"ClassName": "HttpService",
"Properties": {
"HttpEnabled": {
"Type": "Bool",
"Value": true
}
},
"Id": "bf8e2d4f-33a0-42a0-8168-1b62d6ac050c",
"Children": [],
"Parent": "3b5af13f-c997-4009-915c-0810b0e83032"
},
"99eefe5f-ef74-49e6-8a8b-c833e00ca56b": {
"Name": "ReplicatedStorage",
"ClassName": "ReplicatedStorage",
"Properties": {},
"Id": "99eefe5f-ef74-49e6-8a8b-c833e00ca56b",
"Children": [
"14fed1a3-ba97-46a6-ae93-ac26bd9471df",
"c910510c-37a8-4fd8-ae41-01169ccb739c"
],
"Parent": "3b5af13f-c997-4009-915c-0810b0e83032"
}
},
"root_id": "645ba594-4482-441f-9f41-5bb9c444405b",
"root_id": "3b5af13f-c997-4009-915c-0810b0e83032",
"metadata": {
"b1c9928c-bf11-427f-90eb-b672c811d859": {
"ignore_unknown_instances": false,
"source_path": "b",
"project_definition": [
"Bar",
{
"class_name": null,
"children": {},
"properties": {},
"ignore_unknown_instances": null,
"path": "b"
}
]
},
"54f2f276-964f-4c60-87d8-5fb2209c97c9": {
"c55fd55c-258e-4a93-a63a-ea243038c9b9": {
"ignore_unknown_instances": false,
"source_path": "a/foo.txt",
"project_definition": null
},
"8d44bb30-db3c-4366-a6c5-633bd1441885": {
"ignore_unknown_instances": false,
"source_path": "a/main.lua",
"project_definition": null
},
"9f141826-14c2-492b-b360-2558712f0c08": {
"99eefe5f-ef74-49e6-8a8b-c833e00ca56b": {
"ignore_unknown_instances": true,
"source_path": null,
"project_definition": [
@@ -160,7 +141,17 @@
}
]
},
"b1298bcc-e370-44a6-9ef4-fbefa290124c": {
"71a95983-c856-4cf2-aee6-bd8a523e80e4": {
"ignore_unknown_instances": false,
"source_path": "b/something.lua",
"project_definition": null
},
"00f207b1-fc18-4088-a45e-caf8cd98f5dd": {
"ignore_unknown_instances": false,
"source_path": "a/main.lua",
"project_definition": null
},
"bf8e2d4f-33a0-42a0-8168-1b62d6ac050c": {
"ignore_unknown_instances": true,
"source_path": null,
"project_definition": [
@@ -179,31 +170,12 @@
}
]
},
"46353305-8818-48fe-94fd-80cf0c5d974c": {
"b48b369f-5706-4029-9fa6-90651a4910ea": {
"ignore_unknown_instances": false,
"source_path": "a/added",
"project_definition": null
},
"1aafa29b-bdca-40a0-a677-7ead327b84ce": {
"ignore_unknown_instances": false,
"source_path": "a",
"project_definition": [
"Ack",
{
"class_name": null,
"children": {},
"properties": {},
"ignore_unknown_instances": null,
"path": "a"
}
]
},
"8d690a2a-e987-4c86-b9ac-18e6d3a98503": {
"ignore_unknown_instances": false,
"source_path": "b/something.lua",
"project_definition": null
},
"645ba594-4482-441f-9f41-5bb9c444405b": {
"3b5af13f-c997-4009-915c-0810b0e83032": {
"ignore_unknown_instances": true,
"source_path": null,
"project_definition": [
@@ -251,6 +223,34 @@
"path": null
}
]
},
"c910510c-37a8-4fd8-ae41-01169ccb739c": {
"ignore_unknown_instances": false,
"source_path": "b",
"project_definition": [
"Bar",
{
"class_name": null,
"children": {},
"properties": {},
"ignore_unknown_instances": null,
"path": "b"
}
]
},
"14fed1a3-ba97-46a6-ae93-ac26bd9471df": {
"ignore_unknown_instances": false,
"source_path": "a",
"project_definition": [
"Ack",
{
"class_name": null,
"children": {},
"properties": {},
"ignore_unknown_instances": null,
"path": "a"
}
]
}
}
}

View File

@@ -1,6 +1,14 @@
{
"instances": {
"8d44bb30-db3c-4366-a6c5-633bd1441885": {
"866071d6-465a-4b88-8c63-07489d950916": {
"Name": "added",
"ClassName": "Folder",
"Properties": {},
"Id": "866071d6-465a-4b88-8c63-07489d950916",
"Children": [],
"Parent": "c910510c-37a8-4fd8-ae41-01169ccb739c"
},
"00f207b1-fc18-4088-a45e-caf8cd98f5dd": {
"Name": "main",
"ClassName": "ModuleScript",
"Properties": {
@@ -9,70 +17,33 @@
"Value": "-- hello, from a/main.lua"
}
},
"Id": "8d44bb30-db3c-4366-a6c5-633bd1441885",
"Id": "00f207b1-fc18-4088-a45e-caf8cd98f5dd",
"Children": [],
"Parent": "1aafa29b-bdca-40a0-a677-7ead327b84ce"
"Parent": "14fed1a3-ba97-46a6-ae93-ac26bd9471df"
},
"b1c9928c-bf11-427f-90eb-b672c811d859": {
"14fed1a3-ba97-46a6-ae93-ac26bd9471df": {
"Name": "Ack",
"ClassName": "Folder",
"Properties": {},
"Id": "14fed1a3-ba97-46a6-ae93-ac26bd9471df",
"Children": [
"c55fd55c-258e-4a93-a63a-ea243038c9b9",
"00f207b1-fc18-4088-a45e-caf8cd98f5dd"
],
"Parent": "99eefe5f-ef74-49e6-8a8b-c833e00ca56b"
},
"c910510c-37a8-4fd8-ae41-01169ccb739c": {
"Name": "Bar",
"ClassName": "Folder",
"Properties": {},
"Id": "b1c9928c-bf11-427f-90eb-b672c811d859",
"Id": "c910510c-37a8-4fd8-ae41-01169ccb739c",
"Children": [
"8d690a2a-e987-4c86-b9ac-18e6d3a98503",
"a8566e76-0495-45a3-a713-1c59ab39453b"
"866071d6-465a-4b88-8c63-07489d950916",
"71a95983-c856-4cf2-aee6-bd8a523e80e4"
],
"Parent": "9f141826-14c2-492b-b360-2558712f0c08"
"Parent": "99eefe5f-ef74-49e6-8a8b-c833e00ca56b"
},
"645ba594-4482-441f-9f41-5bb9c444405b": {
"Name": "multi_partition_game",
"ClassName": "DataModel",
"Properties": {},
"Id": "645ba594-4482-441f-9f41-5bb9c444405b",
"Children": [
"b1298bcc-e370-44a6-9ef4-fbefa290124c",
"9f141826-14c2-492b-b360-2558712f0c08"
],
"Parent": null
},
"9f141826-14c2-492b-b360-2558712f0c08": {
"Name": "ReplicatedStorage",
"ClassName": "ReplicatedStorage",
"Properties": {},
"Id": "9f141826-14c2-492b-b360-2558712f0c08",
"Children": [
"1aafa29b-bdca-40a0-a677-7ead327b84ce",
"b1c9928c-bf11-427f-90eb-b672c811d859"
],
"Parent": "645ba594-4482-441f-9f41-5bb9c444405b"
},
"8d690a2a-e987-4c86-b9ac-18e6d3a98503": {
"Name": "something",
"ClassName": "ModuleScript",
"Properties": {
"Source": {
"Type": "String",
"Value": "-- b/something.lua"
}
},
"Id": "8d690a2a-e987-4c86-b9ac-18e6d3a98503",
"Children": [],
"Parent": "b1c9928c-bf11-427f-90eb-b672c811d859"
},
"b1298bcc-e370-44a6-9ef4-fbefa290124c": {
"Name": "HttpService",
"ClassName": "HttpService",
"Properties": {
"HttpEnabled": {
"Type": "Bool",
"Value": true
}
},
"Id": "b1298bcc-e370-44a6-9ef4-fbefa290124c",
"Children": [],
"Parent": "645ba594-4482-441f-9f41-5bb9c444405b"
},
"54f2f276-964f-4c60-87d8-5fb2209c97c9": {
"c55fd55c-258e-4a93-a63a-ea243038c9b9": {
"Name": "foo",
"ClassName": "StringValue",
"Properties": {
@@ -81,33 +52,158 @@
"Value": "Hello world, from a/foo.txt"
}
},
"Id": "54f2f276-964f-4c60-87d8-5fb2209c97c9",
"Id": "c55fd55c-258e-4a93-a63a-ea243038c9b9",
"Children": [],
"Parent": "1aafa29b-bdca-40a0-a677-7ead327b84ce"
"Parent": "14fed1a3-ba97-46a6-ae93-ac26bd9471df"
},
"a8566e76-0495-45a3-a713-1c59ab39453b": {
"Name": "added",
"ClassName": "Folder",
"Properties": {},
"Id": "a8566e76-0495-45a3-a713-1c59ab39453b",
"71a95983-c856-4cf2-aee6-bd8a523e80e4": {
"Name": "something",
"ClassName": "ModuleScript",
"Properties": {
"Source": {
"Type": "String",
"Value": "-- b/something.lua"
}
},
"Id": "71a95983-c856-4cf2-aee6-bd8a523e80e4",
"Children": [],
"Parent": "b1c9928c-bf11-427f-90eb-b672c811d859"
"Parent": "c910510c-37a8-4fd8-ae41-01169ccb739c"
},
"1aafa29b-bdca-40a0-a677-7ead327b84ce": {
"Name": "Ack",
"ClassName": "Folder",
"3b5af13f-c997-4009-915c-0810b0e83032": {
"Name": "multi_partition_game",
"ClassName": "DataModel",
"Properties": {},
"Id": "1aafa29b-bdca-40a0-a677-7ead327b84ce",
"Id": "3b5af13f-c997-4009-915c-0810b0e83032",
"Children": [
"54f2f276-964f-4c60-87d8-5fb2209c97c9",
"8d44bb30-db3c-4366-a6c5-633bd1441885"
"bf8e2d4f-33a0-42a0-8168-1b62d6ac050c",
"99eefe5f-ef74-49e6-8a8b-c833e00ca56b"
],
"Parent": "9f141826-14c2-492b-b360-2558712f0c08"
"Parent": null
},
"bf8e2d4f-33a0-42a0-8168-1b62d6ac050c": {
"Name": "HttpService",
"ClassName": "HttpService",
"Properties": {
"HttpEnabled": {
"Type": "Bool",
"Value": true
}
},
"Id": "bf8e2d4f-33a0-42a0-8168-1b62d6ac050c",
"Children": [],
"Parent": "3b5af13f-c997-4009-915c-0810b0e83032"
},
"99eefe5f-ef74-49e6-8a8b-c833e00ca56b": {
"Name": "ReplicatedStorage",
"ClassName": "ReplicatedStorage",
"Properties": {},
"Id": "99eefe5f-ef74-49e6-8a8b-c833e00ca56b",
"Children": [
"14fed1a3-ba97-46a6-ae93-ac26bd9471df",
"c910510c-37a8-4fd8-ae41-01169ccb739c"
],
"Parent": "3b5af13f-c997-4009-915c-0810b0e83032"
}
},
"root_id": "645ba594-4482-441f-9f41-5bb9c444405b",
"root_id": "3b5af13f-c997-4009-915c-0810b0e83032",
"metadata": {
"645ba594-4482-441f-9f41-5bb9c444405b": {
"bf8e2d4f-33a0-42a0-8168-1b62d6ac050c": {
"ignore_unknown_instances": true,
"source_path": null,
"project_definition": [
"HttpService",
{
"class_name": "HttpService",
"children": {},
"properties": {
"HttpEnabled": {
"Type": "Bool",
"Value": true
}
},
"ignore_unknown_instances": null,
"path": null
}
]
},
"c910510c-37a8-4fd8-ae41-01169ccb739c": {
"ignore_unknown_instances": false,
"source_path": "b",
"project_definition": [
"Bar",
{
"class_name": null,
"children": {},
"properties": {},
"ignore_unknown_instances": null,
"path": "b"
}
]
},
"866071d6-465a-4b88-8c63-07489d950916": {
"ignore_unknown_instances": false,
"source_path": "b/added",
"project_definition": null
},
"14fed1a3-ba97-46a6-ae93-ac26bd9471df": {
"ignore_unknown_instances": false,
"source_path": "a",
"project_definition": [
"Ack",
{
"class_name": null,
"children": {},
"properties": {},
"ignore_unknown_instances": null,
"path": "a"
}
]
},
"00f207b1-fc18-4088-a45e-caf8cd98f5dd": {
"ignore_unknown_instances": false,
"source_path": "a/main.lua",
"project_definition": null
},
"99eefe5f-ef74-49e6-8a8b-c833e00ca56b": {
"ignore_unknown_instances": true,
"source_path": null,
"project_definition": [
"ReplicatedStorage",
{
"class_name": "ReplicatedStorage",
"children": {
"Ack": {
"class_name": null,
"children": {},
"properties": {},
"ignore_unknown_instances": null,
"path": "a"
},
"Bar": {
"class_name": null,
"children": {},
"properties": {},
"ignore_unknown_instances": null,
"path": "b"
}
},
"properties": {},
"ignore_unknown_instances": null,
"path": null
}
]
},
"71a95983-c856-4cf2-aee6-bd8a523e80e4": {
"ignore_unknown_instances": false,
"source_path": "b/something.lua",
"project_definition": null
},
"c55fd55c-258e-4a93-a63a-ea243038c9b9": {
"ignore_unknown_instances": false,
"source_path": "a/foo.txt",
"project_definition": null
},
"3b5af13f-c997-4009-915c-0810b0e83032": {
"ignore_unknown_instances": true,
"source_path": null,
"project_definition": [
@@ -155,102 +251,6 @@
"path": null
}
]
},
"1aafa29b-bdca-40a0-a677-7ead327b84ce": {
"ignore_unknown_instances": false,
"source_path": "a",
"project_definition": [
"Ack",
{
"class_name": null,
"children": {},
"properties": {},
"ignore_unknown_instances": null,
"path": "a"
}
]
},
"a8566e76-0495-45a3-a713-1c59ab39453b": {
"ignore_unknown_instances": false,
"source_path": "b/added",
"project_definition": null
},
"8d690a2a-e987-4c86-b9ac-18e6d3a98503": {
"ignore_unknown_instances": false,
"source_path": "b/something.lua",
"project_definition": null
},
"8d44bb30-db3c-4366-a6c5-633bd1441885": {
"ignore_unknown_instances": false,
"source_path": "a/main.lua",
"project_definition": null
},
"9f141826-14c2-492b-b360-2558712f0c08": {
"ignore_unknown_instances": true,
"source_path": null,
"project_definition": [
"ReplicatedStorage",
{
"class_name": "ReplicatedStorage",
"children": {
"Ack": {
"class_name": null,
"children": {},
"properties": {},
"ignore_unknown_instances": null,
"path": "a"
},
"Bar": {
"class_name": null,
"children": {},
"properties": {},
"ignore_unknown_instances": null,
"path": "b"
}
},
"properties": {},
"ignore_unknown_instances": null,
"path": null
}
]
},
"b1c9928c-bf11-427f-90eb-b672c811d859": {
"ignore_unknown_instances": false,
"source_path": "b",
"project_definition": [
"Bar",
{
"class_name": null,
"children": {},
"properties": {},
"ignore_unknown_instances": null,
"path": "b"
}
]
},
"54f2f276-964f-4c60-87d8-5fb2209c97c9": {
"ignore_unknown_instances": false,
"source_path": "a/foo.txt",
"project_definition": null
},
"b1298bcc-e370-44a6-9ef4-fbefa290124c": {
"ignore_unknown_instances": true,
"source_path": null,
"project_definition": [
"HttpService",
{
"class_name": "HttpService",
"children": {},
"properties": {
"HttpEnabled": {
"Type": "Bool",
"Value": true
}
},
"ignore_unknown_instances": null,
"path": null
}
]
}
}
}

View File

@@ -0,0 +1,411 @@
<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="Terrain" referent="RBX6DE6533CDF9645EFB1BBC5A35FCAD7A2">
<Properties>
<bool name="Anchored">true</bool>
<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">4</token>
<token name="BottomSurfaceInput">0</token>
<CoordinateFrame name="CFrame">
<X>0</X>
<Y>0</Y>
<Z>0</Z>
<R00>1</R00>
<R01>0</R01>
<R02>0</R02>
<R10>0</R10>
<R11>1</R11>
<R12>0</R12>
<R20>0</R20>
<R21>0</R21>
<R22>1</R22>
</CoordinateFrame>
<bool name="CanCollide">true</bool>
<bool name="CastShadow">true</bool>
<int name="CollisionGroupId">0</int>
<Color3uint8 name="Color3uint8">4288914085</Color3uint8>
<PhysicalProperties name="CustomPhysicalProperties">
<CustomPhysics>false</CustomPhysics>
</PhysicalProperties>
<float name="FrontParamA">-0.5</float>
<float name="FrontParamB">0.5</float>
<token name="FrontSurface">0</token>
<token name="FrontSurfaceInput">0</token>
<float name="LeftParamA">-0.5</float>
<float name="LeftParamB">0.5</float>
<token name="LeftSurface">0</token>
<token name="LeftSurfaceInput">0</token>
<bool name="Locked">true</bool>
<bool name="Massless">false</bool>
<token name="Material">256</token>
<BinaryString name="MaterialColors"><![CDATA[AAAAAAAAan8/P39rf2Y/ilY+j35fi21PZmxvZbDqw8faiVpHOi4kHh4lZlw76JxKc3trhHta
gcLgc4RKxr21zq2UlJSM]]></BinaryString>
<string name="Name">Terrain</string>
<BinaryString name="PhysicsGrid"><![CDATA[AgMAAADC//8A//8A//8A/P8DAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAD/AAD/AAD/AAH9AAAAAAAAAAAAAAABAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAP//AP//AP//Af/4
AAAAAAAAAAAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAD/AAD/AAD/AAH4AAAAAAAA
AAAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAP//AP//AP//Af/4AAAAAAAAAAAAAAAB
AAAAAAAAAAAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAA
AAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAD/AAD/AAD/AAH4AAAAAAAAAAAAAAABAAAAAAAA
AAAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAABAP//AP//AP//Af/4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAACAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAD/AAD/AAD/AAH4AAAAAAAAAAAAAAABAAAAAAAA
AAAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAACAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAP//AP//AP//Af/4AAAAAAAA
AAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAB
AAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAABAAAAAAAA
AAAAAAABAAD/AAD/AAD/AAH4AAAAAAAAAAAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAB
AAAAAAAAAAAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAABAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAABAP//AP//AP//Af/8AAAAAAAAAAAAAAABAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAABAAAAAAAAAAAAAAABAAD/AAD/AAD/AAH9AAAAAAAAAAAAAAABAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAABAP//AP//AP//Af/5AAAAAAAAAAAAAAAB
AAAAAAAAAAAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAA
AAAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAA
AAD/AAD/AAD/AAH5AAAAAAAAAAAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAABAAAAAAAA
AAAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAP//AP//AP//Af/5AAAAAAAAAAAAAAABAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB
AAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAABAAAAAAAAAAAAAAAAAAD/AAD/AAD/AAH5AAAAAAAAAAAAAAABAAAAAAAAAAAAAAAB
AAAAAAAAAAAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAA
AP//AP//AP//Af/+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAABAAAAAAAAAAAAAAAAAAD/AAD/AAD/AAH+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAP//AP//AP//Af/+AAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAA
AAD/AAD/AAD/AAH+AAAAAAAAAAAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAP//AP//
AP//Af/+AAAAAAAAAAAAAAABAAAAAAAAAAAAAAABAAD/AAD/AAD/AAH+AAAAAAAAAAAAAAAB
AAAAAAAAAAAAAAABAAAAAAAAAAA=]]></BinaryString>
<float name="Reflectance">0</float>
<float name="RightParamA">-0.5</float>
<float name="RightParamB">0.5</float>
<token name="RightSurface">0</token>
<token name="RightSurfaceInput">0</token>
<int name="RootPriority">0</int>
<Vector3 name="RotVelocity">
<X>0</X>
<Y>0</Y>
<Z>0</Z>
</Vector3>
<BinaryString name="SmoothGrid"><![CDATA[AQX///////////////+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/
gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/
gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/
gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/
gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4DMQqFCrYAdQuVC80KigA5CgoAM
Qs5C60KpgA1CgULggAxC00LxQq6ADUKDQuKADELPQvxCx4AOQt6ADEK+QvpC1IAOQsuADEKk
QutC1IAOQqyADEKlQu5C14AOQqmADEKbQvJC60KIgA1CmoAMQoZC6kLyQpyA/4D/gLtCukLH
gBxC8oICgA5CgYALggNCsIAMQtoCgAuCA0LFgAyCAYALggNCzoALQoCCAYALggNC9IAMggGA
C0LlggOADIIBgAtCyIIDgAxC3QKAC0K7ggOADELFAoALQqOCA0KCgAtCoQKAC0KAggNCpIAM
AoD/gP+Aq0KtggFC1YAaQomCA0LJgA1C3YAKQsyCBIAMggGACkKzggSAC0LZggGACkKeggSA
C0LcggGAC4IEgAtCroIBgAuCBEKOgAuCAYALggRCq4ALggGAC4IEQryAC4IBgAuCBELegAuC
AYALQtyCBIALQs0CAAAAAAAAAAAAAAABgP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/
gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/
gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/
gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/
gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4ANQtdC7EKhgBxC2ELs
QqKAHELYQvpCuoAcQs5C+0LHgBxCt0LwQsmAHEK2Qu9CyIAcQrNC+ELdgBxCpUL1QueAHEKV
QudC3IAcQpdC6kLfgBxCnELzQuxCiIAbQpRC7ULpQomAG0KDQttC2IAcQo9C7ELrQo6AG0Ki
QvRC50KAgBtCoULnQs6AHEKsQvJC2YAcQsBC/ELWgBxCuULkQrCAHELMQvhCw4AcQsRC+ULR
gBxCt0L7QuCAHEKUQt1CyYDhSISAHUjYSOhIoYAbSIBI50j0SNeAF4IDQrWADAKADIIDQruA
DAKADIIDQtuADELogAyCA0L1gAxCvYAMQuGCA4AMQpSADELdggOAGkLOggNChgBCjEKKAEKR
QoKAAkKGQoKADkKzggNC6ULYQu5C6kLcQvBC6ELRQuBC6ELsQulC5ULXgAxCn4IDQvBC30L0
Qu5C4ELzQvRC5UL1QvhC90L1QvJC5IAMQqKCA0KZQopCm0KUQohCl0KhQptCqkKowqIBQqBC
k4AMQqSCA0KCgBlCmIIDQoaAGUKIggOAGkKRggNCj4AZQq+CA4AaQrmCA4AaQsSCA4AaQuaC
A4AaQu+CAkLpgBqCA0LvgBpC8YIDgBpC1IIDgBpCqIIDgBtC3IIBQqGAvkjEiAFI+IAbiANI
woARSJqAB4gESImAFEK+ggSAC0KoAoALQrKCBIALQocCgAtCnIIEgAwCgAyCBIAMAoAMggRC
gYALQvOADIIEQo8AQodChgBCj4AGQteADIISgAyCEoAMghKADIISgAyCBELeQpFCoEKYQotC
mUKrQq5CvkK3QqxCrUKsQp+ADEL3ggNC44AZQuSCA0LYgBlC74IDQuyAGYIEQtCAGYIEQrCA
GYIEQqqAGYIEQpGAGYIEgBqCBIAaggRCjoAZggRCuoAZggRCpIAZQpSCA4AcQrNCoICASMRI
2UiTgBuIA0iwgBlIxogEgBBIu0j5SNaABUjRiARI5oAIAAAAAAAAAAAAAAABgP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4AHSJaACEjnSPVI9Ei3gBBIv0jwSM6ABkiKSN1I1UjxSOlIhYAPSM1I90j6SLeA
BUiZSO9I5kjXSPRIyYAPSLZI5Uj6SNyABUitSPdI4ki1SOpI6EidgA5Iwkj6SOtI9EiegARI
vEj7SNkASNtI7kjNgA5Iz0j8SMdI9kjdgARIz0j4SL8ASKRI80jiSLSADUjZSPtIu0jfSO9I
toADSNdI6EisgAFIuEj6SNtIloAMSN1I6kiZSMlI8EjXSIKAAkjbSO1In4ABSIVIxkj8SM+A
DEjbSOhIl0iHSOpI8EitgAFIm0jzSOxIioACSKRI10j6SLuACkicSPNI7EiIAEi3SPtI3IAB
SLBI8kjUgARIuUjjSPNIuYAJSJZI6kjggAFIi0jYSPhItwBIxUj6SMyABUjMSPZI6Ui4gAhI
k0jmSNyAAkjASONI4EiCSNZI/Ei/gAZIxUj5SOZIrYAHSKJI9UjpSIKAAUiNSOdI5Ei1SNlI
8kirgAdI4Uj8SOlIn4AGSJhI6EjZgANIv0j6SOFI7EjwSKCAB0ieSOFI70ilgAZInEjsSN2A
A0iDSOBI8Uj3SOWACUiESJeACEiSSIWABEioSO5I+kjXgBxItUjbSLuA/4DzSIyIAkixgAWI
BEj8gA5I+IgDSJmAA0iTiAVIqoANiARI6IADSKKIBoANSOaIBIADSMKIBkivgAxI64gESNyA
AkjeiAeADIgGSIGAAYgDSOyIA0j3gAuIBkjdgAGIA0jTiARIyIADSIKABYgHSIgAiANIsEih
iARInYACSKuABYgHSPBIoogDSIUASM+IBEiBgAEIgARIpIgDSP6IA0jNiAOAAkj5iASAAQiA
BEifiANIp4gDSPOIA4ACSH+IBEjxAEi6gARInIgDAIgHSOeAA0iniARI+UiTgARIrogDAEiR
iAZIw4AESKSIBEizgARIp4gDgAFI74gFSLiABUjdiANIuYAESKqIA4ABSKaIBYAHSOiIAoAG
iAJI04ACSOGIBIAISItInoAISJZIiYAEiANI+4AbSPOIAUiCgP+A0kjqiAOABEjOiAVIloAN
iARI+IADSPGIBoAMSIOIBYADiAdIqoAMiAVI1IACiAiADIgGgAKICEjOgAuIBkjdAEiGiAlI
kIADSIaABEiciAcASLiICoADSN6ABEjGiAdI5UjTiApI/YACCIAESNqIDUjhAIgFSN0ASIII
gASIDkiwAEitiAVI2QAIgASIDkiVgAFI24gFSMAIgARI/IgDSMuICIADiAZI8oAEiARI00jv
iAeABIgGgASIBEi/SJWIB4AFiAWABIgESMIAiAZI2oAFSKWIA0jAgARIrogDgAKIBUiZgAdI
6Ej+SLKABkikSPVI5oADSMSIBIAaSNGIAkjegBxIpUiGgP+ArQAA/wAA/wAA/wAB/oD/gP+A
q0KuggFC1oAaQoqCA0LKgA1C3oAKQsyCBIAMggGACkK0ggSAC0LaggGACkKfggSAC0LdggGA
CkKAggSAC0KvggGAC4IEQo+AC4IBgAuCBEKrgAuCAYALggRCvYALggGAC4IEQt+AC4IBgAtC
3YIEgAtCzgKA/4D/gKxCvELJgBxC9YICgA5Cg4ALggNCsoAMQt0CgAuCA0LIgAtCgIIBgAuC
A0LRgAtCgoIBgAuCA0L2gAyCAYALQueCA4AMggGAC0LLggOADELgAoALQr6CA4AMQsgCgAtC
poIDQoSAC0KkAoALQoOCA0KmgAwCgP+A/4DMQqVCsoAcQoFC6kL4QqaADkKGgAxC0kLwQq2A
DUKFQuWADELYQvVCsoANQodC54AMQtMCQsuADkLjgAxCwwJC2IAOQtCADEKoQu9C2YAOQrGA
DEKpQvNC3IAOQq6ADEKgQvdC70KMgA1CnoAMQopC70L3QqCADUKCgP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A
/wAAAAAAAAAAAAAAAYALQr6CBIALQqkCgAtCs4IEgAtCiAKAC0KdggSADAKADIIEgAwCgAyC
BEKCgAtC9IAMggRC20LKQuNC4kLUQuxC0UK7QrhCyULXQtJCy0LYgAyCEoAMghKADIISgAyC
EoAMggVC7gJC9kLnQveCBkL+gAxC+IIDQuSAGULlggNC2YAZQu+CA0LtgBmCBELRgBmCBEKx
gBmCBEKrgBmCBEKSgBmCBIAaggSAGoIEQo+AGYIEQruAGYIEQqWAGUKVggOAHEKzQqCAgEjF
SNpIlIAbiANIsYAZSMeIBIAQSLxI+kjXgAVI0ogESOeAFYIDQreADAKADIIDQr6ADAKADIID
Qt2ADELrgAyCA0L4gAxCwIAMQuSCA4AMQpeADELgggNC20LKQuNC4kLUQuxC0UK7QrhCyULX
QtJCy0K+gAxC0IIRgAxCtYIRgAxCooIRgAxCpYIRgAxCpoIEQu4CQvZC50L3ggZC/oAMQpqC
A0KJgBlCioIDQn+AGUKTggNCkYAZQrGCA4AaQryCA4AaQseCA4AaQuiCA4AaQvGCAkLsgBqC
A0LygBpC84IDgBpC1oIDgBpCqoIDgBtC3oIBQqSAoEiAgBxIx4gBSPuAG4gDSMSAEUidgAeI
BEiMgBZC20LwQqWAHELcQvFCpoAcQt0CQr6AHELSAkLLgBxCu0L1Qs2AHEK6QvRCzABCgABC
h0KGAEKPgBVCt0L9QuFC84INgA1CqUL6QuuCDoANQplC7ELggg6ADUKbQu9C44IOgA1CoEL4
QvBCjEKgQpFCoEKYQotCmUKrQq5CvkK3QqxCrUKsQp+ADUKYQvJC7kKNgBtCh0LfQtxCgYAb
QpPC8AFCkoAbQqZC+ULsQoSAG0KlQutC0oAcQrBC90LdgBxCxAJC2oAcQr1C6EK0gBxC0EL9
QseAHELIQv5C1YAcQrsCQuWAHEKZQuJCzoDhSIiAHUjcSO1IpYAbSIRI7Ej5SNuA3EKGAEKM
QooAQpFCgoACQoZCgoASQptC6ULYQu5C6kLcQvBC6ELRQuBC6ELsQulC5ELXgBBCokLwQt9C
9ELuQuBC80L0QuVC9UL4QvdC9ULyQuSAEUKZQopCm0KUQohCl0KhQptCqkKowqIBQqBCk4D/
gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/
gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/
gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/
gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/
gP+A/4D/gP+A/4C/AAAAAAAAAAAAAAABgAVI64gDgARIz4gFSJeADYgESPmAA0jyiAaADEiE
iAWAA4gHSKuADIgFSNWAAogIgAyIBoACiAhIz4ALiAZI3gBIh4gJSJGAA0iHgARInYgHAEi5
iAqAA0jfgARIx4gHSOZI1IgKSP6AAgiABEjbiA1I4gCIBUjeAEiDCIAEiA5IsQBIrogFSNoA
CIAEiA5IloABSNyIBUjBCIAESP2IA0jMiAiAA4gGSPOABIgESNRI8IgHgASIBoAEiARIwEiW
iAeABYgFgASIBEjDAIgGSNuABUimiANIwYAESK+IA4ACiAVImoAHSOkISLOABkilSPZI54AD
SMWIBEiAgBlI0ogCSN+AHEimSIeA/4CzSI+IAkizgAWIBYAOSPuIA0icgANIlYgFSK2ADYgE
SOuAA0iliAaADUjpiASAA0jEiAZIsYAMSO6IBEjfgAJI4IgHgAyIBkiEgAGIA0jviANI+oAL
iAZI4IABiANI1ogESMuAA0iFgAWIB0iLAIgDSLNIo4gESKCAAkiugARIgYgHSPNIpIgDSIcA
SNKIBEiEgAEIgARIpogISNCIA4ACSPyIBEiAAAiABEiiiANIqYgDSPaIA4ACSIKIBEjzAEi+
gARIn4gDAIgHSOqAA0ipiARI/EiXgARIsYgDAEiTiAZIxoAESKeIBEi2gARIqYgDgAFI8YgF
SLuABUjfiANIvIAESK2IA4ABSKiIBUiBgAZI64gCgAaIAkjWgAJI5IgEgAhIjUiggAhImUiM
gASIA0j+gBtI9ogBSISA/4DUSJqAB0iDSOxI+kj5SLyAEEjDSPVI0kiCgAVIjkjiSNpI9kjt
SImAD0jSSPwISLuABUieSPRI60jcSPlIzYAPSLpI6ghI4YAFSLFI/EjmSLpI70jtSKGADkjG
CEjwSPlIooAESMAISN4ASOBI80jSgA5I0whIzEj7SOGABEjUSP1IxABIqUj4SOdIuIANSN4I
SMBI40j0SLqAA0jcSO1IsIABSLwISOBImoAMSOJI70idSM1I9UjcSIaAAkjgSPJIpIABSIlI
ywhI00iCgAtI4EjtSJxIjEjuSPVIsoABSJ9I+EjxSI6AAkioSNsISL+ACkigSPhI8EiMAEi8
CEjhgAFItEj3SNiABEi+SOhI+Ei9gAlImkjuSOVIgABIj0jcSP1IuwBIyghI0YAFSNBI+0jt
SL2ACEiXSOtI4YACSMRI6EjlSIZI2whIxIAGSMpI/kjqSLKAB0inSPpI7kiGgAFIkUjsSOlI
ukjeSPdIsIAGSIJI5QhI7UijgAZInUjtSN6AA0jDCEjmSPFI9UikgAdIokjmSPRIqYAGSKBI
8EjigANIh0jlSPZI/EjqSH+ACEiJSJuACEiWSImABEitSPMISNyAHEi6SOBIv4D/gP+A/4D/
gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/
gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/
gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/
gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/
gP+A/4D/gP+A/4DtAP//AP//AP//Af/+gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/
gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/
gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/
gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/
gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+AlkKAQs5CwYAc
Qs9C9kLoQoCAGkKIQuxC80K7gAVChoAVQtBC2EKFgAVC5EKMgB1C50KTgB1C+EKygB1C+0LJ
gB1C70LSgB1C7ELQgB1C8ULqQoaAHELnQvZCpID/gP+Ak0K2AkL9QpKAGkKyggOAGoIEgARC
hYATQoKCBIAEAkLngBOCA0KOgASCAUKRgBJCsYIBQsmABYIBQqyAHIIBQsuAHIIBQvmAHIIC
gByCAoAcggJCgYAbggJCs4D/gP+Ac0KIgB2CAkLwgBqCBEKqgBhCsoIEQs+AA0LhQomAEkLe
ggRCmIADggFCj4ARQsGCA0LrgASCAULvgBKCA4AFggKAE0KjQquABoICgByCAoAcggJCnoAb
ggJCwoAbggJC3YAbggOAGwAAAAAAAAAAAAAAAYD/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/
gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/
gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/
gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/
gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP9Cz0LzQraAHELH
QupCuoAcQr9C+ELQgBxCqkL3QuSAFEKiQp6ABUKJQuhC60KQgBJClELtQulCiIAFQtpC4kKc
gBJCl0LlQtaABkLbQvRCrIASQp5C7ULegAZCzkL8QsiAEkKyQvhC3oAGQrRC9kLXgBJCvUL6
QtWABkKiQuhC3YASQsFC80LEgAZClkLoQt5CiIARQrtC7EK+gAZCg0LnQu9CmoARQtBC+EK+
gAdC3UL6QrWAEULcQvpCtoAHQsxC/ELKgBFC00L2QriAB0K2QvhC2IARQsJC6EKwgAdCn0Lt
QuGAEULNQvVCvIAHQpNC5ELjQo6AEELOQvxCyIAHQoFC40LpQpyAEELGQvlCyoAIQttC9UKv
gBBCv0L2QsuACELOQvtCxoAQQrdC8kLMgAhCvUL7QtmAEEK0QvNC0YAIQpRC0EKvgBBCsUL0
QtaAHEKlQvJC6UKEgBxCsEKngGFItkimgBxIpkj1SORIgoAbSMdI90j5SMSAA0iJSKhIvUjF
SLZIn0iLgBBIy0jkSPNI6UiNgAJI3kj1SPtI/Ej3SN5I6kjgSM9IsEiVgAtIhkjdSPNI2Ej7
SLyAAkjXSOdI10jQSNhI1kjtSPhI+0j3SOlI10ifgAlIp0jtSOdIrEjtSPCCAkLZgBuCAkLz
gBuCA4ATQrZCsoAFggOAEoICQvqABIIDQpOAEEKZggNChYADggNCs4AQQqaCA4AEggNCx4AQ
Qq2CA4AEggNC94AQQsuCA4AEQvaCA4AQQuKCA4AEQsCCA4AQQvCCAkL3gARCoYIDQpGAD0L1
ggJC8YAFggNCrIAPggNC5oAFggNC0YAPggNC1IAFggNC+oAPggNC2oAFQtWCA4APggNC14AF
QryCA4APggNC5IAFQp6CA0KVgA6CA0L2gAaCA0K0gA5C9YICQv6ABoIDQs+ADkLqggOABoID
QvSADkLdggOABkLhggOADkLXggOABkKzggJC74AOQs+CA4AHQscCQuWAD0LCggOAG4IDgBxC
zULEgCBIi0jXSMaAHIgCSPaAGki3iANIq4ACSJBIu0jhSPFI1Ui9SIqAD0j4iASAAUjYiAhI
3EitgAuIBYABiAxIy4AISIyIBYABiA6AB0jIiAWCA4AbggOAG4IDQpCAEUK7ggFCr4AEggNC
wYAQQq2CA0KYgAOCA0LygBBC+YIDQuKAA4IEgBCCBELOgAOCBIAQggRCwoADggRCl4APggRC
roADggRCq4APggRCk4ADggRCyoAPggSABIIEQu+AD4IEgARC14IEgA5CiIIEgARCqYIEgA5C
p4IEgAWCBIAOQpSCBIAFggRCn4ANQoSCBIAFggRCy4ANQoWCBIAFQv6CA0LzgA6CBIAFQteC
BIAOggSABUKsggSADoIEQn+ABYIEgA6CBEKIgAWCBEKbgA2CBEKTgAWCBIAOggRCvIAFQo2C
AkLFgA6CBELZgAdCgoAQQsKCA0KZgBpC14IBQr2AH0joiAFIuIAaSNiIA0iKgBmIBYABSIVI
74gESOhItUiBgA2IBYABiAtI0oAJSKSIBQBIxIgNSKWAB0jqiAUASL6IDkjEgAaIBgAAAAAA
AAAAAAAAAYD/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+AA0iDAEjUyOwBSJJIr0jGSN1I7Ej8SOtIqYAISL9I
9UjTAEjISPeABEjUSOdI7kikgANIm0jSSPJI24AISNNI90i5AEiYSNuAA0jTSOZI70ihgAVI
nMjpAUiNgAZIkEjpSPBImYABSN6AAkjRSOpI70ijgAZIgEjbSOdIpYAGSK1I9kjggAJIuIAB
SM5I7kjvSKmACEjVSPJIwIAGSOBI+UixgAJIlQBIy0jxSO5IroAJSL5I9EjXgAVIsUj2SNtI
iIAESMlI7EiygApIukj4SNSABUjYSOtIxIAESJ5I9EjqSIWACUiQSNJI+0jDgARIo0jhSOpI
n4AESMpI8ki5gAlImUjNSPpI0EicgARIvkj2SNaABUjESOtIv4AGSIJInkiuSNdI+UjaSKeA
BEiESOdI9UikgAVIqEj2SOSAAkiSSJ9Iu0jYSOZI7UjlSPdI2UidgAVIvkj6SNSABkiRSO1I
60iySMJI4EjqSOBI+Uj4SO5I3kjWSMxIhIAGSKBI2Ei0gAdI30j2SPhI90j5SOVI20jVSLdI
nkiJgBRIwkjqSN1IykiwSIiAGkiGgP+A/4A+SMOIDUjUgAZI/ogFgAJIuIgFSMZI84gFgAaI
A0jdiAGAAUi1iAWAA0jNiANIj4AESKWIA0igiAEASLGIBYAESICIA0jCgARI9ogDAEjbCEit
iAWABogDSPKAA0iHiANI6gBIs4gGgAdI74gDgANIzIgDSJ6AAYgFgAhI3YgDgAOIBIACSNeI
A4AISMKIA0j8gAJIwogDSLGAAkiFiAOABkitSNiIBEjDgAKIBIAEiAOAAUiXSMFI34gHSNOA
AkiniANIy4AEiANI9ogKSOiAA0jliANIgIAEiA5I3IAESMSIAkjwgAWIDUi4gAZI3ghI9YAG
iAhI10i9SJGAE0iSiANIyEiegBpIf4D/gP+AHogPgAVIgIgGgAKIDkiygARIxIgGgAGIBki9
gAFIr4gESO2ABIgHAIgGSLuAA0jciASAA0iDiARIuYgISMCABEigiASAA0jkiAQAiAdIx4AG
iARIooACiAUASLaIBUjOgAZIh4gESJWAAUi2iARIlIABiARI2YAGSJmIBYACiAWAAkjhiANI
pYADSJ1I14gHgAFIpogESLGAA4gDSJ9ItEj3iApInYABiAWABIgQSLWAAogESNyABIgPSKGA
A4gEgAWIDoAFSKaIAkjQgAWICkjwSLtIgIAISJyAB0jxiAVI00iVgBdIq0jbSK6A/4D/gBsA
AP8AAP8AAP8AAf6A/4D/gFdCiIAdggJC8YAaggRCq4AYQrKCBELQgANC4kKKgBJC34IEQpmA
A4IBQpCAEULCggNC7IAEggFC8IASggOABYICgBNCo0KrgAaCAoAcggKAHIICQp+AG4ICQsOA
G4ICQt6AG4IDgP+A/4CSQrmCAUKVgBpCtIIDgBqCBIAEQoeAE0KFggSABAJC6oATggNCkIAE
ggFClIASQrOCAULMgAWCAUKugByCAULOgByCAUL8gByCAoAcggKAHIICQoSAG4ICQrWA/4D/
gLJChELTQsaAHELUQvtC7UKEgBpCjELxQvhCv4AFQoqAFULUQt1CiYAFQulCkYAdQuxCmIAd
Qv1CtoAdAkLOgB1C9ELXgB1C8ULUgB1C9kLvQouAHELsQvtCqYD/gP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A
HAAAAAAAAAAAAAAAAYIDgBuCA4AbggNCkYARQryCAUKwgASCA0LCgBBCroIDQpmAA4IDQvOA
EEL6ggNC44ADggSAEIIEQs+AA4IEQoOAD4IEQsSAA4IEQvWAD4IEQq+AA4IFgA+CBEKUgAOC
BELLgA+CBIAEggRC8IAPggSABELYggSADkKJggSABEKpggSADkKoggSABYIEgA5ClYIEgAWC
BEKggA1ChYIEgAWCBELMgA1ChoIEgAWCBEL0gA6CBIAFQtiCBIAOggSABUKtggSADoIEQoCA
BYIEgA6CBEKJgAWCBEKcgA2CBEKUgAWCBIAOggRCvYAFQo6CAkLGgA6CBELagAdCg4AQQsSC
A0KagBpC2IIBQr6AH0jqiAFIuYAaSNmIA0iLgBmIBYABSIZI8IgESOlItkiCgA2IBYABiAtI
1IAJSKWIBQBIxYgNSKaAB0jriAUASL+IDkjFgAaIBoICQtyAG4ICQveAG4IDgBNCuUK1gAWC
A4ASggJC/YAEggNCloAQQpyCA0KIgAOCA0K2gBBCqYIDgASCBEKDgA9CsIIDgASCBEL1gA9C
zoIDgASCBYAPQuWCA4AEggRCv4APQvSCAkL7gASCBEKTgA9C+YICQvSABYIDQq+AD4IDQuqA
BYIDQtSAD4IDQteABYIDQv2AD4IDQt6ABULYggOAD4IDQtuABUK/ggOAD4IDQueABUKhggNC
l4AOggNC+YAGggNCt4AOQvmCA4AGggNC0oAOQu2CA4AGggNC94AOQuCCA4AGQuSCA4AOQtqC
A4AGQraCAkLygA5C0oIDgAdCygJC6IAPQsWCA0KAgBqCA4AcQtBCx4AgSI5I2kjKgBtIf4gC
SPqAGki6iANIroACSJNIvkjlSPVI2UjASI2AD0j8iASAAUjbiAhI4EiwgAuIBYABiAxIzoAI
SI+IBYABiA6AB0jLiAVC1EL4QruAHELMQu9Cv4AcQsRC/kLVgBxCr0L9QumAFEKnQqOABUKO
Qu1C8EKVgBJCmULzQu5CjYAEQoJC30LnQqGAEkKcQutC3IAFggNC04ARQqNC80LjgAWCBEKX
gBBCt0L+QuSABYIEQquAEELDAkLbgAWCBIARQsZC+ELJgAVCp0KaQu5C40KNgBFCwELyQsOA
BkKHQuxC9UKegBFC1UL9QsOAB0LiAkK6gBFC4gJCu4AHQtECQs6AEULZQvxCvYAHQrtC/ULd
gBFCx0LuQrWAB0KjQvJC5kJ/gBBC0kL6QsGAB0KXQulC6EKSgBBC0wJCzYAHQoVC6ELvQqCA
EELLQv5Cz4AIQuBC+0K0gBBCxEL7QtGACELTAkLLgBBCvEL3QtGACELCAkLegBBCuUL5QtaA
CEKYQtRCtIAQQrZC+ULbgBxCqkL3Qu5CiYAcQrVCrIBhSLtIq4AcSKxI+0jqSIeAG0jMSP0I
SMmAA0iOSK5Iw0jLSLtIpEiRgBBI0EjqSPlI70iSgAJI5Ej8iAFI/UjkSPFI5kjVSLVImoAL
SItI40j5SN4ISMGAAkjdSO1I3UjWSN1I3EjzSP4ISP1I70jcSKSACUisSPNI7UixSPNI9oC/
QonChQGAHELtwuQBQt5CjIAaQvZC6ML0AUKggBpCn0KOQqxCqoD/gP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4DbAAAA
AAAAAAAAAAABgAGID4AFSIGIBoACiA5ItIAESMWIBoABiAZIvoABSLCIBEjugASIBwCIBki8
gANI3YgEgANIhIgESLqICEjBgARIoYgEgANI5YgEAIgHSMiABogESKOAAogFAEi3iAVIz4AG
SIiIBEiWgAFIt4gESJWAAYgESNqABkiaiAVIgIABiAWAAkjiiANIpoADSJ9I2IgHgAFIp4gE
SLKAA4gDSKBItUj4iApInoABiAWABIgQSLeAAogESN2ABIgPSKKAA4gEgAWIDoAFSKeIAkjR
gAWICkjxSLxIgYAISJ2AB0jyiAVI1EiWgBdIrEjdSK+A/4D/gB1Ix4gNSNeABogGgAJIvIgF
SMpI94gFgAaIA0jgiAGAAUi5iAWAA0jQiANIkoAESKiIA0ijiAEASLWIBYAESIOIA0jFgARI
+ogDAEjfCEiwiAWABogDSPWAA0iKiANI7gBItogGgAdI84gDgANIz4gDSKKAAYgFgAhI4IgD
gAOIBIACSNqIA0iAgAdIxYgEgAJIxogDSLSAAkiIiAOABkiwSNyIBEjHgAKIBIAEiAOAAUib
SMVI44gHSNeAAkiqiANIzoAEiANI+ogKSOuAA0joiANIg4AEiA5I4IAESMeIAkjzgAWIDUi7
gAZI4ghI+IAGiAhI2kjASJSAE0iWiANIy0ihgBpIg4D/gP+AIEiIAEjayPIBSJhItUjMSONI
8ghI8UivgAhIxEj7SNkASM1I/YAESNpI7Uj0SKqAA0igSNdI+EjhgAhI2Uj9SL8ASJ1I4YAD
SNhI7Ej1SKeABUihyO8BSJKABkiVSO9I9kiegAFI5IACSNZI8Ej1SKiABkiFSOFI7kirgAZI
s0j8SOaAAki+gAFI1Ej0SPVIroAISNtI+EjGgAZI5ghItoACSJoASNFI90j0SLOACUjDSPpI
3YAFSLZI/EjgSI2ABEjOSPJIuIAKSL9I/kjagAVI3kjxSMmABEikSPpI8EiKgAlIlUjXCEjJ
gARIqEjnSPBIpIAESNBI+Ei/gAlInkjTCEjWSKGABEjDSPxI24AFSMpI8kjEgAZIh0ijSLNI
3QhI30itgARIiUjtSPtIqYAFSK5I/EjqgAJIl0ilSMFI3UjsSPNI60j9SN9IooAFSMQISNqA
BkiWSPNI8Ui4SMhI5kjwSOYISP5I9EjkSNxI0kiKgAZIpUjeSLmAB0jlSPxI/kj9CEjrSOFI
20i8SKRIjoAUSMdI8EjjSNBItkiNSIGAGUiLgP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+APAD//wD/
/wD//wH//4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+AZ0jJSNhIi4AbSIxI7Uj0SKyAG0iSSOVI
30jwSLWAGkibSOtI4EjuSOtIr4ASSJqABUilSPBI3EjOSPFI5kiogP+A/4D/gFhIoogBSM6A
G4gDSJqAGUiJiARIgYASSKyABEiciAWAEgiABEiqiAVI+4ARCEjegANIuIgGSNuA/4D/gP+A
OEiYSKaAHIgDSIOAGUi8iANI+oATSJ6ABEjmiARI3YASCIAESPuIBUjNgBEISL6AA4gHSLyA
EIgBgAOICEiDgA8AAAAAAAAAAAAAAAGA/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/SNyABUixSPNI1QBI00j0
SOBIhoAQSOJIl4AESLxI9UjMAEiISOZI7EjPgBBI8kimgARIz0jtSMCAAUisSO5I80i2gA9I
+kjagARI2UjuSKyAAkjFSOtI7UingA5I6kjzSJ2AAkiUSOhI60iRgANI00j4SN9In4ANSMpI
+0jKgAJIvEj7SNiABEiNSNFI/EjjSJuADEinSPJI6kiGgAFI3Ej3SLCABUibSNtI8kjpSKCA
DEjPSO9IvwBIpkjpSNtIl4AGSI1I20jxSOdIqIALSL9I8EjOAEi8SPFI1YAISIZI2Uj1SLaA
C0iFSOpI80i+SOdI9kilgAlIhUikgA1IuUj4SOVI8EjdgBtI3EjwSORI0YAbSIpI3kj8SMyA
HEifSM5IoYD/gP+AWIgBgANIz4gHSLKAD4gBSKqAAkjliAhIiYAOiAFI4oACiANI+kjtiASA
DogCgAKIA0jKAIgESO+ADYgCSMAASLWIA0iVAEibiARI7IAMiAMASN+IA4ACSLqIBEj4gAuI
A0iTiANI4YADSM2IBEj7gAqIA0jxiANIsIAESNWIBEjKgAlI8IgHgAZIz4gDSNiACUiTiAZI
woAHSMyIAkiKgAqIBkiDgAhIk0iygAxIlogFgBlI1IgEgBpI2YgCSNKAG0jRCEjTgP+A/4A4
iAFIqoACiAmAD4gCgAKICUjmgA6IAoABSJmICki3gA2IAkjKAEjViARIzIgFSKOADIgDAIgE
SPMASPuIBUiogAuIA0ihiARIpIABiAZIr4AKiANI8YgEgAJIgogGSL2ACYgJgANIm4gGgAmI
CEihgARIiIgFgAlI8ogHgAeIA0jngAlIoIgGSOCACEjyCEjUgAtI9YgFSLCAGIgFgBlIhIgE
gBpIoogCSKeA/4D/gDcAAP8AAP8AAP8AAf+A/4D/gP+AJ0iaSKiAHIgDSISAGUi9iANI+4AT
SJ+ABEjniARI3oASCIAESP2IBUjOgBEISL+AA4gHSL2AEIgBgAOICEiEgP+A/4D/gFZIpYgB
SNGAG4gDSJ2AGUiMiARIhIASSLCABEifiAWAEgiABEitiAaAEQhI4YADSLuIBkjegP+A/4D/
gHhIz0jeSJCAG0iRSPNI+kixgBtIl0jrSOVI9ki6gBpIoUjxSOVI80jxSLSAEkifgAVIqkj2
SOJI1Ej3SOxIrYD/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/
gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/
gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/
gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/
gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+AEQAAAAAAAAAAAAAAAYgBSKuAAogJgA+IAoAC
iAlI54AOiAKAAUiaiApIuIANiAJIzABI1ogESM2IBUikgAyIAwCIBEj1AEj8iAVIqYALiANI
oogESKWAAYgGSLCACogDSPKIBIACSIOIBki+gAmICYADSJyIBoAJiAhIooAESImIBYAJSPOI
B4AHiANI6YAJSKGIBkjhgAhI8whI1YALSPaIBUiygBiIBYAZSIWIBIAaSKOIAkipgP+A/4A3
iAGAA0jSiAdItoAPiAFIrYACSOiICEiMgA6IAUjlgAKIA0j+SPCIBIAOiAKAAogDSM0AiARI
84ANiAJIwwBIuIgDSJgASJ6IBEjwgAyIAwBI4ogDgAJIvogESPuAC4gDSJaIA0jlgANI0IgF
gAqIA0j1iANIs4AESNiIBEjNgAlI9IgHgAZI0ogDSNuACUiXiAZIxYAHSM+IAkiNgAqIBkiG
gAhIlki1gAxImYgFgBlI14gEgBpI3YgCSNWAG0jUCEjXgP+A/4A4SOGABUi2SPlI2gBI2Ej6
SOVIi4AQSOhInYAESMJI+0jSAEiNSOtI8kjVgBBI90irgARI1UjzSMWAAUixSPRI+Ui8gA8I
SN+AA0iASN5I9EiygAJIy0jxSPNIrIAOSPBI+UiigAJImUjuSPFIloADSNlI/kjlSKSADUjP
CEjQgAJIwQhI3oAESJJI1whI6UihgAxIrUj4SPBIi4ABSOJI/Ui2gAVIoEjhSPhI70ilgAtI
gUjVSPVIxQBIq0juSOFInIAGSJJI4Uj2SO1IroALSMVI9kjTSIFIwUj3SNqACEiMSN5I+ki7
gAtIikjvSPlIw0jsSPxIqoAJSIpIqYANSL5I/kjrSPZI44AbSOFI9kjpSNeAG0iPSOQISNKA
HEikSNNIpoD/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A
/4D/gP+A/4D/gP+A/4D/gP+A/4D/gP+A/4D/gFg=]]></BinaryString>
<BinaryString name="Tags"></BinaryString>
<float name="TopParamA">-0.5</float>
<float name="TopParamB">0.5</float>
<token name="TopSurface">3</token>
<token name="TopSurfaceInput">0</token>
<float name="Transparency">0</float>
<Vector3 name="Velocity">
<X>0</X>
<Y>0</Y>
<Z>0</Z>
</Vector3>
<Color3 name="WaterColor">
<R>0.0470588244</R>
<G>0.329411775</G>
<B>0.360784322</B>
</Color3>
<float name="WaterReflectance">1</float>
<float name="WaterTransparency">0.300000012</float>
<float name="WaterWaveSize">0.150000006</float>
<float name="WaterWaveSpeed">10</float>
<Vector3 name="size">
<X>2044</X>
<Y>252</Y>
<Z>2044</Z>
</Vector3>
</Properties>
</Item>
</roblox>

View File

@@ -0,0 +1,13 @@
{
"name": "terrain",
"tree": {
"$className": "DataModel",
"Workspace": {
"$className": "Workspace",
"Terrain": {
"$path": "Terrain.rbxmx"
}
}
}
}