mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-04-25 23:26:19 +00:00
Scaffold out model file support, still needs working decoders
This commit is contained in:
@@ -9,14 +9,14 @@ use std::{
|
|||||||
|
|
||||||
use failure::Fail;
|
use failure::Fail;
|
||||||
|
|
||||||
use rbx_tree::{RbxTree, RbxValue, RbxId};
|
use rbx_tree::{RbxTree, RbxInstance, RbxValue, RbxId};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
project::{Project, ProjectNode, InstanceProjectNodeMetadata},
|
project::{Project, ProjectNode, InstanceProjectNodeMetadata},
|
||||||
message_queue::MessageQueue,
|
message_queue::MessageQueue,
|
||||||
imfs::{Imfs, ImfsItem, ImfsFile},
|
imfs::{Imfs, ImfsItem, ImfsFile},
|
||||||
path_map::PathMap,
|
path_map::PathMap,
|
||||||
rbx_snapshot::{RbxSnapshotInstance, InstanceChanges, reify_root, reconcile_subtree},
|
rbx_snapshot::{RbxSnapshotInstance, InstanceChanges, snapshot_from_tree, reify_root, reconcile_subtree},
|
||||||
};
|
};
|
||||||
|
|
||||||
const INIT_SCRIPT: &str = "init.lua";
|
const INIT_SCRIPT: &str = "init.lua";
|
||||||
@@ -235,6 +235,8 @@ enum FileType {
|
|||||||
ClientScript,
|
ClientScript,
|
||||||
StringValue,
|
StringValue,
|
||||||
LocalizationTable,
|
LocalizationTable,
|
||||||
|
XmlModel,
|
||||||
|
BinaryModel,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_trailing<'a>(input: &'a str, trailer: &str) -> Option<&'a str> {
|
fn get_trailing<'a>(input: &'a str, trailer: &str) -> Option<&'a str> {
|
||||||
@@ -247,21 +249,25 @@ fn get_trailing<'a>(input: &'a str, trailer: &str) -> Option<&'a str> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn classify_file(file: &ImfsFile) -> Option<(&str, FileType)> {
|
fn classify_file(file: &ImfsFile) -> Option<(&str, FileType)> {
|
||||||
|
static EXTENSIONS_TO_TYPES: &[(&str, FileType)] = &[
|
||||||
|
(".server.lua", FileType::ServerScript),
|
||||||
|
(".client.lua", FileType::ClientScript),
|
||||||
|
(".lua", FileType::ModuleScript),
|
||||||
|
(".csv", FileType::LocalizationTable),
|
||||||
|
(".txt", FileType::StringValue),
|
||||||
|
(".rbxmx", FileType::XmlModel),
|
||||||
|
(".rbxm", FileType::BinaryModel),
|
||||||
|
];
|
||||||
|
|
||||||
let file_name = file.path.file_name()?.to_str()?;
|
let file_name = file.path.file_name()?.to_str()?;
|
||||||
|
|
||||||
if let Some(instance_name) = get_trailing(file_name, ".server.lua") {
|
for (extension, file_type) in EXTENSIONS_TO_TYPES {
|
||||||
Some((instance_name, FileType::ServerScript))
|
if let Some(instance_name) = get_trailing(file_name, extension) {
|
||||||
} else if let Some(instance_name) = get_trailing(file_name, ".client.lua") {
|
return Some((instance_name, *file_type))
|
||||||
Some((instance_name, FileType::ClientScript))
|
}
|
||||||
} else if let Some(instance_name) = get_trailing(file_name, ".lua") {
|
|
||||||
Some((instance_name, FileType::ModuleScript))
|
|
||||||
} else if let Some(instance_name) = get_trailing(file_name, ".csv") {
|
|
||||||
Some((instance_name, FileType::LocalizationTable))
|
|
||||||
} else if let Some(instance_name) = get_trailing(file_name, ".txt") {
|
|
||||||
Some((instance_name, FileType::StringValue))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
@@ -307,6 +313,16 @@ enum SnapshotError {
|
|||||||
inner: str::Utf8Error,
|
inner: str::Utf8Error,
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
XmlModelDecodeError {
|
||||||
|
inner: rbx_xml::DecodeError,
|
||||||
|
path: PathBuf,
|
||||||
|
},
|
||||||
|
|
||||||
|
BinaryModelDecodeError {
|
||||||
|
inner: rbx_binary::DecodeError,
|
||||||
|
path: PathBuf,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for SnapshotError {
|
impl fmt::Display for SnapshotError {
|
||||||
@@ -316,10 +332,78 @@ impl fmt::Display for SnapshotError {
|
|||||||
SnapshotError::Utf8Error { inner, path } => {
|
SnapshotError::Utf8Error { inner, path } => {
|
||||||
write!(output, "Invalid UTF-8: {} in path {}", inner, path.display())
|
write!(output, "Invalid UTF-8: {} in path {}", inner, path.display())
|
||||||
},
|
},
|
||||||
|
SnapshotError::XmlModelDecodeError { inner, path } => {
|
||||||
|
write!(output, "Malformed rbxmx model: {:?} in path {}", inner, path.display())
|
||||||
|
},
|
||||||
|
SnapshotError::BinaryModelDecodeError { inner, path } => {
|
||||||
|
write!(output, "Malformed rbxm model: {:?} in path {}", inner, path.display())
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn snapshot_xml_model<'a>(
|
||||||
|
instance_name: Cow<'a, str>,
|
||||||
|
file: &ImfsFile,
|
||||||
|
) -> Result<Option<RbxSnapshotInstance<'a>>, SnapshotError> {
|
||||||
|
let mut temp_tree = RbxTree::new(RbxInstance {
|
||||||
|
name: "Temp".to_owned(),
|
||||||
|
class_name: "Folder".to_owned(),
|
||||||
|
properties: HashMap::new(),
|
||||||
|
});
|
||||||
|
|
||||||
|
let root_id = temp_tree.get_root_id();
|
||||||
|
rbx_xml::decode(&mut temp_tree, root_id, file.contents.as_slice())
|
||||||
|
.map_err(|inner| SnapshotError::XmlModelDecodeError {
|
||||||
|
inner,
|
||||||
|
path: file.path.clone(),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let root_instance = temp_tree.get_instance(root_id).unwrap();
|
||||||
|
let children = root_instance.get_children_ids();
|
||||||
|
|
||||||
|
match children.len() {
|
||||||
|
0 => Ok(None),
|
||||||
|
1 => {
|
||||||
|
let mut snapshot = snapshot_from_tree(&temp_tree, children[0]).unwrap();
|
||||||
|
snapshot.name = instance_name;
|
||||||
|
Ok(Some(snapshot))
|
||||||
|
},
|
||||||
|
_ => panic!("Rojo doesn't have support for model files with multiple roots yet"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn snapshot_binary_model<'a>(
|
||||||
|
instance_name: Cow<'a, str>,
|
||||||
|
file: &ImfsFile,
|
||||||
|
) -> Result<Option<RbxSnapshotInstance<'a>>, SnapshotError> {
|
||||||
|
let mut temp_tree = RbxTree::new(RbxInstance {
|
||||||
|
name: "Temp".to_owned(),
|
||||||
|
class_name: "Folder".to_owned(),
|
||||||
|
properties: HashMap::new(),
|
||||||
|
});
|
||||||
|
|
||||||
|
let root_id = temp_tree.get_root_id();
|
||||||
|
rbx_binary::decode(&mut temp_tree, root_id, file.contents.as_slice())
|
||||||
|
.map_err(|inner| SnapshotError::BinaryModelDecodeError {
|
||||||
|
inner,
|
||||||
|
path: file.path.clone(),
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let root_instance = temp_tree.get_instance(root_id).unwrap();
|
||||||
|
let children = root_instance.get_children_ids();
|
||||||
|
|
||||||
|
match children.len() {
|
||||||
|
0 => Ok(None),
|
||||||
|
1 => {
|
||||||
|
let mut snapshot = snapshot_from_tree(&temp_tree, children[0]).unwrap();
|
||||||
|
snapshot.name = instance_name;
|
||||||
|
Ok(Some(snapshot))
|
||||||
|
},
|
||||||
|
_ => panic!("Rojo doesn't have support for model files with multiple roots yet"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn snapshot_instances_from_imfs<'a>(
|
fn snapshot_instances_from_imfs<'a>(
|
||||||
imfs: &'a Imfs,
|
imfs: &'a Imfs,
|
||||||
imfs_path: &Path,
|
imfs_path: &Path,
|
||||||
@@ -344,6 +428,8 @@ fn snapshot_instances_from_imfs<'a>(
|
|||||||
FileType::ClientScript => "LocalScript",
|
FileType::ClientScript => "LocalScript",
|
||||||
FileType::StringValue => "StringValue",
|
FileType::StringValue => "StringValue",
|
||||||
FileType::LocalizationTable => "LocalizationTable",
|
FileType::LocalizationTable => "LocalizationTable",
|
||||||
|
FileType::XmlModel => return snapshot_xml_model(instance_name, file),
|
||||||
|
FileType::BinaryModel => return snapshot_binary_model(instance_name, file),
|
||||||
};
|
};
|
||||||
|
|
||||||
let contents = str::from_utf8(&file.contents)
|
let contents = str::from_utf8(&file.contents)
|
||||||
@@ -379,6 +465,7 @@ fn snapshot_instances_from_imfs<'a>(
|
|||||||
value: table_contents,
|
value: table_contents,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
FileType::XmlModel | FileType::BinaryModel => unreachable!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Some(RbxSnapshotInstance {
|
Ok(Some(RbxSnapshotInstance {
|
||||||
|
|||||||
@@ -65,6 +65,24 @@ pub struct RbxSnapshotInstance<'a> {
|
|||||||
pub metadata: Option<InstanceProjectNodeMetadata>,
|
pub metadata: Option<InstanceProjectNodeMetadata>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn snapshot_from_tree(tree: &RbxTree, id: RbxId) -> Option<RbxSnapshotInstance<'static>> {
|
||||||
|
let instance = tree.get_instance(id)?;
|
||||||
|
|
||||||
|
let mut children = Vec::new();
|
||||||
|
for &child_id in instance.get_children_ids() {
|
||||||
|
children.push(snapshot_from_tree(tree, child_id)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(RbxSnapshotInstance {
|
||||||
|
name: Cow::Owned(instance.name.to_owned()),
|
||||||
|
class_name: Cow::Owned(instance.class_name.to_owned()),
|
||||||
|
properties: instance.properties.clone(),
|
||||||
|
children,
|
||||||
|
source_path: None,
|
||||||
|
metadata: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn reify_root(
|
pub fn reify_root(
|
||||||
snapshot: &RbxSnapshotInstance,
|
snapshot: &RbxSnapshotInstance,
|
||||||
path_map: &mut PathMap<RbxId>,
|
path_map: &mut PathMap<RbxId>,
|
||||||
|
|||||||
6
test-projects/composing-models/roblox-project.json
Normal file
6
test-projects/composing-models/roblox-project.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "composing-models",
|
||||||
|
"tree": {
|
||||||
|
"$path": "src"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
test-projects/composing-models/src/Binary.rbxm
Normal file
BIN
test-projects/composing-models/src/Binary.rbxm
Normal file
Binary file not shown.
16
test-projects/composing-models/src/XML.rbxmx
Normal file
16
test-projects/composing-models/src/XML.rbxmx
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<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="Script" referent="RBX634A9A9988354E4B9D971B2A4DEBD26E">
|
||||||
|
<Properties>
|
||||||
|
<bool name="Disabled">false</bool>
|
||||||
|
<Content name="LinkedSource"><null></null></Content>
|
||||||
|
<string name="Name">Lone Script</string>
|
||||||
|
<string name="ScriptGuid">{C62CD9FB-FF28-4FD9-9712-AD28A1E92C84}</string>
|
||||||
|
<ProtectedString name="Source"><![CDATA[print("Hello world!")
|
||||||
|
]]></ProtectedString>
|
||||||
|
<BinaryString name="Tags"></BinaryString>
|
||||||
|
</Properties>
|
||||||
|
</Item>
|
||||||
|
</roblox>
|
||||||
Reference in New Issue
Block a user