mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-04-20 12:45:05 +00:00
Compare commits
16 Commits
v6.0.0-rc.
...
v6.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0599b50235 | ||
|
|
21f7ef6186 | ||
|
|
de6470bb45 | ||
|
|
b84aab0960 | ||
|
|
0e89b91c38 | ||
|
|
7888a704e1 | ||
|
|
804fd3de8e | ||
|
|
4992c36f08 | ||
|
|
27af0c841b | ||
|
|
cc4f4df4f9 | ||
|
|
5989ab3b85 | ||
|
|
040b9c1452 | ||
|
|
bb7bd2e27e | ||
|
|
02dbd4ba75 | ||
|
|
3b149cc875 | ||
|
|
f3745c68d2 |
18
CHANGELOG.md
18
CHANGELOG.md
@@ -2,6 +2,24 @@
|
||||
|
||||
## Unreleased Changes
|
||||
|
||||
## [6.1.0][6.1.0] (April 12, 2021)
|
||||
* Updated dependencies, fixing OptionalCoordinateFrame-related issues.
|
||||
* Added `--address` flag to `rojo serve` to allow for external connections. ([#403][pr-403])
|
||||
|
||||
[pr-403]: https://github.com/rojo-rbx/rojo/pull/403
|
||||
[6.1.0]: https://github.com/rojo-rbx/rojo/releases/tag/v6.1.0
|
||||
|
||||
## [6.0.2](https://github.com/rojo-rbx/rojo/releases/tag/v6.0.2) (February 9, 2021)
|
||||
* Fixed `rojo upload` to handle CSRF challenges.
|
||||
|
||||
## [6.0.1](https://github.com/rojo-rbx/rojo/releases/tag/v6.0.1) (January 22, 2021)
|
||||
* Fixed `rojo upload` requests being rejected by Roblox
|
||||
|
||||
## [6.0.0](https://github.com/rojo-rbx/rojo/releases/tag/v6.0.0) (January 16, 2021)
|
||||
* Improved server error messages
|
||||
* The server will now keep running in more error cases
|
||||
* Fixed Rojo being unable to diff ClassName changes
|
||||
|
||||
## [6.0.0 Release Candidate 4](https://github.com/rojo-rbx/rojo/releases/tag/v6.0.0-rc.4) (December 14, 2020)
|
||||
* Added brand new Rojo UI ([#367](https://github.com/rojo-rbx/rojo/pull/367))
|
||||
* Added `projectName` to `/api/rojo` output.
|
||||
|
||||
563
Cargo.lock
generated
563
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rojo"
|
||||
version = "6.0.0-rc.4"
|
||||
version = "6.1.0"
|
||||
authors = ["Lucien Greathouse <me@lpghatguy.com>"]
|
||||
description = "Enables professional-grade development tools for Roblox developers"
|
||||
license = "MPL-2.0"
|
||||
|
||||
10
perf-test.sh
10
perf-test.sh
@@ -1,10 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
cargo build --release
|
||||
|
||||
echo "Known good:"
|
||||
time rojo build ../uiblox/test-place.project.json -o UIBlox.rbxlx
|
||||
|
||||
echo "Current:"
|
||||
time ./target/release/rojo build ../uiblox/test-place.project.json -o UIBlox.rbxlx
|
||||
@@ -8,7 +8,7 @@ Error.Kind = {
|
||||
},
|
||||
ConnectFailed = {
|
||||
message = "Couldn't connect to the Rojo server.\n" ..
|
||||
"Make sure the server is running -- use 'rojo serve' to run it!",
|
||||
"Make sure the server is running — use 'rojo serve' to run it!",
|
||||
},
|
||||
Timeout = {
|
||||
message = "HTTP request timed out.",
|
||||
@@ -63,4 +63,4 @@ function Error.fromRobloxErrorString(message)
|
||||
return Error.new(Error.Kind.Unknown, message)
|
||||
end
|
||||
|
||||
return Error
|
||||
return Error
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@ local isDevBuild = script.Parent.Parent:FindFirstChild("ROJO_DEV_BUILD") ~= nil
|
||||
return strict("Config", {
|
||||
isDevBuild = isDevBuild,
|
||||
codename = "Epiphany",
|
||||
version = {6, 0, 0, "-rc.4"},
|
||||
version = {6, 1, 0},
|
||||
expectedServerVersionString = "6.0 or newer",
|
||||
protocolVersion = 3,
|
||||
defaultHost = "localhost",
|
||||
|
||||
@@ -52,8 +52,9 @@ local function diff(instanceMap, virtualInstances, rootId)
|
||||
invariant("Cannot diff an instance not present in InstanceMap\nID: {}", id)
|
||||
end
|
||||
|
||||
local changedClassName = nil
|
||||
if virtualInstance.ClassName ~= instance.ClassName then
|
||||
error("unimplemented: support changing ClassName")
|
||||
changedClassName = virtualInstance.ClassName
|
||||
end
|
||||
|
||||
local changedName = nil
|
||||
@@ -89,11 +90,11 @@ local function diff(instanceMap, virtualInstances, rootId)
|
||||
end
|
||||
end
|
||||
|
||||
if changedName ~= nil or not isEmpty(changedProperties) then
|
||||
if changedName ~= nil or changedClassName ~= nil or not isEmpty(changedProperties) then
|
||||
table.insert(patch.updated, {
|
||||
id = id,
|
||||
changedName = changedName,
|
||||
changedClassName = nil,
|
||||
changedClassName = changedClassName,
|
||||
changedProperties = changedProperties,
|
||||
changedMetadata = nil,
|
||||
})
|
||||
|
||||
@@ -9,7 +9,6 @@ use memofs::{IoResultExt, Vfs, VfsEvent};
|
||||
use rbx_dom_weak::{RbxId, RbxValue};
|
||||
|
||||
use crate::{
|
||||
error::ErrorDisplay,
|
||||
message_queue::MessageQueue,
|
||||
snapshot::{
|
||||
apply_patch_set, compute_patch_set, AppliedPatchSet, InstigatingSource, PatchSet, RojoTree,
|
||||
@@ -292,7 +291,7 @@ fn compute_and_apply_changes(tree: &mut RojoTree, vfs: &Vfs, id: RbxId) -> Optio
|
||||
return None;
|
||||
}
|
||||
Err(err) => {
|
||||
log::error!("Snapshot error: {}", ErrorDisplay(err));
|
||||
log::error!("Snapshot error: {:?}", err);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
@@ -313,7 +312,7 @@ fn compute_and_apply_changes(tree: &mut RojoTree, vfs: &Vfs, id: RbxId) -> Optio
|
||||
apply_patch_set(tree, patch_set)
|
||||
}
|
||||
Err(err) => {
|
||||
log::error!("Error processing filesystem change: {}", ErrorDisplay(err));
|
||||
log::error!("Error processing filesystem change: {:?}", err);
|
||||
return None;
|
||||
}
|
||||
},
|
||||
@@ -340,7 +339,7 @@ fn compute_and_apply_changes(tree: &mut RojoTree, vfs: &Vfs, id: RbxId) -> Optio
|
||||
return None;
|
||||
}
|
||||
Err(err) => {
|
||||
log::error!("{}", ErrorDisplay(err));
|
||||
log::error!("{:?}", err);
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -12,6 +12,7 @@ use std::{
|
||||
env,
|
||||
error::Error,
|
||||
fmt,
|
||||
net::IpAddr,
|
||||
path::{Path, PathBuf},
|
||||
str::FromStr,
|
||||
};
|
||||
@@ -186,7 +187,11 @@ pub struct ServeCommand {
|
||||
#[structopt(default_value = "")]
|
||||
pub project: PathBuf,
|
||||
|
||||
/// The port to listen on. Defaults to the project's preference, or 34872 if
|
||||
/// The IP address to listen on. Defaults to `127.0.0.1`.
|
||||
#[structopt(long)]
|
||||
pub address: Option<IpAddr>,
|
||||
|
||||
/// The port to listen on. Defaults to the project's preference, or `34872` if
|
||||
/// it has none.
|
||||
#[structopt(long)]
|
||||
pub port: Option<u16>,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use std::{
|
||||
io::{self, Write},
|
||||
net::IpAddr,
|
||||
net::Ipv4Addr,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
@@ -13,6 +15,7 @@ use crate::{
|
||||
web::LiveServer,
|
||||
};
|
||||
|
||||
const DEFAULT_BIND_ADDRESS: Ipv4Addr = Ipv4Addr::new(127, 0, 0, 1);
|
||||
const DEFAULT_PORT: u16 = 34872;
|
||||
|
||||
pub fn serve(global: GlobalOptions, options: ServeCommand) -> Result<()> {
|
||||
@@ -20,6 +23,8 @@ pub fn serve(global: GlobalOptions, options: ServeCommand) -> Result<()> {
|
||||
|
||||
let session = Arc::new(ServeSession::new(vfs, &options.absolute_project())?);
|
||||
|
||||
let ip = options.address.unwrap_or(DEFAULT_BIND_ADDRESS.into());
|
||||
|
||||
let port = options
|
||||
.port
|
||||
.or_else(|| session.project_port())
|
||||
@@ -27,13 +32,13 @@ pub fn serve(global: GlobalOptions, options: ServeCommand) -> Result<()> {
|
||||
|
||||
let server = LiveServer::new(session);
|
||||
|
||||
let _ = show_start_message(port, global.color.into());
|
||||
server.start(port);
|
||||
let _ = show_start_message(ip, port, global.color.into());
|
||||
server.start((ip, port).into());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn show_start_message(port: u16, color: ColorChoice) -> io::Result<()> {
|
||||
fn show_start_message(bind_address: IpAddr, port: u16, color: ColorChoice) -> io::Result<()> {
|
||||
let writer = BufferWriter::stdout(color);
|
||||
let mut buffer = writer.buffer();
|
||||
|
||||
@@ -41,7 +46,12 @@ fn show_start_message(port: u16, color: ColorChoice) -> io::Result<()> {
|
||||
|
||||
write!(&mut buffer, " Address: ")?;
|
||||
buffer.set_color(ColorSpec::new().set_fg(Some(Color::Green)).set_bold(true))?;
|
||||
writeln!(&mut buffer, "localhost")?;
|
||||
|
||||
if bind_address.is_loopback() {
|
||||
writeln!(&mut buffer, "localhost")?;
|
||||
} else {
|
||||
writeln!(&mut buffer, "{}", bind_address)?;
|
||||
}
|
||||
|
||||
buffer.set_color(&ColorSpec::new())?;
|
||||
write!(&mut buffer, " Port: ")?;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
use memofs::Vfs;
|
||||
use reqwest::header::{ACCEPT, CONTENT_TYPE, COOKIE, USER_AGENT};
|
||||
use reqwest::{
|
||||
header::{ACCEPT, CONTENT_TYPE, COOKIE, USER_AGENT},
|
||||
StatusCode,
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{auth_cookie::get_auth_cookie, cli::UploadCommand, serve_session::ServeSession};
|
||||
@@ -41,25 +44,42 @@ pub fn upload(options: UploadCommand) -> Result<(), anyhow::Error> {
|
||||
.property_behavior(rbx_xml::EncodePropertyBehavior::WriteUnknown);
|
||||
|
||||
rbx_xml::to_writer(&mut buffer, tree.inner(), &encode_ids, config)?;
|
||||
do_upload(buffer, options.asset_id, &cookie)
|
||||
}
|
||||
|
||||
fn do_upload(buffer: Vec<u8>, asset_id: u64, cookie: &str) -> anyhow::Result<()> {
|
||||
let url = format!(
|
||||
"https://data.roblox.com/Data/Upload.ashx?assetid={}",
|
||||
options.asset_id
|
||||
asset_id
|
||||
);
|
||||
|
||||
log::trace!("POSTing to {}", url);
|
||||
let client = reqwest::Client::new();
|
||||
let mut response = client
|
||||
.post(&url)
|
||||
.header(COOKIE, format!(".ROBLOSECURITY={}", &cookie))
|
||||
.header(USER_AGENT, "Roblox/WinInet")
|
||||
.header("Requester", "Client")
|
||||
.header(CONTENT_TYPE, "application/xml")
|
||||
.header(ACCEPT, "application/json")
|
||||
.body(buffer)
|
||||
.send()?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
let build_request = move || {
|
||||
client
|
||||
.post(&url)
|
||||
.header(COOKIE, format!(".ROBLOSECURITY={}", cookie))
|
||||
.header(USER_AGENT, "Roblox/WinInet")
|
||||
.header(CONTENT_TYPE, "application/xml")
|
||||
.header(ACCEPT, "application/json")
|
||||
.body(buffer.clone())
|
||||
};
|
||||
|
||||
log::debug!("Uploading to Roblox...");
|
||||
let mut response = build_request().send()?;
|
||||
|
||||
// Starting in Feburary, 2021, the upload endpoint performs CSRF challenges.
|
||||
// If we receive an HTTP 403 with a X-CSRF-Token reply, we should retry the
|
||||
// request, echoing the value of that header.
|
||||
if response.status() == StatusCode::FORBIDDEN {
|
||||
if let Some(csrf_token) = response.headers().get("X-CSRF-Token") {
|
||||
log::debug!("Received CSRF challenge, retrying with token...");
|
||||
response = build_request().header("X-CSRF-Token", csrf_token).send()?;
|
||||
}
|
||||
}
|
||||
|
||||
let status = response.status();
|
||||
if !status.is_success() {
|
||||
return Err(Error::RobloxApi {
|
||||
body: response.text()?,
|
||||
}
|
||||
|
||||
18
src/error.rs
18
src/error.rs
@@ -1,18 +0,0 @@
|
||||
use std::{error::Error, fmt};
|
||||
|
||||
/// Wrapper type to print errors with source-chasing.
|
||||
pub struct ErrorDisplay<E>(pub E);
|
||||
|
||||
impl<E: Error> fmt::Display for ErrorDisplay<E> {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(formatter, "{}", self.0)?;
|
||||
|
||||
let mut current_err: &dyn Error = &self.0;
|
||||
while let Some(source) = current_err.source() {
|
||||
writeln!(formatter, " caused by {}", source)?;
|
||||
current_err = &*source;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@ mod tree_view;
|
||||
|
||||
mod auth_cookie;
|
||||
mod change_processor;
|
||||
mod error;
|
||||
mod glob;
|
||||
mod lua_ast;
|
||||
mod message_queue;
|
||||
|
||||
@@ -22,7 +22,7 @@ use crate::{
|
||||
apply_patch_set, compute_patch_set, AppliedPatchSet, InstanceContext,
|
||||
InstancePropertiesWithMeta, PatchSet, RojoTree,
|
||||
},
|
||||
snapshot_middleware::{snapshot_from_vfs, SnapshotError},
|
||||
snapshot_middleware::snapshot_from_vfs,
|
||||
};
|
||||
|
||||
/// Contains all of the state for a Rojo serve session.
|
||||
@@ -234,8 +234,8 @@ pub enum ServeSessionError {
|
||||
},
|
||||
|
||||
#[error(transparent)]
|
||||
Snapshot {
|
||||
Other {
|
||||
#[from]
|
||||
source: SnapshotError,
|
||||
source: anyhow::Error,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::{collections::BTreeMap, path::Path};
|
||||
|
||||
use anyhow::Context;
|
||||
use maplit::hashmap;
|
||||
use memofs::{IoResultExt, Vfs};
|
||||
use rbx_dom_weak::RbxValue;
|
||||
@@ -7,9 +8,7 @@ use serde::Serialize;
|
||||
|
||||
use crate::snapshot::{InstanceContext, InstanceMetadata, InstanceSnapshot};
|
||||
|
||||
use super::{
|
||||
error::SnapshotError, meta_file::AdjacentMetadata, middleware::SnapshotInstanceResult,
|
||||
};
|
||||
use super::{meta_file::AdjacentMetadata, middleware::SnapshotInstanceResult};
|
||||
|
||||
pub fn snapshot_csv(
|
||||
_context: &InstanceContext,
|
||||
@@ -20,8 +19,12 @@ pub fn snapshot_csv(
|
||||
let meta_path = path.with_file_name(format!("{}.meta.json", instance_name));
|
||||
let contents = vfs.read(path)?;
|
||||
|
||||
let table_contents = convert_localization_csv(&contents)
|
||||
.map_err(|source| SnapshotError::malformed_l10n_csv(source, path))?;
|
||||
let table_contents = convert_localization_csv(&contents).with_context(|| {
|
||||
format!(
|
||||
"File was not a valid LocalizationTable CSV file: {}",
|
||||
path.display()
|
||||
)
|
||||
})?;
|
||||
|
||||
let mut snapshot = InstanceSnapshot::new()
|
||||
.name(instance_name)
|
||||
|
||||
@@ -4,10 +4,7 @@ use memofs::{DirEntry, IoResultExt, Vfs};
|
||||
|
||||
use crate::snapshot::{InstanceContext, InstanceMetadata, InstanceSnapshot};
|
||||
|
||||
use super::{
|
||||
error::SnapshotError, meta_file::DirectoryMetadata, middleware::SnapshotInstanceResult,
|
||||
snapshot_from_vfs,
|
||||
};
|
||||
use super::{meta_file::DirectoryMetadata, middleware::SnapshotInstanceResult, snapshot_from_vfs};
|
||||
|
||||
pub fn snapshot_dir(context: &InstanceContext, vfs: &Vfs, path: &Path) -> SnapshotInstanceResult {
|
||||
let passes_filter_rules = |child: &DirEntry| {
|
||||
@@ -35,7 +32,7 @@ pub fn snapshot_dir(context: &InstanceContext, vfs: &Vfs, path: &Path) -> Snapsh
|
||||
.file_name()
|
||||
.expect("Could not extract file name")
|
||||
.to_str()
|
||||
.ok_or_else(|| SnapshotError::file_name_bad_unicode(path))?
|
||||
.ok_or_else(|| anyhow::anyhow!("File name was not valid UTF-8: {}", path.display()))?
|
||||
.to_string();
|
||||
|
||||
let meta_path = path.join("init.meta.json");
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
use std::{io, path::PathBuf};
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::project::ProjectError;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum SnapshotError {
|
||||
#[error("file name had malformed Unicode")]
|
||||
FileNameBadUnicode { path: PathBuf },
|
||||
|
||||
#[error("file had malformed Unicode contents at path {}", .path.display())]
|
||||
FileContentsBadUnicode {
|
||||
source: std::str::Utf8Error,
|
||||
path: PathBuf,
|
||||
},
|
||||
|
||||
#[error("malformed project file at path {}", .path.display())]
|
||||
MalformedProject { source: ProjectError, path: PathBuf },
|
||||
|
||||
#[error("malformed .model.json file at path {}", .path.display())]
|
||||
MalformedModelJson {
|
||||
source: serde_json::Error,
|
||||
path: PathBuf,
|
||||
},
|
||||
|
||||
#[error("malformed .meta.json file at path {}", .path.display())]
|
||||
MalformedMetaJson {
|
||||
source: serde_json::Error,
|
||||
path: PathBuf,
|
||||
},
|
||||
|
||||
#[error("malformed JSON at path {}", .path.display())]
|
||||
MalformedJson {
|
||||
source: serde_json::Error,
|
||||
path: PathBuf,
|
||||
},
|
||||
|
||||
#[error("malformed CSV localization data at path {}", .path.display())]
|
||||
MalformedLocalizationCsv { source: csv::Error, path: PathBuf },
|
||||
|
||||
#[error(transparent)]
|
||||
Io {
|
||||
#[from]
|
||||
source: io::Error,
|
||||
},
|
||||
}
|
||||
|
||||
impl SnapshotError {
|
||||
pub(crate) fn file_name_bad_unicode(path: impl Into<PathBuf>) -> Self {
|
||||
Self::FileNameBadUnicode { path: path.into() }
|
||||
}
|
||||
|
||||
pub(crate) fn file_contents_bad_unicode(
|
||||
source: std::str::Utf8Error,
|
||||
path: impl Into<PathBuf>,
|
||||
) -> Self {
|
||||
Self::FileContentsBadUnicode {
|
||||
source,
|
||||
path: path.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn malformed_project(source: ProjectError, path: impl Into<PathBuf>) -> Self {
|
||||
Self::MalformedProject {
|
||||
source,
|
||||
path: path.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn malformed_model_json(
|
||||
source: serde_json::Error,
|
||||
path: impl Into<PathBuf>,
|
||||
) -> Self {
|
||||
Self::MalformedModelJson {
|
||||
source,
|
||||
path: path.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn malformed_meta_json(source: serde_json::Error, path: impl Into<PathBuf>) -> Self {
|
||||
Self::MalformedMetaJson {
|
||||
source,
|
||||
path: path.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn malformed_json(source: serde_json::Error, path: impl Into<PathBuf>) -> Self {
|
||||
Self::MalformedJson {
|
||||
source,
|
||||
path: path.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn malformed_l10n_csv(source: csv::Error, path: impl Into<PathBuf>) -> Self {
|
||||
Self::MalformedLocalizationCsv {
|
||||
source,
|
||||
path: path.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Context;
|
||||
use maplit::hashmap;
|
||||
use memofs::{IoResultExt, Vfs};
|
||||
use rbx_dom_weak::RbxValue;
|
||||
@@ -9,9 +10,7 @@ use crate::{
|
||||
snapshot::{InstanceContext, InstanceMetadata, InstanceSnapshot},
|
||||
};
|
||||
|
||||
use super::{
|
||||
error::SnapshotError, meta_file::AdjacentMetadata, middleware::SnapshotInstanceResult,
|
||||
};
|
||||
use super::{meta_file::AdjacentMetadata, middleware::SnapshotInstanceResult};
|
||||
|
||||
pub fn snapshot_json(
|
||||
context: &InstanceContext,
|
||||
@@ -22,7 +21,7 @@ pub fn snapshot_json(
|
||||
let contents = vfs.read(path)?;
|
||||
|
||||
let value: serde_json::Value = serde_json::from_slice(&contents)
|
||||
.map_err(|err| SnapshotError::malformed_json(err, path))?;
|
||||
.with_context(|| format!("File contains malformed JSON: {}", path.display()))?;
|
||||
|
||||
let as_lua = json_to_lua(value).to_string();
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::{borrow::Cow, collections::HashMap, path::Path};
|
||||
|
||||
use anyhow::Context;
|
||||
use memofs::Vfs;
|
||||
use rbx_dom_weak::UnresolvedRbxValue;
|
||||
use rbx_reflection::try_resolve_value;
|
||||
@@ -7,7 +8,7 @@ use serde::Deserialize;
|
||||
|
||||
use crate::snapshot::{InstanceContext, InstanceSnapshot};
|
||||
|
||||
use super::{error::SnapshotError, middleware::SnapshotInstanceResult};
|
||||
use super::middleware::SnapshotInstanceResult;
|
||||
|
||||
pub fn snapshot_json_model(
|
||||
context: &InstanceContext,
|
||||
@@ -17,7 +18,7 @@ pub fn snapshot_json_model(
|
||||
) -> SnapshotInstanceResult {
|
||||
let contents = vfs.read(path)?;
|
||||
let instance: JsonModel = serde_json::from_slice(&contents)
|
||||
.map_err(|source| SnapshotError::malformed_model_json(source, path))?;
|
||||
.with_context(|| format!("File is not a valid JSON model: {}", path.display()))?;
|
||||
|
||||
let mut snapshot = instance.core.into_snapshot(instance_name.to_owned());
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::{path::Path, str};
|
||||
|
||||
use anyhow::Context;
|
||||
use maplit::hashmap;
|
||||
use memofs::{IoResultExt, Vfs};
|
||||
use rbx_dom_weak::RbxValue;
|
||||
@@ -28,9 +29,8 @@ pub fn snapshot_lua(context: &InstanceContext, vfs: &Vfs, path: &Path) -> Snapsh
|
||||
|
||||
let contents = vfs.read(path)?;
|
||||
let contents_str = str::from_utf8(&contents)
|
||||
// TODO: Turn into error type
|
||||
.expect("File content was not valid UTF-8")
|
||||
.to_string();
|
||||
.with_context(|| format!("File was not valid UTF-8: {}", path.display()))?
|
||||
.to_owned();
|
||||
|
||||
let meta_path = path.with_file_name(format!("{}.meta.json", instance_name));
|
||||
|
||||
@@ -71,10 +71,14 @@ pub fn snapshot_lua_init(
|
||||
let dir_snapshot = snapshot_dir(context, vfs, folder_path)?.unwrap();
|
||||
|
||||
if dir_snapshot.class_name != "Folder" {
|
||||
panic!(
|
||||
anyhow::bail!(
|
||||
"init.lua, init.server.lua, and init.client.lua can \
|
||||
only be used if the instance produced by the parent \
|
||||
directory would be a Folder."
|
||||
only be used if the instance produced by the containing \
|
||||
directory would be a Folder.\n\n\
|
||||
|
||||
The directory {} turned into an instance of class {}.",
|
||||
folder_path.display(),
|
||||
dir_snapshot.class_name
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
use std::{borrow::Cow, collections::HashMap, path::Path};
|
||||
|
||||
use anyhow::Context;
|
||||
use rbx_dom_weak::UnresolvedRbxValue;
|
||||
use rbx_reflection::try_resolve_value;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::snapshot::InstanceSnapshot;
|
||||
|
||||
use super::error::SnapshotError;
|
||||
|
||||
/// Represents metadata in a sibling file with the same basename.
|
||||
///
|
||||
/// As an example, hello.meta.json next to hello.lua would allow assigning
|
||||
@@ -23,9 +22,13 @@ pub struct AdjacentMetadata {
|
||||
}
|
||||
|
||||
impl AdjacentMetadata {
|
||||
pub fn from_slice(slice: &[u8], path: &Path) -> Result<Self, SnapshotError> {
|
||||
serde_json::from_slice(slice)
|
||||
.map_err(|source| SnapshotError::malformed_meta_json(source, path))
|
||||
pub fn from_slice(slice: &[u8], path: &Path) -> anyhow::Result<Self> {
|
||||
serde_json::from_slice(slice).with_context(|| {
|
||||
format!(
|
||||
"File contained malformed .meta.json data: {}",
|
||||
path.display()
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn apply_ignore_unknown_instances(&mut self, snapshot: &mut InstanceSnapshot) {
|
||||
@@ -75,9 +78,13 @@ pub struct DirectoryMetadata {
|
||||
}
|
||||
|
||||
impl DirectoryMetadata {
|
||||
pub fn from_slice(slice: &[u8], path: &Path) -> Result<Self, SnapshotError> {
|
||||
serde_json::from_slice(slice)
|
||||
.map_err(|source| SnapshotError::malformed_meta_json(source, path))
|
||||
pub fn from_slice(slice: &[u8], path: &Path) -> anyhow::Result<Self> {
|
||||
serde_json::from_slice(slice).with_context(|| {
|
||||
format!(
|
||||
"File contained malformed init.meta.json data: {}",
|
||||
path.display()
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn apply_all(&mut self, snapshot: &mut InstanceSnapshot) {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
use crate::snapshot::InstanceSnapshot;
|
||||
|
||||
use super::error::SnapshotError;
|
||||
|
||||
pub type SnapshotInstanceResult = Result<Option<InstanceSnapshot>, SnapshotError>;
|
||||
pub type SnapshotInstanceResult = anyhow::Result<Option<InstanceSnapshot>>;
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
mod csv;
|
||||
mod dir;
|
||||
mod error;
|
||||
mod json;
|
||||
mod json_model;
|
||||
mod lua;
|
||||
@@ -37,7 +36,6 @@ use self::{
|
||||
util::match_file_name,
|
||||
};
|
||||
|
||||
pub use self::error::*;
|
||||
pub use self::project::snapshot_project_node;
|
||||
|
||||
pub fn snapshot_from_vfs(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::{borrow::Cow, collections::HashMap, path::Path};
|
||||
|
||||
use anyhow::Context;
|
||||
use memofs::Vfs;
|
||||
use rbx_reflection::{get_class_descriptor, try_resolve_value};
|
||||
|
||||
@@ -10,7 +11,7 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
use super::{error::SnapshotError, middleware::SnapshotInstanceResult, snapshot_from_vfs};
|
||||
use super::{middleware::SnapshotInstanceResult, snapshot_from_vfs};
|
||||
|
||||
pub fn snapshot_project(
|
||||
context: &InstanceContext,
|
||||
@@ -18,7 +19,7 @@ pub fn snapshot_project(
|
||||
path: &Path,
|
||||
) -> SnapshotInstanceResult {
|
||||
let project = Project::load_from_slice(&vfs.read(path)?, path)
|
||||
.map_err(|err| SnapshotError::malformed_project(err, path))?;
|
||||
.with_context(|| format!("File was not a valid Rojo project: {}", path.display()))?;
|
||||
|
||||
let mut context = context.clone();
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::{collections::HashMap, path::Path};
|
||||
|
||||
use anyhow::Context;
|
||||
use memofs::Vfs;
|
||||
use rbx_dom_weak::{RbxInstanceProperties, RbxTree};
|
||||
|
||||
@@ -21,7 +22,7 @@ pub fn snapshot_rbxm(
|
||||
|
||||
let root_id = temp_tree.get_root_id();
|
||||
rbx_binary::decode(&mut temp_tree, root_id, vfs.read(path)?.as_slice())
|
||||
.expect("TODO: Handle rbx_binary errors");
|
||||
.with_context(|| format!("Malformed rbxm file: {}", path.display()))?;
|
||||
|
||||
let root_instance = temp_tree.get_instance(root_id).unwrap();
|
||||
let children = root_instance.get_children_ids();
|
||||
@@ -38,7 +39,11 @@ pub fn snapshot_rbxm(
|
||||
|
||||
Ok(Some(snapshot))
|
||||
} else {
|
||||
panic!("Rojo doesn't have support for model files with zero or more than one top-level instances yet.");
|
||||
anyhow::bail!(
|
||||
"Rojo doesn't have support for model files with zero or more than one top-level instances yet.\n\n \
|
||||
Check the model file at path {}",
|
||||
path.display()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Context;
|
||||
use memofs::Vfs;
|
||||
|
||||
use crate::snapshot::{InstanceContext, InstanceMetadata, InstanceSnapshot};
|
||||
@@ -16,7 +17,7 @@ pub fn snapshot_rbxmx(
|
||||
.property_behavior(rbx_xml::DecodePropertyBehavior::ReadUnknown);
|
||||
|
||||
let temp_tree = rbx_xml::from_reader(vfs.read(path)?.as_slice(), options)
|
||||
.expect("TODO: Handle rbx_xml errors");
|
||||
.with_context(|| format!("Malformed rbxm file: {}", path.display()))?;
|
||||
|
||||
let root_instance = temp_tree.get_instance(temp_tree.get_root_id()).unwrap();
|
||||
let children = root_instance.get_children_ids();
|
||||
@@ -33,7 +34,11 @@ pub fn snapshot_rbxmx(
|
||||
|
||||
Ok(Some(snapshot))
|
||||
} else {
|
||||
panic!("Rojo doesn't have support for model files with zero or more than one top-level instances yet.");
|
||||
anyhow::bail!(
|
||||
"Rojo doesn't have support for model files with zero or more than one top-level instances yet.\n\n \
|
||||
Check the model file at path {}",
|
||||
path.display()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
use std::{path::Path, str};
|
||||
|
||||
use anyhow::Context;
|
||||
use maplit::hashmap;
|
||||
use memofs::{IoResultExt, Vfs};
|
||||
use rbx_dom_weak::RbxValue;
|
||||
|
||||
use crate::snapshot::{InstanceContext, InstanceMetadata, InstanceSnapshot};
|
||||
|
||||
use super::{
|
||||
error::SnapshotError, meta_file::AdjacentMetadata, middleware::SnapshotInstanceResult,
|
||||
};
|
||||
use super::{meta_file::AdjacentMetadata, middleware::SnapshotInstanceResult};
|
||||
|
||||
pub fn snapshot_txt(
|
||||
context: &InstanceContext,
|
||||
@@ -18,8 +17,8 @@ pub fn snapshot_txt(
|
||||
) -> SnapshotInstanceResult {
|
||||
let contents = vfs.read(path)?;
|
||||
let contents_str = str::from_utf8(&contents)
|
||||
.map_err(|err| SnapshotError::file_contents_bad_unicode(err, path))?
|
||||
.to_string();
|
||||
.with_context(|| format!("File was not valid UTF-8: {}", path.display()))?
|
||||
.to_owned();
|
||||
|
||||
let properties = hashmap! {
|
||||
"Value".to_owned() => RbxValue::String {
|
||||
|
||||
@@ -4,7 +4,7 @@ pub mod interface;
|
||||
mod ui;
|
||||
mod util;
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::{net::SocketAddr, sync::Arc};
|
||||
|
||||
use futures::{
|
||||
future::{self, FutureResult},
|
||||
@@ -57,9 +57,7 @@ impl LiveServer {
|
||||
LiveServer { serve_session }
|
||||
}
|
||||
|
||||
pub fn start(self, port: u16) {
|
||||
let address = ([127, 0, 0, 1], port).into();
|
||||
|
||||
pub fn start(self, address: SocketAddr) {
|
||||
let server = Server::bind(&address)
|
||||
.serve(move || {
|
||||
let service: FutureResult<_, hyper::Error> =
|
||||
|
||||
2
test-projects/script-unwritable/README.md
Normal file
2
test-projects/script-unwritable/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
# script-unwritable
|
||||
This project is used to test that Rojo successfully builds places with properties that are unwritable to scripts, but are ignored when the same project is used for live sync.
|
||||
18
test-projects/script-unwritable/default.project.json
Normal file
18
test-projects/script-unwritable/default.project.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "script-unwritable",
|
||||
"tree": {
|
||||
"$className": "DataModel",
|
||||
|
||||
"HttpService": {
|
||||
"$properties": {
|
||||
"HttpEnabled": true
|
||||
}
|
||||
},
|
||||
|
||||
"StarterPlayer": {
|
||||
"$properties": {
|
||||
"GameSettingsAvatar": "R15"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user