1use std::collections::HashMap;
2
3use itertools::Itertools;
4
5use crate::{
6 errors::{BrdbError, BrdbWorldError},
7 pending::BrdbPendingFs,
8 schema::{BrdbSchema, BrdbSchemaGlobalData},
9 wrapper::{
10 Brick, BrickChunkIndexSoA, BrickChunkSoA, ChunkIndex, ComponentChunkSoA, Entity,
11 EntityChunkIndexSoA, EntityChunkSoA, LocalWirePortSource, OwnerTableSoA,
12 RemoteWirePortSource, WireChunkSoA, WireConnection, WirePortTarget, WorldMeta, schemas,
13 },
14};
15
16pub struct UnsavedFs {
18 pub meta: WorldMeta,
20 pub worlds: HashMap<usize, UnsavedWorld>,
22}
23
24impl UnsavedFs {
25 pub fn to_pending(self) -> Result<BrdbPendingFs, BrdbError> {
26 BrdbPendingFs::from_unsaved(self)
27 }
28}
29
30pub struct UnsavedWorld {
31 pub global_data: BrdbSchemaGlobalData,
33 pub owners: OwnerTableSoA,
35 pub component_schema: BrdbSchema,
37 pub grids: HashMap<usize, UnsavedGrid>,
39 pub entity_chunks: HashMap<ChunkIndex, EntityChunkSoA>,
41 pub entity_schema: BrdbSchema,
43 pub entity_chunk_index: EntityChunkIndexSoA,
45
46 pub minigame: Option<()>, pub environment: Option<()>, brick_id_map: HashMap<usize, (usize, ChunkIndex, usize)>,
55 entity_index_map: HashMap<usize, u32>,
57}
58
59impl Default for UnsavedWorld {
60 fn default() -> Self {
61 Self {
62 global_data: Default::default(),
63 owners: Default::default(),
64 component_schema: schemas::bricks_components_schema_min(),
65 grids: Default::default(),
66 entity_chunks: Default::default(),
67 entity_schema: schemas::entities_chunks_schema(),
68 entity_chunk_index: Default::default(),
69 minigame: Default::default(),
70 environment: Default::default(),
71 brick_id_map: Default::default(),
72 entity_index_map: Default::default(),
73 }
74 }
75}
76
77impl UnsavedWorld {
78 fn add_brick_meta(&mut self, brick: &Brick) {
79 self.global_data.add_brick_meta(brick);
81
82 for component in &brick.components {
85 let Some((ty_name, _)) = component.get_schema_struct() else {
87 continue;
88 };
89
90 if self.global_data.has_component_type(ty_name.as_ref()) {
92 continue;
93 }
94 self.global_data.add_component_meta(component.as_ref());
95
96 let Some((enums, structs)) = component.get_schema() else {
97 continue;
98 };
99 self.component_schema.add_meta(enums, structs);
100 }
101 }
102
103 fn add_entity_meta(&mut self, entity: &Entity) {
104 let Some((ty_name, _)) = entity.data.get_schema_struct() else {
105 return;
106 };
107 self.global_data.add_entity_type(&ty_name);
108 let Some((enums, structs)) = entity.data.get_schema() else {
109 return;
110 };
111 self.entity_schema.add_meta(enums, structs);
112 }
113
114 pub(super) fn add_bricks_to_grid(&mut self, grid_id: usize, bricks: &[Brick]) {
115 let mut grid = UnsavedGrid::default();
116
117 for b in bricks.iter().sorted_by(|a, b| a.cmp(b)) {
119 self.add_brick_meta(b);
120
121 let owner_id = b.owner_index.unwrap_or(0);
123 self.owners.inc_bricks(owner_id);
124 self.owners
125 .inc_components(owner_id, b.components.len() as u32);
126
127 let (chunk_index, brick_index) = grid.add_brick(&self.global_data, b);
129 if let Some(id) = b.id {
131 self.brick_id_map
132 .insert(id, (grid_id, chunk_index, brick_index));
133 }
134 }
135
136 self.grids.insert(grid_id, grid);
138 }
139
140 pub(super) fn add_entity(&mut self, entity: &Entity) -> usize {
141 self.add_entity_meta(entity);
143
144 let owner_id = entity.owner_index.unwrap_or(0);
146 self.owners.inc_entities(owner_id as usize);
147
148 let entity_index = self.entity_chunk_index.next_persistent_index;
150 self.entity_chunk_index.next_persistent_index += 1;
151
152 let chunk_index = ChunkIndex::ZERO;
154 if self.entity_chunk_index.chunk_3d_indices.is_empty() {
156 self.entity_chunk_index.chunk_3d_indices.push(chunk_index);
157 }
158 if self.entity_chunk_index.num_entities.is_empty() {
159 self.entity_chunk_index.num_entities.push(0);
160 }
161 self.entity_chunk_index.num_entities[0] += 1;
162
163 self.entity_chunks
164 .entry(chunk_index)
165 .or_insert_with(EntityChunkSoA::default)
166 .add_entity(&self.global_data, entity, entity_index);
167
168 if let Some(id) = entity.id {
170 self.entity_index_map.insert(id, entity_index);
171 }
172
173 entity_index as usize
174 }
175
176 pub(super) fn add_wire(&mut self, wire: &WireConnection) -> Result<(), BrdbError> {
177 let (s_grid, s_chunk, s_brick) = self
179 .brick_id_map
180 .get(&wire.source.brick_id)
181 .ok_or_else(|| BrdbWorldError::UnknownBrickId(wire.source.brick_id))?;
182 let s_comp_ty = self
183 .global_data
184 .get_component_type_index(&wire.source.component_type)
185 .ok_or_else(|| {
186 BrdbWorldError::UnknownComponent(wire.source.component_type.to_string())
187 })?;
188 let s_port_index = self
189 .global_data
190 .get_port_index(&wire.source.port_name)
191 .ok_or_else(|| BrdbWorldError::UnknownPort(wire.source.port_name.to_string()))?;
192
193 let (t_grid, t_chunk, t_brick) = self
195 .brick_id_map
196 .get(&wire.target.brick_id)
197 .ok_or_else(|| BrdbWorldError::UnknownBrickId(wire.target.brick_id))?;
198 let t_comp_ty = self
199 .global_data
200 .get_component_type_index(&wire.target.component_type)
201 .ok_or_else(|| {
202 BrdbWorldError::UnknownComponent(wire.target.component_type.to_string())
203 })?;
204 let t_port_index = self
205 .global_data
206 .get_port_index(&wire.target.port_name)
207 .ok_or_else(|| BrdbWorldError::UnknownPort(wire.target.port_name.to_string()))?;
208
209 let target = WirePortTarget {
211 brick_index_in_chunk: *t_brick as u32,
212 component_type_index: t_comp_ty,
213 port_index: t_port_index,
214 };
215
216 let grid = self
218 .grids
219 .get_mut(t_grid)
220 .ok_or_else(|| BrdbWorldError::UnknownGridId(*t_grid))?;
221
222 let chunk_id = grid.get_chunk_index(*t_chunk);
224 grid.chunk_index.num_wires[chunk_id] += 1;
225
226 if t_grid == s_grid && t_chunk == s_chunk {
229 let source = LocalWirePortSource {
230 brick_index_in_chunk: *s_brick as u32,
231 component_type_index: s_comp_ty,
232 port_index: s_port_index,
233 };
234 grid.add_local_wire(*t_chunk, source, target);
235 } else {
236 let source = RemoteWirePortSource {
238 grid_persistent_index: *s_grid as u32,
239 chunk_index: *s_chunk,
240 brick_index_in_chunk: *s_brick as u32,
241 component_type_index: s_comp_ty,
242 port_index: s_port_index,
243 };
244 grid.add_remote_wire(*t_chunk, source, target);
245 }
246
247 Ok(())
248 }
249}
250
251#[derive(Default)]
252pub struct UnsavedGrid {
253 pub chunk_index: BrickChunkIndexSoA,
255 pub bricks: HashMap<ChunkIndex, BrickChunkSoA>,
257 pub components: HashMap<ChunkIndex, ComponentChunkSoA>,
259 pub wires: HashMap<ChunkIndex, WireChunkSoA>,
261
262 chunk_index_map: HashMap<ChunkIndex, usize>,
265}
266
267impl UnsavedGrid {
268 fn get_chunk_index(&mut self, chunk_index: ChunkIndex) -> usize {
270 if let Some(index) = self.chunk_index_map.get(&chunk_index) {
272 *index
273 } else {
274 self.chunk_index.chunk_3d_indices.push(chunk_index);
275 self.chunk_index.num_bricks.push(0);
276 self.chunk_index.num_components.push(0);
277 self.chunk_index.num_wires.push(0);
278 let index = self.chunk_index_map.len();
279 self.chunk_index_map.insert(chunk_index, index);
280 index
281 }
282 }
283
284 fn add_brick(
286 &mut self,
287 global_data: &BrdbSchemaGlobalData,
288 brick: &Brick,
289 ) -> (ChunkIndex, usize) {
290 let chunk_index = brick.position.to_relative().0;
291 self.bricks
293 .entry(chunk_index)
294 .or_insert_with(BrickChunkSoA::default)
295 .add_brick(global_data, brick); let i = self.get_chunk_index(chunk_index);
298 let brick_index = self.chunk_index.num_bricks[i];
300 self.chunk_index.num_bricks[i] += 1;
302 self.chunk_index.num_components[i] += brick.components.len() as u32;
303
304 if !brick.components.is_empty() {
306 let chunk = self
307 .components
308 .entry(chunk_index)
309 .or_insert_with(ComponentChunkSoA::default);
310 for c in &brick.components {
311 chunk.add_component(global_data, brick_index, c.as_ref());
312 }
313 }
314
315 (chunk_index, brick_index as usize)
316 }
317
318 fn add_local_wire(
319 &mut self,
320 chunk: ChunkIndex,
321 source: LocalWirePortSource,
322 target: WirePortTarget,
323 ) {
324 self.wires
325 .entry(chunk)
326 .or_insert_with(WireChunkSoA::default)
327 .add_local_wire(source, target);
328 }
329
330 fn add_remote_wire(
331 &mut self,
332 chunk: ChunkIndex,
333 source: RemoteWirePortSource,
334 target: WirePortTarget,
335 ) {
336 self.wires
337 .entry(chunk)
338 .or_insert_with(WireChunkSoA::default)
339 .add_remote_wire(source, target);
340 }
341}