pub struct World {
pub meta: WorldMeta,
pub owners: IndexMap<Guid, Owner>,
pub bricks: Vec<Brick>,
pub grids: Vec<(Entity, Vec<Brick>)>,
pub wires: Vec<WireConnection>,
pub entities: Vec<Entity>,
pub microchip_links: Vec<(usize, usize)>,
pub prefabs: IndexMap<String, Vec<u8>>,
pub global_data: BrdbSchemaGlobalData,
pub component_schema: BrdbSchema,
pub entity_schema: BrdbSchema,
pub schema_format: BrdbSchemaFormat,
}Fields§
§meta: WorldMeta§owners: IndexMap<Guid, Owner>§bricks: Vec<Brick>Bricks on the main grid
grids: Vec<(Entity, Vec<Brick>)>Non-main grids require an entity to be created for them
wires: Vec<WireConnection>§entities: Vec<Entity>§microchip_links: Vec<(usize, usize)>Per-microchip linkage pairs: (brick_id, entity_id) where the brick
is the outer microchip shell and the entity is the inner grid.
prefabs: IndexMap<String, Vec<u8>>Embedded prefab archives (the root Prefabs/ folder), keyed by
root-relative path (Prefabs/Uploads/<BLAKE3>.brz) with raw .brz
bytes as values. Populate via World::add_prefab; the returned
path is what a Prefab component property references.
The game requires the Prefabs/Uploads/<BLAKE3>.brz naming — it
crashes loading a bundle whose embedded prefab has any other filename.
Use World::add_prefab rather than inserting a custom key here.
global_data: BrdbSchemaGlobalData§component_schema: BrdbSchema§entity_schema: BrdbSchema§schema_format: BrdbSchemaFormatThe save format this world is written in.
BrdbSchemaFormat::Latest (the default) writes every schema in the
map form, as the game writes them.
BrdbSchemaFormat::Auto writes each .schema in the container it
needs: the map form for schemas with a types table or dynamic
containers (the component schema), the array form otherwise. Every
current game build reads both, but a world written this way without
components has an EA3 component schema, so
crate::migrate::needs_migration reports it as an EA3 save.
Writing EA3 worlds is not supported: World::to_unsaved fails with
BrdbWorldError::Ea3WriteUnsupported.
Implementations§
Source§impl World
impl World
Sourcepub fn new() -> Self
pub fn new() -> Self
Examples found in repository?
24fn brick_world() -> World {
25 // Mirror of examples/write_brz.rs — the historical example_brick world.
26 let mut world = World::new();
27 world.meta.bundle.description = "Example World".to_string();
28 world.bricks.push(Brick {
29 position: (0, 0, 6).into(),
30 color: (255, 0, 0).into(),
31 ..Default::default()
32 });
33 world
34}
35
36fn features_world() -> World {
37 // Single chunk (all coords in [0, 2048)): registries, owners, procedural
38 // size run-length grouping (new/extend/reuse), a basic asset, collision
39 // and visibility variants, orientations, material intensity.
40 let mut world = World::new();
41 world.meta.bundle.description = "Feature fixture".to_string();
42
43 let alice = Guid::from_uuid(uuid::Uuid::parse_str(ALICE_UUID).unwrap());
44 let bob = Guid::from_uuid(uuid::Uuid::parse_str(BOB_UUID).unwrap());
45 world.owners.insert(
46 alice,
47 Owner {
48 user_id: alice,
49 user_name: "alice".to_string(),
50 display_name: "Alice".to_string(),
51 },
52 );
53 world.owners.insert(
54 bob,
55 Owner {
56 user_id: bob,
57 user_name: "bob".to_string(),
58 display_name: "Bob".to_string(),
59 },
60 );
61
62 let tile = |size: (u16, u16, u16)| BrickType::Procedural {
63 asset: assets::bricks::PB_DEFAULT_TILE, // "PB_DefaultTile"
64 size: BrickSize {
65 x: size.0,
66 y: size.1,
67 z: size.2,
68 },
69 };
70
71 // 1: default brick (PB_DefaultBrick 5x5x6, plastic, intensity 5) — new size slot
72 world.bricks.push(Brick {
73 position: (0, 0, 6).into(),
74 color: (255, 0, 0).into(),
75 owner_index: Some(1),
76 ..Default::default()
77 });
78 // 2: tile 10x10x2, metallic, intensity 7, XPositive/Deg90 — new counter entry
79 world.bricks.push(Brick {
80 asset: tile((10, 10, 2)),
81 position: (20, 0, 2).into(),
82 color: (0, 255, 0).into(),
83 owner_index: Some(1),
84 material: assets::materials::METALLIC, // "BMC_Metallic"
85 material_intensity: 7,
86 direction: Direction::XPositive,
87 rotation: Rotation::Deg90,
88 ..Default::default()
89 });
90 // 3: same tile size — size_index_map reuse
91 world.bricks.push(Brick {
92 asset: tile((10, 10, 2)),
93 position: (40, 0, 2).into(),
94 color: (0, 0, 255).into(),
95 owner_index: Some(2),
96 ..Default::default()
97 });
98 // 4: tile 20x20x2 — extends the tail counter (same asset, new size)
99 world.bricks.push(Brick {
100 asset: tile((20, 20, 2)),
101 position: (80, 0, 2).into(),
102 color: (255, 255, 0).into(),
103 owner_index: Some(2),
104 ..Default::default()
105 });
106 // 5: default brick again — reuses slot from brick 1 (map hit after other asset)
107 world.bricks.push(Brick {
108 position: (100, 0, 6).into(),
109 color: (255, 255, 255).into(),
110 owner_index: Some(1),
111 ..Default::default()
112 });
113 // 6: BASIC asset, PUBLIC owner, glow, partial collision, YNegative/Deg180
114 world.bricks.push(Brick {
115 asset: assets::bricks::B_2X2_OVERHANG, // "B_2x2_Overhang"
116 position: (200, 0, 10).into(),
117 color: (128, 64, 32).into(),
118 material: assets::materials::GLOW, // "BMC_Glow"
119 material_intensity: 3,
120 direction: Direction::YNegative,
121 rotation: Rotation::Deg180,
122 collision: Collision {
123 player: false,
124 ..Default::default()
125 },
126 ..Default::default()
127 });
128 // 7: invisible, all-collision-off, tiny proc brick
129 world.bricks.push(Brick {
130 asset: BrickType::Procedural {
131 asset: assets::bricks::PB_DEFAULT_BRICK,
132 size: BrickSize { x: 2, y: 2, z: 2 },
133 },
134 position: (300, 0, 2).into(),
135 color: (10, 20, 30).into(),
136 owner_index: Some(1),
137 visible: false,
138 collision: Collision {
139 player: false,
140 weapon: false,
141 interact: false,
142 physics: false,
143 ..Default::default()
144 },
145 ..Default::default()
146 });
147 world
148}
149
150fn chunks_world() -> World {
151 // Multi-chunk: euclidean chunking with negative coords, plus chunks
152 // 0_0_0 and 1_0_0 carry byte-identical SoA payloads → blob dedup.
153 // NOTE: multi-chunk Rust output has nondeterministic FILE ORDER
154 // (HashMap iteration), so this fixture is hashes-gate only — never
155 // byte-compare its container.
156 let mut world = World::new();
157 world.meta.bundle.description = "Chunk fixture".to_string();
158 for (pos, color) in [
159 ((0, 0, 0), (1, 2, 3)),
160 ((-1, -1, -1), (4, 5, 6)),
161 ((2048, 0, 0), (1, 2, 3)), // same rel pos + color as brick 1
162 ((-2048, 4096, 10), (10, 11, 12)),
163 ((500, 500, 500), (42, 42, 42)),
164 ((2548, 500, 500), (42, 42, 42)), // same rel pos + color as brick 5
165 ] {
166 world.bricks.push(Brick {
167 position: pos.into(),
168 color: color.into(),
169 ..Default::default()
170 });
171 }
172 world
173}
174
175fn wires_world() -> World {
176 // Mirror of examples/write_wire.rs (single grid, single chunk — fully
177 // deterministic), extended to 3 bricks/2 wires: a boolean NOT gate feeds
178 // one input of an AND gate, whose output feeds a rerouter.
179 let mut world = World::new();
180 world.register_all_components();
181 world.meta.bundle.description = "Wire fixture".to_string();
182
183 let (a, a_id) = Brick {
184 position: (30, 0, 1).into(),
185 color: (255, 0, 0).into(),
186 asset: assets::bricks::B_REROUTE,
187 ..Default::default()
188 }
189 .with_component(assets::components::Rerouter)
190 .with_id_split();
191 let (b, b_id) = Brick {
192 position: (15, 0, 1).into(),
193 color: (0, 255, 0).into(),
194 asset: assets::components::LogicGate::BoolAnd.brick(),
195 ..Default::default()
196 }
197 .with_component(assets::components::LogicGate::BoolAnd.component())
198 .with_id_split();
199 let (c, c_id) = Brick {
200 position: (0, 0, 1).into(),
201 color: (0, 0, 255).into(),
202 asset: assets::components::LogicGate::BoolNot.brick(),
203 ..Default::default()
204 }
205 .with_component(assets::components::LogicGate::BoolNot.component())
206 .with_id_split();
207
208 world.add_bricks([a, b, c]);
209 // Wire 1: NOT.output -> AND.inputA
210 world.add_wire_connection(
211 assets::components::LogicGate::BoolNot.output_of(c_id),
212 assets::components::LogicGate::BoolAnd.input_a_of(b_id),
213 );
214 // Wire 2: AND.output -> Rerouter.input
215 world.add_wire_connection(
216 assets::components::LogicGate::BoolAnd.output_of(b_id),
217 assets::components::Rerouter::input_of(a_id),
218 );
219
220 world
221}
222
223fn components_world() -> World {
224 // Single grid, single chunk (all positions < 2048) — fully deterministic.
225 // register_all_components() embeds the full component catalog so every
226 // type below (light/interact/buffer/expr) resolves; each
227 // component below carries non-default property values, and #4/#5 exercise
228 // a WireValue-typed property (BufferTicks' Input/Output, and the Blend
229 // gate's inputs).
230 let mut world = World::new();
231 world.register_all_components();
232 world.meta.bundle.description = "Component fixture".to_string();
233
234 // 1: Point light — non-default brightness/radius/color, decoupled from
235 // brick color (bUseBrickColor: false).
236 world.bricks.push(
237 Brick {
238 position: (0, 0, 6).into(),
239 color: (255, 255, 255).into(),
240 ..Default::default()
241 }
242 .with_component(
243 assets::LiteralComponent::new("Component_PointLight").with_data([
244 ("bMatchBrickShape", Box::new(false) as Box<dyn AsBrdbValue>),
245 ("bEnabled", Box::new(true)),
246 ("Brightness", Box::new(500.0f32)),
247 ("Radius", Box::new(800.0f32)),
248 (
249 "Color",
250 Box::new(SavedBrickColor {
251 r: 10,
252 g: 20,
253 b: 30,
254 a: 255,
255 }),
256 ),
257 ("bUseBrickColor", Box::new(false)),
258 ("bCastShadows", Box::new(true)),
259 ]),
260 ),
261 );
262
263 // 2: Spot light — narrow cone, non-default brightness/color.
264 world.bricks.push(
265 Brick {
266 position: (20, 0, 6).into(),
267 color: (255, 255, 0).into(),
268 ..Default::default()
269 }
270 .with_component(
271 assets::LiteralComponent::new("Component_SpotLight").with_data([
272 ("InnerConeAngle", Box::new(15.0f32) as Box<dyn AsBrdbValue>),
273 ("OuterConeAngle", Box::new(45.0f32)),
274 ("bEnabled", Box::new(true)),
275 ("Brightness", Box::new(300.0f32)),
276 ("Radius", Box::new(600.0f32)),
277 (
278 "Color",
279 Box::new(SavedBrickColor {
280 r: 255,
281 g: 0,
282 b: 0,
283 a: 255,
284 }),
285 ),
286 ("bUseBrickColor", Box::new(false)),
287 ("bCastShadows", Box::new(true)),
288 ]),
289 ),
290 );
291
292 // 3: Interact — custom prompt text, hidden interaction.
293 world.bricks.push(
294 Brick {
295 position: (40, 0, 6).into(),
296 color: (0, 255, 255).into(),
297 ..Default::default()
298 }
299 .with_component(
300 assets::LiteralComponent::new("Component_Interact").with_data([
301 (
302 "Message",
303 Box::new("You interacted!".to_string()) as Box<dyn AsBrdbValue>,
304 ),
305 ("ConsoleTag", Box::new("fixture_interact".to_string())),
306 ("bAllowNearbyInteraction", Box::new(false)),
307 ("bHiddenInteraction", Box::new(true)),
308 ("PromptCustomLabel", Box::new("Open Door".to_string())),
309 ]),
310 ),
311 );
312
313 // 4: Buffer (ticks) — Input/Output are WireValue; non-default counters
314 // plus a Number/Bool value pair.
315 world.bricks.push(
316 Brick {
317 position: (60, 0, 1).into(),
318 color: (128, 0, 128).into(),
319 asset: assets::components::BufferTicks::default().brick(),
320 ..Default::default()
321 }
322 .with_component(assets::components::BufferTicks {
323 current_ticks: 3,
324 ticks_to_wait: 10,
325 input: WireValue::Number(2.5),
326 output: WireValue::Bool(true),
327 ..Default::default()
328 }),
329 );
330
331 // 5: Blend gate — Expr_MathBlend's InputA/InputB are each a WireValue
332 // (here an f64 and an i64, showing the value is polymorphic per-port);
333 // Blend itself is a plain f64.
334 world.bricks.push(
335 Brick {
336 position: (80, 0, 1).into(),
337 color: (0, 128, 0).into(),
338 asset: assets::components::LogicGate::Blend.brick(),
339 ..Default::default()
340 }
341 .with_component(
342 assets::components::LogicGate::Blend.component_with_overrides(HashMap::from([
343 (
344 "InputA".into(),
345 Box::new(WireValue::Number(10.0)) as Box<dyn AsBrdbValue>,
346 ),
347 ("InputB".into(), Box::new(WireValue::Int(7))),
348 ("Blend".into(), Box::new(0.75f64)),
349 ])),
350 ),
351 );
352
353 world
354}
355
356fn entities_world() -> World {
357 // Mirror of examples/write_entity.rs's floating sub-grid — the first
358 // grid added after the always-present main grid 1 gets persistent index
359 // 2, so it lands at Grids/2 — plus a couple of main-grid (Grids/1)
360 // bricks. Both grids are single-chunk; note the *outer* Grids/1 vs
361 // Grids/2 folder order comes from a HashMap<usize, UnsavedGrid> in the
362 // writer and is NOT guaranteed stable run-to-run (see stability notes).
363 //
364 // register_all_components() is required here (matching
365 // test_write_entity_save, NOT the plain write_entity.rs example): without
366 // it, Entity_DynamicBrickGrid's class name is never registered and the
367 // write fails with `UnknownType("Entity_DynamicBrickGrid")` — confirmed
368 // by running the crate's own unmodified write_entity example.
369 let mut world = World::new();
370 world.register_all_components();
371 world.meta.bundle.description = "Entity fixture".to_string();
372
373 world.bricks.push(Brick {
374 position: (0, 0, 6).into(),
375 color: (200, 50, 50).into(),
376 ..Default::default()
377 });
378 world.bricks.push(Brick {
379 position: (20, 0, 6).into(),
380 color: (50, 200, 50).into(),
381 ..Default::default()
382 });
383
384 world.add_brick_grid(
385 Entity {
386 frozen: true,
387 location: (0.0, 0.0, 40.0).into(),
388 ..Default::default()
389 },
390 [Brick {
391 position: (0, 0, 3).into(),
392 color: (0, 255, 0).into(),
393 ..Default::default()
394 }],
395 );
396
397 world
398}
399
400fn spawner_world() -> World {
401 // Prefab-embedding fixture: an outer prefab whose spawner gate references
402 // an inner single-brick prefab embedded at Prefabs/Uploads/<BLAKE3>.brz.
403 // Single grid, single chunk, one insertion-ordered prefab map entry —
404 // fully deterministic. The inner archive is written raw (no zstd) so the
405 // embedded bytes are cross-language reproducible.
406 let mut inner = World::new();
407 inner.meta.bundle.description = "Inner prefab".to_string();
408 inner.bricks.push(Brick {
409 position: (0, 0, 6).into(),
410 color: (255, 0, 0).into(),
411 ..Default::default()
412 });
413 inner.make_prefab();
414 let inner_bytes = {
415 let pending = inner.to_unsaved().unwrap().to_pending().unwrap();
416 let mut buf = Vec::new();
417 pending
418 .to_brz_data(None)
419 .unwrap()
420 .write(&mut buf, None)
421 .unwrap();
422 buf
423 };
424
425 let mut world = World::new();
426 world.register_all_components();
427 world.meta.bundle.description = "Spawner fixture".to_string();
428 let prefab_path = world.add_prefab(inner_bytes);
429 world.bricks.push(
430 Brick {
431 asset: brdb::BrickType::str("B_1x1_Gate_Exec_PrefabSpawner"),
432 position: (0, 0, 1).into(),
433 ..Default::default()
434 }
435 .with_component(
436 assets::LiteralComponent::new("BrickComponentType_Exec_PrefabSpawner")
437 .with_data([("Prefab", Box::new(prefab_path) as Box<dyn AsBrdbValue>)]),
438 ),
439 );
440 world.make_prefab();
441 world
442}
443
444/// `features_world` written in the Latest format.
445fn features_latest_world() -> World {
446 let mut world = features_world();
447 world.schema_format = BrdbSchemaFormat::Latest;
448 world
449}
450
451/// `entities_world` with the current build's schemas in the Latest format.
452fn entities_latest_world() -> World {
453 let mut world = World::new();
454 world.schema_format = BrdbSchemaFormat::Latest;
455 world.register_all_components();
456 world.meta.bundle.description = "Entity fixture".to_string();
457 world.bricks.push(Brick {
458 position: (0, 0, 6).into(),
459 color: (200, 50, 50).into(),
460 ..Default::default()
461 });
462 world.add_brick_grid(
463 Entity {
464 frozen: true,
465 location: (0.0, 0.0, 40.0).into(),
466 ..Default::default()
467 },
468 [Brick {
469 position: (0, 0, 3).into(),
470 color: (0, 255, 0).into(),
471 ..Default::default()
472 }],
473 );
474 world
475}
476
477/// Current-build components in the Latest format: WireValue fields, dynamic
478/// arrays/maps (typed and empty) and a dynamic enum, plus a wire.
479fn components_latest_world() -> World {
480 let mut world = World::new();
481 world.meta.bundle.description = "Latest component fixture".to_string();
482 let gate = |x: i32, asset: &'static str| Brick {
483 position: (x, 0, 1).into(),
484 asset: BrickType::str(asset),
485 ..Default::default()
486 };
487
488 // 1: Blend gate with WireValue inputs (f64 and i64 alternatives).
489 let (blend, blend_id) = gate(0, "B_1x1_Gate_Expr_MathBlend")
490 .with_component(
491 assets::components::LogicGate::Blend.component_with_overrides(HashMap::from([
492 (
493 "InputA".into(),
494 Box::new(WireValue::Number(1.5)) as Box<dyn AsBrdbValue>,
495 ),
496 ("InputB".into(), Box::new(WireValue::Int(-3))),
497 ("Blend".into(), Box::new(0.25f64)),
498 ])),
499 )
500 .with_id_split();
501 // 2: Buffer with Str / Bool alternatives.
502 let buffer =
503 gate(20, "B_1x1_Gate_BufferTicks").with_component(assets::components::BufferTicks {
504 ticks_to_wait: 4,
505 input: WireValue::Str("hello".into()),
506 output: WireValue::Bool(true),
507 buffered: WireValue::Vector {
508 x: 1.0,
509 y: -2.0,
510 z: 0.5,
511 },
512 ..Default::default()
513 });
514 // 3: Array variable holding i64 elements.
515 let array = gate(40, "B_1x1_Gate_Variable_Array").with_component(
516 assets::LiteralComponent::new("BrickComponentType_ArrayVar").with_data([(
517 "Value",
518 Box::new(BrdbDynamicArray::new(
519 "i64",
520 vec![BrdbValue::I64(7), BrdbValue::I64(300)],
521 )) as Box<dyn AsBrdbValue>,
522 )]),
523 );
524 // 4: Map variable from Str keys to f64 values.
525 let map = gate(60, "B_1x1_Gate_Variable_Map").with_component(
526 assets::LiteralComponent::new("BrickComponentType_MapVar").with_data([(
527 "Value",
528 Box::new(BrdbDynamicMap::new(
529 "Str",
530 "f64",
531 [(BrdbValue::String("k".into()), BrdbValue::F64(2.5))]
532 .into_iter()
533 .collect(),
534 )) as Box<dyn AsBrdbValue>,
535 )]),
536 );
537 // 5: Unset array variable: the default empty container.
538 let empty_array = gate(80, "B_1x1_Gate_Variable_Array")
539 .with_component(assets::LiteralComponent::new("BrickComponentType_ArrayVar"));
540 // 6: Integer to enum: a dynamic enum field.
541 let to_enum = gate(100, "B_1x1_Gate_Expr_IntegerToEnum").with_component(
542 assets::LiteralComponent::new("BrickComponentType_Expr_IntegerToEnum").with_data([
543 ("Input", Box::new(2i64) as Box<dyn AsBrdbValue>),
544 (
545 "TargetType",
546 Box::new(BrdbDynamicEnum::new("EBrickAxis", 0)),
547 ),
548 ]),
549 );
550 // 7: A rerouter fed by the blend gate.
551 let (reroute, reroute_id) = Brick {
552 position: (120, 0, 1).into(),
553 asset: assets::bricks::B_REROUTE,
554 ..Default::default()
555 }
556 .with_component(assets::components::Rerouter)
557 .with_id_split();
558
559 world.add_bricks([blend, buffer, array, map, empty_array, to_enum, reroute]);
560 world.add_wire_connection(
561 assets::components::LogicGate::Blend.output_of(blend_id),
562 assets::components::Rerouter::input_of(reroute_id),
563 );
564 // Embed only the structs these components use (what brs-js writes).
565 world.register_used_components();
566 world
567}More examples
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}5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 let path = PathBuf::from("./example_entity.brdb");
7
8 // Ensures the memory db can be created without errors
9 let db = Brdb::new(&path)?.into_reader();
10 let mut world = World::new();
11 world.meta.bundle.description = "Example World".to_string();
12 world.add_brick_grid(
13 Entity {
14 frozen: true,
15 location: (0.0, 0.0, 40.0).into(),
16 ..Default::default()
17 },
18 [Brick {
19 position: (0, 0, 3).into(),
20 color: (0, 255, 0).into(),
21 ..Default::default()
22 }],
23 );
24
25 db.save("example world", &world)?;
26
27 println!("{}", db.get_fs()?.render());
28
29 Ok(())
30}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("./example_prefab.brz");
7
8 let mut world = World::new();
9 // A single 1x1 plate-ish brick (half-extent 5,5,2) at the origin so the
10 // bounds match the hand-captured reference prefab.
11 world.bricks.push(Brick {
12 asset: BrickType::Procedural {
13 asset: assets::bricks::PB_DEFAULT_BRICK.into(),
14 size: BrickSize { x: 5, y: 5, z: 2 },
15 },
16 position: (0, 0, 0).into(),
17 ..Default::default()
18 });
19
20 world.make_prefab();
21 println!(
22 "Prefab.json:\n{}",
23 serde_json::to_string_pretty(world.meta.prefab.as_ref().unwrap())?
24 );
25 println!(
26 "Bundle.json:\n{}",
27 serde_json::to_string_pretty(&world.meta.bundle)?
28 );
29
30 if path.exists() {
31 std::fs::remove_file(&path)?;
32 }
33 world.write_brz(&path)?;
34 println!("wrote {}", path.display());
35 Ok(())
36}5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 let path = PathBuf::from("./example_wire.brdb");
7
8 // Ensures the memory db can be created without errors
9 let db = Brdb::new(&path)?.into_reader();
10 let mut world = World::new();
11 // Register the built-in component type/struct mappings so the gate and
12 // rerouter component types resolve when writing.
13 world.register_all_components();
14 world.meta.bundle.description = "Example World".to_string();
15
16 let (a, a_id) = Brick {
17 position: (0, 0, 1).into(),
18 color: (255, 0, 0).into(),
19 asset: assets::bricks::B_REROUTE,
20 ..Default::default()
21 }
22 .with_component(assets::components::Rerouter)
23 .with_id_split();
24 let (b, b_id) = Brick {
25 position: (15, 0, 1).into(),
26 color: (255, 0, 0).into(),
27 asset: assets::components::LogicGate::BoolNot.brick(),
28 ..Default::default()
29 }
30 .with_component(assets::components::LogicGate::BoolNot.component())
31 .with_id_split();
32
33 world.add_bricks([a, b]);
34 world.add_wire_connection(
35 assets::components::LogicGate::BoolNot.output_of(b_id),
36 assets::components::Rerouter::input_of(a_id),
37 );
38
39 db.save("example world", &world)?;
40
41 println!("{}", db.get_fs()?.render());
42
43 Ok(())
44}Sourcepub fn write_brdb(&self, path: impl AsRef<Path>) -> Result<(), BrError>
pub fn write_brdb(&self, path: impl AsRef<Path>) -> Result<(), BrError>
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}Sourcepub fn write_brz(&self, path: impl AsRef<Path>) -> Result<(), BrError>
pub fn write_brz(&self, path: impl AsRef<Path>) -> Result<(), BrError>
Examples found in repository?
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}More examples
5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 let path = PathBuf::from("./example_prefab.brz");
7
8 let mut world = World::new();
9 // A single 1x1 plate-ish brick (half-extent 5,5,2) at the origin so the
10 // bounds match the hand-captured reference prefab.
11 world.bricks.push(Brick {
12 asset: BrickType::Procedural {
13 asset: assets::bricks::PB_DEFAULT_BRICK.into(),
14 size: BrickSize { x: 5, y: 5, z: 2 },
15 },
16 position: (0, 0, 0).into(),
17 ..Default::default()
18 });
19
20 world.make_prefab();
21 println!(
22 "Prefab.json:\n{}",
23 serde_json::to_string_pretty(world.meta.prefab.as_ref().unwrap())?
24 );
25 println!(
26 "Bundle.json:\n{}",
27 serde_json::to_string_pretty(&world.meta.bundle)?
28 );
29
30 if path.exists() {
31 std::fs::remove_file(&path)?;
32 }
33 world.write_brz(&path)?;
34 println!("wrote {}", path.display());
35 Ok(())
36}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 to_brz_vec(&self) -> Result<Vec<u8>, BrError>
pub fn to_brz_vec(&self) -> Result<Vec<u8>, BrError>
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 register_all_components(&mut self)
pub fn register_all_components(&mut self)
Load the full component schema and register all known type→struct mappings and wire port names from the built-in component database.
Examples found in repository?
5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 let path = PathBuf::from("./example_wire.brdb");
7
8 // Ensures the memory db can be created without errors
9 let db = Brdb::new(&path)?.into_reader();
10 let mut world = World::new();
11 // Register the built-in component type/struct mappings so the gate and
12 // rerouter component types resolve when writing.
13 world.register_all_components();
14 world.meta.bundle.description = "Example World".to_string();
15
16 let (a, a_id) = Brick {
17 position: (0, 0, 1).into(),
18 color: (255, 0, 0).into(),
19 asset: assets::bricks::B_REROUTE,
20 ..Default::default()
21 }
22 .with_component(assets::components::Rerouter)
23 .with_id_split();
24 let (b, b_id) = Brick {
25 position: (15, 0, 1).into(),
26 color: (255, 0, 0).into(),
27 asset: assets::components::LogicGate::BoolNot.brick(),
28 ..Default::default()
29 }
30 .with_component(assets::components::LogicGate::BoolNot.component())
31 .with_id_split();
32
33 world.add_bricks([a, b]);
34 world.add_wire_connection(
35 assets::components::LogicGate::BoolNot.output_of(b_id),
36 assets::components::Rerouter::input_of(a_id),
37 );
38
39 db.save("example world", &world)?;
40
41 println!("{}", db.get_fs()?.render());
42
43 Ok(())
44}More examples
175fn wires_world() -> World {
176 // Mirror of examples/write_wire.rs (single grid, single chunk — fully
177 // deterministic), extended to 3 bricks/2 wires: a boolean NOT gate feeds
178 // one input of an AND gate, whose output feeds a rerouter.
179 let mut world = World::new();
180 world.register_all_components();
181 world.meta.bundle.description = "Wire fixture".to_string();
182
183 let (a, a_id) = Brick {
184 position: (30, 0, 1).into(),
185 color: (255, 0, 0).into(),
186 asset: assets::bricks::B_REROUTE,
187 ..Default::default()
188 }
189 .with_component(assets::components::Rerouter)
190 .with_id_split();
191 let (b, b_id) = Brick {
192 position: (15, 0, 1).into(),
193 color: (0, 255, 0).into(),
194 asset: assets::components::LogicGate::BoolAnd.brick(),
195 ..Default::default()
196 }
197 .with_component(assets::components::LogicGate::BoolAnd.component())
198 .with_id_split();
199 let (c, c_id) = Brick {
200 position: (0, 0, 1).into(),
201 color: (0, 0, 255).into(),
202 asset: assets::components::LogicGate::BoolNot.brick(),
203 ..Default::default()
204 }
205 .with_component(assets::components::LogicGate::BoolNot.component())
206 .with_id_split();
207
208 world.add_bricks([a, b, c]);
209 // Wire 1: NOT.output -> AND.inputA
210 world.add_wire_connection(
211 assets::components::LogicGate::BoolNot.output_of(c_id),
212 assets::components::LogicGate::BoolAnd.input_a_of(b_id),
213 );
214 // Wire 2: AND.output -> Rerouter.input
215 world.add_wire_connection(
216 assets::components::LogicGate::BoolAnd.output_of(b_id),
217 assets::components::Rerouter::input_of(a_id),
218 );
219
220 world
221}
222
223fn components_world() -> World {
224 // Single grid, single chunk (all positions < 2048) — fully deterministic.
225 // register_all_components() embeds the full component catalog so every
226 // type below (light/interact/buffer/expr) resolves; each
227 // component below carries non-default property values, and #4/#5 exercise
228 // a WireValue-typed property (BufferTicks' Input/Output, and the Blend
229 // gate's inputs).
230 let mut world = World::new();
231 world.register_all_components();
232 world.meta.bundle.description = "Component fixture".to_string();
233
234 // 1: Point light — non-default brightness/radius/color, decoupled from
235 // brick color (bUseBrickColor: false).
236 world.bricks.push(
237 Brick {
238 position: (0, 0, 6).into(),
239 color: (255, 255, 255).into(),
240 ..Default::default()
241 }
242 .with_component(
243 assets::LiteralComponent::new("Component_PointLight").with_data([
244 ("bMatchBrickShape", Box::new(false) as Box<dyn AsBrdbValue>),
245 ("bEnabled", Box::new(true)),
246 ("Brightness", Box::new(500.0f32)),
247 ("Radius", Box::new(800.0f32)),
248 (
249 "Color",
250 Box::new(SavedBrickColor {
251 r: 10,
252 g: 20,
253 b: 30,
254 a: 255,
255 }),
256 ),
257 ("bUseBrickColor", Box::new(false)),
258 ("bCastShadows", Box::new(true)),
259 ]),
260 ),
261 );
262
263 // 2: Spot light — narrow cone, non-default brightness/color.
264 world.bricks.push(
265 Brick {
266 position: (20, 0, 6).into(),
267 color: (255, 255, 0).into(),
268 ..Default::default()
269 }
270 .with_component(
271 assets::LiteralComponent::new("Component_SpotLight").with_data([
272 ("InnerConeAngle", Box::new(15.0f32) as Box<dyn AsBrdbValue>),
273 ("OuterConeAngle", Box::new(45.0f32)),
274 ("bEnabled", Box::new(true)),
275 ("Brightness", Box::new(300.0f32)),
276 ("Radius", Box::new(600.0f32)),
277 (
278 "Color",
279 Box::new(SavedBrickColor {
280 r: 255,
281 g: 0,
282 b: 0,
283 a: 255,
284 }),
285 ),
286 ("bUseBrickColor", Box::new(false)),
287 ("bCastShadows", Box::new(true)),
288 ]),
289 ),
290 );
291
292 // 3: Interact — custom prompt text, hidden interaction.
293 world.bricks.push(
294 Brick {
295 position: (40, 0, 6).into(),
296 color: (0, 255, 255).into(),
297 ..Default::default()
298 }
299 .with_component(
300 assets::LiteralComponent::new("Component_Interact").with_data([
301 (
302 "Message",
303 Box::new("You interacted!".to_string()) as Box<dyn AsBrdbValue>,
304 ),
305 ("ConsoleTag", Box::new("fixture_interact".to_string())),
306 ("bAllowNearbyInteraction", Box::new(false)),
307 ("bHiddenInteraction", Box::new(true)),
308 ("PromptCustomLabel", Box::new("Open Door".to_string())),
309 ]),
310 ),
311 );
312
313 // 4: Buffer (ticks) — Input/Output are WireValue; non-default counters
314 // plus a Number/Bool value pair.
315 world.bricks.push(
316 Brick {
317 position: (60, 0, 1).into(),
318 color: (128, 0, 128).into(),
319 asset: assets::components::BufferTicks::default().brick(),
320 ..Default::default()
321 }
322 .with_component(assets::components::BufferTicks {
323 current_ticks: 3,
324 ticks_to_wait: 10,
325 input: WireValue::Number(2.5),
326 output: WireValue::Bool(true),
327 ..Default::default()
328 }),
329 );
330
331 // 5: Blend gate — Expr_MathBlend's InputA/InputB are each a WireValue
332 // (here an f64 and an i64, showing the value is polymorphic per-port);
333 // Blend itself is a plain f64.
334 world.bricks.push(
335 Brick {
336 position: (80, 0, 1).into(),
337 color: (0, 128, 0).into(),
338 asset: assets::components::LogicGate::Blend.brick(),
339 ..Default::default()
340 }
341 .with_component(
342 assets::components::LogicGate::Blend.component_with_overrides(HashMap::from([
343 (
344 "InputA".into(),
345 Box::new(WireValue::Number(10.0)) as Box<dyn AsBrdbValue>,
346 ),
347 ("InputB".into(), Box::new(WireValue::Int(7))),
348 ("Blend".into(), Box::new(0.75f64)),
349 ])),
350 ),
351 );
352
353 world
354}
355
356fn entities_world() -> World {
357 // Mirror of examples/write_entity.rs's floating sub-grid — the first
358 // grid added after the always-present main grid 1 gets persistent index
359 // 2, so it lands at Grids/2 — plus a couple of main-grid (Grids/1)
360 // bricks. Both grids are single-chunk; note the *outer* Grids/1 vs
361 // Grids/2 folder order comes from a HashMap<usize, UnsavedGrid> in the
362 // writer and is NOT guaranteed stable run-to-run (see stability notes).
363 //
364 // register_all_components() is required here (matching
365 // test_write_entity_save, NOT the plain write_entity.rs example): without
366 // it, Entity_DynamicBrickGrid's class name is never registered and the
367 // write fails with `UnknownType("Entity_DynamicBrickGrid")` — confirmed
368 // by running the crate's own unmodified write_entity example.
369 let mut world = World::new();
370 world.register_all_components();
371 world.meta.bundle.description = "Entity fixture".to_string();
372
373 world.bricks.push(Brick {
374 position: (0, 0, 6).into(),
375 color: (200, 50, 50).into(),
376 ..Default::default()
377 });
378 world.bricks.push(Brick {
379 position: (20, 0, 6).into(),
380 color: (50, 200, 50).into(),
381 ..Default::default()
382 });
383
384 world.add_brick_grid(
385 Entity {
386 frozen: true,
387 location: (0.0, 0.0, 40.0).into(),
388 ..Default::default()
389 },
390 [Brick {
391 position: (0, 0, 3).into(),
392 color: (0, 255, 0).into(),
393 ..Default::default()
394 }],
395 );
396
397 world
398}
399
400fn spawner_world() -> World {
401 // Prefab-embedding fixture: an outer prefab whose spawner gate references
402 // an inner single-brick prefab embedded at Prefabs/Uploads/<BLAKE3>.brz.
403 // Single grid, single chunk, one insertion-ordered prefab map entry —
404 // fully deterministic. The inner archive is written raw (no zstd) so the
405 // embedded bytes are cross-language reproducible.
406 let mut inner = World::new();
407 inner.meta.bundle.description = "Inner prefab".to_string();
408 inner.bricks.push(Brick {
409 position: (0, 0, 6).into(),
410 color: (255, 0, 0).into(),
411 ..Default::default()
412 });
413 inner.make_prefab();
414 let inner_bytes = {
415 let pending = inner.to_unsaved().unwrap().to_pending().unwrap();
416 let mut buf = Vec::new();
417 pending
418 .to_brz_data(None)
419 .unwrap()
420 .write(&mut buf, None)
421 .unwrap();
422 buf
423 };
424
425 let mut world = World::new();
426 world.register_all_components();
427 world.meta.bundle.description = "Spawner fixture".to_string();
428 let prefab_path = world.add_prefab(inner_bytes);
429 world.bricks.push(
430 Brick {
431 asset: brdb::BrickType::str("B_1x1_Gate_Exec_PrefabSpawner"),
432 position: (0, 0, 1).into(),
433 ..Default::default()
434 }
435 .with_component(
436 assets::LiteralComponent::new("BrickComponentType_Exec_PrefabSpawner")
437 .with_data([("Prefab", Box::new(prefab_path) as Box<dyn AsBrdbValue>)]),
438 ),
439 );
440 world.make_prefab();
441 world
442}
443
444/// `features_world` written in the Latest format.
445fn features_latest_world() -> World {
446 let mut world = features_world();
447 world.schema_format = BrdbSchemaFormat::Latest;
448 world
449}
450
451/// `entities_world` with the current build's schemas in the Latest format.
452fn entities_latest_world() -> World {
453 let mut world = World::new();
454 world.schema_format = BrdbSchemaFormat::Latest;
455 world.register_all_components();
456 world.meta.bundle.description = "Entity fixture".to_string();
457 world.bricks.push(Brick {
458 position: (0, 0, 6).into(),
459 color: (200, 50, 50).into(),
460 ..Default::default()
461 });
462 world.add_brick_grid(
463 Entity {
464 frozen: true,
465 location: (0.0, 0.0, 40.0).into(),
466 ..Default::default()
467 },
468 [Brick {
469 position: (0, 0, 3).into(),
470 color: (0, 255, 0).into(),
471 ..Default::default()
472 }],
473 );
474 world
475}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 register_used_components(&mut self)
pub fn register_used_components(&mut self)
Register only the components actually used by this world’s bricks,
mirroring how the game embeds a bundle: ComponentsShared.schema and the
global-data component tables carry only the data structs that appear,
not the full catalog (which [register_all_components] embeds).
Call this AFTER all bricks/grids have been added. The component schema starts from the minimal SoA scaffolding and gains each used component’s data struct plus its transitive type dependencies; global-data component type/struct/port tables are rebuilt to match. Entity types are registered in full (the catalog is a single microchip-grid entry).
This keeps generated bundles byte-compatible with what the current game build writes — embedding the stale full catalog can make the game reject the schema (“while building schema: while reading struct count”).
Examples found in repository?
479fn components_latest_world() -> World {
480 let mut world = World::new();
481 world.meta.bundle.description = "Latest component fixture".to_string();
482 let gate = |x: i32, asset: &'static str| Brick {
483 position: (x, 0, 1).into(),
484 asset: BrickType::str(asset),
485 ..Default::default()
486 };
487
488 // 1: Blend gate with WireValue inputs (f64 and i64 alternatives).
489 let (blend, blend_id) = gate(0, "B_1x1_Gate_Expr_MathBlend")
490 .with_component(
491 assets::components::LogicGate::Blend.component_with_overrides(HashMap::from([
492 (
493 "InputA".into(),
494 Box::new(WireValue::Number(1.5)) as Box<dyn AsBrdbValue>,
495 ),
496 ("InputB".into(), Box::new(WireValue::Int(-3))),
497 ("Blend".into(), Box::new(0.25f64)),
498 ])),
499 )
500 .with_id_split();
501 // 2: Buffer with Str / Bool alternatives.
502 let buffer =
503 gate(20, "B_1x1_Gate_BufferTicks").with_component(assets::components::BufferTicks {
504 ticks_to_wait: 4,
505 input: WireValue::Str("hello".into()),
506 output: WireValue::Bool(true),
507 buffered: WireValue::Vector {
508 x: 1.0,
509 y: -2.0,
510 z: 0.5,
511 },
512 ..Default::default()
513 });
514 // 3: Array variable holding i64 elements.
515 let array = gate(40, "B_1x1_Gate_Variable_Array").with_component(
516 assets::LiteralComponent::new("BrickComponentType_ArrayVar").with_data([(
517 "Value",
518 Box::new(BrdbDynamicArray::new(
519 "i64",
520 vec![BrdbValue::I64(7), BrdbValue::I64(300)],
521 )) as Box<dyn AsBrdbValue>,
522 )]),
523 );
524 // 4: Map variable from Str keys to f64 values.
525 let map = gate(60, "B_1x1_Gate_Variable_Map").with_component(
526 assets::LiteralComponent::new("BrickComponentType_MapVar").with_data([(
527 "Value",
528 Box::new(BrdbDynamicMap::new(
529 "Str",
530 "f64",
531 [(BrdbValue::String("k".into()), BrdbValue::F64(2.5))]
532 .into_iter()
533 .collect(),
534 )) as Box<dyn AsBrdbValue>,
535 )]),
536 );
537 // 5: Unset array variable: the default empty container.
538 let empty_array = gate(80, "B_1x1_Gate_Variable_Array")
539 .with_component(assets::LiteralComponent::new("BrickComponentType_ArrayVar"));
540 // 6: Integer to enum: a dynamic enum field.
541 let to_enum = gate(100, "B_1x1_Gate_Expr_IntegerToEnum").with_component(
542 assets::LiteralComponent::new("BrickComponentType_Expr_IntegerToEnum").with_data([
543 ("Input", Box::new(2i64) as Box<dyn AsBrdbValue>),
544 (
545 "TargetType",
546 Box::new(BrdbDynamicEnum::new("EBrickAxis", 0)),
547 ),
548 ]),
549 );
550 // 7: A rerouter fed by the blend gate.
551 let (reroute, reroute_id) = Brick {
552 position: (120, 0, 1).into(),
553 asset: assets::bricks::B_REROUTE,
554 ..Default::default()
555 }
556 .with_component(assets::components::Rerouter)
557 .with_id_split();
558
559 world.add_bricks([blend, buffer, array, map, empty_array, to_enum, reroute]);
560 world.add_wire_connection(
561 assets::components::LogicGate::Blend.output_of(blend_id),
562 assets::components::Rerouter::input_of(reroute_id),
563 );
564 // Embed only the structs these components use (what brs-js writes).
565 world.register_used_components();
566 world
567}Sourcepub fn register_component_schema(&mut self, schema_str: &str)
pub fn register_component_schema(&mut self, schema_str: &str)
Parse a schema string and merge its definitions.
Sourcepub fn register_component(&mut self, struct_name: &str)
pub fn register_component(&mut self, struct_name: &str)
Pull a single struct from the max schema.
Sourcepub fn brick_bounds(&self) -> Option<(Position, Position)>
pub fn brick_bounds(&self) -> Option<(Position, Position)>
Inclusive axis-aligned bounding box over the main-grid bricks, in brick
units, as (min, max). Returns None if there are no main-grid bricks.
Non-main grids (microchips) are excluded — their bricks live inside an
entity and are offset to the chunk center.
Sourcepub fn make_prefab(&mut self)
pub fn make_prefab(&mut self)
Mark this world’s metadata as a prefab: sets the bundle type to
"Prefab" and fills Meta/Prefab.json with pivots/bounds computed from
the main-grid brick bounding box (see World::brick_bounds). The
write path then emits a prefab bundle (Bundle.json + Prefab.json, plus
the optional Screenshot/Thumbnail; no World.json).
Examples found in repository?
5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 let path = PathBuf::from("./example_prefab.brz");
7
8 let mut world = World::new();
9 // A single 1x1 plate-ish brick (half-extent 5,5,2) at the origin so the
10 // bounds match the hand-captured reference prefab.
11 world.bricks.push(Brick {
12 asset: BrickType::Procedural {
13 asset: assets::bricks::PB_DEFAULT_BRICK.into(),
14 size: BrickSize { x: 5, y: 5, z: 2 },
15 },
16 position: (0, 0, 0).into(),
17 ..Default::default()
18 });
19
20 world.make_prefab();
21 println!(
22 "Prefab.json:\n{}",
23 serde_json::to_string_pretty(world.meta.prefab.as_ref().unwrap())?
24 );
25 println!(
26 "Bundle.json:\n{}",
27 serde_json::to_string_pretty(&world.meta.bundle)?
28 );
29
30 if path.exists() {
31 std::fs::remove_file(&path)?;
32 }
33 world.write_brz(&path)?;
34 println!("wrote {}", path.display());
35 Ok(())
36}More examples
400fn spawner_world() -> World {
401 // Prefab-embedding fixture: an outer prefab whose spawner gate references
402 // an inner single-brick prefab embedded at Prefabs/Uploads/<BLAKE3>.brz.
403 // Single grid, single chunk, one insertion-ordered prefab map entry —
404 // fully deterministic. The inner archive is written raw (no zstd) so the
405 // embedded bytes are cross-language reproducible.
406 let mut inner = World::new();
407 inner.meta.bundle.description = "Inner prefab".to_string();
408 inner.bricks.push(Brick {
409 position: (0, 0, 6).into(),
410 color: (255, 0, 0).into(),
411 ..Default::default()
412 });
413 inner.make_prefab();
414 let inner_bytes = {
415 let pending = inner.to_unsaved().unwrap().to_pending().unwrap();
416 let mut buf = Vec::new();
417 pending
418 .to_brz_data(None)
419 .unwrap()
420 .write(&mut buf, None)
421 .unwrap();
422 buf
423 };
424
425 let mut world = World::new();
426 world.register_all_components();
427 world.meta.bundle.description = "Spawner fixture".to_string();
428 let prefab_path = world.add_prefab(inner_bytes);
429 world.bricks.push(
430 Brick {
431 asset: brdb::BrickType::str("B_1x1_Gate_Exec_PrefabSpawner"),
432 position: (0, 0, 1).into(),
433 ..Default::default()
434 }
435 .with_component(
436 assets::LiteralComponent::new("BrickComponentType_Exec_PrefabSpawner")
437 .with_data([("Prefab", Box::new(prefab_path) as Box<dyn AsBrdbValue>)]),
438 ),
439 );
440 world.make_prefab();
441 world
442}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 add_prefab(&mut self, brz_bytes: impl Into<Vec<u8>>) -> String
pub fn add_prefab(&mut self, brz_bytes: impl Into<Vec<u8>>) -> String
Embed a prefab archive, content-addressed the way the game does:
Prefabs/Uploads/<BLAKE3-uppercase-hex>.brz. Returns that path — the
exact string to store in a Prefab component property
(bundle_path_ref), e.g. on BrickComponentType_PrefabSpawn or
BrickComponentType_WireGraph_Exec_PrefabSpawner.
Examples found in repository?
400fn spawner_world() -> World {
401 // Prefab-embedding fixture: an outer prefab whose spawner gate references
402 // an inner single-brick prefab embedded at Prefabs/Uploads/<BLAKE3>.brz.
403 // Single grid, single chunk, one insertion-ordered prefab map entry —
404 // fully deterministic. The inner archive is written raw (no zstd) so the
405 // embedded bytes are cross-language reproducible.
406 let mut inner = World::new();
407 inner.meta.bundle.description = "Inner prefab".to_string();
408 inner.bricks.push(Brick {
409 position: (0, 0, 6).into(),
410 color: (255, 0, 0).into(),
411 ..Default::default()
412 });
413 inner.make_prefab();
414 let inner_bytes = {
415 let pending = inner.to_unsaved().unwrap().to_pending().unwrap();
416 let mut buf = Vec::new();
417 pending
418 .to_brz_data(None)
419 .unwrap()
420 .write(&mut buf, None)
421 .unwrap();
422 buf
423 };
424
425 let mut world = World::new();
426 world.register_all_components();
427 world.meta.bundle.description = "Spawner fixture".to_string();
428 let prefab_path = world.add_prefab(inner_bytes);
429 world.bricks.push(
430 Brick {
431 asset: brdb::BrickType::str("B_1x1_Gate_Exec_PrefabSpawner"),
432 position: (0, 0, 1).into(),
433 ..Default::default()
434 }
435 .with_component(
436 assets::LiteralComponent::new("BrickComponentType_Exec_PrefabSpawner")
437 .with_data([("Prefab", Box::new(prefab_path) as Box<dyn AsBrdbValue>)]),
438 ),
439 );
440 world.make_prefab();
441 world
442}More examples
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 add_prefab_world(&mut self, world: &World) -> Result<String, BrError>
pub fn add_prefab_world(&mut self, world: &World) -> Result<String, BrError>
Serialize world to an in-memory .brz and embed it as a prefab.
Sourcepub fn to_unsaved(&self) -> Result<UnsavedFs, BrError>
pub fn to_unsaved(&self) -> Result<UnsavedFs, BrError>
Examples found in repository?
400fn spawner_world() -> World {
401 // Prefab-embedding fixture: an outer prefab whose spawner gate references
402 // an inner single-brick prefab embedded at Prefabs/Uploads/<BLAKE3>.brz.
403 // Single grid, single chunk, one insertion-ordered prefab map entry —
404 // fully deterministic. The inner archive is written raw (no zstd) so the
405 // embedded bytes are cross-language reproducible.
406 let mut inner = World::new();
407 inner.meta.bundle.description = "Inner prefab".to_string();
408 inner.bricks.push(Brick {
409 position: (0, 0, 6).into(),
410 color: (255, 0, 0).into(),
411 ..Default::default()
412 });
413 inner.make_prefab();
414 let inner_bytes = {
415 let pending = inner.to_unsaved().unwrap().to_pending().unwrap();
416 let mut buf = Vec::new();
417 pending
418 .to_brz_data(None)
419 .unwrap()
420 .write(&mut buf, None)
421 .unwrap();
422 buf
423 };
424
425 let mut world = World::new();
426 world.register_all_components();
427 world.meta.bundle.description = "Spawner fixture".to_string();
428 let prefab_path = world.add_prefab(inner_bytes);
429 world.bricks.push(
430 Brick {
431 asset: brdb::BrickType::str("B_1x1_Gate_Exec_PrefabSpawner"),
432 position: (0, 0, 1).into(),
433 ..Default::default()
434 }
435 .with_component(
436 assets::LiteralComponent::new("BrickComponentType_Exec_PrefabSpawner")
437 .with_data([("Prefab", Box::new(prefab_path) as Box<dyn AsBrdbValue>)]),
438 ),
439 );
440 world.make_prefab();
441 world
442}
443
444/// `features_world` written in the Latest format.
445fn features_latest_world() -> World {
446 let mut world = features_world();
447 world.schema_format = BrdbSchemaFormat::Latest;
448 world
449}
450
451/// `entities_world` with the current build's schemas in the Latest format.
452fn entities_latest_world() -> World {
453 let mut world = World::new();
454 world.schema_format = BrdbSchemaFormat::Latest;
455 world.register_all_components();
456 world.meta.bundle.description = "Entity fixture".to_string();
457 world.bricks.push(Brick {
458 position: (0, 0, 6).into(),
459 color: (200, 50, 50).into(),
460 ..Default::default()
461 });
462 world.add_brick_grid(
463 Entity {
464 frozen: true,
465 location: (0.0, 0.0, 40.0).into(),
466 ..Default::default()
467 },
468 [Brick {
469 position: (0, 0, 3).into(),
470 color: (0, 255, 0).into(),
471 ..Default::default()
472 }],
473 );
474 world
475}
476
477/// Current-build components in the Latest format: WireValue fields, dynamic
478/// arrays/maps (typed and empty) and a dynamic enum, plus a wire.
479fn components_latest_world() -> World {
480 let mut world = World::new();
481 world.meta.bundle.description = "Latest component fixture".to_string();
482 let gate = |x: i32, asset: &'static str| Brick {
483 position: (x, 0, 1).into(),
484 asset: BrickType::str(asset),
485 ..Default::default()
486 };
487
488 // 1: Blend gate with WireValue inputs (f64 and i64 alternatives).
489 let (blend, blend_id) = gate(0, "B_1x1_Gate_Expr_MathBlend")
490 .with_component(
491 assets::components::LogicGate::Blend.component_with_overrides(HashMap::from([
492 (
493 "InputA".into(),
494 Box::new(WireValue::Number(1.5)) as Box<dyn AsBrdbValue>,
495 ),
496 ("InputB".into(), Box::new(WireValue::Int(-3))),
497 ("Blend".into(), Box::new(0.25f64)),
498 ])),
499 )
500 .with_id_split();
501 // 2: Buffer with Str / Bool alternatives.
502 let buffer =
503 gate(20, "B_1x1_Gate_BufferTicks").with_component(assets::components::BufferTicks {
504 ticks_to_wait: 4,
505 input: WireValue::Str("hello".into()),
506 output: WireValue::Bool(true),
507 buffered: WireValue::Vector {
508 x: 1.0,
509 y: -2.0,
510 z: 0.5,
511 },
512 ..Default::default()
513 });
514 // 3: Array variable holding i64 elements.
515 let array = gate(40, "B_1x1_Gate_Variable_Array").with_component(
516 assets::LiteralComponent::new("BrickComponentType_ArrayVar").with_data([(
517 "Value",
518 Box::new(BrdbDynamicArray::new(
519 "i64",
520 vec![BrdbValue::I64(7), BrdbValue::I64(300)],
521 )) as Box<dyn AsBrdbValue>,
522 )]),
523 );
524 // 4: Map variable from Str keys to f64 values.
525 let map = gate(60, "B_1x1_Gate_Variable_Map").with_component(
526 assets::LiteralComponent::new("BrickComponentType_MapVar").with_data([(
527 "Value",
528 Box::new(BrdbDynamicMap::new(
529 "Str",
530 "f64",
531 [(BrdbValue::String("k".into()), BrdbValue::F64(2.5))]
532 .into_iter()
533 .collect(),
534 )) as Box<dyn AsBrdbValue>,
535 )]),
536 );
537 // 5: Unset array variable: the default empty container.
538 let empty_array = gate(80, "B_1x1_Gate_Variable_Array")
539 .with_component(assets::LiteralComponent::new("BrickComponentType_ArrayVar"));
540 // 6: Integer to enum: a dynamic enum field.
541 let to_enum = gate(100, "B_1x1_Gate_Expr_IntegerToEnum").with_component(
542 assets::LiteralComponent::new("BrickComponentType_Expr_IntegerToEnum").with_data([
543 ("Input", Box::new(2i64) as Box<dyn AsBrdbValue>),
544 (
545 "TargetType",
546 Box::new(BrdbDynamicEnum::new("EBrickAxis", 0)),
547 ),
548 ]),
549 );
550 // 7: A rerouter fed by the blend gate.
551 let (reroute, reroute_id) = Brick {
552 position: (120, 0, 1).into(),
553 asset: assets::bricks::B_REROUTE,
554 ..Default::default()
555 }
556 .with_component(assets::components::Rerouter)
557 .with_id_split();
558
559 world.add_bricks([blend, buffer, array, map, empty_array, to_enum, reroute]);
560 world.add_wire_connection(
561 assets::components::LogicGate::Blend.output_of(blend_id),
562 assets::components::Rerouter::input_of(reroute_id),
563 );
564 // Embed only the structs these components use (what brs-js writes).
565 world.register_used_components();
566 world
567}
568
569fn hash_archive(
570 path: &PathBuf,
571) -> Result<BTreeMap<String, serde_json::Value>, Box<dyn std::error::Error>> {
572 fn collect(fs: &BrFs, prefix: &str, out: &mut Vec<(String, Option<i64>)>) {
573 match fs {
574 BrFs::Root(children) => {
575 for (n, c) in children {
576 collect(c, n, out);
577 }
578 }
579 BrFs::Folder(_, children) => {
580 for (n, c) in children {
581 collect(c, &format!("{prefix}/{n}"), out);
582 }
583 }
584 BrFs::File(f) => out.push((prefix.to_string(), f.content_id)),
585 }
586 }
587 let reader = Brz::open(path)?.into_reader();
588 let reader = &*reader;
589 let mut files = Vec::new();
590 collect(&reader.get_fs()?, "", &mut files);
591 let mut out = BTreeMap::new();
592 for (p, content_id) in files {
593 let content = match content_id {
594 Some(id) => reader.find_blob(id)?.read()?,
595 None => Vec::new(),
596 };
597 out.insert(
598 p,
599 serde_json::json!({
600 "blake3": blake3::hash(&content).to_hex().to_string(),
601 "len": content.len(),
602 }),
603 );
604 }
605 Ok(out)
606}
607
608fn main() -> Result<(), Box<dyn std::error::Error>> {
609 let dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("fixtures");
610 fs::create_dir_all(dir.join("schemas"))?;
611
612 let mut hashes = BTreeMap::new();
613 for (name, world) in [
614 ("brick", brick_world()),
615 ("features", features_world()),
616 ("chunks", chunks_world()),
617 ("wires", wires_world()),
618 ("components", components_world()),
619 ("entities", entities_world()),
620 ("spawner", spawner_world()),
621 ("features_latest", features_latest_world()),
622 ("components_latest", components_latest_world()),
623 ("entities_latest", entities_latest_world()),
624 ] {
625 let pending = world.to_unsaved()?.to_pending()?;
626 // raw variant: no zstd anywhere — byte-comparable across languages
627 let raw_path = dir.join(format!("{name}_raw.brz"));
628 let mut f = fs::File::create(&raw_path)?;
629 pending.clone().to_brz_data(None)?.write(&mut f, None)?;
630 // compressed variant: zstd level 14 (matches Brz::save)
631 let mut f = fs::File::create(dir.join(format!("{name}.brz")))?;
632 pending.to_brz_data(Some(14))?.write(&mut f, Some(14))?;
633
634 // .brdb variant: content parity only (the .brdb container is not
635 // byte-deterministic across runs).
636 let db_path = dir.join(format!("{name}.brdb"));
637 let _ = fs::remove_file(&db_path);
638 Brdb::create(&db_path)?.save("Fixture", &world)?;
639
640 hashes.insert(name.to_string(), hash_archive(&raw_path)?);
641 }
642 fs::write(
643 dir.join("hashes.json"),
644 serde_json::to_string_pretty(&hashes)?,
645 )?;
646
647 // The nine embedded schemas as binary msgpack (the exact bytes the
648 // writer embeds as *.schema files inside archives).
649 for (name, schema) in [
650 ("BRSavedGlobalDataSoA", schemas::global_data_schema()),
651 ("BRSavedOwnerTableSoA", schemas::owners_schema()),
652 (
653 "BRSavedBrickChunkIndexSoA",
654 schemas::bricks_chunk_index_schema(),
655 ),
656 ("BRSavedBrickChunkSoA", schemas::bricks_chunks_schema()),
657 ("BRSavedWireChunkSoA", schemas::bricks_wires_schema()),
658 (
659 "BRSavedComponentChunkSoA",
660 schemas::bricks_components_schema_min(),
661 ),
662 (
663 "BRSavedComponentChunkSoA_max",
664 schemas::bricks_components_schema_max(),
665 ),
666 (
667 "BRSavedEntityChunkIndexSoA",
668 schemas::entities_chunk_index_schema(),
669 ),
670 ("BRSavedEntityChunkSoA", schemas::entities_chunks_schema()),
671 ] {
672 // Each schema in its own format (the current max schema has a
673 // types table, so it is written in the Latest form).
674 fs::write(dir.join(format!("schemas/{name}.bin")), schema.to_bytes()?)?;
675 }
676 eprintln!("fixtures written to {}", dir.display());
677 Ok(())
678}Sourcepub fn add_bricks(&mut self, bricks: impl IntoIterator<Item = Brick>)
pub fn add_bricks(&mut self, bricks: impl IntoIterator<Item = Brick>)
Add multiple bricks to the world
Examples found in repository?
5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 let path = PathBuf::from("./example_wire.brdb");
7
8 // Ensures the memory db can be created without errors
9 let db = Brdb::new(&path)?.into_reader();
10 let mut world = World::new();
11 // Register the built-in component type/struct mappings so the gate and
12 // rerouter component types resolve when writing.
13 world.register_all_components();
14 world.meta.bundle.description = "Example World".to_string();
15
16 let (a, a_id) = Brick {
17 position: (0, 0, 1).into(),
18 color: (255, 0, 0).into(),
19 asset: assets::bricks::B_REROUTE,
20 ..Default::default()
21 }
22 .with_component(assets::components::Rerouter)
23 .with_id_split();
24 let (b, b_id) = Brick {
25 position: (15, 0, 1).into(),
26 color: (255, 0, 0).into(),
27 asset: assets::components::LogicGate::BoolNot.brick(),
28 ..Default::default()
29 }
30 .with_component(assets::components::LogicGate::BoolNot.component())
31 .with_id_split();
32
33 world.add_bricks([a, b]);
34 world.add_wire_connection(
35 assets::components::LogicGate::BoolNot.output_of(b_id),
36 assets::components::Rerouter::input_of(a_id),
37 );
38
39 db.save("example world", &world)?;
40
41 println!("{}", db.get_fs()?.render());
42
43 Ok(())
44}More examples
175fn wires_world() -> World {
176 // Mirror of examples/write_wire.rs (single grid, single chunk — fully
177 // deterministic), extended to 3 bricks/2 wires: a boolean NOT gate feeds
178 // one input of an AND gate, whose output feeds a rerouter.
179 let mut world = World::new();
180 world.register_all_components();
181 world.meta.bundle.description = "Wire fixture".to_string();
182
183 let (a, a_id) = Brick {
184 position: (30, 0, 1).into(),
185 color: (255, 0, 0).into(),
186 asset: assets::bricks::B_REROUTE,
187 ..Default::default()
188 }
189 .with_component(assets::components::Rerouter)
190 .with_id_split();
191 let (b, b_id) = Brick {
192 position: (15, 0, 1).into(),
193 color: (0, 255, 0).into(),
194 asset: assets::components::LogicGate::BoolAnd.brick(),
195 ..Default::default()
196 }
197 .with_component(assets::components::LogicGate::BoolAnd.component())
198 .with_id_split();
199 let (c, c_id) = Brick {
200 position: (0, 0, 1).into(),
201 color: (0, 0, 255).into(),
202 asset: assets::components::LogicGate::BoolNot.brick(),
203 ..Default::default()
204 }
205 .with_component(assets::components::LogicGate::BoolNot.component())
206 .with_id_split();
207
208 world.add_bricks([a, b, c]);
209 // Wire 1: NOT.output -> AND.inputA
210 world.add_wire_connection(
211 assets::components::LogicGate::BoolNot.output_of(c_id),
212 assets::components::LogicGate::BoolAnd.input_a_of(b_id),
213 );
214 // Wire 2: AND.output -> Rerouter.input
215 world.add_wire_connection(
216 assets::components::LogicGate::BoolAnd.output_of(b_id),
217 assets::components::Rerouter::input_of(a_id),
218 );
219
220 world
221}
222
223fn components_world() -> World {
224 // Single grid, single chunk (all positions < 2048) — fully deterministic.
225 // register_all_components() embeds the full component catalog so every
226 // type below (light/interact/buffer/expr) resolves; each
227 // component below carries non-default property values, and #4/#5 exercise
228 // a WireValue-typed property (BufferTicks' Input/Output, and the Blend
229 // gate's inputs).
230 let mut world = World::new();
231 world.register_all_components();
232 world.meta.bundle.description = "Component fixture".to_string();
233
234 // 1: Point light — non-default brightness/radius/color, decoupled from
235 // brick color (bUseBrickColor: false).
236 world.bricks.push(
237 Brick {
238 position: (0, 0, 6).into(),
239 color: (255, 255, 255).into(),
240 ..Default::default()
241 }
242 .with_component(
243 assets::LiteralComponent::new("Component_PointLight").with_data([
244 ("bMatchBrickShape", Box::new(false) as Box<dyn AsBrdbValue>),
245 ("bEnabled", Box::new(true)),
246 ("Brightness", Box::new(500.0f32)),
247 ("Radius", Box::new(800.0f32)),
248 (
249 "Color",
250 Box::new(SavedBrickColor {
251 r: 10,
252 g: 20,
253 b: 30,
254 a: 255,
255 }),
256 ),
257 ("bUseBrickColor", Box::new(false)),
258 ("bCastShadows", Box::new(true)),
259 ]),
260 ),
261 );
262
263 // 2: Spot light — narrow cone, non-default brightness/color.
264 world.bricks.push(
265 Brick {
266 position: (20, 0, 6).into(),
267 color: (255, 255, 0).into(),
268 ..Default::default()
269 }
270 .with_component(
271 assets::LiteralComponent::new("Component_SpotLight").with_data([
272 ("InnerConeAngle", Box::new(15.0f32) as Box<dyn AsBrdbValue>),
273 ("OuterConeAngle", Box::new(45.0f32)),
274 ("bEnabled", Box::new(true)),
275 ("Brightness", Box::new(300.0f32)),
276 ("Radius", Box::new(600.0f32)),
277 (
278 "Color",
279 Box::new(SavedBrickColor {
280 r: 255,
281 g: 0,
282 b: 0,
283 a: 255,
284 }),
285 ),
286 ("bUseBrickColor", Box::new(false)),
287 ("bCastShadows", Box::new(true)),
288 ]),
289 ),
290 );
291
292 // 3: Interact — custom prompt text, hidden interaction.
293 world.bricks.push(
294 Brick {
295 position: (40, 0, 6).into(),
296 color: (0, 255, 255).into(),
297 ..Default::default()
298 }
299 .with_component(
300 assets::LiteralComponent::new("Component_Interact").with_data([
301 (
302 "Message",
303 Box::new("You interacted!".to_string()) as Box<dyn AsBrdbValue>,
304 ),
305 ("ConsoleTag", Box::new("fixture_interact".to_string())),
306 ("bAllowNearbyInteraction", Box::new(false)),
307 ("bHiddenInteraction", Box::new(true)),
308 ("PromptCustomLabel", Box::new("Open Door".to_string())),
309 ]),
310 ),
311 );
312
313 // 4: Buffer (ticks) — Input/Output are WireValue; non-default counters
314 // plus a Number/Bool value pair.
315 world.bricks.push(
316 Brick {
317 position: (60, 0, 1).into(),
318 color: (128, 0, 128).into(),
319 asset: assets::components::BufferTicks::default().brick(),
320 ..Default::default()
321 }
322 .with_component(assets::components::BufferTicks {
323 current_ticks: 3,
324 ticks_to_wait: 10,
325 input: WireValue::Number(2.5),
326 output: WireValue::Bool(true),
327 ..Default::default()
328 }),
329 );
330
331 // 5: Blend gate — Expr_MathBlend's InputA/InputB are each a WireValue
332 // (here an f64 and an i64, showing the value is polymorphic per-port);
333 // Blend itself is a plain f64.
334 world.bricks.push(
335 Brick {
336 position: (80, 0, 1).into(),
337 color: (0, 128, 0).into(),
338 asset: assets::components::LogicGate::Blend.brick(),
339 ..Default::default()
340 }
341 .with_component(
342 assets::components::LogicGate::Blend.component_with_overrides(HashMap::from([
343 (
344 "InputA".into(),
345 Box::new(WireValue::Number(10.0)) as Box<dyn AsBrdbValue>,
346 ),
347 ("InputB".into(), Box::new(WireValue::Int(7))),
348 ("Blend".into(), Box::new(0.75f64)),
349 ])),
350 ),
351 );
352
353 world
354}
355
356fn entities_world() -> World {
357 // Mirror of examples/write_entity.rs's floating sub-grid — the first
358 // grid added after the always-present main grid 1 gets persistent index
359 // 2, so it lands at Grids/2 — plus a couple of main-grid (Grids/1)
360 // bricks. Both grids are single-chunk; note the *outer* Grids/1 vs
361 // Grids/2 folder order comes from a HashMap<usize, UnsavedGrid> in the
362 // writer and is NOT guaranteed stable run-to-run (see stability notes).
363 //
364 // register_all_components() is required here (matching
365 // test_write_entity_save, NOT the plain write_entity.rs example): without
366 // it, Entity_DynamicBrickGrid's class name is never registered and the
367 // write fails with `UnknownType("Entity_DynamicBrickGrid")` — confirmed
368 // by running the crate's own unmodified write_entity example.
369 let mut world = World::new();
370 world.register_all_components();
371 world.meta.bundle.description = "Entity fixture".to_string();
372
373 world.bricks.push(Brick {
374 position: (0, 0, 6).into(),
375 color: (200, 50, 50).into(),
376 ..Default::default()
377 });
378 world.bricks.push(Brick {
379 position: (20, 0, 6).into(),
380 color: (50, 200, 50).into(),
381 ..Default::default()
382 });
383
384 world.add_brick_grid(
385 Entity {
386 frozen: true,
387 location: (0.0, 0.0, 40.0).into(),
388 ..Default::default()
389 },
390 [Brick {
391 position: (0, 0, 3).into(),
392 color: (0, 255, 0).into(),
393 ..Default::default()
394 }],
395 );
396
397 world
398}
399
400fn spawner_world() -> World {
401 // Prefab-embedding fixture: an outer prefab whose spawner gate references
402 // an inner single-brick prefab embedded at Prefabs/Uploads/<BLAKE3>.brz.
403 // Single grid, single chunk, one insertion-ordered prefab map entry —
404 // fully deterministic. The inner archive is written raw (no zstd) so the
405 // embedded bytes are cross-language reproducible.
406 let mut inner = World::new();
407 inner.meta.bundle.description = "Inner prefab".to_string();
408 inner.bricks.push(Brick {
409 position: (0, 0, 6).into(),
410 color: (255, 0, 0).into(),
411 ..Default::default()
412 });
413 inner.make_prefab();
414 let inner_bytes = {
415 let pending = inner.to_unsaved().unwrap().to_pending().unwrap();
416 let mut buf = Vec::new();
417 pending
418 .to_brz_data(None)
419 .unwrap()
420 .write(&mut buf, None)
421 .unwrap();
422 buf
423 };
424
425 let mut world = World::new();
426 world.register_all_components();
427 world.meta.bundle.description = "Spawner fixture".to_string();
428 let prefab_path = world.add_prefab(inner_bytes);
429 world.bricks.push(
430 Brick {
431 asset: brdb::BrickType::str("B_1x1_Gate_Exec_PrefabSpawner"),
432 position: (0, 0, 1).into(),
433 ..Default::default()
434 }
435 .with_component(
436 assets::LiteralComponent::new("BrickComponentType_Exec_PrefabSpawner")
437 .with_data([("Prefab", Box::new(prefab_path) as Box<dyn AsBrdbValue>)]),
438 ),
439 );
440 world.make_prefab();
441 world
442}
443
444/// `features_world` written in the Latest format.
445fn features_latest_world() -> World {
446 let mut world = features_world();
447 world.schema_format = BrdbSchemaFormat::Latest;
448 world
449}
450
451/// `entities_world` with the current build's schemas in the Latest format.
452fn entities_latest_world() -> World {
453 let mut world = World::new();
454 world.schema_format = BrdbSchemaFormat::Latest;
455 world.register_all_components();
456 world.meta.bundle.description = "Entity fixture".to_string();
457 world.bricks.push(Brick {
458 position: (0, 0, 6).into(),
459 color: (200, 50, 50).into(),
460 ..Default::default()
461 });
462 world.add_brick_grid(
463 Entity {
464 frozen: true,
465 location: (0.0, 0.0, 40.0).into(),
466 ..Default::default()
467 },
468 [Brick {
469 position: (0, 0, 3).into(),
470 color: (0, 255, 0).into(),
471 ..Default::default()
472 }],
473 );
474 world
475}
476
477/// Current-build components in the Latest format: WireValue fields, dynamic
478/// arrays/maps (typed and empty) and a dynamic enum, plus a wire.
479fn components_latest_world() -> World {
480 let mut world = World::new();
481 world.meta.bundle.description = "Latest component fixture".to_string();
482 let gate = |x: i32, asset: &'static str| Brick {
483 position: (x, 0, 1).into(),
484 asset: BrickType::str(asset),
485 ..Default::default()
486 };
487
488 // 1: Blend gate with WireValue inputs (f64 and i64 alternatives).
489 let (blend, blend_id) = gate(0, "B_1x1_Gate_Expr_MathBlend")
490 .with_component(
491 assets::components::LogicGate::Blend.component_with_overrides(HashMap::from([
492 (
493 "InputA".into(),
494 Box::new(WireValue::Number(1.5)) as Box<dyn AsBrdbValue>,
495 ),
496 ("InputB".into(), Box::new(WireValue::Int(-3))),
497 ("Blend".into(), Box::new(0.25f64)),
498 ])),
499 )
500 .with_id_split();
501 // 2: Buffer with Str / Bool alternatives.
502 let buffer =
503 gate(20, "B_1x1_Gate_BufferTicks").with_component(assets::components::BufferTicks {
504 ticks_to_wait: 4,
505 input: WireValue::Str("hello".into()),
506 output: WireValue::Bool(true),
507 buffered: WireValue::Vector {
508 x: 1.0,
509 y: -2.0,
510 z: 0.5,
511 },
512 ..Default::default()
513 });
514 // 3: Array variable holding i64 elements.
515 let array = gate(40, "B_1x1_Gate_Variable_Array").with_component(
516 assets::LiteralComponent::new("BrickComponentType_ArrayVar").with_data([(
517 "Value",
518 Box::new(BrdbDynamicArray::new(
519 "i64",
520 vec![BrdbValue::I64(7), BrdbValue::I64(300)],
521 )) as Box<dyn AsBrdbValue>,
522 )]),
523 );
524 // 4: Map variable from Str keys to f64 values.
525 let map = gate(60, "B_1x1_Gate_Variable_Map").with_component(
526 assets::LiteralComponent::new("BrickComponentType_MapVar").with_data([(
527 "Value",
528 Box::new(BrdbDynamicMap::new(
529 "Str",
530 "f64",
531 [(BrdbValue::String("k".into()), BrdbValue::F64(2.5))]
532 .into_iter()
533 .collect(),
534 )) as Box<dyn AsBrdbValue>,
535 )]),
536 );
537 // 5: Unset array variable: the default empty container.
538 let empty_array = gate(80, "B_1x1_Gate_Variable_Array")
539 .with_component(assets::LiteralComponent::new("BrickComponentType_ArrayVar"));
540 // 6: Integer to enum: a dynamic enum field.
541 let to_enum = gate(100, "B_1x1_Gate_Expr_IntegerToEnum").with_component(
542 assets::LiteralComponent::new("BrickComponentType_Expr_IntegerToEnum").with_data([
543 ("Input", Box::new(2i64) as Box<dyn AsBrdbValue>),
544 (
545 "TargetType",
546 Box::new(BrdbDynamicEnum::new("EBrickAxis", 0)),
547 ),
548 ]),
549 );
550 // 7: A rerouter fed by the blend gate.
551 let (reroute, reroute_id) = Brick {
552 position: (120, 0, 1).into(),
553 asset: assets::bricks::B_REROUTE,
554 ..Default::default()
555 }
556 .with_component(assets::components::Rerouter)
557 .with_id_split();
558
559 world.add_bricks([blend, buffer, array, map, empty_array, to_enum, reroute]);
560 world.add_wire_connection(
561 assets::components::LogicGate::Blend.output_of(blend_id),
562 assets::components::Rerouter::input_of(reroute_id),
563 );
564 // Embed only the structs these components use (what brs-js writes).
565 world.register_used_components();
566 world
567}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}pub fn add_entity(&mut self, entity: Entity)
Sourcepub fn add_brick_grid(
&mut self,
entity: Entity,
bricks: impl IntoIterator<Item = Brick>,
)
pub fn add_brick_grid( &mut self, entity: Entity, bricks: impl IntoIterator<Item = Brick>, )
Examples found in repository?
5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 let path = PathBuf::from("./example_entity.brdb");
7
8 // Ensures the memory db can be created without errors
9 let db = Brdb::new(&path)?.into_reader();
10 let mut world = World::new();
11 world.meta.bundle.description = "Example World".to_string();
12 world.add_brick_grid(
13 Entity {
14 frozen: true,
15 location: (0.0, 0.0, 40.0).into(),
16 ..Default::default()
17 },
18 [Brick {
19 position: (0, 0, 3).into(),
20 color: (0, 255, 0).into(),
21 ..Default::default()
22 }],
23 );
24
25 db.save("example world", &world)?;
26
27 println!("{}", db.get_fs()?.render());
28
29 Ok(())
30}More examples
356fn entities_world() -> World {
357 // Mirror of examples/write_entity.rs's floating sub-grid — the first
358 // grid added after the always-present main grid 1 gets persistent index
359 // 2, so it lands at Grids/2 — plus a couple of main-grid (Grids/1)
360 // bricks. Both grids are single-chunk; note the *outer* Grids/1 vs
361 // Grids/2 folder order comes from a HashMap<usize, UnsavedGrid> in the
362 // writer and is NOT guaranteed stable run-to-run (see stability notes).
363 //
364 // register_all_components() is required here (matching
365 // test_write_entity_save, NOT the plain write_entity.rs example): without
366 // it, Entity_DynamicBrickGrid's class name is never registered and the
367 // write fails with `UnknownType("Entity_DynamicBrickGrid")` — confirmed
368 // by running the crate's own unmodified write_entity example.
369 let mut world = World::new();
370 world.register_all_components();
371 world.meta.bundle.description = "Entity fixture".to_string();
372
373 world.bricks.push(Brick {
374 position: (0, 0, 6).into(),
375 color: (200, 50, 50).into(),
376 ..Default::default()
377 });
378 world.bricks.push(Brick {
379 position: (20, 0, 6).into(),
380 color: (50, 200, 50).into(),
381 ..Default::default()
382 });
383
384 world.add_brick_grid(
385 Entity {
386 frozen: true,
387 location: (0.0, 0.0, 40.0).into(),
388 ..Default::default()
389 },
390 [Brick {
391 position: (0, 0, 3).into(),
392 color: (0, 255, 0).into(),
393 ..Default::default()
394 }],
395 );
396
397 world
398}
399
400fn spawner_world() -> World {
401 // Prefab-embedding fixture: an outer prefab whose spawner gate references
402 // an inner single-brick prefab embedded at Prefabs/Uploads/<BLAKE3>.brz.
403 // Single grid, single chunk, one insertion-ordered prefab map entry —
404 // fully deterministic. The inner archive is written raw (no zstd) so the
405 // embedded bytes are cross-language reproducible.
406 let mut inner = World::new();
407 inner.meta.bundle.description = "Inner prefab".to_string();
408 inner.bricks.push(Brick {
409 position: (0, 0, 6).into(),
410 color: (255, 0, 0).into(),
411 ..Default::default()
412 });
413 inner.make_prefab();
414 let inner_bytes = {
415 let pending = inner.to_unsaved().unwrap().to_pending().unwrap();
416 let mut buf = Vec::new();
417 pending
418 .to_brz_data(None)
419 .unwrap()
420 .write(&mut buf, None)
421 .unwrap();
422 buf
423 };
424
425 let mut world = World::new();
426 world.register_all_components();
427 world.meta.bundle.description = "Spawner fixture".to_string();
428 let prefab_path = world.add_prefab(inner_bytes);
429 world.bricks.push(
430 Brick {
431 asset: brdb::BrickType::str("B_1x1_Gate_Exec_PrefabSpawner"),
432 position: (0, 0, 1).into(),
433 ..Default::default()
434 }
435 .with_component(
436 assets::LiteralComponent::new("BrickComponentType_Exec_PrefabSpawner")
437 .with_data([("Prefab", Box::new(prefab_path) as Box<dyn AsBrdbValue>)]),
438 ),
439 );
440 world.make_prefab();
441 world
442}
443
444/// `features_world` written in the Latest format.
445fn features_latest_world() -> World {
446 let mut world = features_world();
447 world.schema_format = BrdbSchemaFormat::Latest;
448 world
449}
450
451/// `entities_world` with the current build's schemas in the Latest format.
452fn entities_latest_world() -> World {
453 let mut world = World::new();
454 world.schema_format = BrdbSchemaFormat::Latest;
455 world.register_all_components();
456 world.meta.bundle.description = "Entity fixture".to_string();
457 world.bricks.push(Brick {
458 position: (0, 0, 6).into(),
459 color: (200, 50, 50).into(),
460 ..Default::default()
461 });
462 world.add_brick_grid(
463 Entity {
464 frozen: true,
465 location: (0.0, 0.0, 40.0).into(),
466 ..Default::default()
467 },
468 [Brick {
469 position: (0, 0, 3).into(),
470 color: (0, 255, 0).into(),
471 ..Default::default()
472 }],
473 );
474 world
475}Sourcepub fn add_wire(&mut self, conn: WireConnection)
pub fn add_wire(&mut self, conn: WireConnection)
Add a single wire connection to the world
Sourcepub fn add_wires(&mut self, wires: impl IntoIterator<Item = WireConnection>)
pub fn add_wires(&mut self, wires: impl IntoIterator<Item = WireConnection>)
Add multiple wire connections to the world
Sourcepub fn add_wire_connection(&mut self, source: WirePort, target: WirePort)
pub fn add_wire_connection(&mut self, source: WirePort, target: WirePort)
Add a wire connection from one port to another
Examples found in repository?
5fn main() -> Result<(), Box<dyn std::error::Error>> {
6 let path = PathBuf::from("./example_wire.brdb");
7
8 // Ensures the memory db can be created without errors
9 let db = Brdb::new(&path)?.into_reader();
10 let mut world = World::new();
11 // Register the built-in component type/struct mappings so the gate and
12 // rerouter component types resolve when writing.
13 world.register_all_components();
14 world.meta.bundle.description = "Example World".to_string();
15
16 let (a, a_id) = Brick {
17 position: (0, 0, 1).into(),
18 color: (255, 0, 0).into(),
19 asset: assets::bricks::B_REROUTE,
20 ..Default::default()
21 }
22 .with_component(assets::components::Rerouter)
23 .with_id_split();
24 let (b, b_id) = Brick {
25 position: (15, 0, 1).into(),
26 color: (255, 0, 0).into(),
27 asset: assets::components::LogicGate::BoolNot.brick(),
28 ..Default::default()
29 }
30 .with_component(assets::components::LogicGate::BoolNot.component())
31 .with_id_split();
32
33 world.add_bricks([a, b]);
34 world.add_wire_connection(
35 assets::components::LogicGate::BoolNot.output_of(b_id),
36 assets::components::Rerouter::input_of(a_id),
37 );
38
39 db.save("example world", &world)?;
40
41 println!("{}", db.get_fs()?.render());
42
43 Ok(())
44}More examples
175fn wires_world() -> World {
176 // Mirror of examples/write_wire.rs (single grid, single chunk — fully
177 // deterministic), extended to 3 bricks/2 wires: a boolean NOT gate feeds
178 // one input of an AND gate, whose output feeds a rerouter.
179 let mut world = World::new();
180 world.register_all_components();
181 world.meta.bundle.description = "Wire fixture".to_string();
182
183 let (a, a_id) = Brick {
184 position: (30, 0, 1).into(),
185 color: (255, 0, 0).into(),
186 asset: assets::bricks::B_REROUTE,
187 ..Default::default()
188 }
189 .with_component(assets::components::Rerouter)
190 .with_id_split();
191 let (b, b_id) = Brick {
192 position: (15, 0, 1).into(),
193 color: (0, 255, 0).into(),
194 asset: assets::components::LogicGate::BoolAnd.brick(),
195 ..Default::default()
196 }
197 .with_component(assets::components::LogicGate::BoolAnd.component())
198 .with_id_split();
199 let (c, c_id) = Brick {
200 position: (0, 0, 1).into(),
201 color: (0, 0, 255).into(),
202 asset: assets::components::LogicGate::BoolNot.brick(),
203 ..Default::default()
204 }
205 .with_component(assets::components::LogicGate::BoolNot.component())
206 .with_id_split();
207
208 world.add_bricks([a, b, c]);
209 // Wire 1: NOT.output -> AND.inputA
210 world.add_wire_connection(
211 assets::components::LogicGate::BoolNot.output_of(c_id),
212 assets::components::LogicGate::BoolAnd.input_a_of(b_id),
213 );
214 // Wire 2: AND.output -> Rerouter.input
215 world.add_wire_connection(
216 assets::components::LogicGate::BoolAnd.output_of(b_id),
217 assets::components::Rerouter::input_of(a_id),
218 );
219
220 world
221}
222
223fn components_world() -> World {
224 // Single grid, single chunk (all positions < 2048) — fully deterministic.
225 // register_all_components() embeds the full component catalog so every
226 // type below (light/interact/buffer/expr) resolves; each
227 // component below carries non-default property values, and #4/#5 exercise
228 // a WireValue-typed property (BufferTicks' Input/Output, and the Blend
229 // gate's inputs).
230 let mut world = World::new();
231 world.register_all_components();
232 world.meta.bundle.description = "Component fixture".to_string();
233
234 // 1: Point light — non-default brightness/radius/color, decoupled from
235 // brick color (bUseBrickColor: false).
236 world.bricks.push(
237 Brick {
238 position: (0, 0, 6).into(),
239 color: (255, 255, 255).into(),
240 ..Default::default()
241 }
242 .with_component(
243 assets::LiteralComponent::new("Component_PointLight").with_data([
244 ("bMatchBrickShape", Box::new(false) as Box<dyn AsBrdbValue>),
245 ("bEnabled", Box::new(true)),
246 ("Brightness", Box::new(500.0f32)),
247 ("Radius", Box::new(800.0f32)),
248 (
249 "Color",
250 Box::new(SavedBrickColor {
251 r: 10,
252 g: 20,
253 b: 30,
254 a: 255,
255 }),
256 ),
257 ("bUseBrickColor", Box::new(false)),
258 ("bCastShadows", Box::new(true)),
259 ]),
260 ),
261 );
262
263 // 2: Spot light — narrow cone, non-default brightness/color.
264 world.bricks.push(
265 Brick {
266 position: (20, 0, 6).into(),
267 color: (255, 255, 0).into(),
268 ..Default::default()
269 }
270 .with_component(
271 assets::LiteralComponent::new("Component_SpotLight").with_data([
272 ("InnerConeAngle", Box::new(15.0f32) as Box<dyn AsBrdbValue>),
273 ("OuterConeAngle", Box::new(45.0f32)),
274 ("bEnabled", Box::new(true)),
275 ("Brightness", Box::new(300.0f32)),
276 ("Radius", Box::new(600.0f32)),
277 (
278 "Color",
279 Box::new(SavedBrickColor {
280 r: 255,
281 g: 0,
282 b: 0,
283 a: 255,
284 }),
285 ),
286 ("bUseBrickColor", Box::new(false)),
287 ("bCastShadows", Box::new(true)),
288 ]),
289 ),
290 );
291
292 // 3: Interact — custom prompt text, hidden interaction.
293 world.bricks.push(
294 Brick {
295 position: (40, 0, 6).into(),
296 color: (0, 255, 255).into(),
297 ..Default::default()
298 }
299 .with_component(
300 assets::LiteralComponent::new("Component_Interact").with_data([
301 (
302 "Message",
303 Box::new("You interacted!".to_string()) as Box<dyn AsBrdbValue>,
304 ),
305 ("ConsoleTag", Box::new("fixture_interact".to_string())),
306 ("bAllowNearbyInteraction", Box::new(false)),
307 ("bHiddenInteraction", Box::new(true)),
308 ("PromptCustomLabel", Box::new("Open Door".to_string())),
309 ]),
310 ),
311 );
312
313 // 4: Buffer (ticks) — Input/Output are WireValue; non-default counters
314 // plus a Number/Bool value pair.
315 world.bricks.push(
316 Brick {
317 position: (60, 0, 1).into(),
318 color: (128, 0, 128).into(),
319 asset: assets::components::BufferTicks::default().brick(),
320 ..Default::default()
321 }
322 .with_component(assets::components::BufferTicks {
323 current_ticks: 3,
324 ticks_to_wait: 10,
325 input: WireValue::Number(2.5),
326 output: WireValue::Bool(true),
327 ..Default::default()
328 }),
329 );
330
331 // 5: Blend gate — Expr_MathBlend's InputA/InputB are each a WireValue
332 // (here an f64 and an i64, showing the value is polymorphic per-port);
333 // Blend itself is a plain f64.
334 world.bricks.push(
335 Brick {
336 position: (80, 0, 1).into(),
337 color: (0, 128, 0).into(),
338 asset: assets::components::LogicGate::Blend.brick(),
339 ..Default::default()
340 }
341 .with_component(
342 assets::components::LogicGate::Blend.component_with_overrides(HashMap::from([
343 (
344 "InputA".into(),
345 Box::new(WireValue::Number(10.0)) as Box<dyn AsBrdbValue>,
346 ),
347 ("InputB".into(), Box::new(WireValue::Int(7))),
348 ("Blend".into(), Box::new(0.75f64)),
349 ])),
350 ),
351 );
352
353 world
354}
355
356fn entities_world() -> World {
357 // Mirror of examples/write_entity.rs's floating sub-grid — the first
358 // grid added after the always-present main grid 1 gets persistent index
359 // 2, so it lands at Grids/2 — plus a couple of main-grid (Grids/1)
360 // bricks. Both grids are single-chunk; note the *outer* Grids/1 vs
361 // Grids/2 folder order comes from a HashMap<usize, UnsavedGrid> in the
362 // writer and is NOT guaranteed stable run-to-run (see stability notes).
363 //
364 // register_all_components() is required here (matching
365 // test_write_entity_save, NOT the plain write_entity.rs example): without
366 // it, Entity_DynamicBrickGrid's class name is never registered and the
367 // write fails with `UnknownType("Entity_DynamicBrickGrid")` — confirmed
368 // by running the crate's own unmodified write_entity example.
369 let mut world = World::new();
370 world.register_all_components();
371 world.meta.bundle.description = "Entity fixture".to_string();
372
373 world.bricks.push(Brick {
374 position: (0, 0, 6).into(),
375 color: (200, 50, 50).into(),
376 ..Default::default()
377 });
378 world.bricks.push(Brick {
379 position: (20, 0, 6).into(),
380 color: (50, 200, 50).into(),
381 ..Default::default()
382 });
383
384 world.add_brick_grid(
385 Entity {
386 frozen: true,
387 location: (0.0, 0.0, 40.0).into(),
388 ..Default::default()
389 },
390 [Brick {
391 position: (0, 0, 3).into(),
392 color: (0, 255, 0).into(),
393 ..Default::default()
394 }],
395 );
396
397 world
398}
399
400fn spawner_world() -> World {
401 // Prefab-embedding fixture: an outer prefab whose spawner gate references
402 // an inner single-brick prefab embedded at Prefabs/Uploads/<BLAKE3>.brz.
403 // Single grid, single chunk, one insertion-ordered prefab map entry —
404 // fully deterministic. The inner archive is written raw (no zstd) so the
405 // embedded bytes are cross-language reproducible.
406 let mut inner = World::new();
407 inner.meta.bundle.description = "Inner prefab".to_string();
408 inner.bricks.push(Brick {
409 position: (0, 0, 6).into(),
410 color: (255, 0, 0).into(),
411 ..Default::default()
412 });
413 inner.make_prefab();
414 let inner_bytes = {
415 let pending = inner.to_unsaved().unwrap().to_pending().unwrap();
416 let mut buf = Vec::new();
417 pending
418 .to_brz_data(None)
419 .unwrap()
420 .write(&mut buf, None)
421 .unwrap();
422 buf
423 };
424
425 let mut world = World::new();
426 world.register_all_components();
427 world.meta.bundle.description = "Spawner fixture".to_string();
428 let prefab_path = world.add_prefab(inner_bytes);
429 world.bricks.push(
430 Brick {
431 asset: brdb::BrickType::str("B_1x1_Gate_Exec_PrefabSpawner"),
432 position: (0, 0, 1).into(),
433 ..Default::default()
434 }
435 .with_component(
436 assets::LiteralComponent::new("BrickComponentType_Exec_PrefabSpawner")
437 .with_data([("Prefab", Box::new(prefab_path) as Box<dyn AsBrdbValue>)]),
438 ),
439 );
440 world.make_prefab();
441 world
442}
443
444/// `features_world` written in the Latest format.
445fn features_latest_world() -> World {
446 let mut world = features_world();
447 world.schema_format = BrdbSchemaFormat::Latest;
448 world
449}
450
451/// `entities_world` with the current build's schemas in the Latest format.
452fn entities_latest_world() -> World {
453 let mut world = World::new();
454 world.schema_format = BrdbSchemaFormat::Latest;
455 world.register_all_components();
456 world.meta.bundle.description = "Entity fixture".to_string();
457 world.bricks.push(Brick {
458 position: (0, 0, 6).into(),
459 color: (200, 50, 50).into(),
460 ..Default::default()
461 });
462 world.add_brick_grid(
463 Entity {
464 frozen: true,
465 location: (0.0, 0.0, 40.0).into(),
466 ..Default::default()
467 },
468 [Brick {
469 position: (0, 0, 3).into(),
470 color: (0, 255, 0).into(),
471 ..Default::default()
472 }],
473 );
474 world
475}
476
477/// Current-build components in the Latest format: WireValue fields, dynamic
478/// arrays/maps (typed and empty) and a dynamic enum, plus a wire.
479fn components_latest_world() -> World {
480 let mut world = World::new();
481 world.meta.bundle.description = "Latest component fixture".to_string();
482 let gate = |x: i32, asset: &'static str| Brick {
483 position: (x, 0, 1).into(),
484 asset: BrickType::str(asset),
485 ..Default::default()
486 };
487
488 // 1: Blend gate with WireValue inputs (f64 and i64 alternatives).
489 let (blend, blend_id) = gate(0, "B_1x1_Gate_Expr_MathBlend")
490 .with_component(
491 assets::components::LogicGate::Blend.component_with_overrides(HashMap::from([
492 (
493 "InputA".into(),
494 Box::new(WireValue::Number(1.5)) as Box<dyn AsBrdbValue>,
495 ),
496 ("InputB".into(), Box::new(WireValue::Int(-3))),
497 ("Blend".into(), Box::new(0.25f64)),
498 ])),
499 )
500 .with_id_split();
501 // 2: Buffer with Str / Bool alternatives.
502 let buffer =
503 gate(20, "B_1x1_Gate_BufferTicks").with_component(assets::components::BufferTicks {
504 ticks_to_wait: 4,
505 input: WireValue::Str("hello".into()),
506 output: WireValue::Bool(true),
507 buffered: WireValue::Vector {
508 x: 1.0,
509 y: -2.0,
510 z: 0.5,
511 },
512 ..Default::default()
513 });
514 // 3: Array variable holding i64 elements.
515 let array = gate(40, "B_1x1_Gate_Variable_Array").with_component(
516 assets::LiteralComponent::new("BrickComponentType_ArrayVar").with_data([(
517 "Value",
518 Box::new(BrdbDynamicArray::new(
519 "i64",
520 vec![BrdbValue::I64(7), BrdbValue::I64(300)],
521 )) as Box<dyn AsBrdbValue>,
522 )]),
523 );
524 // 4: Map variable from Str keys to f64 values.
525 let map = gate(60, "B_1x1_Gate_Variable_Map").with_component(
526 assets::LiteralComponent::new("BrickComponentType_MapVar").with_data([(
527 "Value",
528 Box::new(BrdbDynamicMap::new(
529 "Str",
530 "f64",
531 [(BrdbValue::String("k".into()), BrdbValue::F64(2.5))]
532 .into_iter()
533 .collect(),
534 )) as Box<dyn AsBrdbValue>,
535 )]),
536 );
537 // 5: Unset array variable: the default empty container.
538 let empty_array = gate(80, "B_1x1_Gate_Variable_Array")
539 .with_component(assets::LiteralComponent::new("BrickComponentType_ArrayVar"));
540 // 6: Integer to enum: a dynamic enum field.
541 let to_enum = gate(100, "B_1x1_Gate_Expr_IntegerToEnum").with_component(
542 assets::LiteralComponent::new("BrickComponentType_Expr_IntegerToEnum").with_data([
543 ("Input", Box::new(2i64) as Box<dyn AsBrdbValue>),
544 (
545 "TargetType",
546 Box::new(BrdbDynamicEnum::new("EBrickAxis", 0)),
547 ),
548 ]),
549 );
550 // 7: A rerouter fed by the blend gate.
551 let (reroute, reroute_id) = Brick {
552 position: (120, 0, 1).into(),
553 asset: assets::bricks::B_REROUTE,
554 ..Default::default()
555 }
556 .with_component(assets::components::Rerouter)
557 .with_id_split();
558
559 world.add_bricks([blend, buffer, array, map, empty_array, to_enum, reroute]);
560 world.add_wire_connection(
561 assets::components::LogicGate::Blend.output_of(blend_id),
562 assets::components::Rerouter::input_of(reroute_id),
563 );
564 // Embed only the structs these components use (what brs-js writes).
565 world.register_used_components();
566 world
567}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 register_microchip_link(&mut self, brick_id: usize, entity_id: usize)
pub fn register_microchip_link(&mut self, brick_id: usize, entity_id: usize)
Register an outer-microchip-brick ↔ inner-grid-entity pairing.
The write path consumes this into
ComponentChunkSoA.microchip_brick_indices /
microchip_brick_grid_references. Most callers get this for free by
going through add_microchip; use this directly only when
constructing the pair manually.
Sourcepub fn add_microchip(
&mut self,
position: Position,
entity_location: Vector3f,
plane_extent: IntVector,
collapsed: bool,
) -> (usize, usize, (Entity, Vec<Brick>))
pub fn add_microchip( &mut self, position: Position, entity_location: Vector3f, plane_extent: IntVector, collapsed: bool, ) -> (usize, usize, (Entity, Vec<Brick>))
Build a microchip: spawns the outer microchip brick on the main grid,
calls register_microchip_link with the pairing, and returns the
outer brick’s id, the grid entity’s id, and the
(Entity, Vec<Brick>) pair the caller populates before pushing to
world.grids.
Typical usage:
let (chip_brick_id, chip_entity_id, mut inner) = world.add_microchip(
Position { x: 0, y: 0, z: 6 },
Vector3f { x: 0.0, y: 0.0, z: 40.0 },
IntVector { x: 14, y: 14, z: 2 },
true, // collapsed
);
inner.1.push(some_gate_brick);
world.grids.push(inner);