pub struct BrReader<T> { /* private fields */ }Implementations§
Source§impl<T> BrReader<T>
impl<T> BrReader<T>
pub fn new(brdb: T) -> Selfwhere
T: BrFsReader,
Sourcepub fn to_pending(&self) -> Result<BrPendingFs, BrFsError>where
T: BrFsReader,
pub fn to_pending(&self) -> Result<BrPendingFs, BrFsError>where
T: BrFsReader,
Convert this filesystem to a pending filesystem with all files present
Examples found in repository?
7fn main() -> Result<(), Box<dyn std::error::Error>> {
8 let src = PathBuf::from("world.brdb");
9 let dst = PathBuf::from("world_patched.brz");
10
11 assert!(src.exists());
12
13 let db = Brdb::open(src)?.into_reader();
14
15 let owners = db.owners_soa()?;
16
17 // Parse the owners from BrdbValues
18 let mut new_soa = OwnerTableSoA::try_from(&owners.to_value())?;
19
20 // Modify the owner ids
21 new_soa
22 .display_names
23 .iter_mut()
24 .for_each(|id| *id = "PUBLIC".to_owned());
25 new_soa
26 .user_names
27 .iter_mut()
28 .for_each(|id| *id = "PUBLIC".to_owned());
29 new_soa
30 .user_ids
31 .iter_mut()
32 .for_each(|id| *id = Guid::default());
33
34 // convert the owners struct of arrays into bytes using the owners schema
35 let content = db.owners_schema()?.write_brdb(OWNER_TABLE_SOA, &new_soa)?;
36
37 let patch = BrPendingFs::Root(vec![(
38 "World".to_owned(),
39 BrPendingFs::Folder(Some(vec![(
40 "0".to_string(),
41 BrPendingFs::Folder(Some(vec![(
42 "Owners.mps".to_string(),
43 BrPendingFs::File(Some(content)),
44 )])),
45 )])),
46 )]);
47
48 // use .to_pending_patch() if you want to update the same world
49 let pending = db.to_pending()?.with_patch(patch)?;
50 if dst.exists() {
51 std::fs::remove_file(&dst)?;
52 }
53 Brz::write_pending(&dst, pending)?;
54
55 println!("{}", Brz::open(&dst)?.into_reader().owners_soa()?);
56
57 Ok(())
58}More examples
5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 let path = PathBuf::from("world.brdb");
7 let dst = PathBuf::from("world_patched.brdb");
8
9 println!("Warning - This code will break if the brick chunk struct changes!!");
10
11 let db = Brdb::open(path)?.into_reader();
12
13 let data = db.global_data()?;
14 let mut grid = UnsavedGrid::default();
15
16 let mut total_bricks = 0;
17 for chunk in db.brick_chunk_index(1)? {
18 for brick in db
19 .brick_chunk_soa(1, chunk.index)?
20 .iter_bricks(chunk.index, data.clone())
21 {
22 // If we wanted wires/components, we'd need to track the bricks here by their chunk index and brick index
23 total_bricks += 1;
24
25 let mut brick = brick?;
26 brick.position += Position::new(3000, 0, 0);
27 grid.add_brick(data.as_ref(), &brick);
28 }
29
30 if chunk.num_components > 0 {
31 println!("sorry, this example doesn't handle components");
32 }
33 if chunk.num_wires > 0 {
34 println!("sorry, this example doesn't handle wires");
35 }
36 }
37 println!("{total_bricks} bricks");
38
39 let mut pending = db.to_pending()?;
40
41 // Replace the main grid (1) with the grid we created
42 *pending.cd_mut("World/0/Bricks/Grids/1")? = grid.to_pending(
43 data.proc_brick_starting_index(),
44 db.components_schema()?.as_ref(),
45 )?;
46
47 if dst.exists() {
48 std::fs::remove_file(&dst)?;
49 }
50 Brdb::new(&dst)?.write_pending("Move the bricks", pending)?;
51
52 // Verify bricks can be read
53 let db = Brdb::open(dst)?.into_reader();
54 for chunk in db.brick_chunk_index(1)? {
55 let _ = db.brick_chunk_soa(1, chunk.index)?;
56 }
57
58 Ok(())
59}5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 let src = PathBuf::from("world.brdb");
7 let dst = PathBuf::from("world_patched.brdb");
8
9 assert!(src.exists());
10
11 let db = Brdb::open(src)?.into_reader();
12
13 let chunks = db.entity_chunk_index()?;
14 let entity_schema = db.entities_schema()?;
15 let global_data = db.global_data()?;
16 let mut chunk_files = vec![];
17
18 for index in chunks {
19 // Entity_chunk loads entities and their entity data
20 let entities = db.entity_chunk(index)?;
21
22 // Re-assemble the soa. using add_entity ensures the extra data is correctly handled
23 let mut soa = EntityChunkSoA::default();
24 for mut e in entities.into_iter() {
25 e.frozen = true;
26 soa.add_entity(&global_data, &e, e.id.unwrap() as u32);
27 }
28
29 chunk_files.push((
30 format!("{index}.mps"),
31 // EntityChunkSoA::to_bytes ensures the extra data is written after the SoA data
32 BrPendingFs::File(Some(soa.to_bytes(&entity_schema)?)),
33 ));
34 }
35
36 let patch = BrPendingFs::Root(vec![(
37 "World".to_owned(),
38 BrPendingFs::Folder(Some(vec![(
39 "0".to_string(),
40 BrPendingFs::Folder(Some(vec![(
41 "Entities".to_string(),
42 BrPendingFs::Folder(Some(vec![(
43 "Chunks".to_string(),
44 BrPendingFs::Folder(Some(chunk_files)),
45 )])),
46 )])),
47 )])),
48 )]);
49
50 // Use .to_pending_patch() if you want to update the same world
51 let pending = db.to_pending()?.with_patch(patch)?;
52 if dst.exists() {
53 std::fs::remove_file(&dst)?;
54 }
55 Brdb::new(&dst)?.write_pending("Freeze Entities", pending)?;
56
57 // Ensure entities can be read
58 let db = Brdb::open(dst)?.into_reader();
59 let chunks = db.entity_chunk_index()?;
60 for index in chunks {
61 let _ = db.entity_chunk(index)?;
62 }
63
64 Ok(())
65}5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 let src = PathBuf::from("world.brdb");
7 let dst = PathBuf::from("world_patched.brdb");
8
9 assert!(src.exists());
10
11 let db = Brdb::open(src)?.into_reader();
12
13 let mut grid_ids = vec![1];
14
15 // Iterate all entity chunks to find dynamic brick grids...
16 // This could totally be a helper function
17 for index in db.entity_chunk_index()? {
18 for e in db.entity_chunk(index)? {
19 // Ensure the chunk is a dynamic brick grid
20 if !e.is_brick_grid() {
21 continue;
22 }
23 let Some(id) = e.id else {
24 continue;
25 };
26 grid_ids.push(id);
27 }
28 }
29
30 let component_schema = db.components_schema()?;
31 let mut grids_files = vec![];
32
33 // Iterate all grids (there can be bricks on entities)
34 for grid in &grid_ids {
35 let chunks = db.brick_chunk_index(*grid)?;
36 let mut chunk_files = vec![];
37 let mut num_grid_modified = 0;
38
39 // Iterate all chunks in the grid
40 for index in chunks {
41 let mut num_chunk_modified = 0;
42 if index.num_components == 0 {
43 println!("ignoring grid {grid} chunk {} with no components", *index);
44 continue;
45 }
46
47 // Iterate all the components in the chunk
48 let (mut soa, components) = db.component_chunk(*grid, *index)?;
49 for mut s in components {
50 // Disable the shadow casting property if it's present and true
51 if s.prop("bCastShadows")
52 .is_ok_and(|v| v.as_brdb_bool().unwrap_or_default())
53 {
54 println!(
55 "grid {grid} chunk {} mutating component {}",
56 *index,
57 s.get_name()
58 );
59 s.set_prop("bCastShadows", BrdbValue::Bool(false))?;
60 num_grid_modified += 1;
61 num_chunk_modified += 1;
62 }
63
64 soa.unwritten_struct_data.push(Box::new(s));
65 }
66
67 if num_chunk_modified == 0 {
68 continue;
69 }
70
71 chunk_files.push((
72 format!("{}.mps", *index),
73 // ComponentChunkSoA::to_bytes ensures the extra data is written after the SoA data
74 BrPendingFs::File(Some(soa.to_bytes(&component_schema)?)),
75 ));
76 }
77
78 if num_grid_modified == 0 {
79 println!("grid {grid} has no shadow-casting components, skipping");
80 continue;
81 } else {
82 println!(
83 "grid {grid} has {num_grid_modified} shadow-casting components in {} files",
84 chunk_files.len()
85 );
86 }
87
88 grids_files.push((
89 grid.to_string(),
90 BrPendingFs::Folder(Some(vec![(
91 "Components".to_string(),
92 BrPendingFs::Folder(Some(chunk_files)),
93 )])),
94 ))
95 }
96
97 let patch = BrPendingFs::Root(vec![(
98 "World".to_owned(),
99 BrPendingFs::Folder(Some(vec![(
100 "0".to_string(),
101 BrPendingFs::Folder(Some(vec![(
102 "Bricks".to_string(),
103 BrPendingFs::Folder(Some(vec![(
104 "Grids".to_string(),
105 BrPendingFs::Folder(Some(grids_files)),
106 )])),
107 )])),
108 )])),
109 )]);
110
111 // Use .to_pending_patch() if you want to update the same world
112 let pending = db.to_pending()?.with_patch(patch)?;
113 if dst.exists() {
114 std::fs::remove_file(&dst)?;
115 }
116 Brdb::new(&dst)?.write_pending("Disable Shadow Casting", pending)?;
117
118 // Ensure all the components can be read
119 let db = Brdb::open(dst)?.into_reader();
120 for grid in grid_ids {
121 let chunks = db.brick_chunk_index(grid)?;
122 for index in chunks {
123 if index.num_components == 0 {
124 continue;
125 }
126 let (_soa, _components) = db.component_chunk(grid, *index)?;
127 }
128 }
129
130 Ok(())
131}Sourcepub fn to_pending_patch(&self) -> Result<BrPendingFs, BrFsError>where
T: BrFsReader,
pub fn to_pending_patch(&self) -> Result<BrPendingFs, BrFsError>where
T: BrFsReader,
Convert this filesystem to a pending filesystem all files in Patch mode (None for unchanged)
Examples found in repository?
6fn main() -> Result<(), Box<dyn std::error::Error>> {
7 let meta_dst = PathBuf::from("dst.brdb");
8 let meta_src = PathBuf::from("src.brdb");
9
10 let src_f = Brdb::open(meta_src)?.into_reader();
11 let dst_f = Brdb::open(meta_dst)?.into_reader();
12
13 println!(
14 "replacing: {}",
15 String::from_utf8(dst_f.read_file("Meta/Bundle.json")?).unwrap()
16 );
17 println!(
18 "with: {}",
19 String::from_utf8(src_f.read_file("Meta/Bundle.json")?).unwrap()
20 );
21
22 let patch = BrPendingFs::Root(vec![(
23 "Meta".to_owned(),
24 BrPendingFs::Folder(Some(vec![(
25 "Bundle.json".to_string(),
26 BrPendingFs::File(Some(src_f.read_file("Meta/Bundle.json")?)),
27 )])),
28 )]);
29
30 dst_f.write_pending(
31 "Replace Bundle",
32 dst_f.to_pending_patch()?.with_patch(patch)?,
33 )?;
34
35 Ok(())
36}More examples
6fn main() -> Result<(), Box<dyn std::error::Error>> {
7 let mut args = std::env::args().into_iter().peekable();
8 let cmd = args.next().unwrap();
9 if !args.peek().is_some() {
10 println!("usage: {cmd} show world.brdb");
11 println!("usage: {cmd} apply world.brdb owners.csv");
12 println!(
13 "owners.csv must be `display_name,user_name,user_id,old_user_id` where old_user_id is the one to replace."
14 );
15 process::exit(0);
16 }
17
18 let command = args.next().unwrap();
19 if command != "show" && command != "apply" {
20 eprintln!("unknown command. expected `show` or `apply`");
21 process::exit(1);
22 }
23
24 let Some(file) = args.next() else {
25 eprintln!("missing world file arg");
26 process::exit(1);
27 };
28
29 let dst = PathBuf::from(&file);
30 if !dst.exists() {
31 eprintln!("file {file} does not exist");
32 process::exit(1);
33 }
34
35 let db = Brdb::open(dst)?.into_reader();
36
37 let owners = db.owners_soa()?;
38
39 if command == "show" {
40 if args.peek().is_some() {
41 eprintln!("too many arguments!");
42 process::exit(1);
43 }
44
45 let owners_csv = owners
46 .prop("DisplayNames")?
47 .as_array()?
48 .iter()
49 .zip(owners.prop("UserNames")?.as_array()?.iter())
50 .zip(owners.prop("UserIds")?.as_array()?.iter())
51 .map(|((display_name, user_name), user_id)| {
52 format!(
53 "{},{},{}",
54 display_name.as_str().unwrap(),
55 user_name.as_str().unwrap(),
56 Guid::try_from(user_id).unwrap().uuid(),
57 )
58 })
59 .collect::<Vec<_>>();
60 println!("display_name,user_name,user_id\n{}", owners_csv.join("\n"));
61 } else if command == "apply" {
62 let Some(apply_file) = args.next() else {
63 eprintln!("missing owners csv file arg");
64 process::exit(1);
65 };
66 if args.peek().is_some() {
67 eprintln!("too many arguments!");
68 process::exit(1);
69 }
70
71 let apply_path = PathBuf::from(&apply_file);
72 if !apply_path.exists() {
73 eprintln!("file {apply_file} does not exist");
74 process::exit(1);
75 }
76
77 let mut display_name_index = None;
78 let mut user_name_index = None;
79 let mut user_id_index = None;
80 let mut old_user_id_index = None;
81 let mut apply_data = String::new();
82 File::open(apply_path)?.read_to_string(&mut apply_data)?;
83 let Some((header, rows)) = apply_data.split_once("\n") else {
84 eprintln!("file {apply_file} does not have any rows");
85 process::exit(1);
86 };
87 for (i, key) in header.split(",").enumerate() {
88 match key.trim().to_ascii_lowercase().as_ref() {
89 "display_name" => {
90 display_name_index = Some(i);
91 }
92 "user_name" => {
93 user_name_index = Some(i);
94 }
95 "user_id" => {
96 user_id_index = Some(i);
97 }
98 "old_user_id" => old_user_id_index = Some(i),
99 other => {
100 eprintln!("unknown column {other} in {apply_file}");
101 process::exit(1);
102 }
103 }
104 }
105
106 let missing = [
107 ("display_name", display_name_index.is_none()),
108 ("user_name", user_name_index.is_none()),
109 ("user_id", user_id_index.is_none()),
110 ("old_user_id", old_user_id_index.is_none()),
111 ]
112 .into_iter()
113 .filter_map(|(k, cond)| cond.then_some(k.to_owned()))
114 .collect::<Vec<_>>();
115 if !missing.is_empty() {
116 eprintln!("missing columns: {}", missing.join(","));
117 process::exit(1);
118 }
119
120 let display_name_index = display_name_index.unwrap();
121 let user_name_index = user_name_index.unwrap();
122 let user_id_index = user_id_index.unwrap();
123 let old_user_id_index = old_user_id_index.unwrap();
124
125 // Parse the owners from BrdbValues
126 let mut new_soa = OwnerTableSoA::try_from(&owners.to_value())?;
127 let owners_lut = rows
128 .trim()
129 .split("\n")
130 .map(|r| r.trim().split(",").collect::<Vec<&str>>())
131 .map(|cols| {
132 let user_name = cols[user_name_index];
133 let display_name = cols[display_name_index];
134 let user_id = Uuid::parse_str(&cols[user_id_index])
135 .expect(&format!("invalid uuid: {}", cols[user_id_index]));
136 let old_user_id = Uuid::parse_str(&cols[old_user_id_index])
137 .expect(&format!("invalid old uuid: {}", cols[old_user_id_index]));
138 (old_user_id, (user_name, display_name, user_id))
139 })
140 .collect::<HashMap<_, _>>();
141 println!("{owners_lut:?}");
142
143 let mut changes = 0;
144
145 for i in 0..new_soa.user_ids.len() {
146 let old_id = new_soa.user_ids[i].uuid();
147 let Some((user_name, display_name, user_id)) = owners_lut.get(&old_id) else {
148 println!("missing old id for {old_id} - ignoring");
149 continue;
150 };
151 println!("replacing {old_id} with {user_id} - {user_name} ({display_name})");
152 new_soa.user_names[i] = (*user_name).to_owned();
153 new_soa.display_names[i] = (*display_name).to_owned();
154 new_soa.user_ids[i] = Guid::from_uuid((*user_id).clone());
155 changes += 1;
156 }
157
158 if changes == 0 {
159 println!("world left unchanged");
160 std::process::exit(0);
161 }
162
163 // convert the owners struct of arrays into bytes using the owners schema
164 let content = db.owners_schema()?.write_brdb(OWNER_TABLE_SOA, &new_soa)?;
165
166 let patch = BrPendingFs::Root(vec![(
167 "World".to_owned(),
168 BrPendingFs::Folder(Some(vec![(
169 "0".to_string(),
170 BrPendingFs::Folder(Some(vec![(
171 "Owners.mps".to_string(),
172 BrPendingFs::File(Some(content)),
173 )])),
174 )])),
175 )]);
176 db.write_pending("Replace owners", db.to_pending_patch()?.with_patch(patch)?)?;
177 println!("revision created")
178 }
179
180 Ok(())
181}Sourcepub fn bundle_json(&self) -> Result<BundleJson, BrError>where
T: BrFsReader,
pub fn bundle_json(&self) -> Result<BundleJson, BrError>where
T: BrFsReader,
Parse Meta/Bundle.json (present in every bundle).
Sourcepub fn world_json(&self) -> Result<Option<WorldJson>, BrError>where
T: BrFsReader,
pub fn world_json(&self) -> Result<Option<WorldJson>, BrError>where
T: BrFsReader,
Parse Meta/World.json; None for prefab bundles.
Sourcepub fn prefab_json(&self) -> Result<Option<PrefabJson>, BrError>where
T: BrFsReader,
pub fn prefab_json(&self) -> Result<Option<PrefabJson>, BrError>where
T: BrFsReader,
Parse Meta/Prefab.json; None for world bundles.
Sourcepub fn thumbnail(&self) -> Result<Option<Vec<u8>>, BrError>where
T: BrFsReader,
pub fn thumbnail(&self) -> Result<Option<Vec<u8>>, BrError>where
T: BrFsReader,
Meta/Thumbnail.png bytes, when present.
Sourcepub fn screenshot(&self) -> Result<Option<Vec<u8>>, BrError>where
T: BrFsReader,
pub fn screenshot(&self) -> Result<Option<Vec<u8>>, BrError>where
T: BrFsReader,
Meta/Screenshot.jpg bytes, when present.
Sourcepub fn world_meta(&self) -> Result<WorldMeta, BrError>where
T: BrFsReader,
pub fn world_meta(&self) -> Result<WorldMeta, BrError>where
T: BrFsReader,
Assemble the same crate::wrapper::WorldMeta the wrapper writes:
bundle, world/prefab JSON, screenshot and thumbnail.
Sourcepub fn prefab_paths(&self) -> Result<Vec<String>, BrError>where
T: BrFsReader,
pub fn prefab_paths(&self) -> Result<Vec<String>, BrError>where
T: BrFsReader,
Root-relative paths of every embedded prefab (files under Prefabs/),
e.g. Prefabs/Uploads/<HASH>.brz. Empty when the bundle embeds none.
These are the exact strings Prefab component properties
(bundle_path_ref) reference.
Examples found in repository?
10fn main() -> Result<(), Box<dyn std::error::Error>> {
11 // 1. Build the prefab that gets spawned: a single red brick.
12 let mut prefab = World::new();
13 prefab.meta.bundle.name = "Spawned Brick".to_string();
14 prefab.bricks.push(Brick {
15 position: (0, 0, 6).into(),
16 color: (255, 0, 0).into(),
17 ..Default::default()
18 });
19 prefab.make_prefab();
20 let prefab_bytes = prefab.to_brz_vec()?;
21
22 // 2. Build the outer world holding the button + spawner.
23 let mut world = World::new();
24 // Registers the built-in component/port tables so the Button and
25 // PrefabSpawner component types and their wire ports resolve.
26 world.register_all_components();
27 world.meta.bundle.description = "Button-triggered prefab spawner".to_string();
28
29 // Embed the prefab; the returned path is what the spawner references.
30 let prefab_path = world.add_prefab(prefab_bytes);
31
32 // A pressable button (Component_Button on a 1x1 flat round brick). The
33 // crate exposes brick assets as constants; components it doesn't model as
34 // typed gates (like the button and spawner) are built from string names
35 // via LiteralComponent.
36 let (button, button_id) = Brick {
37 position: (0, 0, 2).into(),
38 color: (0, 255, 0).into(),
39 asset: assets::bricks::B_1X1F_ROUND,
40 ..Default::default()
41 }
42 .with_component(assets::LiteralComponent::new("Component_Button").with_data([(
43 "PromptCustomLabel",
44 Box::new("Spawn Brick".to_string()) as Box<dyn AsBrdbValue>,
45 )]))
46 .with_id_split();
47
48 // The prefab-spawner gate, pointed at the embedded prefab.
49 let (spawner, spawner_id) = Brick {
50 position: (15, 0, 1).into(),
51 color: (0, 0, 255).into(),
52 asset: assets::bricks::B_1X1_GATE_EXEC_PREFAB_SPAWNER,
53 ..Default::default()
54 }
55 .with_component(
56 assets::LiteralComponent::new("BrickComponentType_WireGraph_Exec_PrefabSpawner").with_data(
57 [(
58 "Prefab",
59 Box::new(prefab_path.clone()) as Box<dyn AsBrdbValue>,
60 )],
61 ),
62 )
63 .with_id_split();
64
65 world.add_bricks([button, spawner]);
66
67 // Wire the button's held signal into the spawner's Exec input, so a
68 // press fires the spawn.
69 world.add_wire_connection(
70 WirePort::new(button_id, "Component_Button", "bHeld"),
71 WirePort::new(
72 spawner_id,
73 "BrickComponentType_WireGraph_Exec_PrefabSpawner",
74 "Exec",
75 ),
76 );
77
78 // 3. Write it out.
79 let path = PathBuf::from("./example_prefab_spawner.brz");
80 world.write_brz(&path)?;
81 println!("wrote {}", path.display());
82
83 // Read it back to confirm the embedded prefab and wire survived.
84 let reader = Brz::open(&path)?.into_reader();
85 println!("embedded prefab: {}", prefab_path);
86 println!("prefab paths in archive: {:?}", reader.prefab_paths()?);
87 println!("{}", reader.get_fs()?.render());
88
89 Ok(())
90}Sourcepub fn read_prefabs(&self) -> Result<IndexMap<String, Vec<u8>>, BrError>where
T: BrFsReader,
pub fn read_prefabs(&self) -> Result<IndexMap<String, Vec<u8>>, BrError>where
T: BrFsReader,
Read every embedded prefab: root-relative path → raw .brz bytes.
The result is directly assignable to World::prefabs.
Sourcepub fn open_prefab(&self, path: &str) -> Result<Brz, BrError>where
T: BrFsReader,
pub fn open_prefab(&self, path: &str) -> Result<Brz, BrError>where
T: BrFsReader,
Parse an embedded prefab archive (a path from Self::prefab_paths or
a component’s Prefab property). Chain .into_reader() to read inside.
Sourcepub fn read_global_data(&self) -> Result<Arc<BrdbSchemaGlobalData>, BrError>where
T: BrFsReader,
pub fn read_global_data(&self) -> Result<Arc<BrdbSchemaGlobalData>, BrError>where
T: BrFsReader,
Read the GlobalData
Sourcepub fn global_data(&self) -> Result<Arc<BrdbSchemaGlobalData>, BrError>where
T: BrFsReader,
pub fn global_data(&self) -> Result<Arc<BrdbSchemaGlobalData>, BrError>where
T: BrFsReader,
Read and cache the GlobalData
Examples found in repository?
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5 let p = PathBuf::from(std::env::args().nth(1).unwrap());
6 let db = Brdb::open(&p)?.into_reader();
7 let gd = db.global_data()?;
8 let mut by_type: BTreeMap<String, Vec<String>> = BTreeMap::new();
9 for (ty, name) in &gd.external_asset_references {
10 by_type.entry(ty.clone()).or_default().push(name.clone());
11 }
12 println!("=== external_asset_references: {} total, {} types ===",
13 gd.external_asset_references.len(), by_type.len());
14 for (ty, names) in &by_type {
15 println!("\n[{}] ({})", ty, names.len());
16 for n in names { println!(" {}", n); }
17 }
18 println!("\n=== external_asset_types ===");
19 let mut ts: Vec<_> = gd.external_asset_types.iter().collect();
20 ts.sort();
21 for t in ts { println!(" {}", t); }
22 Ok(())
23}More examples
5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 let path = PathBuf::from("./world.brz");
7
8 // The API for reading a brz is identical to reading a Brdb
9 let db = Brz::open(path)?.into_reader();
10
11 let data = db.global_data()?;
12 println!("Basic Brick assets: {:?}", data.basic_brick_asset_names);
13 println!("Wire ports: {:?}", data.component_wire_port_names);
14 println!("Component types: {:?}", data.component_type_names);
15 println!("Component structs: {:?}", data.component_data_struct_names);
16 println!("Component schemas: {}", db.components_schema()?);
17
18 let chunks = db.brick_chunk_index(1)?;
19 println!("Brick chunks: {chunks:?}");
20 for chunk in chunks {
21 let soa = db.brick_chunk_soa(1, chunk.index)?;
22 println!("Brick soa: {soa:?}");
23 if chunk.num_components > 0 {
24 let (_soa, components) = db.component_chunk_soa(1, chunk.index)?;
25 for c in components {
26 println!("Component: {c}");
27 }
28 }
29 if chunk.num_wires > 0 {
30 let soa = db.wire_chunk_soa(1, chunk.index)?;
31 println!("Wires soa: {soa}");
32 }
33 }
34
35 println!("Files: {}", db.get_fs()?.render());
36
37 Ok(())
38}4fn run<T: BrFsReader>(db: &BrReader<T>) -> Result<(), Box<dyn std::error::Error>> {
5 println!(
6 "Component types: {:?}",
7 db.global_data()?.component_type_names
8 );
9 println!(
10 "Component structs: {:?}",
11 db.global_data()?.component_data_struct_names
12 );
13 // Probe grid ids 1.. until one is missing (covers the main grid plus any
14 // microchip inner grids, which entity discovery may not surface).
15 for gid in 1..32 {
16 let chunks = match db.brick_chunk_index(gid) {
17 Ok(c) => c,
18 Err(_) => break,
19 };
20 println!("=== grid {gid} ===");
21 for chunk in chunks {
22 println!(
23 "chunk {} bricks={} components={} wires={}",
24 chunk.index, chunk.num_bricks, chunk.num_components, chunk.num_wires
25 );
26 if chunk.num_components > 0 {
27 match db.component_chunk_soa(gid, chunk.index) {
28 Ok((_soa, components)) => {
29 for c in components {
30 println!(" component: {c}");
31 }
32 }
33 Err(e) => {
34 println!(" ERROR reading components: {e}");
35 return Err(e.into());
36 }
37 }
38 }
39 }
40 }
41 Ok(())
42}5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 let path = PathBuf::from("world.brdb");
7 let dst = PathBuf::from("world_patched.brdb");
8
9 println!("Warning - This code will break if the brick chunk struct changes!!");
10
11 let db = Brdb::open(path)?.into_reader();
12
13 let data = db.global_data()?;
14 let mut grid = UnsavedGrid::default();
15
16 let mut total_bricks = 0;
17 for chunk in db.brick_chunk_index(1)? {
18 for brick in db
19 .brick_chunk_soa(1, chunk.index)?
20 .iter_bricks(chunk.index, data.clone())
21 {
22 // If we wanted wires/components, we'd need to track the bricks here by their chunk index and brick index
23 total_bricks += 1;
24
25 let mut brick = brick?;
26 brick.position += Position::new(3000, 0, 0);
27 grid.add_brick(data.as_ref(), &brick);
28 }
29
30 if chunk.num_components > 0 {
31 println!("sorry, this example doesn't handle components");
32 }
33 if chunk.num_wires > 0 {
34 println!("sorry, this example doesn't handle wires");
35 }
36 }
37 println!("{total_bricks} bricks");
38
39 let mut pending = db.to_pending()?;
40
41 // Replace the main grid (1) with the grid we created
42 *pending.cd_mut("World/0/Bricks/Grids/1")? = grid.to_pending(
43 data.proc_brick_starting_index(),
44 db.components_schema()?.as_ref(),
45 )?;
46
47 if dst.exists() {
48 std::fs::remove_file(&dst)?;
49 }
50 Brdb::new(&dst)?.write_pending("Move the bricks", pending)?;
51
52 // Verify bricks can be read
53 let db = Brdb::open(dst)?.into_reader();
54 for chunk in db.brick_chunk_index(1)? {
55 let _ = db.brick_chunk_soa(1, chunk.index)?;
56 }
57
58 Ok(())
59}5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 let src = PathBuf::from("world.brdb");
7 let dst = PathBuf::from("world_patched.brdb");
8
9 assert!(src.exists());
10
11 let db = Brdb::open(src)?.into_reader();
12
13 let chunks = db.entity_chunk_index()?;
14 let entity_schema = db.entities_schema()?;
15 let global_data = db.global_data()?;
16 let mut chunk_files = vec![];
17
18 for index in chunks {
19 // Entity_chunk loads entities and their entity data
20 let entities = db.entity_chunk(index)?;
21
22 // Re-assemble the soa. using add_entity ensures the extra data is correctly handled
23 let mut soa = EntityChunkSoA::default();
24 for mut e in entities.into_iter() {
25 e.frozen = true;
26 soa.add_entity(&global_data, &e, e.id.unwrap() as u32);
27 }
28
29 chunk_files.push((
30 format!("{index}.mps"),
31 // EntityChunkSoA::to_bytes ensures the extra data is written after the SoA data
32 BrPendingFs::File(Some(soa.to_bytes(&entity_schema)?)),
33 ));
34 }
35
36 let patch = BrPendingFs::Root(vec![(
37 "World".to_owned(),
38 BrPendingFs::Folder(Some(vec![(
39 "0".to_string(),
40 BrPendingFs::Folder(Some(vec![(
41 "Entities".to_string(),
42 BrPendingFs::Folder(Some(vec![(
43 "Chunks".to_string(),
44 BrPendingFs::Folder(Some(chunk_files)),
45 )])),
46 )])),
47 )])),
48 )]);
49
50 // Use .to_pending_patch() if you want to update the same world
51 let pending = db.to_pending()?.with_patch(patch)?;
52 if dst.exists() {
53 std::fs::remove_file(&dst)?;
54 }
55 Brdb::new(&dst)?.write_pending("Freeze Entities", pending)?;
56
57 // Ensure entities can be read
58 let db = Brdb::open(dst)?.into_reader();
59 let chunks = db.entity_chunk_index()?;
60 for index in chunks {
61 let _ = db.entity_chunk(index)?;
62 }
63
64 Ok(())
65}5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 // world file from argv
7 let filename = std::env::args()
8 .nth(1)
9 .unwrap_or_else(|| "world.brdb".to_string());
10 let path = PathBuf::from(filename);
11 if !path.exists() {
12 eprintln!("File does not exist: {}", path.display());
13 std::process::exit(1);
14 }
15
16 let db = Brdb::open(path)?.into_reader();
17
18 let data = db.global_data()?;
19 println!("Basic Brick assets: {:?}", data.basic_brick_asset_names);
20 println!("Wire ports: {:?}", data.component_wire_port_names);
21 println!("Component types: {:?}", data.component_type_names);
22 println!("Component structs: {:?}", data.component_data_struct_names);
23 println!("Component schemas: {}", db.components_schema()?);
24
25 let mut grid_ids = vec![1];
26
27 // Iterate all entity chunks to find dynamic brick grids...
28 // This could totally be a helper function
29 for index in db.entity_chunk_index()? {
30 for e in db.entity_chunk(index)? {
31 // Ensure the chunk is a dynamic brick grid
32 if !e.is_brick_grid() {
33 continue;
34 }
35 let Some(id) = e.id else {
36 continue;
37 };
38 grid_ids.push(id);
39 }
40 }
41
42 for gid in grid_ids {
43 println!("Reading grid {gid}");
44 let chunks = db.brick_chunk_index(gid)?;
45 println!("Brick chunks: {chunks:?}");
46 for chunk in chunks {
47 let soa = db.brick_chunk_soa(gid, chunk.index)?;
48 println!("Brick Soa {chunk}: {soa:?}");
49 if chunk.num_components > 0 {
50 let (_soa, components) = db.component_chunk_soa(gid, chunk.index)?;
51 // println!("Components soa: {soa}");
52 for c in components {
53 println!("Component: {c}");
54 }
55 }
56 if chunk.num_wires > 0 {
57 let soa = db.wire_chunk_soa(gid, chunk.index)?;
58 println!("Wires soa: {soa}");
59 }
60 }
61 }
62
63 println!("Files: {}", db.get_fs()?.render());
64
65 Ok(())
66}Sourcepub fn owners_soa(&self) -> Result<BrdbStruct, BrError>where
T: BrFsReader,
pub fn owners_soa(&self) -> Result<BrdbStruct, BrError>where
T: BrFsReader,
Read the Owners table
Examples found in repository?
7fn main() -> Result<(), Box<dyn std::error::Error>> {
8 let src = PathBuf::from("world.brdb");
9 let dst = PathBuf::from("world_patched.brz");
10
11 assert!(src.exists());
12
13 let db = Brdb::open(src)?.into_reader();
14
15 let owners = db.owners_soa()?;
16
17 // Parse the owners from BrdbValues
18 let mut new_soa = OwnerTableSoA::try_from(&owners.to_value())?;
19
20 // Modify the owner ids
21 new_soa
22 .display_names
23 .iter_mut()
24 .for_each(|id| *id = "PUBLIC".to_owned());
25 new_soa
26 .user_names
27 .iter_mut()
28 .for_each(|id| *id = "PUBLIC".to_owned());
29 new_soa
30 .user_ids
31 .iter_mut()
32 .for_each(|id| *id = Guid::default());
33
34 // convert the owners struct of arrays into bytes using the owners schema
35 let content = db.owners_schema()?.write_brdb(OWNER_TABLE_SOA, &new_soa)?;
36
37 let patch = BrPendingFs::Root(vec![(
38 "World".to_owned(),
39 BrPendingFs::Folder(Some(vec![(
40 "0".to_string(),
41 BrPendingFs::Folder(Some(vec![(
42 "Owners.mps".to_string(),
43 BrPendingFs::File(Some(content)),
44 )])),
45 )])),
46 )]);
47
48 // use .to_pending_patch() if you want to update the same world
49 let pending = db.to_pending()?.with_patch(patch)?;
50 if dst.exists() {
51 std::fs::remove_file(&dst)?;
52 }
53 Brz::write_pending(&dst, pending)?;
54
55 println!("{}", Brz::open(&dst)?.into_reader().owners_soa()?);
56
57 Ok(())
58}More examples
21fn main() -> Result<(), Box<dyn std::error::Error>> {
22 let mut args = std::env::args().into_iter();
23 args.next();
24 let Some(file) = args.next() else {
25 eprintln!("missing world file arg");
26 process::exit(1);
27 };
28
29 let dst = env::current_dir()?.join(PathBuf::from(&file));
30 if !dst.exists() {
31 eprintln!("file {} does not exist", dst.display());
32 process::exit(1);
33 }
34
35 eprintln!("Opening world: {}", path::absolute(&dst)?.display());
36
37 let db = Brdb::open(dst)?.into_reader();
38
39 let owners_soa = db.owners_soa()?.to_value();
40 let owners = OwnerTableSoA::try_from(&owners_soa)?;
41
42 let rows = (0..owners.user_ids.len())
43 .map(|i| {
44 let user_id = &owners.user_ids[i];
45 let display_name = &owners.display_names[i];
46 let user_name = &owners.user_names[i];
47 let entity_count = owners.entity_counts[i];
48 let brick_count = owners.brick_counts[i];
49 let component_count = owners.component_counts[i];
50 let wire_count = owners.wire_counts[i];
51 Row {
52 user_id: user_id.uuid().to_string(),
53 user_name: user_name.clone(),
54 display_name: display_name.clone(),
55 entity_counts: entity_count,
56 brick_counts: brick_count,
57 component_counts: component_count,
58 wire_counts: wire_count,
59 }
60 })
61 .collect::<Vec<_>>();
62 print!("{}", serde_json::to_string_pretty(&rows)?);
63
64 Ok(())
65}6fn main() -> Result<(), Box<dyn std::error::Error>> {
7 let mut args = std::env::args().into_iter().peekable();
8 let cmd = args.next().unwrap();
9 if !args.peek().is_some() {
10 println!("usage: {cmd} show world.brdb");
11 println!("usage: {cmd} apply world.brdb owners.csv");
12 println!(
13 "owners.csv must be `display_name,user_name,user_id,old_user_id` where old_user_id is the one to replace."
14 );
15 process::exit(0);
16 }
17
18 let command = args.next().unwrap();
19 if command != "show" && command != "apply" {
20 eprintln!("unknown command. expected `show` or `apply`");
21 process::exit(1);
22 }
23
24 let Some(file) = args.next() else {
25 eprintln!("missing world file arg");
26 process::exit(1);
27 };
28
29 let dst = PathBuf::from(&file);
30 if !dst.exists() {
31 eprintln!("file {file} does not exist");
32 process::exit(1);
33 }
34
35 let db = Brdb::open(dst)?.into_reader();
36
37 let owners = db.owners_soa()?;
38
39 if command == "show" {
40 if args.peek().is_some() {
41 eprintln!("too many arguments!");
42 process::exit(1);
43 }
44
45 let owners_csv = owners
46 .prop("DisplayNames")?
47 .as_array()?
48 .iter()
49 .zip(owners.prop("UserNames")?.as_array()?.iter())
50 .zip(owners.prop("UserIds")?.as_array()?.iter())
51 .map(|((display_name, user_name), user_id)| {
52 format!(
53 "{},{},{}",
54 display_name.as_str().unwrap(),
55 user_name.as_str().unwrap(),
56 Guid::try_from(user_id).unwrap().uuid(),
57 )
58 })
59 .collect::<Vec<_>>();
60 println!("display_name,user_name,user_id\n{}", owners_csv.join("\n"));
61 } else if command == "apply" {
62 let Some(apply_file) = args.next() else {
63 eprintln!("missing owners csv file arg");
64 process::exit(1);
65 };
66 if args.peek().is_some() {
67 eprintln!("too many arguments!");
68 process::exit(1);
69 }
70
71 let apply_path = PathBuf::from(&apply_file);
72 if !apply_path.exists() {
73 eprintln!("file {apply_file} does not exist");
74 process::exit(1);
75 }
76
77 let mut display_name_index = None;
78 let mut user_name_index = None;
79 let mut user_id_index = None;
80 let mut old_user_id_index = None;
81 let mut apply_data = String::new();
82 File::open(apply_path)?.read_to_string(&mut apply_data)?;
83 let Some((header, rows)) = apply_data.split_once("\n") else {
84 eprintln!("file {apply_file} does not have any rows");
85 process::exit(1);
86 };
87 for (i, key) in header.split(",").enumerate() {
88 match key.trim().to_ascii_lowercase().as_ref() {
89 "display_name" => {
90 display_name_index = Some(i);
91 }
92 "user_name" => {
93 user_name_index = Some(i);
94 }
95 "user_id" => {
96 user_id_index = Some(i);
97 }
98 "old_user_id" => old_user_id_index = Some(i),
99 other => {
100 eprintln!("unknown column {other} in {apply_file}");
101 process::exit(1);
102 }
103 }
104 }
105
106 let missing = [
107 ("display_name", display_name_index.is_none()),
108 ("user_name", user_name_index.is_none()),
109 ("user_id", user_id_index.is_none()),
110 ("old_user_id", old_user_id_index.is_none()),
111 ]
112 .into_iter()
113 .filter_map(|(k, cond)| cond.then_some(k.to_owned()))
114 .collect::<Vec<_>>();
115 if !missing.is_empty() {
116 eprintln!("missing columns: {}", missing.join(","));
117 process::exit(1);
118 }
119
120 let display_name_index = display_name_index.unwrap();
121 let user_name_index = user_name_index.unwrap();
122 let user_id_index = user_id_index.unwrap();
123 let old_user_id_index = old_user_id_index.unwrap();
124
125 // Parse the owners from BrdbValues
126 let mut new_soa = OwnerTableSoA::try_from(&owners.to_value())?;
127 let owners_lut = rows
128 .trim()
129 .split("\n")
130 .map(|r| r.trim().split(",").collect::<Vec<&str>>())
131 .map(|cols| {
132 let user_name = cols[user_name_index];
133 let display_name = cols[display_name_index];
134 let user_id = Uuid::parse_str(&cols[user_id_index])
135 .expect(&format!("invalid uuid: {}", cols[user_id_index]));
136 let old_user_id = Uuid::parse_str(&cols[old_user_id_index])
137 .expect(&format!("invalid old uuid: {}", cols[old_user_id_index]));
138 (old_user_id, (user_name, display_name, user_id))
139 })
140 .collect::<HashMap<_, _>>();
141 println!("{owners_lut:?}");
142
143 let mut changes = 0;
144
145 for i in 0..new_soa.user_ids.len() {
146 let old_id = new_soa.user_ids[i].uuid();
147 let Some((user_name, display_name, user_id)) = owners_lut.get(&old_id) else {
148 println!("missing old id for {old_id} - ignoring");
149 continue;
150 };
151 println!("replacing {old_id} with {user_id} - {user_name} ({display_name})");
152 new_soa.user_names[i] = (*user_name).to_owned();
153 new_soa.display_names[i] = (*display_name).to_owned();
154 new_soa.user_ids[i] = Guid::from_uuid((*user_id).clone());
155 changes += 1;
156 }
157
158 if changes == 0 {
159 println!("world left unchanged");
160 std::process::exit(0);
161 }
162
163 // convert the owners struct of arrays into bytes using the owners schema
164 let content = db.owners_schema()?.write_brdb(OWNER_TABLE_SOA, &new_soa)?;
165
166 let patch = BrPendingFs::Root(vec![(
167 "World".to_owned(),
168 BrPendingFs::Folder(Some(vec![(
169 "0".to_string(),
170 BrPendingFs::Folder(Some(vec![(
171 "Owners.mps".to_string(),
172 BrPendingFs::File(Some(content)),
173 )])),
174 )])),
175 )]);
176 db.write_pending("Replace owners", db.to_pending_patch()?.with_patch(patch)?)?;
177 println!("revision created")
178 }
179
180 Ok(())
181}Sourcepub fn owners_schema_rev(
&self,
revision: i64,
) -> Result<Arc<BrdbSchema>, BrError>where
T: BrFsReader,
pub fn owners_schema_rev(
&self,
revision: i64,
) -> Result<Arc<BrdbSchema>, BrError>where
T: BrFsReader,
Read the Owners schema at a specific revision
Sourcepub fn owners_schema(&self) -> Result<Arc<BrdbSchema>, BrError>where
T: BrFsReader,
pub fn owners_schema(&self) -> Result<Arc<BrdbSchema>, BrError>where
T: BrFsReader,
Read the Owners schema (latest revision)
Examples found in repository?
7fn main() -> Result<(), Box<dyn std::error::Error>> {
8 let src = PathBuf::from("world.brdb");
9 let dst = PathBuf::from("world_patched.brz");
10
11 assert!(src.exists());
12
13 let db = Brdb::open(src)?.into_reader();
14
15 let owners = db.owners_soa()?;
16
17 // Parse the owners from BrdbValues
18 let mut new_soa = OwnerTableSoA::try_from(&owners.to_value())?;
19
20 // Modify the owner ids
21 new_soa
22 .display_names
23 .iter_mut()
24 .for_each(|id| *id = "PUBLIC".to_owned());
25 new_soa
26 .user_names
27 .iter_mut()
28 .for_each(|id| *id = "PUBLIC".to_owned());
29 new_soa
30 .user_ids
31 .iter_mut()
32 .for_each(|id| *id = Guid::default());
33
34 // convert the owners struct of arrays into bytes using the owners schema
35 let content = db.owners_schema()?.write_brdb(OWNER_TABLE_SOA, &new_soa)?;
36
37 let patch = BrPendingFs::Root(vec![(
38 "World".to_owned(),
39 BrPendingFs::Folder(Some(vec![(
40 "0".to_string(),
41 BrPendingFs::Folder(Some(vec![(
42 "Owners.mps".to_string(),
43 BrPendingFs::File(Some(content)),
44 )])),
45 )])),
46 )]);
47
48 // use .to_pending_patch() if you want to update the same world
49 let pending = db.to_pending()?.with_patch(patch)?;
50 if dst.exists() {
51 std::fs::remove_file(&dst)?;
52 }
53 Brz::write_pending(&dst, pending)?;
54
55 println!("{}", Brz::open(&dst)?.into_reader().owners_soa()?);
56
57 Ok(())
58}More examples
6fn main() -> Result<(), Box<dyn std::error::Error>> {
7 let mut args = std::env::args().into_iter().peekable();
8 let cmd = args.next().unwrap();
9 if !args.peek().is_some() {
10 println!("usage: {cmd} show world.brdb");
11 println!("usage: {cmd} apply world.brdb owners.csv");
12 println!(
13 "owners.csv must be `display_name,user_name,user_id,old_user_id` where old_user_id is the one to replace."
14 );
15 process::exit(0);
16 }
17
18 let command = args.next().unwrap();
19 if command != "show" && command != "apply" {
20 eprintln!("unknown command. expected `show` or `apply`");
21 process::exit(1);
22 }
23
24 let Some(file) = args.next() else {
25 eprintln!("missing world file arg");
26 process::exit(1);
27 };
28
29 let dst = PathBuf::from(&file);
30 if !dst.exists() {
31 eprintln!("file {file} does not exist");
32 process::exit(1);
33 }
34
35 let db = Brdb::open(dst)?.into_reader();
36
37 let owners = db.owners_soa()?;
38
39 if command == "show" {
40 if args.peek().is_some() {
41 eprintln!("too many arguments!");
42 process::exit(1);
43 }
44
45 let owners_csv = owners
46 .prop("DisplayNames")?
47 .as_array()?
48 .iter()
49 .zip(owners.prop("UserNames")?.as_array()?.iter())
50 .zip(owners.prop("UserIds")?.as_array()?.iter())
51 .map(|((display_name, user_name), user_id)| {
52 format!(
53 "{},{},{}",
54 display_name.as_str().unwrap(),
55 user_name.as_str().unwrap(),
56 Guid::try_from(user_id).unwrap().uuid(),
57 )
58 })
59 .collect::<Vec<_>>();
60 println!("display_name,user_name,user_id\n{}", owners_csv.join("\n"));
61 } else if command == "apply" {
62 let Some(apply_file) = args.next() else {
63 eprintln!("missing owners csv file arg");
64 process::exit(1);
65 };
66 if args.peek().is_some() {
67 eprintln!("too many arguments!");
68 process::exit(1);
69 }
70
71 let apply_path = PathBuf::from(&apply_file);
72 if !apply_path.exists() {
73 eprintln!("file {apply_file} does not exist");
74 process::exit(1);
75 }
76
77 let mut display_name_index = None;
78 let mut user_name_index = None;
79 let mut user_id_index = None;
80 let mut old_user_id_index = None;
81 let mut apply_data = String::new();
82 File::open(apply_path)?.read_to_string(&mut apply_data)?;
83 let Some((header, rows)) = apply_data.split_once("\n") else {
84 eprintln!("file {apply_file} does not have any rows");
85 process::exit(1);
86 };
87 for (i, key) in header.split(",").enumerate() {
88 match key.trim().to_ascii_lowercase().as_ref() {
89 "display_name" => {
90 display_name_index = Some(i);
91 }
92 "user_name" => {
93 user_name_index = Some(i);
94 }
95 "user_id" => {
96 user_id_index = Some(i);
97 }
98 "old_user_id" => old_user_id_index = Some(i),
99 other => {
100 eprintln!("unknown column {other} in {apply_file}");
101 process::exit(1);
102 }
103 }
104 }
105
106 let missing = [
107 ("display_name", display_name_index.is_none()),
108 ("user_name", user_name_index.is_none()),
109 ("user_id", user_id_index.is_none()),
110 ("old_user_id", old_user_id_index.is_none()),
111 ]
112 .into_iter()
113 .filter_map(|(k, cond)| cond.then_some(k.to_owned()))
114 .collect::<Vec<_>>();
115 if !missing.is_empty() {
116 eprintln!("missing columns: {}", missing.join(","));
117 process::exit(1);
118 }
119
120 let display_name_index = display_name_index.unwrap();
121 let user_name_index = user_name_index.unwrap();
122 let user_id_index = user_id_index.unwrap();
123 let old_user_id_index = old_user_id_index.unwrap();
124
125 // Parse the owners from BrdbValues
126 let mut new_soa = OwnerTableSoA::try_from(&owners.to_value())?;
127 let owners_lut = rows
128 .trim()
129 .split("\n")
130 .map(|r| r.trim().split(",").collect::<Vec<&str>>())
131 .map(|cols| {
132 let user_name = cols[user_name_index];
133 let display_name = cols[display_name_index];
134 let user_id = Uuid::parse_str(&cols[user_id_index])
135 .expect(&format!("invalid uuid: {}", cols[user_id_index]));
136 let old_user_id = Uuid::parse_str(&cols[old_user_id_index])
137 .expect(&format!("invalid old uuid: {}", cols[old_user_id_index]));
138 (old_user_id, (user_name, display_name, user_id))
139 })
140 .collect::<HashMap<_, _>>();
141 println!("{owners_lut:?}");
142
143 let mut changes = 0;
144
145 for i in 0..new_soa.user_ids.len() {
146 let old_id = new_soa.user_ids[i].uuid();
147 let Some((user_name, display_name, user_id)) = owners_lut.get(&old_id) else {
148 println!("missing old id for {old_id} - ignoring");
149 continue;
150 };
151 println!("replacing {old_id} with {user_id} - {user_name} ({display_name})");
152 new_soa.user_names[i] = (*user_name).to_owned();
153 new_soa.display_names[i] = (*display_name).to_owned();
154 new_soa.user_ids[i] = Guid::from_uuid((*user_id).clone());
155 changes += 1;
156 }
157
158 if changes == 0 {
159 println!("world left unchanged");
160 std::process::exit(0);
161 }
162
163 // convert the owners struct of arrays into bytes using the owners schema
164 let content = db.owners_schema()?.write_brdb(OWNER_TABLE_SOA, &new_soa)?;
165
166 let patch = BrPendingFs::Root(vec![(
167 "World".to_owned(),
168 BrPendingFs::Folder(Some(vec![(
169 "0".to_string(),
170 BrPendingFs::Folder(Some(vec![(
171 "Owners.mps".to_string(),
172 BrPendingFs::File(Some(content)),
173 )])),
174 )])),
175 )]);
176 db.write_pending("Replace owners", db.to_pending_patch()?.with_patch(patch)?)?;
177 println!("revision created")
178 }
179
180 Ok(())
181}Sourcepub fn components_schema_rev(
&self,
revision: i64,
) -> Result<Arc<BrdbSchema>, BrError>where
T: BrFsReader,
pub fn components_schema_rev(
&self,
revision: i64,
) -> Result<Arc<BrdbSchema>, BrError>where
T: BrFsReader,
Read the shared components chunk schema at a specific revision
Sourcepub fn components_schema(&self) -> Result<Arc<BrdbSchema>, BrError>where
T: BrFsReader,
pub fn components_schema(&self) -> Result<Arc<BrdbSchema>, BrError>where
T: BrFsReader,
Read the shared components chunk schema (latest revision)
Examples found in repository?
5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 let path = PathBuf::from("./world.brz");
7
8 // The API for reading a brz is identical to reading a Brdb
9 let db = Brz::open(path)?.into_reader();
10
11 let data = db.global_data()?;
12 println!("Basic Brick assets: {:?}", data.basic_brick_asset_names);
13 println!("Wire ports: {:?}", data.component_wire_port_names);
14 println!("Component types: {:?}", data.component_type_names);
15 println!("Component structs: {:?}", data.component_data_struct_names);
16 println!("Component schemas: {}", db.components_schema()?);
17
18 let chunks = db.brick_chunk_index(1)?;
19 println!("Brick chunks: {chunks:?}");
20 for chunk in chunks {
21 let soa = db.brick_chunk_soa(1, chunk.index)?;
22 println!("Brick soa: {soa:?}");
23 if chunk.num_components > 0 {
24 let (_soa, components) = db.component_chunk_soa(1, chunk.index)?;
25 for c in components {
26 println!("Component: {c}");
27 }
28 }
29 if chunk.num_wires > 0 {
30 let soa = db.wire_chunk_soa(1, chunk.index)?;
31 println!("Wires soa: {soa}");
32 }
33 }
34
35 println!("Files: {}", db.get_fs()?.render());
36
37 Ok(())
38}More examples
5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 let path = PathBuf::from("world.brdb");
7 let dst = PathBuf::from("world_patched.brdb");
8
9 println!("Warning - This code will break if the brick chunk struct changes!!");
10
11 let db = Brdb::open(path)?.into_reader();
12
13 let data = db.global_data()?;
14 let mut grid = UnsavedGrid::default();
15
16 let mut total_bricks = 0;
17 for chunk in db.brick_chunk_index(1)? {
18 for brick in db
19 .brick_chunk_soa(1, chunk.index)?
20 .iter_bricks(chunk.index, data.clone())
21 {
22 // If we wanted wires/components, we'd need to track the bricks here by their chunk index and brick index
23 total_bricks += 1;
24
25 let mut brick = brick?;
26 brick.position += Position::new(3000, 0, 0);
27 grid.add_brick(data.as_ref(), &brick);
28 }
29
30 if chunk.num_components > 0 {
31 println!("sorry, this example doesn't handle components");
32 }
33 if chunk.num_wires > 0 {
34 println!("sorry, this example doesn't handle wires");
35 }
36 }
37 println!("{total_bricks} bricks");
38
39 let mut pending = db.to_pending()?;
40
41 // Replace the main grid (1) with the grid we created
42 *pending.cd_mut("World/0/Bricks/Grids/1")? = grid.to_pending(
43 data.proc_brick_starting_index(),
44 db.components_schema()?.as_ref(),
45 )?;
46
47 if dst.exists() {
48 std::fs::remove_file(&dst)?;
49 }
50 Brdb::new(&dst)?.write_pending("Move the bricks", pending)?;
51
52 // Verify bricks can be read
53 let db = Brdb::open(dst)?.into_reader();
54 for chunk in db.brick_chunk_index(1)? {
55 let _ = db.brick_chunk_soa(1, chunk.index)?;
56 }
57
58 Ok(())
59}5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 // world file from argv
7 let filename = std::env::args()
8 .nth(1)
9 .unwrap_or_else(|| "world.brdb".to_string());
10 let path = PathBuf::from(filename);
11 if !path.exists() {
12 eprintln!("File does not exist: {}", path.display());
13 std::process::exit(1);
14 }
15
16 let db = Brdb::open(path)?.into_reader();
17
18 let data = db.global_data()?;
19 println!("Basic Brick assets: {:?}", data.basic_brick_asset_names);
20 println!("Wire ports: {:?}", data.component_wire_port_names);
21 println!("Component types: {:?}", data.component_type_names);
22 println!("Component structs: {:?}", data.component_data_struct_names);
23 println!("Component schemas: {}", db.components_schema()?);
24
25 let mut grid_ids = vec![1];
26
27 // Iterate all entity chunks to find dynamic brick grids...
28 // This could totally be a helper function
29 for index in db.entity_chunk_index()? {
30 for e in db.entity_chunk(index)? {
31 // Ensure the chunk is a dynamic brick grid
32 if !e.is_brick_grid() {
33 continue;
34 }
35 let Some(id) = e.id else {
36 continue;
37 };
38 grid_ids.push(id);
39 }
40 }
41
42 for gid in grid_ids {
43 println!("Reading grid {gid}");
44 let chunks = db.brick_chunk_index(gid)?;
45 println!("Brick chunks: {chunks:?}");
46 for chunk in chunks {
47 let soa = db.brick_chunk_soa(gid, chunk.index)?;
48 println!("Brick Soa {chunk}: {soa:?}");
49 if chunk.num_components > 0 {
50 let (_soa, components) = db.component_chunk_soa(gid, chunk.index)?;
51 // println!("Components soa: {soa}");
52 for c in components {
53 println!("Component: {c}");
54 }
55 }
56 if chunk.num_wires > 0 {
57 let soa = db.wire_chunk_soa(gid, chunk.index)?;
58 println!("Wires soa: {soa}");
59 }
60 }
61 }
62
63 println!("Files: {}", db.get_fs()?.render());
64
65 Ok(())
66}5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 let src = PathBuf::from("world.brdb");
7 let dst = PathBuf::from("world_patched.brdb");
8
9 assert!(src.exists());
10
11 let db = Brdb::open(src)?.into_reader();
12
13 let mut grid_ids = vec![1];
14
15 // Iterate all entity chunks to find dynamic brick grids...
16 // This could totally be a helper function
17 for index in db.entity_chunk_index()? {
18 for e in db.entity_chunk(index)? {
19 // Ensure the chunk is a dynamic brick grid
20 if !e.is_brick_grid() {
21 continue;
22 }
23 let Some(id) = e.id else {
24 continue;
25 };
26 grid_ids.push(id);
27 }
28 }
29
30 let component_schema = db.components_schema()?;
31 let mut grids_files = vec![];
32
33 // Iterate all grids (there can be bricks on entities)
34 for grid in &grid_ids {
35 let chunks = db.brick_chunk_index(*grid)?;
36 let mut chunk_files = vec![];
37 let mut num_grid_modified = 0;
38
39 // Iterate all chunks in the grid
40 for index in chunks {
41 let mut num_chunk_modified = 0;
42 if index.num_components == 0 {
43 println!("ignoring grid {grid} chunk {} with no components", *index);
44 continue;
45 }
46
47 // Iterate all the components in the chunk
48 let (mut soa, components) = db.component_chunk(*grid, *index)?;
49 for mut s in components {
50 // Disable the shadow casting property if it's present and true
51 if s.prop("bCastShadows")
52 .is_ok_and(|v| v.as_brdb_bool().unwrap_or_default())
53 {
54 println!(
55 "grid {grid} chunk {} mutating component {}",
56 *index,
57 s.get_name()
58 );
59 s.set_prop("bCastShadows", BrdbValue::Bool(false))?;
60 num_grid_modified += 1;
61 num_chunk_modified += 1;
62 }
63
64 soa.unwritten_struct_data.push(Box::new(s));
65 }
66
67 if num_chunk_modified == 0 {
68 continue;
69 }
70
71 chunk_files.push((
72 format!("{}.mps", *index),
73 // ComponentChunkSoA::to_bytes ensures the extra data is written after the SoA data
74 BrPendingFs::File(Some(soa.to_bytes(&component_schema)?)),
75 ));
76 }
77
78 if num_grid_modified == 0 {
79 println!("grid {grid} has no shadow-casting components, skipping");
80 continue;
81 } else {
82 println!(
83 "grid {grid} has {num_grid_modified} shadow-casting components in {} files",
84 chunk_files.len()
85 );
86 }
87
88 grids_files.push((
89 grid.to_string(),
90 BrPendingFs::Folder(Some(vec![(
91 "Components".to_string(),
92 BrPendingFs::Folder(Some(chunk_files)),
93 )])),
94 ))
95 }
96
97 let patch = BrPendingFs::Root(vec![(
98 "World".to_owned(),
99 BrPendingFs::Folder(Some(vec![(
100 "0".to_string(),
101 BrPendingFs::Folder(Some(vec![(
102 "Bricks".to_string(),
103 BrPendingFs::Folder(Some(vec![(
104 "Grids".to_string(),
105 BrPendingFs::Folder(Some(grids_files)),
106 )])),
107 )])),
108 )])),
109 )]);
110
111 // Use .to_pending_patch() if you want to update the same world
112 let pending = db.to_pending()?.with_patch(patch)?;
113 if dst.exists() {
114 std::fs::remove_file(&dst)?;
115 }
116 Brdb::new(&dst)?.write_pending("Disable Shadow Casting", pending)?;
117
118 // Ensure all the components can be read
119 let db = Brdb::open(dst)?.into_reader();
120 for grid in grid_ids {
121 let chunks = db.brick_chunk_index(grid)?;
122 for index in chunks {
123 if index.num_components == 0 {
124 continue;
125 }
126 let (_soa, _components) = db.component_chunk(grid, *index)?;
127 }
128 }
129
130 Ok(())
131}104fn main() -> Result<(), Box<dyn std::error::Error>> {
105 let args: Vec<String> = std::env::args().collect();
106 let path = PathBuf::from(args.get(1).expect("usage: extract_defaults <dump.brdb> [output.rs]"));
107 let output_path = args.get(2).map(PathBuf::from);
108 let db = Brdb::open(path)?.into_reader();
109 let global_data = db.global_data()?;
110 let schema = db.components_schema()?;
111
112 let mut type_to_struct: BTreeMap<String, String> = BTreeMap::new();
113 let mut wire_ports: BTreeSet<String> = BTreeSet::new();
114 let mut struct_defaults: BTreeMap<String, Vec<(String, String, String)>> = BTreeMap::new();
115
116 for (i, type_name) in global_data.component_type_names.iter().enumerate() {
117 if let Some(struct_name) = global_data.component_data_struct_names.get(i) {
118 if struct_name != "None" {
119 type_to_struct.insert(type_name.clone(), struct_name.clone());
120 }
121 }
122 }
123
124 for port in &global_data.component_wire_port_names {
125 wire_ports.insert(port.clone());
126 }
127
128 for chunk in db.brick_chunk_index(1)? {
129 if chunk.num_components == 0 {
130 continue;
131 }
132 let Ok((_soa, components)) = db.component_chunk(1, *chunk) else {
133 continue;
134 };
135 for s in components {
136 let name = s.get_name().to_owned();
137 if struct_defaults.contains_key(&name) {
138 continue;
139 }
140
141 let Some(struct_def) = schema.get_struct(&name) else {
142 continue;
143 };
144 let s_id = match schema.intern.get(&name) {
145 Some(id) => id,
146 None => continue,
147 };
148
149 let mut fields = Vec::new();
150 for (field_id, prop_ty) in struct_def {
151 let field_name = match field_id.get(&schema) {
152 Some(n) => n.to_owned(),
153 None => continue,
154 };
155 let ty_str = match prop_ty {
156 BrdbSchemaStructProperty::Type(t) => {
157 match schema.intern.lookup_ref(*t) {
158 Some(s) => s.to_owned(),
159 None => continue,
160 }
161 }
162 _ => continue,
163 };
164 let val = match s.as_brdb_struct_prop_value(&schema, s_id, *field_id) {
165 Ok(v) => v,
166 Err(_) => continue,
167 };
168 let formatted = match format_value(&schema, &ty_str, val) {
169 Some(f) => f,
170 None => continue,
171 };
172 fields.push((field_name, ty_str, formatted));
173 }
174 struct_defaults.insert(name, fields);
175 }
176 }
177
178 use std::fmt::Write;
179 let mut out = String::new();
180 macro_rules! w { ($($t:tt)*) => { writeln!(out, $($t)*).unwrap() } }
181
182 w!("// Autogenerated from: cargo run --example extract_defaults -- path/to/dump.brdb");
183 w!();
184
185 w!("pub static COMPONENT_TYPE_STRUCT_PAIRS: &[(&str, &str)] = &[");
186 for (type_name, struct_name) in &type_to_struct {
187 w!(" (\"{type_name}\", \"{struct_name}\"),");
188 }
189 w!("];");
190 w!();
191
192 w!("pub static WIRE_PORT_NAMES: &[&str] = &[");
193 for port in &wire_ports {
194 w!(" \"{port}\",");
195 }
196 w!("];");
197 w!();
198
199 w!("pub static ENTITY_TYPE_STRUCT_PAIRS: &[(&str, &str)] = &[");
200 for (i, type_name) in global_data.entity_type_names.iter().enumerate() {
201 if let Some(class_name) = global_data.entity_data_class_names.get_index(i) {
202 w!(" (\"{type_name}\", \"{class_name}\"),");
203 }
204 }
205 w!("];");
206 w!();
207
208 w!("use std::sync::LazyLock;");
209 // WireVariant is only referenced when a dump carries wire-variant defaults;
210 // allow it to be unused so a dump without any still compiles clean.
211 w!("#[allow(unused_imports)]");
212 w!("use crate::schema::WireVariant;");
213 w!("use crate::schema::as_brdb::AsBrdbValue;");
214 // NestedStructDefault carries struct-typed defaults (Vector2D/LinearColor/...); allow it
215 // to be unused so a dump without any nested-struct defaults still compiles clean.
216 w!("#[allow(unused_imports)]");
217 w!("use crate::schema::as_brdb::NestedStructDefault;");
218 w!("use crate::SavedBrickColor;");
219 w!();
220 w!("/// Default field values for every component data struct.");
221 w!("pub static STRUCT_DEFAULTS: LazyLock<Vec<(&'static str, Vec<(&'static str, Box<dyn AsBrdbValue>)>)>> =");
222 w!(" LazyLock::new(|| vec![");
223 let mut first_entry = true;
224 for (name, fields) in &struct_defaults {
225 if fields.is_empty() {
226 continue;
227 }
228 w!(" (\"{name}\", vec![");
229 for (field_name, _ty, val) in fields {
230 if first_entry {
231 w!(" (\"{field_name}\", {val}),");
232 first_entry = false;
233 } else {
234 let short = val.trim_end_matches(" as Box<dyn AsBrdbValue>");
235 w!(" (\"{field_name}\", {short}),");
236 }
237 }
238 w!(" ]),");
239 }
240 w!(" ]);");
241
242 if let Some(ref p) = output_path {
243 std::fs::write(p, &out)?;
244 eprintln!("Wrote {}", p.display());
245 } else {
246 print!("{out}");
247 }
248
249 eprintln!(
250 "Extracted: {} type mappings, {} wire ports, {} struct defaults, {} entity types",
251 type_to_struct.len(),
252 wire_ports.len(),
253 struct_defaults.len(),
254 global_data.entity_type_names.len(),
255 );
256
257 Ok(())
258}18fn main() -> Result<(), Box<dyn std::error::Error>> {
19 let path = PathBuf::from("./world.brdb");
20
21 let db = Brdb::open(path)?.into_reader();
22
23 let data = db.global_data()?;
24 let component_schema = db.components_schema()?;
25
26 let chunks = db.brick_chunk_index(1)?;
27
28 // Track seen brick types
29 let mut brick_type_set = HashSet::new();
30
31 // Track seen component types to map to their wire ports
32 let mut component_map = HashMap::new();
33
34 // Track brick -> component mappings
35 let mut brick_map = HashMap::new();
36
37 for chunk in &chunks {
38 let soa = db.brick_chunk_soa(1, chunk.index)?;
39
40 // Iterate basic bricks
41 let pb_index = soa.procedural_brick_starting_index;
42 for (i, t) in soa.brick_type_indices.into_iter().enumerate() {
43 if t >= pb_index {
44 continue;
45 }
46 if brick_type_set.contains(&t) {
47 continue;
48 }
49 brick_type_set.insert(t);
50
51 // Insert bricks of unique types
52 brick_map.insert(
53 (chunk.index, i),
54 BrickMeta {
55 type_index: t,
56 components: Vec::new(),
57 },
58 );
59 }
60
61 if chunk.num_components > 0 {
62 let (soa, components) = db.component_chunk_soa(1, chunk.index)?;
63 let indices = soa.component_brick_indices;
64
65 // Expand the type index/num instances into a flat list of type indices
66 let type_indices = soa
67 .component_type_counters
68 .iter()
69 .flat_map(|v| {
70 let index = v.type_index as u16;
71 (0..v.num_instances).map(move |_| index)
72 })
73 .collect::<Vec<_>>();
74
75 // Add each component and its type to the brick map
76 for (i, c) in components.iter().enumerate() {
77 let brick_index = indices[i as usize].as_brdb_u32()?;
78 let type_index = type_indices[i as usize];
79 if let Some(brick) = brick_map.get_mut(&(chunk.index, brick_index as usize)) {
80 brick.components.push((type_index, c.clone()));
81 } else {
82 continue;
83 }
84
85 // Register the component type if not already registered
86 if !component_map.contains_key(&type_index) {
87 component_map.insert(
88 type_index,
89 ComponentMeta {
90 wire_inputs: HashSet::new(),
91 wire_outputs: HashSet::new(),
92 },
93 );
94 }
95 }
96 }
97 }
98
99 // Add the wire ports to the component map
100 for chunk in &chunks {
101 if chunk.num_wires > 0 {
102 let soa = db.wire_chunk_soa(1, chunk.index)?.to_value();
103 let soa: WireChunkSoA = (&soa).try_into()?;
104 for port in soa.local_wire_sources {
105 if let Some(meta) = component_map.get_mut(&port.component_type_index) {
106 meta.wire_outputs.insert(port.port_index);
107 }
108 }
109 for port in soa.local_wire_targets {
110 if let Some(meta) = component_map.get_mut(&port.component_type_index) {
111 meta.wire_inputs.insert(port.port_index);
112 }
113 }
114 for port in soa.remote_wire_sources {
115 if let Some(meta) = component_map.get_mut(&port.component_type_index) {
116 meta.wire_outputs.insert(port.port_index);
117 }
118 }
119 for port in soa.remote_wire_targets {
120 if let Some(meta) = component_map.get_mut(&port.component_type_index) {
121 meta.wire_inputs.insert(port.port_index);
122 }
123 }
124 }
125 }
126
127 // Print the brick -> component mappings
128 for meta in brick_map.values() {
129 let brick_type_str = data.basic_brick_asset_names[meta.type_index as usize].clone();
130 for c in &meta.components {
131 let component_type_str = data.component_type_names[c.0 as usize].clone();
132 let c_entry = component_map.get(&c.0).unwrap();
133 let wire_inputs = c_entry
134 .wire_inputs
135 .iter()
136 .map(|i| {
137 format!(
138 " {}",
139 data.component_wire_port_names[*i as usize].to_owned()
140 )
141 })
142 .collect::<Vec<_>>()
143 .join("\n");
144 let wire_outputs = c_entry
145 .wire_outputs
146 .iter()
147 .map(|i| {
148 format!(
149 " {}",
150 data.component_wire_port_names[*i as usize].to_owned()
151 )
152 })
153 .collect::<Vec<_>>()
154 .join("\n");
155
156 let mut component_struct = String::new();
157 for (name, properties) in &component_schema.structs {
158 if name != &c.1.name {
159 continue;
160 }
161
162 let name = component_schema
163 .intern
164 .lookup(*name)
165 .unwrap_or("UnknownStruct".to_owned());
166 writeln!(component_struct, "struct {name} {{")?;
167 for (prop_name, prop_type) in properties {
168 let prop_name = component_schema
169 .intern
170 .lookup(*prop_name)
171 .unwrap_or("UnknownProperty".to_owned());
172 writeln!(
173 component_struct,
174 " {prop_name}: {},",
175 prop_type.as_string(&component_schema)
176 )?;
177 }
178 writeln!(component_struct, "}}")?;
179 }
180
181 println!(
182 "Brick: {}\nComponent: {}\n{}Inputs:\n{}\nOutputs:\n{}\n\n",
183 brick_type_str, component_type_str, component_struct, wire_inputs, wire_outputs
184 );
185 }
186 }
187
188 Ok(())
189}Sourcepub fn component_chunk_soa(
&self,
grid_id: usize,
chunk: ChunkIndex,
) -> Result<(ComponentChunkSoA, Vec<BrdbStruct>), BrError>where
T: BrFsReader,
pub fn component_chunk_soa(
&self,
grid_id: usize,
chunk: ChunkIndex,
) -> Result<(ComponentChunkSoA, Vec<BrdbStruct>), BrError>where
T: BrFsReader,
Read the shared component chunks
Examples found in repository?
5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 let path = PathBuf::from("./world.brz");
7
8 // The API for reading a brz is identical to reading a Brdb
9 let db = Brz::open(path)?.into_reader();
10
11 let data = db.global_data()?;
12 println!("Basic Brick assets: {:?}", data.basic_brick_asset_names);
13 println!("Wire ports: {:?}", data.component_wire_port_names);
14 println!("Component types: {:?}", data.component_type_names);
15 println!("Component structs: {:?}", data.component_data_struct_names);
16 println!("Component schemas: {}", db.components_schema()?);
17
18 let chunks = db.brick_chunk_index(1)?;
19 println!("Brick chunks: {chunks:?}");
20 for chunk in chunks {
21 let soa = db.brick_chunk_soa(1, chunk.index)?;
22 println!("Brick soa: {soa:?}");
23 if chunk.num_components > 0 {
24 let (_soa, components) = db.component_chunk_soa(1, chunk.index)?;
25 for c in components {
26 println!("Component: {c}");
27 }
28 }
29 if chunk.num_wires > 0 {
30 let soa = db.wire_chunk_soa(1, chunk.index)?;
31 println!("Wires soa: {soa}");
32 }
33 }
34
35 println!("Files: {}", db.get_fs()?.render());
36
37 Ok(())
38}More examples
4fn run<T: BrFsReader>(db: &BrReader<T>) -> Result<(), Box<dyn std::error::Error>> {
5 println!(
6 "Component types: {:?}",
7 db.global_data()?.component_type_names
8 );
9 println!(
10 "Component structs: {:?}",
11 db.global_data()?.component_data_struct_names
12 );
13 // Probe grid ids 1.. until one is missing (covers the main grid plus any
14 // microchip inner grids, which entity discovery may not surface).
15 for gid in 1..32 {
16 let chunks = match db.brick_chunk_index(gid) {
17 Ok(c) => c,
18 Err(_) => break,
19 };
20 println!("=== grid {gid} ===");
21 for chunk in chunks {
22 println!(
23 "chunk {} bricks={} components={} wires={}",
24 chunk.index, chunk.num_bricks, chunk.num_components, chunk.num_wires
25 );
26 if chunk.num_components > 0 {
27 match db.component_chunk_soa(gid, chunk.index) {
28 Ok((_soa, components)) => {
29 for c in components {
30 println!(" component: {c}");
31 }
32 }
33 Err(e) => {
34 println!(" ERROR reading components: {e}");
35 return Err(e.into());
36 }
37 }
38 }
39 }
40 }
41 Ok(())
42}5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 // world file from argv
7 let filename = std::env::args()
8 .nth(1)
9 .unwrap_or_else(|| "world.brdb".to_string());
10 let path = PathBuf::from(filename);
11 if !path.exists() {
12 eprintln!("File does not exist: {}", path.display());
13 std::process::exit(1);
14 }
15
16 let db = Brdb::open(path)?.into_reader();
17
18 let data = db.global_data()?;
19 println!("Basic Brick assets: {:?}", data.basic_brick_asset_names);
20 println!("Wire ports: {:?}", data.component_wire_port_names);
21 println!("Component types: {:?}", data.component_type_names);
22 println!("Component structs: {:?}", data.component_data_struct_names);
23 println!("Component schemas: {}", db.components_schema()?);
24
25 let mut grid_ids = vec![1];
26
27 // Iterate all entity chunks to find dynamic brick grids...
28 // This could totally be a helper function
29 for index in db.entity_chunk_index()? {
30 for e in db.entity_chunk(index)? {
31 // Ensure the chunk is a dynamic brick grid
32 if !e.is_brick_grid() {
33 continue;
34 }
35 let Some(id) = e.id else {
36 continue;
37 };
38 grid_ids.push(id);
39 }
40 }
41
42 for gid in grid_ids {
43 println!("Reading grid {gid}");
44 let chunks = db.brick_chunk_index(gid)?;
45 println!("Brick chunks: {chunks:?}");
46 for chunk in chunks {
47 let soa = db.brick_chunk_soa(gid, chunk.index)?;
48 println!("Brick Soa {chunk}: {soa:?}");
49 if chunk.num_components > 0 {
50 let (_soa, components) = db.component_chunk_soa(gid, chunk.index)?;
51 // println!("Components soa: {soa}");
52 for c in components {
53 println!("Component: {c}");
54 }
55 }
56 if chunk.num_wires > 0 {
57 let soa = db.wire_chunk_soa(gid, chunk.index)?;
58 println!("Wires soa: {soa}");
59 }
60 }
61 }
62
63 println!("Files: {}", db.get_fs()?.render());
64
65 Ok(())
66}18fn main() -> Result<(), Box<dyn std::error::Error>> {
19 let path = PathBuf::from("./world.brdb");
20
21 let db = Brdb::open(path)?.into_reader();
22
23 let data = db.global_data()?;
24 let component_schema = db.components_schema()?;
25
26 let chunks = db.brick_chunk_index(1)?;
27
28 // Track seen brick types
29 let mut brick_type_set = HashSet::new();
30
31 // Track seen component types to map to their wire ports
32 let mut component_map = HashMap::new();
33
34 // Track brick -> component mappings
35 let mut brick_map = HashMap::new();
36
37 for chunk in &chunks {
38 let soa = db.brick_chunk_soa(1, chunk.index)?;
39
40 // Iterate basic bricks
41 let pb_index = soa.procedural_brick_starting_index;
42 for (i, t) in soa.brick_type_indices.into_iter().enumerate() {
43 if t >= pb_index {
44 continue;
45 }
46 if brick_type_set.contains(&t) {
47 continue;
48 }
49 brick_type_set.insert(t);
50
51 // Insert bricks of unique types
52 brick_map.insert(
53 (chunk.index, i),
54 BrickMeta {
55 type_index: t,
56 components: Vec::new(),
57 },
58 );
59 }
60
61 if chunk.num_components > 0 {
62 let (soa, components) = db.component_chunk_soa(1, chunk.index)?;
63 let indices = soa.component_brick_indices;
64
65 // Expand the type index/num instances into a flat list of type indices
66 let type_indices = soa
67 .component_type_counters
68 .iter()
69 .flat_map(|v| {
70 let index = v.type_index as u16;
71 (0..v.num_instances).map(move |_| index)
72 })
73 .collect::<Vec<_>>();
74
75 // Add each component and its type to the brick map
76 for (i, c) in components.iter().enumerate() {
77 let brick_index = indices[i as usize].as_brdb_u32()?;
78 let type_index = type_indices[i as usize];
79 if let Some(brick) = brick_map.get_mut(&(chunk.index, brick_index as usize)) {
80 brick.components.push((type_index, c.clone()));
81 } else {
82 continue;
83 }
84
85 // Register the component type if not already registered
86 if !component_map.contains_key(&type_index) {
87 component_map.insert(
88 type_index,
89 ComponentMeta {
90 wire_inputs: HashSet::new(),
91 wire_outputs: HashSet::new(),
92 },
93 );
94 }
95 }
96 }
97 }
98
99 // Add the wire ports to the component map
100 for chunk in &chunks {
101 if chunk.num_wires > 0 {
102 let soa = db.wire_chunk_soa(1, chunk.index)?.to_value();
103 let soa: WireChunkSoA = (&soa).try_into()?;
104 for port in soa.local_wire_sources {
105 if let Some(meta) = component_map.get_mut(&port.component_type_index) {
106 meta.wire_outputs.insert(port.port_index);
107 }
108 }
109 for port in soa.local_wire_targets {
110 if let Some(meta) = component_map.get_mut(&port.component_type_index) {
111 meta.wire_inputs.insert(port.port_index);
112 }
113 }
114 for port in soa.remote_wire_sources {
115 if let Some(meta) = component_map.get_mut(&port.component_type_index) {
116 meta.wire_outputs.insert(port.port_index);
117 }
118 }
119 for port in soa.remote_wire_targets {
120 if let Some(meta) = component_map.get_mut(&port.component_type_index) {
121 meta.wire_inputs.insert(port.port_index);
122 }
123 }
124 }
125 }
126
127 // Print the brick -> component mappings
128 for meta in brick_map.values() {
129 let brick_type_str = data.basic_brick_asset_names[meta.type_index as usize].clone();
130 for c in &meta.components {
131 let component_type_str = data.component_type_names[c.0 as usize].clone();
132 let c_entry = component_map.get(&c.0).unwrap();
133 let wire_inputs = c_entry
134 .wire_inputs
135 .iter()
136 .map(|i| {
137 format!(
138 " {}",
139 data.component_wire_port_names[*i as usize].to_owned()
140 )
141 })
142 .collect::<Vec<_>>()
143 .join("\n");
144 let wire_outputs = c_entry
145 .wire_outputs
146 .iter()
147 .map(|i| {
148 format!(
149 " {}",
150 data.component_wire_port_names[*i as usize].to_owned()
151 )
152 })
153 .collect::<Vec<_>>()
154 .join("\n");
155
156 let mut component_struct = String::new();
157 for (name, properties) in &component_schema.structs {
158 if name != &c.1.name {
159 continue;
160 }
161
162 let name = component_schema
163 .intern
164 .lookup(*name)
165 .unwrap_or("UnknownStruct".to_owned());
166 writeln!(component_struct, "struct {name} {{")?;
167 for (prop_name, prop_type) in properties {
168 let prop_name = component_schema
169 .intern
170 .lookup(*prop_name)
171 .unwrap_or("UnknownProperty".to_owned());
172 writeln!(
173 component_struct,
174 " {prop_name}: {},",
175 prop_type.as_string(&component_schema)
176 )?;
177 }
178 writeln!(component_struct, "}}")?;
179 }
180
181 println!(
182 "Brick: {}\nComponent: {}\n{}Inputs:\n{}\nOutputs:\n{}\n\n",
183 brick_type_str, component_type_str, component_struct, wire_inputs, wire_outputs
184 );
185 }
186 }
187
188 Ok(())
189}18fn run<T: brdb::BrFsReader>(
19 db: brdb::BrReader<T>,
20) -> Result<(), Box<dyn std::error::Error>> {
21 let data = db.global_data()?;
22
23 // Collect all grid ids: main grid (1) + every brick-grid entity.
24 let mut grid_ids = vec![1usize];
25 for index in db.entity_chunk_index()? {
26 for e in db.entity_chunk(index)? {
27 if e.is_brick_grid() || e.is_microchip_grid() {
28 if let Some(id) = e.id {
29 grid_ids.push(id);
30 }
31 }
32 }
33 }
34
35 // Pass 1: per (grid, chunk) build brick component lists + brick counts.
36 // brick_components[(gid, chunk)][brick_index] = Vec<component_type_index>
37 let mut brick_counts: HashMap<(usize, String), usize> = HashMap::new();
38 let mut brick_components: HashMap<(usize, String), HashMap<u32, Vec<u16>>> = HashMap::new();
39 let mut brick_types: HashMap<(usize, String), Vec<u32>> = HashMap::new();
40 for &gid in &grid_ids {
41 let chunks = match db.brick_chunk_index(gid) {
42 Ok(c) => c,
43 Err(_) => continue,
44 };
45 for chunk in &chunks {
46 let key = (gid, format!("{:?}", chunk.index));
47 let soa = db.brick_chunk_soa(gid, chunk.index)?;
48 brick_counts.insert(key.clone(), soa.brick_type_indices.len());
49 brick_types.insert(key.clone(), soa.brick_type_indices.clone());
50 let mut per_brick: HashMap<u32, Vec<u16>> = HashMap::new();
51 if chunk.num_components > 0 {
52 let (csoa, _components) = db.component_chunk_soa(gid, chunk.index)?;
53 let type_indices: Vec<u16> = csoa
54 .component_type_counters
55 .iter()
56 .flat_map(|v| {
57 let index = v.type_index as u16;
58 (0..v.num_instances).map(move |_| index)
59 })
60 .collect();
61 for (i, bi) in csoa.component_brick_indices.iter().enumerate() {
62 let brick_index = *bi;
63 per_brick.entry(brick_index).or_default().push(type_indices[i]);
64 }
65 }
66 brick_components.insert(key, per_brick);
67 }
68 }
69
70 let cname = |t: u16| -> String {
71 data.component_type_names
72 .get_index(t as usize)
73 .map(|s| s.to_string())
74 .unwrap_or_else(|| format!("<type {t}>"))
75 };
76 let pname = |p: u16| -> String {
77 data.component_wire_port_names
78 .get_index(p as usize)
79 .map(|s| s.to_string())
80 .unwrap_or_else(|| format!("<port {p}>"))
81 };
82
83 // Pass 2: validate wires.
84 let mut total = 0u64;
85 let mut bad = 0u64;
86 for &gid in &grid_ids {
87 let chunks = match db.brick_chunk_index(gid) {
88 Ok(c) => c,
89 Err(_) => continue,
90 };
91 for chunk in &chunks {
92 if chunk.num_wires == 0 {
93 continue;
94 }
95 let key = (gid, format!("{:?}", chunk.index));
96 let soa = db.wire_chunk_soa(gid, chunk.index)?.to_value();
97 let soa: WireChunkSoA = (&soa).try_into()?;
98
99 let mut check = |ctx: &str,
100 ggid: usize,
101 ckey: &str,
102 brick_index: u32,
103 ct: u16,
104 port: u16| {
105 total += 1;
106 let k = (ggid, ckey.to_string());
107 let n = brick_counts.get(&k).copied().unwrap_or(0);
108 if brick_index as usize >= n {
109 bad += 1;
110 println!(
111 "BAD {ctx}: grid {ggid} chunk {ckey} brick {brick_index} OUT OF RANGE (chunk has {n} bricks); wanted {} {}",
112 cname(ct),
113 pname(port)
114 );
115 return;
116 }
117 let comps = brick_components
118 .get(&k)
119 .and_then(|m| m.get(&brick_index))
120 .cloned()
121 .unwrap_or_default();
122 if !comps.contains(&ct) {
123 bad += 1;
124 println!(
125 "BAD {ctx}: grid {ggid} chunk {ckey} brick {brick_index} has components [{}] but wire wants {} {}",
126 comps.iter().map(|c| cname(*c)).collect::<Vec<_>>().join(", "),
127 cname(ct),
128 pname(port)
129 );
130 }
131 };
132
133 for p in &soa.local_wire_sources {
134 check("local-src", gid, &key.1, p.brick_index_in_chunk, p.component_type_index, p.port_index);
135 }
136 for p in &soa.local_wire_targets {
137 check("local-tgt", gid, &key.1, p.brick_index_in_chunk, p.component_type_index, p.port_index);
138 }
139 for p in &soa.remote_wire_sources {
140 let ckey = format!("{:?}", p.chunk_index);
141 check(
142 "remote-src",
143 p.grid_persistent_index as usize,
144 &ckey,
145 p.brick_index_in_chunk,
146 p.component_type_index,
147 p.port_index,
148 );
149 }
150 // Remote wires: the source names another grid; the target is
151 // local to THIS chunk.
152 for p in &soa.remote_wire_targets {
153 check("remote-tgt", gid, &key.1, p.brick_index_in_chunk, p.component_type_index, p.port_index);
154 }
155 }
156 }
157
158 println!("validated {total} wire endpoints, {bad} bad");
159
160 // Optional dump: grid + index range, e.g. `-- file 2 580 590`
161 let args: Vec<String> = std::env::args().collect();
162 if args.len() >= 5 {
163 let gid: usize = args[2].parse()?;
164 let lo: u32 = args[3].parse()?;
165 let hi: u32 = args[4].parse()?;
166 for ((g, ckey), types) in &brick_types {
167 if *g != gid {
168 continue;
169 }
170 for i in lo..=hi.min(types.len().saturating_sub(1) as u32) {
171 let comps = brick_components
172 .get(&(gid, ckey.clone()))
173 .and_then(|m| m.get(&i))
174 .cloned()
175 .unwrap_or_default();
176 let asset = data
177 .basic_brick_asset_names
178 .get_index(types[i as usize] as usize)
179 .map(|s| s.to_string())
180 .unwrap_or_else(|| format!("<pb {}>", types[i as usize]));
181 println!(
182 "grid {gid} chunk {ckey} brick {i}: {asset} [{}]",
183 comps.iter().map(|c| cname(*c)).collect::<Vec<_>>().join(", ")
184 );
185 }
186 }
187 }
188 Ok(())
189}11fn run<T: BrFsReader>(db: &BrReader<T>) -> Result<(), Box<dyn std::error::Error>> {
12 let data = db.global_data()?;
13 println!("Basic Brick assets: {:?}", data.basic_brick_asset_names);
14 println!("Procedural Brick assets: {:?}", data.procedural_brick_asset_names);
15 // register_all_components() embeds the FULL catalog (hundreds of wire
16 // ports/component types), so only print counts for those — the fixture's
17 // OWN types/ports are visible per-brick below via the type-name lookups.
18 println!(
19 "Wire port count: {} (registered catalog)",
20 data.component_wire_port_names.len()
21 );
22 println!(
23 "Component type count: {} (registered catalog)",
24 data.component_type_names.len()
25 );
26 println!("Entity types: {:?}", data.entity_type_names);
27 println!("Entity classes: {:?}", data.entity_data_class_names);
28
29 // Probe grid ids 1.. until one is missing (grid 1 is always the main
30 // grid; higher ids are sub-grids / microchip inner grids).
31 for gid in 1..32 {
32 let chunks = match db.brick_chunk_index(gid) {
33 Ok(c) => c,
34 Err(_) => break,
35 };
36 println!("=== grid {gid} ===");
37 println!("Brick chunks: {chunks:?}");
38 for chunk in &chunks {
39 let soa = db.brick_chunk_soa(gid, chunk.index)?;
40 println!("Brick soa: {soa:?}");
41 let asset_names: Vec<String> = soa
42 .brick_type_indices
43 .iter()
44 .map(|&t| {
45 if (t as usize) < soa.procedural_brick_starting_index as usize {
46 data.basic_brick_asset_names
47 .get_index(t as usize)
48 .cloned()
49 .unwrap_or_default()
50 } else {
51 "<procedural>".to_string()
52 }
53 })
54 .collect();
55 println!("Brick asset names (by index in chunk): {asset_names:?}");
56
57 if chunk.num_components > 0 {
58 let (soa, components) = db.component_chunk_soa(gid, chunk.index)?;
59 let type_names: Vec<String> = soa
60 .component_type_counters
61 .iter()
62 .flat_map(|c| {
63 let name = data
64 .component_type_names
65 .get_index(c.type_index as usize)
66 .cloned()
67 .unwrap_or_default();
68 (0..c.num_instances).map(move |_| name.clone())
69 })
70 .collect();
71 println!(
72 "Component chunk soa: component_brick_indices={:?} microchip_brick_indices={:?} microchip_brick_grid_references={:?}",
73 soa.component_brick_indices,
74 soa.microchip_brick_indices,
75 soa.microchip_brick_grid_references
76 );
77 println!("Component type names (parallel to ComponentBrickIndices): {type_names:?}");
78 for c in components {
79 println!("Component: {c}");
80 }
81 }
82 if chunk.num_wires > 0 {
83 let raw = db.wire_chunk_soa(gid, chunk.index)?;
84 println!("Wire chunk soa (raw struct): {raw}");
85 let value = raw.to_value();
86 let soa: WireChunkSoA = (&value).try_into()?;
87 let port_name = |i: u16| {
88 data.component_wire_port_names
89 .get_index(i as usize)
90 .cloned()
91 .unwrap_or_default()
92 };
93 let type_name = |i: u16| {
94 data.component_type_names
95 .get_index(i as usize)
96 .cloned()
97 .unwrap_or_default()
98 };
99 for s in &soa.local_wire_sources {
100 println!(
101 " local source: brick_in_chunk={} type={} port={}",
102 s.brick_index_in_chunk,
103 type_name(s.component_type_index),
104 port_name(s.port_index)
105 );
106 }
107 for t in &soa.local_wire_targets {
108 println!(
109 " local target: brick_in_chunk={} type={} port={}",
110 t.brick_index_in_chunk,
111 type_name(t.component_type_index),
112 port_name(t.port_index)
113 );
114 }
115 for s in &soa.remote_wire_sources {
116 println!(
117 " remote source: grid_persistent_index={} chunk={} brick_in_chunk={} type={} port={}",
118 s.grid_persistent_index,
119 s.chunk_index,
120 s.brick_index_in_chunk,
121 type_name(s.component_type_index),
122 port_name(s.port_index)
123 );
124 }
125 for t in &soa.remote_wire_targets {
126 println!(
127 " remote target: brick_in_chunk={} type={} port={}",
128 t.brick_index_in_chunk,
129 type_name(t.component_type_index),
130 port_name(t.port_index)
131 );
132 }
133 }
134 }
135 }
136
137 // Entity chunks (present whenever the world has any grids/entities).
138 let entity_chunk_indices = db.entity_chunk_index()?;
139 println!("=== entities ===");
140 println!("Entity chunk indices: {entity_chunk_indices:?}");
141 for chunk_index in entity_chunk_indices {
142 let (soa, entity_data) = db.entity_chunk_soa(chunk_index)?;
143 println!("--- Chunk {chunk_index} (SoA) ---");
144 println!("Type counters: {:?}", soa.type_counters);
145 let type_names: Vec<String> = soa
146 .type_counters
147 .iter()
148 .flat_map(|c| {
149 let name = data
150 .entity_type_names
151 .get_index(c.type_index as usize)
152 .cloned()
153 .unwrap_or_default();
154 (0..c.num_entities).map(move |_| name.clone())
155 })
156 .collect();
157 println!("Entity type names (parallel to PersistentIndices): {type_names:?}");
158 println!("Persistent indices: {:?}", soa.persistent_indices);
159 println!("Locations: {:?}", soa.locations);
160 println!("Rotations: {:?}", soa.rotations);
161 println!("Physics locked (frozen): {:?}", soa.physics_locked_flags);
162 println!("Physics sleeping: {:?}", soa.physics_sleeping_flags);
163 for (i, data) in entity_data.iter().enumerate() {
164 match data {
165 Some(struct_data) => println!(" Entity {i} struct: {struct_data}"),
166 None => println!(" Entity {i}: None"),
167 }
168 }
169 }
170
171 println!("Files: {}", db.get_fs()?.render());
172
173 Ok(())
174}Sourcepub fn component_chunk(
&self,
grid_id: usize,
chunk: ChunkIndex,
) -> Result<(ComponentChunkSoA, Vec<BrdbStruct>), BrError>where
T: BrFsReader,
pub fn component_chunk(
&self,
grid_id: usize,
chunk: ChunkIndex,
) -> Result<(ComponentChunkSoA, Vec<BrdbStruct>), BrError>where
T: BrFsReader,
Read the shared component chunks
Examples found in repository?
5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 let src = PathBuf::from("world.brdb");
7 let dst = PathBuf::from("world_patched.brdb");
8
9 assert!(src.exists());
10
11 let db = Brdb::open(src)?.into_reader();
12
13 let mut grid_ids = vec![1];
14
15 // Iterate all entity chunks to find dynamic brick grids...
16 // This could totally be a helper function
17 for index in db.entity_chunk_index()? {
18 for e in db.entity_chunk(index)? {
19 // Ensure the chunk is a dynamic brick grid
20 if !e.is_brick_grid() {
21 continue;
22 }
23 let Some(id) = e.id else {
24 continue;
25 };
26 grid_ids.push(id);
27 }
28 }
29
30 let component_schema = db.components_schema()?;
31 let mut grids_files = vec![];
32
33 // Iterate all grids (there can be bricks on entities)
34 for grid in &grid_ids {
35 let chunks = db.brick_chunk_index(*grid)?;
36 let mut chunk_files = vec![];
37 let mut num_grid_modified = 0;
38
39 // Iterate all chunks in the grid
40 for index in chunks {
41 let mut num_chunk_modified = 0;
42 if index.num_components == 0 {
43 println!("ignoring grid {grid} chunk {} with no components", *index);
44 continue;
45 }
46
47 // Iterate all the components in the chunk
48 let (mut soa, components) = db.component_chunk(*grid, *index)?;
49 for mut s in components {
50 // Disable the shadow casting property if it's present and true
51 if s.prop("bCastShadows")
52 .is_ok_and(|v| v.as_brdb_bool().unwrap_or_default())
53 {
54 println!(
55 "grid {grid} chunk {} mutating component {}",
56 *index,
57 s.get_name()
58 );
59 s.set_prop("bCastShadows", BrdbValue::Bool(false))?;
60 num_grid_modified += 1;
61 num_chunk_modified += 1;
62 }
63
64 soa.unwritten_struct_data.push(Box::new(s));
65 }
66
67 if num_chunk_modified == 0 {
68 continue;
69 }
70
71 chunk_files.push((
72 format!("{}.mps", *index),
73 // ComponentChunkSoA::to_bytes ensures the extra data is written after the SoA data
74 BrPendingFs::File(Some(soa.to_bytes(&component_schema)?)),
75 ));
76 }
77
78 if num_grid_modified == 0 {
79 println!("grid {grid} has no shadow-casting components, skipping");
80 continue;
81 } else {
82 println!(
83 "grid {grid} has {num_grid_modified} shadow-casting components in {} files",
84 chunk_files.len()
85 );
86 }
87
88 grids_files.push((
89 grid.to_string(),
90 BrPendingFs::Folder(Some(vec![(
91 "Components".to_string(),
92 BrPendingFs::Folder(Some(chunk_files)),
93 )])),
94 ))
95 }
96
97 let patch = BrPendingFs::Root(vec![(
98 "World".to_owned(),
99 BrPendingFs::Folder(Some(vec![(
100 "0".to_string(),
101 BrPendingFs::Folder(Some(vec![(
102 "Bricks".to_string(),
103 BrPendingFs::Folder(Some(vec![(
104 "Grids".to_string(),
105 BrPendingFs::Folder(Some(grids_files)),
106 )])),
107 )])),
108 )])),
109 )]);
110
111 // Use .to_pending_patch() if you want to update the same world
112 let pending = db.to_pending()?.with_patch(patch)?;
113 if dst.exists() {
114 std::fs::remove_file(&dst)?;
115 }
116 Brdb::new(&dst)?.write_pending("Disable Shadow Casting", pending)?;
117
118 // Ensure all the components can be read
119 let db = Brdb::open(dst)?.into_reader();
120 for grid in grid_ids {
121 let chunks = db.brick_chunk_index(grid)?;
122 for index in chunks {
123 if index.num_components == 0 {
124 continue;
125 }
126 let (_soa, _components) = db.component_chunk(grid, *index)?;
127 }
128 }
129
130 Ok(())
131}More examples
104fn main() -> Result<(), Box<dyn std::error::Error>> {
105 let args: Vec<String> = std::env::args().collect();
106 let path = PathBuf::from(args.get(1).expect("usage: extract_defaults <dump.brdb> [output.rs]"));
107 let output_path = args.get(2).map(PathBuf::from);
108 let db = Brdb::open(path)?.into_reader();
109 let global_data = db.global_data()?;
110 let schema = db.components_schema()?;
111
112 let mut type_to_struct: BTreeMap<String, String> = BTreeMap::new();
113 let mut wire_ports: BTreeSet<String> = BTreeSet::new();
114 let mut struct_defaults: BTreeMap<String, Vec<(String, String, String)>> = BTreeMap::new();
115
116 for (i, type_name) in global_data.component_type_names.iter().enumerate() {
117 if let Some(struct_name) = global_data.component_data_struct_names.get(i) {
118 if struct_name != "None" {
119 type_to_struct.insert(type_name.clone(), struct_name.clone());
120 }
121 }
122 }
123
124 for port in &global_data.component_wire_port_names {
125 wire_ports.insert(port.clone());
126 }
127
128 for chunk in db.brick_chunk_index(1)? {
129 if chunk.num_components == 0 {
130 continue;
131 }
132 let Ok((_soa, components)) = db.component_chunk(1, *chunk) else {
133 continue;
134 };
135 for s in components {
136 let name = s.get_name().to_owned();
137 if struct_defaults.contains_key(&name) {
138 continue;
139 }
140
141 let Some(struct_def) = schema.get_struct(&name) else {
142 continue;
143 };
144 let s_id = match schema.intern.get(&name) {
145 Some(id) => id,
146 None => continue,
147 };
148
149 let mut fields = Vec::new();
150 for (field_id, prop_ty) in struct_def {
151 let field_name = match field_id.get(&schema) {
152 Some(n) => n.to_owned(),
153 None => continue,
154 };
155 let ty_str = match prop_ty {
156 BrdbSchemaStructProperty::Type(t) => {
157 match schema.intern.lookup_ref(*t) {
158 Some(s) => s.to_owned(),
159 None => continue,
160 }
161 }
162 _ => continue,
163 };
164 let val = match s.as_brdb_struct_prop_value(&schema, s_id, *field_id) {
165 Ok(v) => v,
166 Err(_) => continue,
167 };
168 let formatted = match format_value(&schema, &ty_str, val) {
169 Some(f) => f,
170 None => continue,
171 };
172 fields.push((field_name, ty_str, formatted));
173 }
174 struct_defaults.insert(name, fields);
175 }
176 }
177
178 use std::fmt::Write;
179 let mut out = String::new();
180 macro_rules! w { ($($t:tt)*) => { writeln!(out, $($t)*).unwrap() } }
181
182 w!("// Autogenerated from: cargo run --example extract_defaults -- path/to/dump.brdb");
183 w!();
184
185 w!("pub static COMPONENT_TYPE_STRUCT_PAIRS: &[(&str, &str)] = &[");
186 for (type_name, struct_name) in &type_to_struct {
187 w!(" (\"{type_name}\", \"{struct_name}\"),");
188 }
189 w!("];");
190 w!();
191
192 w!("pub static WIRE_PORT_NAMES: &[&str] = &[");
193 for port in &wire_ports {
194 w!(" \"{port}\",");
195 }
196 w!("];");
197 w!();
198
199 w!("pub static ENTITY_TYPE_STRUCT_PAIRS: &[(&str, &str)] = &[");
200 for (i, type_name) in global_data.entity_type_names.iter().enumerate() {
201 if let Some(class_name) = global_data.entity_data_class_names.get_index(i) {
202 w!(" (\"{type_name}\", \"{class_name}\"),");
203 }
204 }
205 w!("];");
206 w!();
207
208 w!("use std::sync::LazyLock;");
209 // WireVariant is only referenced when a dump carries wire-variant defaults;
210 // allow it to be unused so a dump without any still compiles clean.
211 w!("#[allow(unused_imports)]");
212 w!("use crate::schema::WireVariant;");
213 w!("use crate::schema::as_brdb::AsBrdbValue;");
214 // NestedStructDefault carries struct-typed defaults (Vector2D/LinearColor/...); allow it
215 // to be unused so a dump without any nested-struct defaults still compiles clean.
216 w!("#[allow(unused_imports)]");
217 w!("use crate::schema::as_brdb::NestedStructDefault;");
218 w!("use crate::SavedBrickColor;");
219 w!();
220 w!("/// Default field values for every component data struct.");
221 w!("pub static STRUCT_DEFAULTS: LazyLock<Vec<(&'static str, Vec<(&'static str, Box<dyn AsBrdbValue>)>)>> =");
222 w!(" LazyLock::new(|| vec![");
223 let mut first_entry = true;
224 for (name, fields) in &struct_defaults {
225 if fields.is_empty() {
226 continue;
227 }
228 w!(" (\"{name}\", vec![");
229 for (field_name, _ty, val) in fields {
230 if first_entry {
231 w!(" (\"{field_name}\", {val}),");
232 first_entry = false;
233 } else {
234 let short = val.trim_end_matches(" as Box<dyn AsBrdbValue>");
235 w!(" (\"{field_name}\", {short}),");
236 }
237 }
238 w!(" ]),");
239 }
240 w!(" ]);");
241
242 if let Some(ref p) = output_path {
243 std::fs::write(p, &out)?;
244 eprintln!("Wrote {}", p.display());
245 } else {
246 print!("{out}");
247 }
248
249 eprintln!(
250 "Extracted: {} type mappings, {} wire ports, {} struct defaults, {} entity types",
251 type_to_struct.len(),
252 wire_ports.len(),
253 struct_defaults.len(),
254 global_data.entity_type_names.len(),
255 );
256
257 Ok(())
258}Sourcepub fn wires_schema_rev(
&self,
revision: i64,
) -> Result<Arc<BrdbSchema>, BrError>where
T: BrFsReader,
pub fn wires_schema_rev(
&self,
revision: i64,
) -> Result<Arc<BrdbSchema>, BrError>where
T: BrFsReader,
Read the shared wires chunk schema at a specific revision
Sourcepub fn wires_schema(&self) -> Result<Arc<BrdbSchema>, BrError>where
T: BrFsReader,
pub fn wires_schema(&self) -> Result<Arc<BrdbSchema>, BrError>where
T: BrFsReader,
Read the shared wires chunk schema (latest revision)
Sourcepub fn wire_chunk_soa(
&self,
grid_id: usize,
chunk: ChunkIndex,
) -> Result<BrdbStruct, BrError>where
T: BrFsReader,
pub fn wire_chunk_soa(
&self,
grid_id: usize,
chunk: ChunkIndex,
) -> Result<BrdbStruct, BrError>where
T: BrFsReader,
Examples found in repository?
5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 let path = PathBuf::from("./world.brz");
7
8 // The API for reading a brz is identical to reading a Brdb
9 let db = Brz::open(path)?.into_reader();
10
11 let data = db.global_data()?;
12 println!("Basic Brick assets: {:?}", data.basic_brick_asset_names);
13 println!("Wire ports: {:?}", data.component_wire_port_names);
14 println!("Component types: {:?}", data.component_type_names);
15 println!("Component structs: {:?}", data.component_data_struct_names);
16 println!("Component schemas: {}", db.components_schema()?);
17
18 let chunks = db.brick_chunk_index(1)?;
19 println!("Brick chunks: {chunks:?}");
20 for chunk in chunks {
21 let soa = db.brick_chunk_soa(1, chunk.index)?;
22 println!("Brick soa: {soa:?}");
23 if chunk.num_components > 0 {
24 let (_soa, components) = db.component_chunk_soa(1, chunk.index)?;
25 for c in components {
26 println!("Component: {c}");
27 }
28 }
29 if chunk.num_wires > 0 {
30 let soa = db.wire_chunk_soa(1, chunk.index)?;
31 println!("Wires soa: {soa}");
32 }
33 }
34
35 println!("Files: {}", db.get_fs()?.render());
36
37 Ok(())
38}More examples
5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 // world file from argv
7 let filename = std::env::args()
8 .nth(1)
9 .unwrap_or_else(|| "world.brdb".to_string());
10 let path = PathBuf::from(filename);
11 if !path.exists() {
12 eprintln!("File does not exist: {}", path.display());
13 std::process::exit(1);
14 }
15
16 let db = Brdb::open(path)?.into_reader();
17
18 let data = db.global_data()?;
19 println!("Basic Brick assets: {:?}", data.basic_brick_asset_names);
20 println!("Wire ports: {:?}", data.component_wire_port_names);
21 println!("Component types: {:?}", data.component_type_names);
22 println!("Component structs: {:?}", data.component_data_struct_names);
23 println!("Component schemas: {}", db.components_schema()?);
24
25 let mut grid_ids = vec![1];
26
27 // Iterate all entity chunks to find dynamic brick grids...
28 // This could totally be a helper function
29 for index in db.entity_chunk_index()? {
30 for e in db.entity_chunk(index)? {
31 // Ensure the chunk is a dynamic brick grid
32 if !e.is_brick_grid() {
33 continue;
34 }
35 let Some(id) = e.id else {
36 continue;
37 };
38 grid_ids.push(id);
39 }
40 }
41
42 for gid in grid_ids {
43 println!("Reading grid {gid}");
44 let chunks = db.brick_chunk_index(gid)?;
45 println!("Brick chunks: {chunks:?}");
46 for chunk in chunks {
47 let soa = db.brick_chunk_soa(gid, chunk.index)?;
48 println!("Brick Soa {chunk}: {soa:?}");
49 if chunk.num_components > 0 {
50 let (_soa, components) = db.component_chunk_soa(gid, chunk.index)?;
51 // println!("Components soa: {soa}");
52 for c in components {
53 println!("Component: {c}");
54 }
55 }
56 if chunk.num_wires > 0 {
57 let soa = db.wire_chunk_soa(gid, chunk.index)?;
58 println!("Wires soa: {soa}");
59 }
60 }
61 }
62
63 println!("Files: {}", db.get_fs()?.render());
64
65 Ok(())
66}18fn main() -> Result<(), Box<dyn std::error::Error>> {
19 let path = PathBuf::from("./world.brdb");
20
21 let db = Brdb::open(path)?.into_reader();
22
23 let data = db.global_data()?;
24 let component_schema = db.components_schema()?;
25
26 let chunks = db.brick_chunk_index(1)?;
27
28 // Track seen brick types
29 let mut brick_type_set = HashSet::new();
30
31 // Track seen component types to map to their wire ports
32 let mut component_map = HashMap::new();
33
34 // Track brick -> component mappings
35 let mut brick_map = HashMap::new();
36
37 for chunk in &chunks {
38 let soa = db.brick_chunk_soa(1, chunk.index)?;
39
40 // Iterate basic bricks
41 let pb_index = soa.procedural_brick_starting_index;
42 for (i, t) in soa.brick_type_indices.into_iter().enumerate() {
43 if t >= pb_index {
44 continue;
45 }
46 if brick_type_set.contains(&t) {
47 continue;
48 }
49 brick_type_set.insert(t);
50
51 // Insert bricks of unique types
52 brick_map.insert(
53 (chunk.index, i),
54 BrickMeta {
55 type_index: t,
56 components: Vec::new(),
57 },
58 );
59 }
60
61 if chunk.num_components > 0 {
62 let (soa, components) = db.component_chunk_soa(1, chunk.index)?;
63 let indices = soa.component_brick_indices;
64
65 // Expand the type index/num instances into a flat list of type indices
66 let type_indices = soa
67 .component_type_counters
68 .iter()
69 .flat_map(|v| {
70 let index = v.type_index as u16;
71 (0..v.num_instances).map(move |_| index)
72 })
73 .collect::<Vec<_>>();
74
75 // Add each component and its type to the brick map
76 for (i, c) in components.iter().enumerate() {
77 let brick_index = indices[i as usize].as_brdb_u32()?;
78 let type_index = type_indices[i as usize];
79 if let Some(brick) = brick_map.get_mut(&(chunk.index, brick_index as usize)) {
80 brick.components.push((type_index, c.clone()));
81 } else {
82 continue;
83 }
84
85 // Register the component type if not already registered
86 if !component_map.contains_key(&type_index) {
87 component_map.insert(
88 type_index,
89 ComponentMeta {
90 wire_inputs: HashSet::new(),
91 wire_outputs: HashSet::new(),
92 },
93 );
94 }
95 }
96 }
97 }
98
99 // Add the wire ports to the component map
100 for chunk in &chunks {
101 if chunk.num_wires > 0 {
102 let soa = db.wire_chunk_soa(1, chunk.index)?.to_value();
103 let soa: WireChunkSoA = (&soa).try_into()?;
104 for port in soa.local_wire_sources {
105 if let Some(meta) = component_map.get_mut(&port.component_type_index) {
106 meta.wire_outputs.insert(port.port_index);
107 }
108 }
109 for port in soa.local_wire_targets {
110 if let Some(meta) = component_map.get_mut(&port.component_type_index) {
111 meta.wire_inputs.insert(port.port_index);
112 }
113 }
114 for port in soa.remote_wire_sources {
115 if let Some(meta) = component_map.get_mut(&port.component_type_index) {
116 meta.wire_outputs.insert(port.port_index);
117 }
118 }
119 for port in soa.remote_wire_targets {
120 if let Some(meta) = component_map.get_mut(&port.component_type_index) {
121 meta.wire_inputs.insert(port.port_index);
122 }
123 }
124 }
125 }
126
127 // Print the brick -> component mappings
128 for meta in brick_map.values() {
129 let brick_type_str = data.basic_brick_asset_names[meta.type_index as usize].clone();
130 for c in &meta.components {
131 let component_type_str = data.component_type_names[c.0 as usize].clone();
132 let c_entry = component_map.get(&c.0).unwrap();
133 let wire_inputs = c_entry
134 .wire_inputs
135 .iter()
136 .map(|i| {
137 format!(
138 " {}",
139 data.component_wire_port_names[*i as usize].to_owned()
140 )
141 })
142 .collect::<Vec<_>>()
143 .join("\n");
144 let wire_outputs = c_entry
145 .wire_outputs
146 .iter()
147 .map(|i| {
148 format!(
149 " {}",
150 data.component_wire_port_names[*i as usize].to_owned()
151 )
152 })
153 .collect::<Vec<_>>()
154 .join("\n");
155
156 let mut component_struct = String::new();
157 for (name, properties) in &component_schema.structs {
158 if name != &c.1.name {
159 continue;
160 }
161
162 let name = component_schema
163 .intern
164 .lookup(*name)
165 .unwrap_or("UnknownStruct".to_owned());
166 writeln!(component_struct, "struct {name} {{")?;
167 for (prop_name, prop_type) in properties {
168 let prop_name = component_schema
169 .intern
170 .lookup(*prop_name)
171 .unwrap_or("UnknownProperty".to_owned());
172 writeln!(
173 component_struct,
174 " {prop_name}: {},",
175 prop_type.as_string(&component_schema)
176 )?;
177 }
178 writeln!(component_struct, "}}")?;
179 }
180
181 println!(
182 "Brick: {}\nComponent: {}\n{}Inputs:\n{}\nOutputs:\n{}\n\n",
183 brick_type_str, component_type_str, component_struct, wire_inputs, wire_outputs
184 );
185 }
186 }
187
188 Ok(())
189}18fn run<T: brdb::BrFsReader>(
19 db: brdb::BrReader<T>,
20) -> Result<(), Box<dyn std::error::Error>> {
21 let data = db.global_data()?;
22
23 // Collect all grid ids: main grid (1) + every brick-grid entity.
24 let mut grid_ids = vec![1usize];
25 for index in db.entity_chunk_index()? {
26 for e in db.entity_chunk(index)? {
27 if e.is_brick_grid() || e.is_microchip_grid() {
28 if let Some(id) = e.id {
29 grid_ids.push(id);
30 }
31 }
32 }
33 }
34
35 // Pass 1: per (grid, chunk) build brick component lists + brick counts.
36 // brick_components[(gid, chunk)][brick_index] = Vec<component_type_index>
37 let mut brick_counts: HashMap<(usize, String), usize> = HashMap::new();
38 let mut brick_components: HashMap<(usize, String), HashMap<u32, Vec<u16>>> = HashMap::new();
39 let mut brick_types: HashMap<(usize, String), Vec<u32>> = HashMap::new();
40 for &gid in &grid_ids {
41 let chunks = match db.brick_chunk_index(gid) {
42 Ok(c) => c,
43 Err(_) => continue,
44 };
45 for chunk in &chunks {
46 let key = (gid, format!("{:?}", chunk.index));
47 let soa = db.brick_chunk_soa(gid, chunk.index)?;
48 brick_counts.insert(key.clone(), soa.brick_type_indices.len());
49 brick_types.insert(key.clone(), soa.brick_type_indices.clone());
50 let mut per_brick: HashMap<u32, Vec<u16>> = HashMap::new();
51 if chunk.num_components > 0 {
52 let (csoa, _components) = db.component_chunk_soa(gid, chunk.index)?;
53 let type_indices: Vec<u16> = csoa
54 .component_type_counters
55 .iter()
56 .flat_map(|v| {
57 let index = v.type_index as u16;
58 (0..v.num_instances).map(move |_| index)
59 })
60 .collect();
61 for (i, bi) in csoa.component_brick_indices.iter().enumerate() {
62 let brick_index = *bi;
63 per_brick.entry(brick_index).or_default().push(type_indices[i]);
64 }
65 }
66 brick_components.insert(key, per_brick);
67 }
68 }
69
70 let cname = |t: u16| -> String {
71 data.component_type_names
72 .get_index(t as usize)
73 .map(|s| s.to_string())
74 .unwrap_or_else(|| format!("<type {t}>"))
75 };
76 let pname = |p: u16| -> String {
77 data.component_wire_port_names
78 .get_index(p as usize)
79 .map(|s| s.to_string())
80 .unwrap_or_else(|| format!("<port {p}>"))
81 };
82
83 // Pass 2: validate wires.
84 let mut total = 0u64;
85 let mut bad = 0u64;
86 for &gid in &grid_ids {
87 let chunks = match db.brick_chunk_index(gid) {
88 Ok(c) => c,
89 Err(_) => continue,
90 };
91 for chunk in &chunks {
92 if chunk.num_wires == 0 {
93 continue;
94 }
95 let key = (gid, format!("{:?}", chunk.index));
96 let soa = db.wire_chunk_soa(gid, chunk.index)?.to_value();
97 let soa: WireChunkSoA = (&soa).try_into()?;
98
99 let mut check = |ctx: &str,
100 ggid: usize,
101 ckey: &str,
102 brick_index: u32,
103 ct: u16,
104 port: u16| {
105 total += 1;
106 let k = (ggid, ckey.to_string());
107 let n = brick_counts.get(&k).copied().unwrap_or(0);
108 if brick_index as usize >= n {
109 bad += 1;
110 println!(
111 "BAD {ctx}: grid {ggid} chunk {ckey} brick {brick_index} OUT OF RANGE (chunk has {n} bricks); wanted {} {}",
112 cname(ct),
113 pname(port)
114 );
115 return;
116 }
117 let comps = brick_components
118 .get(&k)
119 .and_then(|m| m.get(&brick_index))
120 .cloned()
121 .unwrap_or_default();
122 if !comps.contains(&ct) {
123 bad += 1;
124 println!(
125 "BAD {ctx}: grid {ggid} chunk {ckey} brick {brick_index} has components [{}] but wire wants {} {}",
126 comps.iter().map(|c| cname(*c)).collect::<Vec<_>>().join(", "),
127 cname(ct),
128 pname(port)
129 );
130 }
131 };
132
133 for p in &soa.local_wire_sources {
134 check("local-src", gid, &key.1, p.brick_index_in_chunk, p.component_type_index, p.port_index);
135 }
136 for p in &soa.local_wire_targets {
137 check("local-tgt", gid, &key.1, p.brick_index_in_chunk, p.component_type_index, p.port_index);
138 }
139 for p in &soa.remote_wire_sources {
140 let ckey = format!("{:?}", p.chunk_index);
141 check(
142 "remote-src",
143 p.grid_persistent_index as usize,
144 &ckey,
145 p.brick_index_in_chunk,
146 p.component_type_index,
147 p.port_index,
148 );
149 }
150 // Remote wires: the source names another grid; the target is
151 // local to THIS chunk.
152 for p in &soa.remote_wire_targets {
153 check("remote-tgt", gid, &key.1, p.brick_index_in_chunk, p.component_type_index, p.port_index);
154 }
155 }
156 }
157
158 println!("validated {total} wire endpoints, {bad} bad");
159
160 // Optional dump: grid + index range, e.g. `-- file 2 580 590`
161 let args: Vec<String> = std::env::args().collect();
162 if args.len() >= 5 {
163 let gid: usize = args[2].parse()?;
164 let lo: u32 = args[3].parse()?;
165 let hi: u32 = args[4].parse()?;
166 for ((g, ckey), types) in &brick_types {
167 if *g != gid {
168 continue;
169 }
170 for i in lo..=hi.min(types.len().saturating_sub(1) as u32) {
171 let comps = brick_components
172 .get(&(gid, ckey.clone()))
173 .and_then(|m| m.get(&i))
174 .cloned()
175 .unwrap_or_default();
176 let asset = data
177 .basic_brick_asset_names
178 .get_index(types[i as usize] as usize)
179 .map(|s| s.to_string())
180 .unwrap_or_else(|| format!("<pb {}>", types[i as usize]));
181 println!(
182 "grid {gid} chunk {ckey} brick {i}: {asset} [{}]",
183 comps.iter().map(|c| cname(*c)).collect::<Vec<_>>().join(", ")
184 );
185 }
186 }
187 }
188 Ok(())
189}11fn run<T: BrFsReader>(db: &BrReader<T>) -> Result<(), Box<dyn std::error::Error>> {
12 let data = db.global_data()?;
13 println!("Basic Brick assets: {:?}", data.basic_brick_asset_names);
14 println!("Procedural Brick assets: {:?}", data.procedural_brick_asset_names);
15 // register_all_components() embeds the FULL catalog (hundreds of wire
16 // ports/component types), so only print counts for those — the fixture's
17 // OWN types/ports are visible per-brick below via the type-name lookups.
18 println!(
19 "Wire port count: {} (registered catalog)",
20 data.component_wire_port_names.len()
21 );
22 println!(
23 "Component type count: {} (registered catalog)",
24 data.component_type_names.len()
25 );
26 println!("Entity types: {:?}", data.entity_type_names);
27 println!("Entity classes: {:?}", data.entity_data_class_names);
28
29 // Probe grid ids 1.. until one is missing (grid 1 is always the main
30 // grid; higher ids are sub-grids / microchip inner grids).
31 for gid in 1..32 {
32 let chunks = match db.brick_chunk_index(gid) {
33 Ok(c) => c,
34 Err(_) => break,
35 };
36 println!("=== grid {gid} ===");
37 println!("Brick chunks: {chunks:?}");
38 for chunk in &chunks {
39 let soa = db.brick_chunk_soa(gid, chunk.index)?;
40 println!("Brick soa: {soa:?}");
41 let asset_names: Vec<String> = soa
42 .brick_type_indices
43 .iter()
44 .map(|&t| {
45 if (t as usize) < soa.procedural_brick_starting_index as usize {
46 data.basic_brick_asset_names
47 .get_index(t as usize)
48 .cloned()
49 .unwrap_or_default()
50 } else {
51 "<procedural>".to_string()
52 }
53 })
54 .collect();
55 println!("Brick asset names (by index in chunk): {asset_names:?}");
56
57 if chunk.num_components > 0 {
58 let (soa, components) = db.component_chunk_soa(gid, chunk.index)?;
59 let type_names: Vec<String> = soa
60 .component_type_counters
61 .iter()
62 .flat_map(|c| {
63 let name = data
64 .component_type_names
65 .get_index(c.type_index as usize)
66 .cloned()
67 .unwrap_or_default();
68 (0..c.num_instances).map(move |_| name.clone())
69 })
70 .collect();
71 println!(
72 "Component chunk soa: component_brick_indices={:?} microchip_brick_indices={:?} microchip_brick_grid_references={:?}",
73 soa.component_brick_indices,
74 soa.microchip_brick_indices,
75 soa.microchip_brick_grid_references
76 );
77 println!("Component type names (parallel to ComponentBrickIndices): {type_names:?}");
78 for c in components {
79 println!("Component: {c}");
80 }
81 }
82 if chunk.num_wires > 0 {
83 let raw = db.wire_chunk_soa(gid, chunk.index)?;
84 println!("Wire chunk soa (raw struct): {raw}");
85 let value = raw.to_value();
86 let soa: WireChunkSoA = (&value).try_into()?;
87 let port_name = |i: u16| {
88 data.component_wire_port_names
89 .get_index(i as usize)
90 .cloned()
91 .unwrap_or_default()
92 };
93 let type_name = |i: u16| {
94 data.component_type_names
95 .get_index(i as usize)
96 .cloned()
97 .unwrap_or_default()
98 };
99 for s in &soa.local_wire_sources {
100 println!(
101 " local source: brick_in_chunk={} type={} port={}",
102 s.brick_index_in_chunk,
103 type_name(s.component_type_index),
104 port_name(s.port_index)
105 );
106 }
107 for t in &soa.local_wire_targets {
108 println!(
109 " local target: brick_in_chunk={} type={} port={}",
110 t.brick_index_in_chunk,
111 type_name(t.component_type_index),
112 port_name(t.port_index)
113 );
114 }
115 for s in &soa.remote_wire_sources {
116 println!(
117 " remote source: grid_persistent_index={} chunk={} brick_in_chunk={} type={} port={}",
118 s.grid_persistent_index,
119 s.chunk_index,
120 s.brick_index_in_chunk,
121 type_name(s.component_type_index),
122 port_name(s.port_index)
123 );
124 }
125 for t in &soa.remote_wire_targets {
126 println!(
127 " remote target: brick_in_chunk={} type={} port={}",
128 t.brick_index_in_chunk,
129 type_name(t.component_type_index),
130 port_name(t.port_index)
131 );
132 }
133 }
134 }
135 }
136
137 // Entity chunks (present whenever the world has any grids/entities).
138 let entity_chunk_indices = db.entity_chunk_index()?;
139 println!("=== entities ===");
140 println!("Entity chunk indices: {entity_chunk_indices:?}");
141 for chunk_index in entity_chunk_indices {
142 let (soa, entity_data) = db.entity_chunk_soa(chunk_index)?;
143 println!("--- Chunk {chunk_index} (SoA) ---");
144 println!("Type counters: {:?}", soa.type_counters);
145 let type_names: Vec<String> = soa
146 .type_counters
147 .iter()
148 .flat_map(|c| {
149 let name = data
150 .entity_type_names
151 .get_index(c.type_index as usize)
152 .cloned()
153 .unwrap_or_default();
154 (0..c.num_entities).map(move |_| name.clone())
155 })
156 .collect();
157 println!("Entity type names (parallel to PersistentIndices): {type_names:?}");
158 println!("Persistent indices: {:?}", soa.persistent_indices);
159 println!("Locations: {:?}", soa.locations);
160 println!("Rotations: {:?}", soa.rotations);
161 println!("Physics locked (frozen): {:?}", soa.physics_locked_flags);
162 println!("Physics sleeping: {:?}", soa.physics_sleeping_flags);
163 for (i, data) in entity_data.iter().enumerate() {
164 match data {
165 Some(struct_data) => println!(" Entity {i} struct: {struct_data}"),
166 None => println!(" Entity {i}: None"),
167 }
168 }
169 }
170
171 println!("Files: {}", db.get_fs()?.render());
172
173 Ok(())
174}18fn main() -> Result<(), Box<dyn std::error::Error>> {
19 let args: Vec<String> = std::env::args().collect();
20 let path = PathBuf::from(
21 args.get(1)
22 .expect("usage: extract_components <zoo.brdb> [out.rs]"),
23 );
24 let out_path = args.get(2).map(PathBuf::from);
25 let db = Brdb::open(path)?.into_reader();
26 let data = db.global_data()?;
27
28 // The standalone phase attaches otherwise-brickless components to this generic host for
29 // probing; it is never a real component host, so it is filtered out of the host lists.
30 let standalone_host_brick = brdb::assets::bricks::B_1X1F_ROUND;
31 let standalone_host: &str = standalone_host_brick.asset().as_ref();
32
33 // component type index -> host brick asset name(s) / wire port indices.
34 // Host names are resolved inline (basic and procedural bricks index different
35 // name sets), so this stores names rather than type indices.
36 let mut bricks: BTreeMap<u16, BTreeSet<String>> = BTreeMap::new();
37 let mut inputs: BTreeMap<u16, BTreeSet<u16>> = BTreeMap::new();
38 let mut outputs: BTreeMap<u16, BTreeSet<u16>> = BTreeMap::new();
39
40 // Grid 1 is the main grid; higher ids are microchip inner grids. Probe
41 // until a grid id is missing (same convention as read_components).
42 for gid in 1..64 {
43 let chunks = match db.brick_chunk_index(gid) {
44 Ok(c) => c,
45 Err(_) => break,
46 };
47 for chunk in &chunks {
48 // brick index -> brick type index (basic-brick asset), for the
49 // host-brick mapping.
50 let bsoa = db.brick_chunk_soa(gid, chunk.index)?;
51 let pb_start = bsoa.procedural_brick_starting_index;
52 // Procedural brick type index (>= pb_start) -> procedural asset index, via the
53 // per-size run-length counters (same expansion SoA::iter_bricks uses).
54 let proc_asset_by_size: Vec<u32> = bsoa
55 .brick_size_counters
56 .iter()
57 .flat_map(|c| std::iter::repeat(c.asset_index).take(c.num_sizes as usize))
58 .collect();
59 let brick_types = bsoa.brick_type_indices;
60
61 if chunk.num_components > 0 {
62 // component_chunk's Vec<BrdbStruct> only contains STRUCT-BEARING components
63 // (it skips whole counters whose type has no data struct, e.g. some
64 // Component_Internal_* gates), so its len() < the per-instance total. Looping
65 // `0..components.len()` truncated the per-instance type/brick arrays, dropping
66 // the host brick of any component in the tail (e.g. Component_Internal_InputSplitter).
67 // Iterate the FULL per-instance count instead, matching brs-js's reader.
68 let (csoa, _components) = db.component_chunk_soa(gid, chunk.index)?;
69 let brick_indices = csoa.component_brick_indices;
70 // Expand run-length (type_index, num_instances) into a flat
71 // per-instance list of component type indices.
72 let type_indices = csoa
73 .component_type_counters
74 .iter()
75 .flat_map(|v| {
76 let ti = v.type_index as u16;
77 (0..v.num_instances).map(move |_| ti)
78 })
79 .collect::<Vec<_>>();
80 for i in 0..type_indices.len() {
81 let comp_ty = type_indices[i];
82 // ensure every placed component is present even with no wires
83 inputs.entry(comp_ty).or_default();
84 outputs.entry(comp_ty).or_default();
85 let brick_index = brick_indices[i].as_brdb_u32()? as usize;
86 if let Some(&bt) = brick_types.get(brick_index) {
87 // Basic bricks index basic_brick_asset_names directly; procedural
88 // bricks (type index >= pb_start) map through the size run-lengths to
89 // a procedural_brick_asset_names index.
90 let host = if bt < pb_start {
91 data.basic_brick_asset_names.get_index(bt as usize).cloned()
92 } else {
93 proc_asset_by_size
94 .get((bt - pb_start) as usize)
95 .and_then(|&ai| {
96 data.procedural_brick_asset_names.get_index(ai as usize).cloned()
97 })
98 };
99 if let Some(host) = host {
100 if host != standalone_host {
101 bricks.entry(comp_ty).or_default().insert(host);
102 }
103 }
104 }
105 }
106 }
107
108 if chunk.num_wires > 0 {
109 let soa = db.wire_chunk_soa(gid, chunk.index)?.to_value();
110 let soa: WireChunkSoA = (&soa).try_into()?;
111 // local and remote ports are distinct types, so handle each in
112 // its own loop (they share component_type_index / port_index).
113 for p in &soa.local_wire_sources {
114 outputs
115 .entry(p.component_type_index)
116 .or_default()
117 .insert(p.port_index);
118 }
119 for p in &soa.remote_wire_sources {
120 outputs
121 .entry(p.component_type_index)
122 .or_default()
123 .insert(p.port_index);
124 }
125 for p in &soa.local_wire_targets {
126 inputs
127 .entry(p.component_type_index)
128 .or_default()
129 .insert(p.port_index);
130 }
131 for p in &soa.remote_wire_targets {
132 inputs
133 .entry(p.component_type_index)
134 .or_default()
135 .insert(p.port_index);
136 }
137 }
138 }
139 }
140
141 // Resolve indices to names.
142 let name_of = |idx: u16| data.component_type_names.get_index(idx as usize).cloned();
143 let port_of = |idx: u16| data.component_wire_port_names.get_index(idx as usize).cloned();
144
145 let all: BTreeSet<u16> = bricks
146 .keys()
147 .chain(inputs.keys())
148 .chain(outputs.keys())
149 .copied()
150 .collect();
151
152 // (name, host bricks, input ports, output ports), sorted by name.
153 let mut rows: Vec<(String, Vec<String>, Vec<String>, Vec<String>)> = Vec::new();
154 for ty in all {
155 let Some(name) = name_of(ty) else { continue };
156 let mut bs: Vec<String> = bricks
157 .get(&ty)
158 .map(|s| s.iter().cloned().collect())
159 .unwrap_or_default();
160 bs.sort();
161 bs.dedup();
162 let mut ins: Vec<String> = inputs
163 .get(&ty)
164 .map(|s| s.iter().filter_map(|&p| port_of(p)).collect())
165 .unwrap_or_default();
166 ins.sort();
167 ins.dedup();
168 let mut outs: Vec<String> = outputs
169 .get(&ty)
170 .map(|s| s.iter().filter_map(|&p| port_of(p)).collect())
171 .unwrap_or_default();
172 outs.sort();
173 outs.dedup();
174 rows.push((name, bs, ins, outs));
175 }
176 rows.sort_by(|a, b| a.0.cmp(&b.0));
177
178 let mut out = String::new();
179 macro_rules! w {
180 ($($t:tt)*) => { writeln!(out, $($t)*).unwrap() };
181 }
182 let slice = |v: &[String]| {
183 v.iter()
184 .map(|s| format!("{s:?}"))
185 .collect::<Vec<_>>()
186 .join(", ")
187 };
188
189 w!("// Autogenerated from a zoo save:");
190 w!("// cargo run --example extract_components -- <zoo.brdb> src/assets/component_catalog.rs");
191 w!("// Do not edit by hand.");
192 w!();
193 w!("/// Per-component catalog entry: the full component type name, its host");
194 w!("/// brick asset(s), and its wire input/output port names. Extracted from a");
195 w!("/// fully-placed, fully-wired \"zoo\" save (mirrors brs-js's COMPONENTS).");
196 w!("#[derive(Debug, Clone, Copy, PartialEq, Eq)]");
197 w!("pub struct ComponentInfo {{");
198 w!(" /// e.g. \"BrickComponentType_WireGraph_Exec_Branch\".");
199 w!(" pub name: &'static str,");
200 w!(" /// Host brick asset name(s) that carry this component.");
201 w!(" pub bricks: &'static [&'static str],");
202 w!(" /// Wire input port names.");
203 w!(" pub inputs: &'static [&'static str],");
204 w!(" /// Wire output port names.");
205 w!(" pub outputs: &'static [&'static str],");
206 w!("}}");
207 w!();
208 w!("impl ComponentInfo {{");
209 w!(" /// The primary host brick asset (first, if any).");
210 w!(" pub const fn brick(&self) -> Option<&'static str> {{");
211 w!(" self.bricks.first().copied()");
212 w!(" }}");
213 w!("}}");
214 w!();
215 w!(
216 "/// Every component present in the zoo, sorted by `name` (binary-searchable)."
217 );
218 w!("pub static COMPONENTS: &[ComponentInfo] = &[");
219 for (name, bs, ins, outs) in &rows {
220 w!(
221 " ComponentInfo {{ name: {name:?}, bricks: &[{}], inputs: &[{}], outputs: &[{}] }},",
222 slice(bs),
223 slice(ins),
224 slice(outs),
225 );
226 }
227 w!("];");
228 w!();
229 w!("/// Look up a component by its full type name.");
230 w!("pub fn component(name: &str) -> Option<&'static ComponentInfo> {{");
231 w!(" COMPONENTS");
232 w!(" .binary_search_by(|c| c.name.cmp(name))");
233 w!(" .ok()");
234 w!(" .map(|i| &COMPONENTS[i])");
235 w!("}}");
236
237 if let Some(ref p) = out_path {
238 std::fs::write(p, &out)?;
239 eprintln!("Wrote {}", p.display());
240 } else {
241 print!("{out}");
242 }
243 eprintln!(
244 "Extracted {} components ({} with host bricks)",
245 rows.len(),
246 rows.iter().filter(|r| !r.1.is_empty()).count(),
247 );
248 Ok(())
249}Sourcepub fn brick_chunk_index_schema_rev(
&self,
revision: i64,
) -> Result<Arc<BrdbSchema>, BrError>where
T: BrFsReader,
pub fn brick_chunk_index_schema_rev(
&self,
revision: i64,
) -> Result<Arc<BrdbSchema>, BrError>where
T: BrFsReader,
Read the shared brick-chunk-index schema at a specific revision
Sourcepub fn brick_chunk_index_schema(&self) -> Result<Arc<BrdbSchema>, BrError>where
T: BrFsReader,
pub fn brick_chunk_index_schema(&self) -> Result<Arc<BrdbSchema>, BrError>where
T: BrFsReader,
Read the shared brick-chunk-index schema (latest revision)
Sourcepub fn bricks_schema_rev(
&self,
revision: i64,
) -> Result<Arc<BrdbSchema>, BrError>where
T: BrFsReader,
pub fn bricks_schema_rev(
&self,
revision: i64,
) -> Result<Arc<BrdbSchema>, BrError>where
T: BrFsReader,
Read the shared bricks chunk schema at a specific revision
Sourcepub fn bricks_schema(&self) -> Result<Arc<BrdbSchema>, BrError>where
T: BrFsReader,
pub fn bricks_schema(&self) -> Result<Arc<BrdbSchema>, BrError>where
T: BrFsReader,
Read the shared bricks chunk schema (latest revision)
Sourcepub fn brick_chunk_index(
&self,
grid_id: usize,
) -> Result<Vec<ChunkMeta>, BrError>where
T: BrFsReader,
pub fn brick_chunk_index(
&self,
grid_id: usize,
) -> Result<Vec<ChunkMeta>, BrError>where
T: BrFsReader,
Read the brick chunk indices for a specific grid
Examples found in repository?
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5 let path = PathBuf::from(
6 std::env::args()
7 .nth(1)
8 .unwrap_or_else(|| "world.brz".to_string()),
9 );
10 let db = Brz::open(path)?.into_reader();
11
12 let mut grid_ids = vec![1];
13 for index in db.entity_chunk_index()? {
14 for e in db.entity_chunk(index)? {
15 if e.is_brick_grid() || e.is_microchip_grid() {
16 if let Some(id) = e.id {
17 grid_ids.push(id);
18 }
19 }
20 }
21 }
22
23 let mut total_wires = 0u64;
24 let mut total_bricks = 0u64;
25 for &gid in &grid_ids {
26 let chunks = db.brick_chunk_index(gid)?;
27 for chunk in &chunks {
28 total_bricks += chunk.num_bricks as u64;
29 total_wires += chunk.num_wires as u64;
30 }
31 }
32
33 println!("grids: {}", grid_ids.len());
34 println!("bricks: {total_bricks}");
35 println!("wires: {total_wires}");
36 Ok(())
37}More examples
5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 let path = PathBuf::from("./world.brz");
7
8 // The API for reading a brz is identical to reading a Brdb
9 let db = Brz::open(path)?.into_reader();
10
11 let data = db.global_data()?;
12 println!("Basic Brick assets: {:?}", data.basic_brick_asset_names);
13 println!("Wire ports: {:?}", data.component_wire_port_names);
14 println!("Component types: {:?}", data.component_type_names);
15 println!("Component structs: {:?}", data.component_data_struct_names);
16 println!("Component schemas: {}", db.components_schema()?);
17
18 let chunks = db.brick_chunk_index(1)?;
19 println!("Brick chunks: {chunks:?}");
20 for chunk in chunks {
21 let soa = db.brick_chunk_soa(1, chunk.index)?;
22 println!("Brick soa: {soa:?}");
23 if chunk.num_components > 0 {
24 let (_soa, components) = db.component_chunk_soa(1, chunk.index)?;
25 for c in components {
26 println!("Component: {c}");
27 }
28 }
29 if chunk.num_wires > 0 {
30 let soa = db.wire_chunk_soa(1, chunk.index)?;
31 println!("Wires soa: {soa}");
32 }
33 }
34
35 println!("Files: {}", db.get_fs()?.render());
36
37 Ok(())
38}4fn run<T: BrFsReader>(db: &BrReader<T>) -> Result<(), Box<dyn std::error::Error>> {
5 println!(
6 "Component types: {:?}",
7 db.global_data()?.component_type_names
8 );
9 println!(
10 "Component structs: {:?}",
11 db.global_data()?.component_data_struct_names
12 );
13 // Probe grid ids 1.. until one is missing (covers the main grid plus any
14 // microchip inner grids, which entity discovery may not surface).
15 for gid in 1..32 {
16 let chunks = match db.brick_chunk_index(gid) {
17 Ok(c) => c,
18 Err(_) => break,
19 };
20 println!("=== grid {gid} ===");
21 for chunk in chunks {
22 println!(
23 "chunk {} bricks={} components={} wires={}",
24 chunk.index, chunk.num_bricks, chunk.num_components, chunk.num_wires
25 );
26 if chunk.num_components > 0 {
27 match db.component_chunk_soa(gid, chunk.index) {
28 Ok((_soa, components)) => {
29 for c in components {
30 println!(" component: {c}");
31 }
32 }
33 Err(e) => {
34 println!(" ERROR reading components: {e}");
35 return Err(e.into());
36 }
37 }
38 }
39 }
40 }
41 Ok(())
42}5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 let path = PathBuf::from("world.brdb");
7 let dst = PathBuf::from("world_patched.brdb");
8
9 println!("Warning - This code will break if the brick chunk struct changes!!");
10
11 let db = Brdb::open(path)?.into_reader();
12
13 let data = db.global_data()?;
14 let mut grid = UnsavedGrid::default();
15
16 let mut total_bricks = 0;
17 for chunk in db.brick_chunk_index(1)? {
18 for brick in db
19 .brick_chunk_soa(1, chunk.index)?
20 .iter_bricks(chunk.index, data.clone())
21 {
22 // If we wanted wires/components, we'd need to track the bricks here by their chunk index and brick index
23 total_bricks += 1;
24
25 let mut brick = brick?;
26 brick.position += Position::new(3000, 0, 0);
27 grid.add_brick(data.as_ref(), &brick);
28 }
29
30 if chunk.num_components > 0 {
31 println!("sorry, this example doesn't handle components");
32 }
33 if chunk.num_wires > 0 {
34 println!("sorry, this example doesn't handle wires");
35 }
36 }
37 println!("{total_bricks} bricks");
38
39 let mut pending = db.to_pending()?;
40
41 // Replace the main grid (1) with the grid we created
42 *pending.cd_mut("World/0/Bricks/Grids/1")? = grid.to_pending(
43 data.proc_brick_starting_index(),
44 db.components_schema()?.as_ref(),
45 )?;
46
47 if dst.exists() {
48 std::fs::remove_file(&dst)?;
49 }
50 Brdb::new(&dst)?.write_pending("Move the bricks", pending)?;
51
52 // Verify bricks can be read
53 let db = Brdb::open(dst)?.into_reader();
54 for chunk in db.brick_chunk_index(1)? {
55 let _ = db.brick_chunk_soa(1, chunk.index)?;
56 }
57
58 Ok(())
59}5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 // world file from argv
7 let filename = std::env::args()
8 .nth(1)
9 .unwrap_or_else(|| "world.brdb".to_string());
10 let path = PathBuf::from(filename);
11 if !path.exists() {
12 eprintln!("File does not exist: {}", path.display());
13 std::process::exit(1);
14 }
15
16 let db = Brdb::open(path)?.into_reader();
17
18 let data = db.global_data()?;
19 println!("Basic Brick assets: {:?}", data.basic_brick_asset_names);
20 println!("Wire ports: {:?}", data.component_wire_port_names);
21 println!("Component types: {:?}", data.component_type_names);
22 println!("Component structs: {:?}", data.component_data_struct_names);
23 println!("Component schemas: {}", db.components_schema()?);
24
25 let mut grid_ids = vec![1];
26
27 // Iterate all entity chunks to find dynamic brick grids...
28 // This could totally be a helper function
29 for index in db.entity_chunk_index()? {
30 for e in db.entity_chunk(index)? {
31 // Ensure the chunk is a dynamic brick grid
32 if !e.is_brick_grid() {
33 continue;
34 }
35 let Some(id) = e.id else {
36 continue;
37 };
38 grid_ids.push(id);
39 }
40 }
41
42 for gid in grid_ids {
43 println!("Reading grid {gid}");
44 let chunks = db.brick_chunk_index(gid)?;
45 println!("Brick chunks: {chunks:?}");
46 for chunk in chunks {
47 let soa = db.brick_chunk_soa(gid, chunk.index)?;
48 println!("Brick Soa {chunk}: {soa:?}");
49 if chunk.num_components > 0 {
50 let (_soa, components) = db.component_chunk_soa(gid, chunk.index)?;
51 // println!("Components soa: {soa}");
52 for c in components {
53 println!("Component: {c}");
54 }
55 }
56 if chunk.num_wires > 0 {
57 let soa = db.wire_chunk_soa(gid, chunk.index)?;
58 println!("Wires soa: {soa}");
59 }
60 }
61 }
62
63 println!("Files: {}", db.get_fs()?.render());
64
65 Ok(())
66}5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 let path = PathBuf::from(
7 std::env::args()
8 .nth(1)
9 .unwrap_or_else(|| "world.brz".to_string()),
10 );
11 let db = Brz::open(path)?.into_reader();
12
13 let mut grid_ids = vec![1];
14 for index in db.entity_chunk_index()? {
15 for e in db.entity_chunk(index)? {
16 if e.is_brick_grid() || e.is_microchip_grid() {
17 if let Some(id) = e.id {
18 grid_ids.push(id);
19 }
20 }
21 }
22 }
23
24 // For each grid, collect a fingerprint: (num_bricks, num_wires, num_chunks)
25 // to find grids that look identical.
26 let mut fingerprints: HashMap<(u64, u64, usize), Vec<usize>> = HashMap::new();
27 let mut total_bricks = 0u64;
28 let mut total_wires = 0u64;
29 let mut total_components = 0u64;
30
31 for &gid in &grid_ids {
32 let chunks = db.brick_chunk_index(gid)?;
33 let mut grid_bricks = 0u64;
34 let mut grid_wires = 0u64;
35 let mut grid_components = 0u64;
36 for chunk in &chunks {
37 grid_bricks += chunk.num_bricks as u64;
38 grid_wires += chunk.num_wires as u64;
39 grid_components += chunk.num_components as u64;
40 }
41 total_bricks += grid_bricks;
42 total_wires += grid_wires;
43 total_components += grid_components;
44
45 let fp = (grid_bricks, grid_wires, chunks.len());
46 fingerprints.entry(fp).or_default().push(gid);
47 }
48
49 println!("=== Grid Summary ===");
50 println!("total grids: {}", grid_ids.len());
51 println!("total bricks: {total_bricks}");
52 println!("total wires: {total_wires}");
53 println!("total components: {total_components}");
54 println!();
55
56 println!("=== Unique Grid Shapes ===");
57 let mut fps: Vec<_> = fingerprints.iter().collect();
58 fps.sort_by_key(|((b, _w, _), grids)| std::cmp::Reverse(*b * grids.len() as u64));
59
60 for ((bricks, wires, chunks), grids) in &fps {
61 let savings = if grids.len() > 1 {
62 format!(
63 " → {} could be deduplicated (save {} bricks, {} wires)",
64 grids.len() - 1,
65 bricks * (grids.len() as u64 - 1),
66 wires * (grids.len() as u64 - 1)
67 )
68 } else {
69 String::new()
70 };
71 println!(
72 " {}× ({} bricks, {} wires, {} chunks){}",
73 grids.len(),
74 bricks,
75 wires,
76 chunks,
77 savings
78 );
79 }
80
81 // Top 10 largest grids
82 println!();
83 println!("=== Top 10 Largest Grids ===");
84 let mut grid_sizes: Vec<(usize, u64, u64)> = Vec::new();
85 for &gid in &grid_ids {
86 let chunks = db.brick_chunk_index(gid)?;
87 let b: u64 = chunks.iter().map(|c| c.num_bricks as u64).sum();
88 let w: u64 = chunks.iter().map(|c| c.num_wires as u64).sum();
89 grid_sizes.push((gid, b, w));
90 }
91 grid_sizes.sort_by_key(|(_, b, _)| std::cmp::Reverse(*b));
92 for (gid, b, w) in grid_sizes.iter().take(10) {
93 println!(" grid {gid}: {b} bricks, {w} wires");
94 }
95
96 Ok(())
97}Sourcepub fn brick_chunk_soa(
&self,
grid_id: usize,
chunk: ChunkIndex,
) -> Result<BrickChunkSoA, BrError>where
T: BrFsReader,
pub fn brick_chunk_soa(
&self,
grid_id: usize,
chunk: ChunkIndex,
) -> Result<BrickChunkSoA, BrError>where
T: BrFsReader,
Examples found in repository?
5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 let path = PathBuf::from("./example_brick.brdb");
7
8 let mut world = World::new();
9 world.meta.bundle.description = "Example World".to_string();
10 world.bricks.push(Brick {
11 position: (0, 0, 6).into(),
12 color: (255, 0, 0).into(),
13 ..Default::default()
14 });
15
16 world.write_brdb(&path)?;
17
18 let db = Brdb::new(&path)?.into_reader();
19
20 println!("file structure: {}", db.get_fs()?.render());
21
22 let soa = db.brick_chunk_soa(1, (0, 0, 0).into())?;
23 let color = soa.colors_and_alphas[0];
24 assert_eq!(color.r, 255);
25 assert_eq!(color.g, 0);
26 assert_eq!(color.b, 0);
27 assert_eq!(color.a, 5);
28
29 Ok(())
30}More examples
5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 let path = PathBuf::from("./example_brick.brz");
7
8 let mut world = World::new();
9 world.meta.bundle.description = "Example World".to_string();
10 world.bricks.push(Brick {
11 position: (0, 0, 6).into(),
12 color: (255, 0, 0).into(),
13 ..Default::default()
14 });
15
16 if path.exists() {
17 std::fs::remove_file(&path)?;
18 }
19 world.write_brz(&path)?;
20
21 let db = Brz::new(&path)?.into_reader();
22
23 println!("{}", db.get_fs()?.render());
24
25 let soa = db.brick_chunk_soa(1, (0, 0, 0).into())?;
26 let color = soa.colors_and_alphas[0];
27 assert_eq!(color.r, 255);
28 assert_eq!(color.g, 0);
29 assert_eq!(color.b, 0);
30 assert_eq!(color.a, 5);
31
32 Ok(())
33}5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 let path = PathBuf::from("./world.brz");
7
8 // The API for reading a brz is identical to reading a Brdb
9 let db = Brz::open(path)?.into_reader();
10
11 let data = db.global_data()?;
12 println!("Basic Brick assets: {:?}", data.basic_brick_asset_names);
13 println!("Wire ports: {:?}", data.component_wire_port_names);
14 println!("Component types: {:?}", data.component_type_names);
15 println!("Component structs: {:?}", data.component_data_struct_names);
16 println!("Component schemas: {}", db.components_schema()?);
17
18 let chunks = db.brick_chunk_index(1)?;
19 println!("Brick chunks: {chunks:?}");
20 for chunk in chunks {
21 let soa = db.brick_chunk_soa(1, chunk.index)?;
22 println!("Brick soa: {soa:?}");
23 if chunk.num_components > 0 {
24 let (_soa, components) = db.component_chunk_soa(1, chunk.index)?;
25 for c in components {
26 println!("Component: {c}");
27 }
28 }
29 if chunk.num_wires > 0 {
30 let soa = db.wire_chunk_soa(1, chunk.index)?;
31 println!("Wires soa: {soa}");
32 }
33 }
34
35 println!("Files: {}", db.get_fs()?.render());
36
37 Ok(())
38}5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 let path = PathBuf::from("world.brdb");
7 let dst = PathBuf::from("world_patched.brdb");
8
9 println!("Warning - This code will break if the brick chunk struct changes!!");
10
11 let db = Brdb::open(path)?.into_reader();
12
13 let data = db.global_data()?;
14 let mut grid = UnsavedGrid::default();
15
16 let mut total_bricks = 0;
17 for chunk in db.brick_chunk_index(1)? {
18 for brick in db
19 .brick_chunk_soa(1, chunk.index)?
20 .iter_bricks(chunk.index, data.clone())
21 {
22 // If we wanted wires/components, we'd need to track the bricks here by their chunk index and brick index
23 total_bricks += 1;
24
25 let mut brick = brick?;
26 brick.position += Position::new(3000, 0, 0);
27 grid.add_brick(data.as_ref(), &brick);
28 }
29
30 if chunk.num_components > 0 {
31 println!("sorry, this example doesn't handle components");
32 }
33 if chunk.num_wires > 0 {
34 println!("sorry, this example doesn't handle wires");
35 }
36 }
37 println!("{total_bricks} bricks");
38
39 let mut pending = db.to_pending()?;
40
41 // Replace the main grid (1) with the grid we created
42 *pending.cd_mut("World/0/Bricks/Grids/1")? = grid.to_pending(
43 data.proc_brick_starting_index(),
44 db.components_schema()?.as_ref(),
45 )?;
46
47 if dst.exists() {
48 std::fs::remove_file(&dst)?;
49 }
50 Brdb::new(&dst)?.write_pending("Move the bricks", pending)?;
51
52 // Verify bricks can be read
53 let db = Brdb::open(dst)?.into_reader();
54 for chunk in db.brick_chunk_index(1)? {
55 let _ = db.brick_chunk_soa(1, chunk.index)?;
56 }
57
58 Ok(())
59}5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 // world file from argv
7 let filename = std::env::args()
8 .nth(1)
9 .unwrap_or_else(|| "world.brdb".to_string());
10 let path = PathBuf::from(filename);
11 if !path.exists() {
12 eprintln!("File does not exist: {}", path.display());
13 std::process::exit(1);
14 }
15
16 let db = Brdb::open(path)?.into_reader();
17
18 let data = db.global_data()?;
19 println!("Basic Brick assets: {:?}", data.basic_brick_asset_names);
20 println!("Wire ports: {:?}", data.component_wire_port_names);
21 println!("Component types: {:?}", data.component_type_names);
22 println!("Component structs: {:?}", data.component_data_struct_names);
23 println!("Component schemas: {}", db.components_schema()?);
24
25 let mut grid_ids = vec![1];
26
27 // Iterate all entity chunks to find dynamic brick grids...
28 // This could totally be a helper function
29 for index in db.entity_chunk_index()? {
30 for e in db.entity_chunk(index)? {
31 // Ensure the chunk is a dynamic brick grid
32 if !e.is_brick_grid() {
33 continue;
34 }
35 let Some(id) = e.id else {
36 continue;
37 };
38 grid_ids.push(id);
39 }
40 }
41
42 for gid in grid_ids {
43 println!("Reading grid {gid}");
44 let chunks = db.brick_chunk_index(gid)?;
45 println!("Brick chunks: {chunks:?}");
46 for chunk in chunks {
47 let soa = db.brick_chunk_soa(gid, chunk.index)?;
48 println!("Brick Soa {chunk}: {soa:?}");
49 if chunk.num_components > 0 {
50 let (_soa, components) = db.component_chunk_soa(gid, chunk.index)?;
51 // println!("Components soa: {soa}");
52 for c in components {
53 println!("Component: {c}");
54 }
55 }
56 if chunk.num_wires > 0 {
57 let soa = db.wire_chunk_soa(gid, chunk.index)?;
58 println!("Wires soa: {soa}");
59 }
60 }
61 }
62
63 println!("Files: {}", db.get_fs()?.render());
64
65 Ok(())
66}18fn main() -> Result<(), Box<dyn std::error::Error>> {
19 let path = PathBuf::from("./world.brdb");
20
21 let db = Brdb::open(path)?.into_reader();
22
23 let data = db.global_data()?;
24 let component_schema = db.components_schema()?;
25
26 let chunks = db.brick_chunk_index(1)?;
27
28 // Track seen brick types
29 let mut brick_type_set = HashSet::new();
30
31 // Track seen component types to map to their wire ports
32 let mut component_map = HashMap::new();
33
34 // Track brick -> component mappings
35 let mut brick_map = HashMap::new();
36
37 for chunk in &chunks {
38 let soa = db.brick_chunk_soa(1, chunk.index)?;
39
40 // Iterate basic bricks
41 let pb_index = soa.procedural_brick_starting_index;
42 for (i, t) in soa.brick_type_indices.into_iter().enumerate() {
43 if t >= pb_index {
44 continue;
45 }
46 if brick_type_set.contains(&t) {
47 continue;
48 }
49 brick_type_set.insert(t);
50
51 // Insert bricks of unique types
52 brick_map.insert(
53 (chunk.index, i),
54 BrickMeta {
55 type_index: t,
56 components: Vec::new(),
57 },
58 );
59 }
60
61 if chunk.num_components > 0 {
62 let (soa, components) = db.component_chunk_soa(1, chunk.index)?;
63 let indices = soa.component_brick_indices;
64
65 // Expand the type index/num instances into a flat list of type indices
66 let type_indices = soa
67 .component_type_counters
68 .iter()
69 .flat_map(|v| {
70 let index = v.type_index as u16;
71 (0..v.num_instances).map(move |_| index)
72 })
73 .collect::<Vec<_>>();
74
75 // Add each component and its type to the brick map
76 for (i, c) in components.iter().enumerate() {
77 let brick_index = indices[i as usize].as_brdb_u32()?;
78 let type_index = type_indices[i as usize];
79 if let Some(brick) = brick_map.get_mut(&(chunk.index, brick_index as usize)) {
80 brick.components.push((type_index, c.clone()));
81 } else {
82 continue;
83 }
84
85 // Register the component type if not already registered
86 if !component_map.contains_key(&type_index) {
87 component_map.insert(
88 type_index,
89 ComponentMeta {
90 wire_inputs: HashSet::new(),
91 wire_outputs: HashSet::new(),
92 },
93 );
94 }
95 }
96 }
97 }
98
99 // Add the wire ports to the component map
100 for chunk in &chunks {
101 if chunk.num_wires > 0 {
102 let soa = db.wire_chunk_soa(1, chunk.index)?.to_value();
103 let soa: WireChunkSoA = (&soa).try_into()?;
104 for port in soa.local_wire_sources {
105 if let Some(meta) = component_map.get_mut(&port.component_type_index) {
106 meta.wire_outputs.insert(port.port_index);
107 }
108 }
109 for port in soa.local_wire_targets {
110 if let Some(meta) = component_map.get_mut(&port.component_type_index) {
111 meta.wire_inputs.insert(port.port_index);
112 }
113 }
114 for port in soa.remote_wire_sources {
115 if let Some(meta) = component_map.get_mut(&port.component_type_index) {
116 meta.wire_outputs.insert(port.port_index);
117 }
118 }
119 for port in soa.remote_wire_targets {
120 if let Some(meta) = component_map.get_mut(&port.component_type_index) {
121 meta.wire_inputs.insert(port.port_index);
122 }
123 }
124 }
125 }
126
127 // Print the brick -> component mappings
128 for meta in brick_map.values() {
129 let brick_type_str = data.basic_brick_asset_names[meta.type_index as usize].clone();
130 for c in &meta.components {
131 let component_type_str = data.component_type_names[c.0 as usize].clone();
132 let c_entry = component_map.get(&c.0).unwrap();
133 let wire_inputs = c_entry
134 .wire_inputs
135 .iter()
136 .map(|i| {
137 format!(
138 " {}",
139 data.component_wire_port_names[*i as usize].to_owned()
140 )
141 })
142 .collect::<Vec<_>>()
143 .join("\n");
144 let wire_outputs = c_entry
145 .wire_outputs
146 .iter()
147 .map(|i| {
148 format!(
149 " {}",
150 data.component_wire_port_names[*i as usize].to_owned()
151 )
152 })
153 .collect::<Vec<_>>()
154 .join("\n");
155
156 let mut component_struct = String::new();
157 for (name, properties) in &component_schema.structs {
158 if name != &c.1.name {
159 continue;
160 }
161
162 let name = component_schema
163 .intern
164 .lookup(*name)
165 .unwrap_or("UnknownStruct".to_owned());
166 writeln!(component_struct, "struct {name} {{")?;
167 for (prop_name, prop_type) in properties {
168 let prop_name = component_schema
169 .intern
170 .lookup(*prop_name)
171 .unwrap_or("UnknownProperty".to_owned());
172 writeln!(
173 component_struct,
174 " {prop_name}: {},",
175 prop_type.as_string(&component_schema)
176 )?;
177 }
178 writeln!(component_struct, "}}")?;
179 }
180
181 println!(
182 "Brick: {}\nComponent: {}\n{}Inputs:\n{}\nOutputs:\n{}\n\n",
183 brick_type_str, component_type_str, component_struct, wire_inputs, wire_outputs
184 );
185 }
186 }
187
188 Ok(())
189}Sourcepub fn entities_schema_rev(
&self,
revision: i64,
) -> Result<Arc<BrdbSchema>, BrError>where
T: BrFsReader,
pub fn entities_schema_rev(
&self,
revision: i64,
) -> Result<Arc<BrdbSchema>, BrError>where
T: BrFsReader,
Read the shared entity chunk schema at a specific revision
Sourcepub fn entities_schema(&self) -> Result<Arc<BrdbSchema>, BrError>where
T: BrFsReader,
pub fn entities_schema(&self) -> Result<Arc<BrdbSchema>, BrError>where
T: BrFsReader,
Read the shared entity chunk schema (latest revision)
Examples found in repository?
5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 let src = PathBuf::from("world.brdb");
7 let dst = PathBuf::from("world_patched.brdb");
8
9 assert!(src.exists());
10
11 let db = Brdb::open(src)?.into_reader();
12
13 let chunks = db.entity_chunk_index()?;
14 let entity_schema = db.entities_schema()?;
15 let global_data = db.global_data()?;
16 let mut chunk_files = vec![];
17
18 for index in chunks {
19 // Entity_chunk loads entities and their entity data
20 let entities = db.entity_chunk(index)?;
21
22 // Re-assemble the soa. using add_entity ensures the extra data is correctly handled
23 let mut soa = EntityChunkSoA::default();
24 for mut e in entities.into_iter() {
25 e.frozen = true;
26 soa.add_entity(&global_data, &e, e.id.unwrap() as u32);
27 }
28
29 chunk_files.push((
30 format!("{index}.mps"),
31 // EntityChunkSoA::to_bytes ensures the extra data is written after the SoA data
32 BrPendingFs::File(Some(soa.to_bytes(&entity_schema)?)),
33 ));
34 }
35
36 let patch = BrPendingFs::Root(vec![(
37 "World".to_owned(),
38 BrPendingFs::Folder(Some(vec![(
39 "0".to_string(),
40 BrPendingFs::Folder(Some(vec![(
41 "Entities".to_string(),
42 BrPendingFs::Folder(Some(vec![(
43 "Chunks".to_string(),
44 BrPendingFs::Folder(Some(chunk_files)),
45 )])),
46 )])),
47 )])),
48 )]);
49
50 // Use .to_pending_patch() if you want to update the same world
51 let pending = db.to_pending()?.with_patch(patch)?;
52 if dst.exists() {
53 std::fs::remove_file(&dst)?;
54 }
55 Brdb::new(&dst)?.write_pending("Freeze Entities", pending)?;
56
57 // Ensure entities can be read
58 let db = Brdb::open(dst)?.into_reader();
59 let chunks = db.entity_chunk_index()?;
60 for index in chunks {
61 let _ = db.entity_chunk(index)?;
62 }
63
64 Ok(())
65}Sourcepub fn entities_chunk_index_schema_rev(
&self,
revision: i64,
) -> Result<Arc<BrdbSchema>, BrError>where
T: BrFsReader,
pub fn entities_chunk_index_schema_rev(
&self,
revision: i64,
) -> Result<Arc<BrdbSchema>, BrError>where
T: BrFsReader,
Read the entity chunk index schema at a specific revision
Sourcepub fn entities_chunk_index_schema(&self) -> Result<Arc<BrdbSchema>, BrError>where
T: BrFsReader,
pub fn entities_chunk_index_schema(&self) -> Result<Arc<BrdbSchema>, BrError>where
T: BrFsReader,
Read the entity chunk index schema (latest revision)
Sourcepub fn entity_chunk_index(&self) -> Result<Vec<ChunkIndex>, BrError>where
T: BrFsReader,
pub fn entity_chunk_index(&self) -> Result<Vec<ChunkIndex>, BrError>where
T: BrFsReader,
Read the entity chunk indices
Examples found in repository?
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5 let path = PathBuf::from(
6 std::env::args()
7 .nth(1)
8 .unwrap_or_else(|| "world.brz".to_string()),
9 );
10 let db = Brz::open(path)?.into_reader();
11
12 let mut grid_ids = vec![1];
13 for index in db.entity_chunk_index()? {
14 for e in db.entity_chunk(index)? {
15 if e.is_brick_grid() || e.is_microchip_grid() {
16 if let Some(id) = e.id {
17 grid_ids.push(id);
18 }
19 }
20 }
21 }
22
23 let mut total_wires = 0u64;
24 let mut total_bricks = 0u64;
25 for &gid in &grid_ids {
26 let chunks = db.brick_chunk_index(gid)?;
27 for chunk in &chunks {
28 total_bricks += chunk.num_bricks as u64;
29 total_wires += chunk.num_wires as u64;
30 }
31 }
32
33 println!("grids: {}", grid_ids.len());
34 println!("bricks: {total_bricks}");
35 println!("wires: {total_wires}");
36 Ok(())
37}More examples
5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 let src = PathBuf::from("world.brdb");
7 let dst = PathBuf::from("world_patched.brdb");
8
9 assert!(src.exists());
10
11 let db = Brdb::open(src)?.into_reader();
12
13 let chunks = db.entity_chunk_index()?;
14 let entity_schema = db.entities_schema()?;
15 let global_data = db.global_data()?;
16 let mut chunk_files = vec![];
17
18 for index in chunks {
19 // Entity_chunk loads entities and their entity data
20 let entities = db.entity_chunk(index)?;
21
22 // Re-assemble the soa. using add_entity ensures the extra data is correctly handled
23 let mut soa = EntityChunkSoA::default();
24 for mut e in entities.into_iter() {
25 e.frozen = true;
26 soa.add_entity(&global_data, &e, e.id.unwrap() as u32);
27 }
28
29 chunk_files.push((
30 format!("{index}.mps"),
31 // EntityChunkSoA::to_bytes ensures the extra data is written after the SoA data
32 BrPendingFs::File(Some(soa.to_bytes(&entity_schema)?)),
33 ));
34 }
35
36 let patch = BrPendingFs::Root(vec![(
37 "World".to_owned(),
38 BrPendingFs::Folder(Some(vec![(
39 "0".to_string(),
40 BrPendingFs::Folder(Some(vec![(
41 "Entities".to_string(),
42 BrPendingFs::Folder(Some(vec![(
43 "Chunks".to_string(),
44 BrPendingFs::Folder(Some(chunk_files)),
45 )])),
46 )])),
47 )])),
48 )]);
49
50 // Use .to_pending_patch() if you want to update the same world
51 let pending = db.to_pending()?.with_patch(patch)?;
52 if dst.exists() {
53 std::fs::remove_file(&dst)?;
54 }
55 Brdb::new(&dst)?.write_pending("Freeze Entities", pending)?;
56
57 // Ensure entities can be read
58 let db = Brdb::open(dst)?.into_reader();
59 let chunks = db.entity_chunk_index()?;
60 for index in chunks {
61 let _ = db.entity_chunk(index)?;
62 }
63
64 Ok(())
65}5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 // world file from argv
7 let filename = std::env::args()
8 .nth(1)
9 .unwrap_or_else(|| "world.brdb".to_string());
10 let path = PathBuf::from(filename);
11 if !path.exists() {
12 eprintln!("File does not exist: {}", path.display());
13 std::process::exit(1);
14 }
15
16 let db = Brdb::open(path)?.into_reader();
17
18 let data = db.global_data()?;
19 println!("Basic Brick assets: {:?}", data.basic_brick_asset_names);
20 println!("Wire ports: {:?}", data.component_wire_port_names);
21 println!("Component types: {:?}", data.component_type_names);
22 println!("Component structs: {:?}", data.component_data_struct_names);
23 println!("Component schemas: {}", db.components_schema()?);
24
25 let mut grid_ids = vec![1];
26
27 // Iterate all entity chunks to find dynamic brick grids...
28 // This could totally be a helper function
29 for index in db.entity_chunk_index()? {
30 for e in db.entity_chunk(index)? {
31 // Ensure the chunk is a dynamic brick grid
32 if !e.is_brick_grid() {
33 continue;
34 }
35 let Some(id) = e.id else {
36 continue;
37 };
38 grid_ids.push(id);
39 }
40 }
41
42 for gid in grid_ids {
43 println!("Reading grid {gid}");
44 let chunks = db.brick_chunk_index(gid)?;
45 println!("Brick chunks: {chunks:?}");
46 for chunk in chunks {
47 let soa = db.brick_chunk_soa(gid, chunk.index)?;
48 println!("Brick Soa {chunk}: {soa:?}");
49 if chunk.num_components > 0 {
50 let (_soa, components) = db.component_chunk_soa(gid, chunk.index)?;
51 // println!("Components soa: {soa}");
52 for c in components {
53 println!("Component: {c}");
54 }
55 }
56 if chunk.num_wires > 0 {
57 let soa = db.wire_chunk_soa(gid, chunk.index)?;
58 println!("Wires soa: {soa}");
59 }
60 }
61 }
62
63 println!("Files: {}", db.get_fs()?.render());
64
65 Ok(())
66}5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 // world file from argv
7 let filename = std::env::args()
8 .nth(1)
9 .unwrap_or_else(|| "world.brdb".to_string());
10 let path = PathBuf::from(filename);
11 if !path.exists() {
12 eprintln!("File does not exist: {}", path.display());
13 std::process::exit(1);
14 }
15
16 let db = Brdb::open(path)?.into_reader();
17
18 // Get global data to access entity type names and class names
19 let data = db.global_data()?;
20 println!("=== Entity Type Names ===");
21 for ((i, name), class) in data
22 .entity_type_names
23 .iter()
24 .enumerate()
25 .zip(data.entity_data_class_names.iter())
26 {
27 println!("{i}: {name} - {class}");
28 }
29 println!();
30
31 // Read all entity chunks
32 println!("=== Entity Chunks ===");
33 let entity_chunk_indices = db.entity_chunk_index()?;
34 println!("Found {} entity chunks", entity_chunk_indices.len());
35 println!();
36
37 let mut total_entities = 0;
38 for chunk_index in entity_chunk_indices {
39 println!("--- Chunk {} ---", chunk_index);
40 let entities = db.entity_chunk(chunk_index)?;
41
42 for (i, entity) in entities.iter().enumerate() {
43 total_entities += 1;
44 println!(" Entity {i}:");
45 println!(" Asset: {}", entity.asset);
46 println!(" ID: {:?}", entity.id);
47 println!(" Owner Index: {:?}", entity.owner_index);
48 println!(" Location: {:?}", entity.location);
49 println!(" Rotation: {:?}", entity.rotation);
50 println!(" Velocity: {:?}", entity.velocity);
51 println!(" Angular Velocity: {:?}", entity.angular_velocity);
52 println!(" Frozen: {:?}", entity.frozen);
53 println!(" Sleeping: {:?}", entity.sleeping);
54 println!();
55 }
56 }
57
58 println!("=== Summary ===");
59 println!("Total entities: {total_entities}");
60
61 // Also print the entity chunk SoA data
62 println!();
63 println!("=== Entity Chunk SoA Data ===");
64 for chunk_index in db.entity_chunk_index()? {
65 println!("--- Chunk {} (SoA) ---", chunk_index);
66 let (soa, entity_data) = db.entity_chunk_soa(chunk_index)?;
67
68 println!(" Type Counters: {:?}", soa.type_counters);
69 println!(" Number of entities with data: {}", entity_data.len());
70
71 for (i, data) in entity_data.iter().enumerate() {
72 if let Some(struct_data) = data {
73 println!(" Entity {i} struct: {struct_data}");
74 } else {
75 println!(" Entity {i}: None");
76 }
77 }
78 println!();
79 }
80
81 Ok(())
82}5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 let path = PathBuf::from(
7 std::env::args()
8 .nth(1)
9 .unwrap_or_else(|| "world.brz".to_string()),
10 );
11 let db = Brz::open(path)?.into_reader();
12
13 let mut grid_ids = vec![1];
14 for index in db.entity_chunk_index()? {
15 for e in db.entity_chunk(index)? {
16 if e.is_brick_grid() || e.is_microchip_grid() {
17 if let Some(id) = e.id {
18 grid_ids.push(id);
19 }
20 }
21 }
22 }
23
24 // For each grid, collect a fingerprint: (num_bricks, num_wires, num_chunks)
25 // to find grids that look identical.
26 let mut fingerprints: HashMap<(u64, u64, usize), Vec<usize>> = HashMap::new();
27 let mut total_bricks = 0u64;
28 let mut total_wires = 0u64;
29 let mut total_components = 0u64;
30
31 for &gid in &grid_ids {
32 let chunks = db.brick_chunk_index(gid)?;
33 let mut grid_bricks = 0u64;
34 let mut grid_wires = 0u64;
35 let mut grid_components = 0u64;
36 for chunk in &chunks {
37 grid_bricks += chunk.num_bricks as u64;
38 grid_wires += chunk.num_wires as u64;
39 grid_components += chunk.num_components as u64;
40 }
41 total_bricks += grid_bricks;
42 total_wires += grid_wires;
43 total_components += grid_components;
44
45 let fp = (grid_bricks, grid_wires, chunks.len());
46 fingerprints.entry(fp).or_default().push(gid);
47 }
48
49 println!("=== Grid Summary ===");
50 println!("total grids: {}", grid_ids.len());
51 println!("total bricks: {total_bricks}");
52 println!("total wires: {total_wires}");
53 println!("total components: {total_components}");
54 println!();
55
56 println!("=== Unique Grid Shapes ===");
57 let mut fps: Vec<_> = fingerprints.iter().collect();
58 fps.sort_by_key(|((b, _w, _), grids)| std::cmp::Reverse(*b * grids.len() as u64));
59
60 for ((bricks, wires, chunks), grids) in &fps {
61 let savings = if grids.len() > 1 {
62 format!(
63 " → {} could be deduplicated (save {} bricks, {} wires)",
64 grids.len() - 1,
65 bricks * (grids.len() as u64 - 1),
66 wires * (grids.len() as u64 - 1)
67 )
68 } else {
69 String::new()
70 };
71 println!(
72 " {}× ({} bricks, {} wires, {} chunks){}",
73 grids.len(),
74 bricks,
75 wires,
76 chunks,
77 savings
78 );
79 }
80
81 // Top 10 largest grids
82 println!();
83 println!("=== Top 10 Largest Grids ===");
84 let mut grid_sizes: Vec<(usize, u64, u64)> = Vec::new();
85 for &gid in &grid_ids {
86 let chunks = db.brick_chunk_index(gid)?;
87 let b: u64 = chunks.iter().map(|c| c.num_bricks as u64).sum();
88 let w: u64 = chunks.iter().map(|c| c.num_wires as u64).sum();
89 grid_sizes.push((gid, b, w));
90 }
91 grid_sizes.sort_by_key(|(_, b, _)| std::cmp::Reverse(*b));
92 for (gid, b, w) in grid_sizes.iter().take(10) {
93 println!(" grid {gid}: {b} bricks, {w} wires");
94 }
95
96 Ok(())
97}5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 let src = PathBuf::from("world.brdb");
7 let dst = PathBuf::from("world_patched.brdb");
8
9 assert!(src.exists());
10
11 let db = Brdb::open(src)?.into_reader();
12
13 let mut grid_ids = vec![1];
14
15 // Iterate all entity chunks to find dynamic brick grids...
16 // This could totally be a helper function
17 for index in db.entity_chunk_index()? {
18 for e in db.entity_chunk(index)? {
19 // Ensure the chunk is a dynamic brick grid
20 if !e.is_brick_grid() {
21 continue;
22 }
23 let Some(id) = e.id else {
24 continue;
25 };
26 grid_ids.push(id);
27 }
28 }
29
30 let component_schema = db.components_schema()?;
31 let mut grids_files = vec![];
32
33 // Iterate all grids (there can be bricks on entities)
34 for grid in &grid_ids {
35 let chunks = db.brick_chunk_index(*grid)?;
36 let mut chunk_files = vec![];
37 let mut num_grid_modified = 0;
38
39 // Iterate all chunks in the grid
40 for index in chunks {
41 let mut num_chunk_modified = 0;
42 if index.num_components == 0 {
43 println!("ignoring grid {grid} chunk {} with no components", *index);
44 continue;
45 }
46
47 // Iterate all the components in the chunk
48 let (mut soa, components) = db.component_chunk(*grid, *index)?;
49 for mut s in components {
50 // Disable the shadow casting property if it's present and true
51 if s.prop("bCastShadows")
52 .is_ok_and(|v| v.as_brdb_bool().unwrap_or_default())
53 {
54 println!(
55 "grid {grid} chunk {} mutating component {}",
56 *index,
57 s.get_name()
58 );
59 s.set_prop("bCastShadows", BrdbValue::Bool(false))?;
60 num_grid_modified += 1;
61 num_chunk_modified += 1;
62 }
63
64 soa.unwritten_struct_data.push(Box::new(s));
65 }
66
67 if num_chunk_modified == 0 {
68 continue;
69 }
70
71 chunk_files.push((
72 format!("{}.mps", *index),
73 // ComponentChunkSoA::to_bytes ensures the extra data is written after the SoA data
74 BrPendingFs::File(Some(soa.to_bytes(&component_schema)?)),
75 ));
76 }
77
78 if num_grid_modified == 0 {
79 println!("grid {grid} has no shadow-casting components, skipping");
80 continue;
81 } else {
82 println!(
83 "grid {grid} has {num_grid_modified} shadow-casting components in {} files",
84 chunk_files.len()
85 );
86 }
87
88 grids_files.push((
89 grid.to_string(),
90 BrPendingFs::Folder(Some(vec![(
91 "Components".to_string(),
92 BrPendingFs::Folder(Some(chunk_files)),
93 )])),
94 ))
95 }
96
97 let patch = BrPendingFs::Root(vec![(
98 "World".to_owned(),
99 BrPendingFs::Folder(Some(vec![(
100 "0".to_string(),
101 BrPendingFs::Folder(Some(vec![(
102 "Bricks".to_string(),
103 BrPendingFs::Folder(Some(vec![(
104 "Grids".to_string(),
105 BrPendingFs::Folder(Some(grids_files)),
106 )])),
107 )])),
108 )])),
109 )]);
110
111 // Use .to_pending_patch() if you want to update the same world
112 let pending = db.to_pending()?.with_patch(patch)?;
113 if dst.exists() {
114 std::fs::remove_file(&dst)?;
115 }
116 Brdb::new(&dst)?.write_pending("Disable Shadow Casting", pending)?;
117
118 // Ensure all the components can be read
119 let db = Brdb::open(dst)?.into_reader();
120 for grid in grid_ids {
121 let chunks = db.brick_chunk_index(grid)?;
122 for index in chunks {
123 if index.num_components == 0 {
124 continue;
125 }
126 let (_soa, _components) = db.component_chunk(grid, *index)?;
127 }
128 }
129
130 Ok(())
131}Sourcepub fn entity_chunk_index_soa(&self) -> Result<EntityChunkIndexSoA, BrError>where
T: BrFsReader,
pub fn entity_chunk_index_soa(&self) -> Result<EntityChunkIndexSoA, BrError>where
T: BrFsReader,
Read the entity chunk indices
Sourcepub fn entity_chunk(&self, chunk: ChunkIndex) -> Result<Vec<Entity>, BrError>where
T: BrFsReader,
pub fn entity_chunk(&self, chunk: ChunkIndex) -> Result<Vec<Entity>, BrError>where
T: BrFsReader,
Examples found in repository?
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5 let path = PathBuf::from(
6 std::env::args()
7 .nth(1)
8 .unwrap_or_else(|| "world.brz".to_string()),
9 );
10 let db = Brz::open(path)?.into_reader();
11
12 let mut grid_ids = vec![1];
13 for index in db.entity_chunk_index()? {
14 for e in db.entity_chunk(index)? {
15 if e.is_brick_grid() || e.is_microchip_grid() {
16 if let Some(id) = e.id {
17 grid_ids.push(id);
18 }
19 }
20 }
21 }
22
23 let mut total_wires = 0u64;
24 let mut total_bricks = 0u64;
25 for &gid in &grid_ids {
26 let chunks = db.brick_chunk_index(gid)?;
27 for chunk in &chunks {
28 total_bricks += chunk.num_bricks as u64;
29 total_wires += chunk.num_wires as u64;
30 }
31 }
32
33 println!("grids: {}", grid_ids.len());
34 println!("bricks: {total_bricks}");
35 println!("wires: {total_wires}");
36 Ok(())
37}More examples
5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 let src = PathBuf::from("world.brdb");
7 let dst = PathBuf::from("world_patched.brdb");
8
9 assert!(src.exists());
10
11 let db = Brdb::open(src)?.into_reader();
12
13 let chunks = db.entity_chunk_index()?;
14 let entity_schema = db.entities_schema()?;
15 let global_data = db.global_data()?;
16 let mut chunk_files = vec![];
17
18 for index in chunks {
19 // Entity_chunk loads entities and their entity data
20 let entities = db.entity_chunk(index)?;
21
22 // Re-assemble the soa. using add_entity ensures the extra data is correctly handled
23 let mut soa = EntityChunkSoA::default();
24 for mut e in entities.into_iter() {
25 e.frozen = true;
26 soa.add_entity(&global_data, &e, e.id.unwrap() as u32);
27 }
28
29 chunk_files.push((
30 format!("{index}.mps"),
31 // EntityChunkSoA::to_bytes ensures the extra data is written after the SoA data
32 BrPendingFs::File(Some(soa.to_bytes(&entity_schema)?)),
33 ));
34 }
35
36 let patch = BrPendingFs::Root(vec![(
37 "World".to_owned(),
38 BrPendingFs::Folder(Some(vec![(
39 "0".to_string(),
40 BrPendingFs::Folder(Some(vec![(
41 "Entities".to_string(),
42 BrPendingFs::Folder(Some(vec![(
43 "Chunks".to_string(),
44 BrPendingFs::Folder(Some(chunk_files)),
45 )])),
46 )])),
47 )])),
48 )]);
49
50 // Use .to_pending_patch() if you want to update the same world
51 let pending = db.to_pending()?.with_patch(patch)?;
52 if dst.exists() {
53 std::fs::remove_file(&dst)?;
54 }
55 Brdb::new(&dst)?.write_pending("Freeze Entities", pending)?;
56
57 // Ensure entities can be read
58 let db = Brdb::open(dst)?.into_reader();
59 let chunks = db.entity_chunk_index()?;
60 for index in chunks {
61 let _ = db.entity_chunk(index)?;
62 }
63
64 Ok(())
65}5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 // world file from argv
7 let filename = std::env::args()
8 .nth(1)
9 .unwrap_or_else(|| "world.brdb".to_string());
10 let path = PathBuf::from(filename);
11 if !path.exists() {
12 eprintln!("File does not exist: {}", path.display());
13 std::process::exit(1);
14 }
15
16 let db = Brdb::open(path)?.into_reader();
17
18 let data = db.global_data()?;
19 println!("Basic Brick assets: {:?}", data.basic_brick_asset_names);
20 println!("Wire ports: {:?}", data.component_wire_port_names);
21 println!("Component types: {:?}", data.component_type_names);
22 println!("Component structs: {:?}", data.component_data_struct_names);
23 println!("Component schemas: {}", db.components_schema()?);
24
25 let mut grid_ids = vec![1];
26
27 // Iterate all entity chunks to find dynamic brick grids...
28 // This could totally be a helper function
29 for index in db.entity_chunk_index()? {
30 for e in db.entity_chunk(index)? {
31 // Ensure the chunk is a dynamic brick grid
32 if !e.is_brick_grid() {
33 continue;
34 }
35 let Some(id) = e.id else {
36 continue;
37 };
38 grid_ids.push(id);
39 }
40 }
41
42 for gid in grid_ids {
43 println!("Reading grid {gid}");
44 let chunks = db.brick_chunk_index(gid)?;
45 println!("Brick chunks: {chunks:?}");
46 for chunk in chunks {
47 let soa = db.brick_chunk_soa(gid, chunk.index)?;
48 println!("Brick Soa {chunk}: {soa:?}");
49 if chunk.num_components > 0 {
50 let (_soa, components) = db.component_chunk_soa(gid, chunk.index)?;
51 // println!("Components soa: {soa}");
52 for c in components {
53 println!("Component: {c}");
54 }
55 }
56 if chunk.num_wires > 0 {
57 let soa = db.wire_chunk_soa(gid, chunk.index)?;
58 println!("Wires soa: {soa}");
59 }
60 }
61 }
62
63 println!("Files: {}", db.get_fs()?.render());
64
65 Ok(())
66}5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 // world file from argv
7 let filename = std::env::args()
8 .nth(1)
9 .unwrap_or_else(|| "world.brdb".to_string());
10 let path = PathBuf::from(filename);
11 if !path.exists() {
12 eprintln!("File does not exist: {}", path.display());
13 std::process::exit(1);
14 }
15
16 let db = Brdb::open(path)?.into_reader();
17
18 // Get global data to access entity type names and class names
19 let data = db.global_data()?;
20 println!("=== Entity Type Names ===");
21 for ((i, name), class) in data
22 .entity_type_names
23 .iter()
24 .enumerate()
25 .zip(data.entity_data_class_names.iter())
26 {
27 println!("{i}: {name} - {class}");
28 }
29 println!();
30
31 // Read all entity chunks
32 println!("=== Entity Chunks ===");
33 let entity_chunk_indices = db.entity_chunk_index()?;
34 println!("Found {} entity chunks", entity_chunk_indices.len());
35 println!();
36
37 let mut total_entities = 0;
38 for chunk_index in entity_chunk_indices {
39 println!("--- Chunk {} ---", chunk_index);
40 let entities = db.entity_chunk(chunk_index)?;
41
42 for (i, entity) in entities.iter().enumerate() {
43 total_entities += 1;
44 println!(" Entity {i}:");
45 println!(" Asset: {}", entity.asset);
46 println!(" ID: {:?}", entity.id);
47 println!(" Owner Index: {:?}", entity.owner_index);
48 println!(" Location: {:?}", entity.location);
49 println!(" Rotation: {:?}", entity.rotation);
50 println!(" Velocity: {:?}", entity.velocity);
51 println!(" Angular Velocity: {:?}", entity.angular_velocity);
52 println!(" Frozen: {:?}", entity.frozen);
53 println!(" Sleeping: {:?}", entity.sleeping);
54 println!();
55 }
56 }
57
58 println!("=== Summary ===");
59 println!("Total entities: {total_entities}");
60
61 // Also print the entity chunk SoA data
62 println!();
63 println!("=== Entity Chunk SoA Data ===");
64 for chunk_index in db.entity_chunk_index()? {
65 println!("--- Chunk {} (SoA) ---", chunk_index);
66 let (soa, entity_data) = db.entity_chunk_soa(chunk_index)?;
67
68 println!(" Type Counters: {:?}", soa.type_counters);
69 println!(" Number of entities with data: {}", entity_data.len());
70
71 for (i, data) in entity_data.iter().enumerate() {
72 if let Some(struct_data) = data {
73 println!(" Entity {i} struct: {struct_data}");
74 } else {
75 println!(" Entity {i}: None");
76 }
77 }
78 println!();
79 }
80
81 Ok(())
82}5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 let path = PathBuf::from(
7 std::env::args()
8 .nth(1)
9 .unwrap_or_else(|| "world.brz".to_string()),
10 );
11 let db = Brz::open(path)?.into_reader();
12
13 let mut grid_ids = vec![1];
14 for index in db.entity_chunk_index()? {
15 for e in db.entity_chunk(index)? {
16 if e.is_brick_grid() || e.is_microchip_grid() {
17 if let Some(id) = e.id {
18 grid_ids.push(id);
19 }
20 }
21 }
22 }
23
24 // For each grid, collect a fingerprint: (num_bricks, num_wires, num_chunks)
25 // to find grids that look identical.
26 let mut fingerprints: HashMap<(u64, u64, usize), Vec<usize>> = HashMap::new();
27 let mut total_bricks = 0u64;
28 let mut total_wires = 0u64;
29 let mut total_components = 0u64;
30
31 for &gid in &grid_ids {
32 let chunks = db.brick_chunk_index(gid)?;
33 let mut grid_bricks = 0u64;
34 let mut grid_wires = 0u64;
35 let mut grid_components = 0u64;
36 for chunk in &chunks {
37 grid_bricks += chunk.num_bricks as u64;
38 grid_wires += chunk.num_wires as u64;
39 grid_components += chunk.num_components as u64;
40 }
41 total_bricks += grid_bricks;
42 total_wires += grid_wires;
43 total_components += grid_components;
44
45 let fp = (grid_bricks, grid_wires, chunks.len());
46 fingerprints.entry(fp).or_default().push(gid);
47 }
48
49 println!("=== Grid Summary ===");
50 println!("total grids: {}", grid_ids.len());
51 println!("total bricks: {total_bricks}");
52 println!("total wires: {total_wires}");
53 println!("total components: {total_components}");
54 println!();
55
56 println!("=== Unique Grid Shapes ===");
57 let mut fps: Vec<_> = fingerprints.iter().collect();
58 fps.sort_by_key(|((b, _w, _), grids)| std::cmp::Reverse(*b * grids.len() as u64));
59
60 for ((bricks, wires, chunks), grids) in &fps {
61 let savings = if grids.len() > 1 {
62 format!(
63 " → {} could be deduplicated (save {} bricks, {} wires)",
64 grids.len() - 1,
65 bricks * (grids.len() as u64 - 1),
66 wires * (grids.len() as u64 - 1)
67 )
68 } else {
69 String::new()
70 };
71 println!(
72 " {}× ({} bricks, {} wires, {} chunks){}",
73 grids.len(),
74 bricks,
75 wires,
76 chunks,
77 savings
78 );
79 }
80
81 // Top 10 largest grids
82 println!();
83 println!("=== Top 10 Largest Grids ===");
84 let mut grid_sizes: Vec<(usize, u64, u64)> = Vec::new();
85 for &gid in &grid_ids {
86 let chunks = db.brick_chunk_index(gid)?;
87 let b: u64 = chunks.iter().map(|c| c.num_bricks as u64).sum();
88 let w: u64 = chunks.iter().map(|c| c.num_wires as u64).sum();
89 grid_sizes.push((gid, b, w));
90 }
91 grid_sizes.sort_by_key(|(_, b, _)| std::cmp::Reverse(*b));
92 for (gid, b, w) in grid_sizes.iter().take(10) {
93 println!(" grid {gid}: {b} bricks, {w} wires");
94 }
95
96 Ok(())
97}5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 let src = PathBuf::from("world.brdb");
7 let dst = PathBuf::from("world_patched.brdb");
8
9 assert!(src.exists());
10
11 let db = Brdb::open(src)?.into_reader();
12
13 let mut grid_ids = vec![1];
14
15 // Iterate all entity chunks to find dynamic brick grids...
16 // This could totally be a helper function
17 for index in db.entity_chunk_index()? {
18 for e in db.entity_chunk(index)? {
19 // Ensure the chunk is a dynamic brick grid
20 if !e.is_brick_grid() {
21 continue;
22 }
23 let Some(id) = e.id else {
24 continue;
25 };
26 grid_ids.push(id);
27 }
28 }
29
30 let component_schema = db.components_schema()?;
31 let mut grids_files = vec![];
32
33 // Iterate all grids (there can be bricks on entities)
34 for grid in &grid_ids {
35 let chunks = db.brick_chunk_index(*grid)?;
36 let mut chunk_files = vec![];
37 let mut num_grid_modified = 0;
38
39 // Iterate all chunks in the grid
40 for index in chunks {
41 let mut num_chunk_modified = 0;
42 if index.num_components == 0 {
43 println!("ignoring grid {grid} chunk {} with no components", *index);
44 continue;
45 }
46
47 // Iterate all the components in the chunk
48 let (mut soa, components) = db.component_chunk(*grid, *index)?;
49 for mut s in components {
50 // Disable the shadow casting property if it's present and true
51 if s.prop("bCastShadows")
52 .is_ok_and(|v| v.as_brdb_bool().unwrap_or_default())
53 {
54 println!(
55 "grid {grid} chunk {} mutating component {}",
56 *index,
57 s.get_name()
58 );
59 s.set_prop("bCastShadows", BrdbValue::Bool(false))?;
60 num_grid_modified += 1;
61 num_chunk_modified += 1;
62 }
63
64 soa.unwritten_struct_data.push(Box::new(s));
65 }
66
67 if num_chunk_modified == 0 {
68 continue;
69 }
70
71 chunk_files.push((
72 format!("{}.mps", *index),
73 // ComponentChunkSoA::to_bytes ensures the extra data is written after the SoA data
74 BrPendingFs::File(Some(soa.to_bytes(&component_schema)?)),
75 ));
76 }
77
78 if num_grid_modified == 0 {
79 println!("grid {grid} has no shadow-casting components, skipping");
80 continue;
81 } else {
82 println!(
83 "grid {grid} has {num_grid_modified} shadow-casting components in {} files",
84 chunk_files.len()
85 );
86 }
87
88 grids_files.push((
89 grid.to_string(),
90 BrPendingFs::Folder(Some(vec![(
91 "Components".to_string(),
92 BrPendingFs::Folder(Some(chunk_files)),
93 )])),
94 ))
95 }
96
97 let patch = BrPendingFs::Root(vec![(
98 "World".to_owned(),
99 BrPendingFs::Folder(Some(vec![(
100 "0".to_string(),
101 BrPendingFs::Folder(Some(vec![(
102 "Bricks".to_string(),
103 BrPendingFs::Folder(Some(vec![(
104 "Grids".to_string(),
105 BrPendingFs::Folder(Some(grids_files)),
106 )])),
107 )])),
108 )])),
109 )]);
110
111 // Use .to_pending_patch() if you want to update the same world
112 let pending = db.to_pending()?.with_patch(patch)?;
113 if dst.exists() {
114 std::fs::remove_file(&dst)?;
115 }
116 Brdb::new(&dst)?.write_pending("Disable Shadow Casting", pending)?;
117
118 // Ensure all the components can be read
119 let db = Brdb::open(dst)?.into_reader();
120 for grid in grid_ids {
121 let chunks = db.brick_chunk_index(grid)?;
122 for index in chunks {
123 if index.num_components == 0 {
124 continue;
125 }
126 let (_soa, _components) = db.component_chunk(grid, *index)?;
127 }
128 }
129
130 Ok(())
131}Sourcepub fn entity_chunk_soa(
&self,
chunk: ChunkIndex,
) -> Result<(EntityChunkSoA, Vec<Option<BrdbStruct>>), BrError>where
T: BrFsReader,
pub fn entity_chunk_soa(
&self,
chunk: ChunkIndex,
) -> Result<(EntityChunkSoA, Vec<Option<BrdbStruct>>), BrError>where
T: BrFsReader,
Examples found in repository?
5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 // world file from argv
7 let filename = std::env::args()
8 .nth(1)
9 .unwrap_or_else(|| "world.brdb".to_string());
10 let path = PathBuf::from(filename);
11 if !path.exists() {
12 eprintln!("File does not exist: {}", path.display());
13 std::process::exit(1);
14 }
15
16 let db = Brdb::open(path)?.into_reader();
17
18 // Get global data to access entity type names and class names
19 let data = db.global_data()?;
20 println!("=== Entity Type Names ===");
21 for ((i, name), class) in data
22 .entity_type_names
23 .iter()
24 .enumerate()
25 .zip(data.entity_data_class_names.iter())
26 {
27 println!("{i}: {name} - {class}");
28 }
29 println!();
30
31 // Read all entity chunks
32 println!("=== Entity Chunks ===");
33 let entity_chunk_indices = db.entity_chunk_index()?;
34 println!("Found {} entity chunks", entity_chunk_indices.len());
35 println!();
36
37 let mut total_entities = 0;
38 for chunk_index in entity_chunk_indices {
39 println!("--- Chunk {} ---", chunk_index);
40 let entities = db.entity_chunk(chunk_index)?;
41
42 for (i, entity) in entities.iter().enumerate() {
43 total_entities += 1;
44 println!(" Entity {i}:");
45 println!(" Asset: {}", entity.asset);
46 println!(" ID: {:?}", entity.id);
47 println!(" Owner Index: {:?}", entity.owner_index);
48 println!(" Location: {:?}", entity.location);
49 println!(" Rotation: {:?}", entity.rotation);
50 println!(" Velocity: {:?}", entity.velocity);
51 println!(" Angular Velocity: {:?}", entity.angular_velocity);
52 println!(" Frozen: {:?}", entity.frozen);
53 println!(" Sleeping: {:?}", entity.sleeping);
54 println!();
55 }
56 }
57
58 println!("=== Summary ===");
59 println!("Total entities: {total_entities}");
60
61 // Also print the entity chunk SoA data
62 println!();
63 println!("=== Entity Chunk SoA Data ===");
64 for chunk_index in db.entity_chunk_index()? {
65 println!("--- Chunk {} (SoA) ---", chunk_index);
66 let (soa, entity_data) = db.entity_chunk_soa(chunk_index)?;
67
68 println!(" Type Counters: {:?}", soa.type_counters);
69 println!(" Number of entities with data: {}", entity_data.len());
70
71 for (i, data) in entity_data.iter().enumerate() {
72 if let Some(struct_data) = data {
73 println!(" Entity {i} struct: {struct_data}");
74 } else {
75 println!(" Entity {i}: None");
76 }
77 }
78 println!();
79 }
80
81 Ok(())
82}More examples
11fn run<T: BrFsReader>(db: &BrReader<T>) -> Result<(), Box<dyn std::error::Error>> {
12 let data = db.global_data()?;
13 println!("Basic Brick assets: {:?}", data.basic_brick_asset_names);
14 println!("Procedural Brick assets: {:?}", data.procedural_brick_asset_names);
15 // register_all_components() embeds the FULL catalog (hundreds of wire
16 // ports/component types), so only print counts for those — the fixture's
17 // OWN types/ports are visible per-brick below via the type-name lookups.
18 println!(
19 "Wire port count: {} (registered catalog)",
20 data.component_wire_port_names.len()
21 );
22 println!(
23 "Component type count: {} (registered catalog)",
24 data.component_type_names.len()
25 );
26 println!("Entity types: {:?}", data.entity_type_names);
27 println!("Entity classes: {:?}", data.entity_data_class_names);
28
29 // Probe grid ids 1.. until one is missing (grid 1 is always the main
30 // grid; higher ids are sub-grids / microchip inner grids).
31 for gid in 1..32 {
32 let chunks = match db.brick_chunk_index(gid) {
33 Ok(c) => c,
34 Err(_) => break,
35 };
36 println!("=== grid {gid} ===");
37 println!("Brick chunks: {chunks:?}");
38 for chunk in &chunks {
39 let soa = db.brick_chunk_soa(gid, chunk.index)?;
40 println!("Brick soa: {soa:?}");
41 let asset_names: Vec<String> = soa
42 .brick_type_indices
43 .iter()
44 .map(|&t| {
45 if (t as usize) < soa.procedural_brick_starting_index as usize {
46 data.basic_brick_asset_names
47 .get_index(t as usize)
48 .cloned()
49 .unwrap_or_default()
50 } else {
51 "<procedural>".to_string()
52 }
53 })
54 .collect();
55 println!("Brick asset names (by index in chunk): {asset_names:?}");
56
57 if chunk.num_components > 0 {
58 let (soa, components) = db.component_chunk_soa(gid, chunk.index)?;
59 let type_names: Vec<String> = soa
60 .component_type_counters
61 .iter()
62 .flat_map(|c| {
63 let name = data
64 .component_type_names
65 .get_index(c.type_index as usize)
66 .cloned()
67 .unwrap_or_default();
68 (0..c.num_instances).map(move |_| name.clone())
69 })
70 .collect();
71 println!(
72 "Component chunk soa: component_brick_indices={:?} microchip_brick_indices={:?} microchip_brick_grid_references={:?}",
73 soa.component_brick_indices,
74 soa.microchip_brick_indices,
75 soa.microchip_brick_grid_references
76 );
77 println!("Component type names (parallel to ComponentBrickIndices): {type_names:?}");
78 for c in components {
79 println!("Component: {c}");
80 }
81 }
82 if chunk.num_wires > 0 {
83 let raw = db.wire_chunk_soa(gid, chunk.index)?;
84 println!("Wire chunk soa (raw struct): {raw}");
85 let value = raw.to_value();
86 let soa: WireChunkSoA = (&value).try_into()?;
87 let port_name = |i: u16| {
88 data.component_wire_port_names
89 .get_index(i as usize)
90 .cloned()
91 .unwrap_or_default()
92 };
93 let type_name = |i: u16| {
94 data.component_type_names
95 .get_index(i as usize)
96 .cloned()
97 .unwrap_or_default()
98 };
99 for s in &soa.local_wire_sources {
100 println!(
101 " local source: brick_in_chunk={} type={} port={}",
102 s.brick_index_in_chunk,
103 type_name(s.component_type_index),
104 port_name(s.port_index)
105 );
106 }
107 for t in &soa.local_wire_targets {
108 println!(
109 " local target: brick_in_chunk={} type={} port={}",
110 t.brick_index_in_chunk,
111 type_name(t.component_type_index),
112 port_name(t.port_index)
113 );
114 }
115 for s in &soa.remote_wire_sources {
116 println!(
117 " remote source: grid_persistent_index={} chunk={} brick_in_chunk={} type={} port={}",
118 s.grid_persistent_index,
119 s.chunk_index,
120 s.brick_index_in_chunk,
121 type_name(s.component_type_index),
122 port_name(s.port_index)
123 );
124 }
125 for t in &soa.remote_wire_targets {
126 println!(
127 " remote target: brick_in_chunk={} type={} port={}",
128 t.brick_index_in_chunk,
129 type_name(t.component_type_index),
130 port_name(t.port_index)
131 );
132 }
133 }
134 }
135 }
136
137 // Entity chunks (present whenever the world has any grids/entities).
138 let entity_chunk_indices = db.entity_chunk_index()?;
139 println!("=== entities ===");
140 println!("Entity chunk indices: {entity_chunk_indices:?}");
141 for chunk_index in entity_chunk_indices {
142 let (soa, entity_data) = db.entity_chunk_soa(chunk_index)?;
143 println!("--- Chunk {chunk_index} (SoA) ---");
144 println!("Type counters: {:?}", soa.type_counters);
145 let type_names: Vec<String> = soa
146 .type_counters
147 .iter()
148 .flat_map(|c| {
149 let name = data
150 .entity_type_names
151 .get_index(c.type_index as usize)
152 .cloned()
153 .unwrap_or_default();
154 (0..c.num_entities).map(move |_| name.clone())
155 })
156 .collect();
157 println!("Entity type names (parallel to PersistentIndices): {type_names:?}");
158 println!("Persistent indices: {:?}", soa.persistent_indices);
159 println!("Locations: {:?}", soa.locations);
160 println!("Rotations: {:?}", soa.rotations);
161 println!("Physics locked (frozen): {:?}", soa.physics_locked_flags);
162 println!("Physics sleeping: {:?}", soa.physics_sleeping_flags);
163 for (i, data) in entity_data.iter().enumerate() {
164 match data {
165 Some(struct_data) => println!(" Entity {i} struct: {struct_data}"),
166 None => println!(" Entity {i}: None"),
167 }
168 }
169 }
170
171 println!("Files: {}", db.get_fs()?.render());
172
173 Ok(())
174}