use std::error::Error;
use crate::{
pending::BrPendingFs,
schema::{ReadBrdbSchema, as_brdb::AsBrdbValue},
wrapper::{
Brick, Entity, World,
schemas::{
BRICK_CHUNK_INDEX_SOA, BRICK_CHUNK_SOA, BRICK_COMPONENT_SOA, BRICK_WIRE_SOA,
ENTITY_CHUNK_SOA, GLOBAL_DATA_SOA, OWNER_TABLE_SOA,
},
},
};
#[test]
fn test_brick_write() -> Result<(), Box<dyn Error>> {
let mut world = World::new();
world.register_all_components();
world.add_brick(Brick {
position: (0, 0, 3).into(),
color: (255, 0, 0).into(),
..Default::default()
});
world.add_brick_grid(
Entity {
frozen: true,
..Default::default()
},
[Brick {
position: (0, 0, 3).into(),
color: (255, 0, 0).into(),
..Default::default()
}],
);
let pending = world.to_unsaved()?.to_pending()?;
let root = pending.to_root().unwrap();
let world_dir = 'world: {
for (name, root_dir) in root {
let children = root_dir.to_folder().unwrap();
match name.as_str() {
"Meta" => {
children.into_iter().for_each(|(n, _)| match n.as_str() {
"World.json" | "Bundle.json" | "Screenshot.jpg" | "Thumbnail.png" => {}
other => panic!("unknown Meta/{other}"),
});
continue;
}
"World" => {
assert_eq!(children.len(), 1);
break 'world children.into_iter().next().unwrap().1.to_folder().unwrap();
}
other => panic!("unknown {other}"),
};
}
unreachable!()
};
let mut owners_schema = None;
let mut owners_vec = None;
let mut global_data_schema = None;
let mut global_data_vec = None;
let mut bricks_dir = None;
let mut entities_dir = None;
for (n, d) in world_dir {
match (n.as_str(), d) {
("Owners.schema", BrPendingFs::File(Some(data))) => {
owners_schema = Some(data.as_slice().read_brdb_schema()?);
}
("Owners.mps", BrPendingFs::File(data)) => {
owners_vec = data;
}
("GlobalData.schema", BrPendingFs::File(Some(data))) => {
global_data_schema = Some(data.as_slice().read_brdb_schema()?);
}
("GlobalData.mps", BrPendingFs::File(data)) => {
global_data_vec = data;
}
("Bricks", BrPendingFs::Folder(items)) => {
bricks_dir = items;
}
("Entities", BrPendingFs::Folder(items)) => {
entities_dir = items;
}
(name, BrPendingFs::File(_)) => unreachable!("{name}: no more files"),
(name, BrPendingFs::Folder(_)) => unreachable!("{name}: no more folders"),
(name, BrPendingFs::Root(_)) => unreachable!("{name}: no root"),
}
}
let global_data = global_data_vec
.unwrap()
.as_slice()
.read_brdb(global_data_schema.as_ref().unwrap(), GLOBAL_DATA_SOA)?;
let _owners = owners_vec
.unwrap()
.as_slice()
.read_brdb(&owners_schema.unwrap(), OWNER_TABLE_SOA)?;
let mut brick_index_schema = None;
let mut brick_schema = None;
let mut component_schema = None;
let mut wire_schema = None;
let mut brick_grids = None;
for (n, fs) in bricks_dir.unwrap() {
match (n.as_str(), fs) {
("Grids", BrPendingFs::Folder(items)) => {
brick_grids = items;
}
("ChunkIndexShared.schema", BrPendingFs::File(Some(data))) => {
brick_index_schema = Some(data.as_slice().read_brdb_schema()?);
}
("ChunksShared.schema", BrPendingFs::File(Some(data))) => {
brick_schema = Some(data.as_slice().read_brdb_schema()?);
}
("ComponentsShared.schema", BrPendingFs::File(Some(data))) => {
component_schema = Some(data.as_slice().read_brdb_schema()?);
}
("WiresShared.schema", BrPendingFs::File(Some(data))) => {
wire_schema = Some(data.as_slice().read_brdb_schema()?);
}
(other, f) => unreachable!("unknown Bricks/{other}: {f}"),
}
}
let component_schema = component_schema.as_ref().unwrap();
for (grid_id, grid) in brick_grids.unwrap() {
let children = grid.to_folder().unwrap();
for (n, child) in children {
match (n.as_str(), child) {
("Chunks", BrPendingFs::Folder(Some(chunks))) => {
for (_, c) in chunks {
let _chunk = c
.to_file()
.unwrap()
.as_slice()
.read_brdb(brick_schema.as_ref().unwrap(), BRICK_CHUNK_SOA)?;
}
}
("Components", BrPendingFs::Folder(Some(chunks))) => {
for (_, c) in chunks {
let content = c.to_file().unwrap();
let buf = &mut content.as_slice();
let chunk = buf.read_brdb(&component_schema, BRICK_COMPONENT_SOA)?;
let type_counters = chunk.prop("ComponentTypeCounters")?.as_array()?;
for counter in type_counters {
let type_idx = counter.as_struct()?.prop("TypeIndex")?.as_brdb_u32()?;
let num_instances =
counter.as_struct()?.prop("NumInstances")?.as_brdb_u32()?;
let struct_name = global_data
.prop("ComponentDataStructNames")?
.index(type_idx as usize)?
.map(|s| s.as_str())
.transpose()?
.unwrap_or("illegal")
.to_owned();
if struct_name == "None" {
continue;
}
for _ in 0..num_instances {
let _component = buf.read_brdb(&component_schema, &struct_name)?;
}
}
}
}
("Wires", BrPendingFs::Folder(Some(chunks))) => {
for (_, c) in chunks {
let _chunk = c
.to_file()
.unwrap()
.as_slice()
.read_brdb(wire_schema.as_ref().unwrap(), BRICK_WIRE_SOA)?;
}
}
("ChunkIndex.mps", BrPendingFs::File(data)) => {
let _chunk_index = data
.unwrap()
.as_slice()
.read_brdb(brick_index_schema.as_ref().unwrap(), BRICK_CHUNK_INDEX_SOA)?;
}
(n, other) => unreachable!("unknown Grids/{grid_id}/{n}: {other}"),
}
}
}
let mut _entity_index_schema = None;
let mut _entity_index_vec = None;
let mut entity_schema = None;
let mut entity_chunks = None;
for (n, fs) in entities_dir.unwrap() {
match (n.as_str(), fs) {
("Chunks", BrPendingFs::Folder(items)) => {
entity_chunks = items;
}
("ChunksShared.schema", BrPendingFs::File(data)) => {
entity_schema = Some(data.unwrap().as_slice().read_brdb_schema()?);
}
("ChunkIndex.schema", BrPendingFs::File(data)) => {
_entity_index_schema = Some(data.unwrap().as_slice().read_brdb_schema()?);
}
("ChunkIndex.mps", BrPendingFs::File(data)) => {
_entity_index_vec = data;
}
(n, other) => unreachable!("unknown Entities/{n}: {other}"),
}
}
for (_chunk_id, chunk) in entity_chunks.unwrap() {
let content = chunk.to_file().unwrap();
let buf = &mut content.as_slice();
let _chunk_data = buf.read_brdb(entity_schema.as_ref().unwrap(), ENTITY_CHUNK_SOA)?;
}
Ok(())
}
#[test]
fn test_world_embeds_prefabs_in_pending_fs() -> Result<(), Box<dyn Error>> {
let mut inner = World::new();
inner.bricks.push(Brick::default());
inner.make_prefab();
let inner_bytes = inner.to_brz_vec()?;
let expected_hash = blake3::hash(&inner_bytes).to_hex().to_string().to_uppercase();
let mut outer = World::new();
outer.bricks.push(Brick::default());
let path = outer.add_prefab(inner_bytes.clone());
assert_eq!(path, format!("Prefabs/Uploads/{expected_hash}.brz"));
outer.make_prefab();
outer.meta.screenshot = Some(vec![4, 5, 6]);
outer.meta.thumbnail = Some(vec![1, 2, 3]);
let pending = outer.to_unsaved()?.to_pending()?;
let root = pending.to_root().unwrap();
let names: Vec<&str> = root.iter().map(|(n, _)| n.as_str()).collect();
assert_eq!(names, ["Meta", "World", "Prefabs"]);
let meta = root[0].1.clone().to_folder().unwrap();
let meta_names: Vec<&str> = meta.iter().map(|(n, _)| n.as_str()).collect();
assert_eq!(
meta_names,
["Bundle.json", "Prefab.json", "Screenshot.jpg", "Thumbnail.png"]
);
assert_eq!(meta[2].1.clone().to_file().unwrap(), vec![4, 5, 6]);
assert_eq!(meta[3].1.clone().to_file().unwrap(), vec![1, 2, 3]);
let prefabs = root[2].1.clone().to_folder().unwrap();
assert_eq!(prefabs[0].0, "Uploads");
let uploads = prefabs[0].1.clone().to_folder().unwrap();
assert_eq!(uploads[0].0, format!("{expected_hash}.brz"));
assert_eq!(uploads[0].1.clone().to_file().unwrap(), inner_bytes);
Ok(())
}
#[test]
fn test_world_without_prefabs_has_no_prefabs_folder() -> Result<(), Box<dyn Error>> {
let mut world = World::new();
world.bricks.push(Brick::default());
let pending = world.to_unsaved()?.to_pending()?;
let root = pending.to_root().unwrap();
assert!(root.iter().all(|(n, _)| n != "Prefabs"));
Ok(())
}