1use std::borrow::Cow;
4use std::collections::BTreeMap;
5use std::fmt::{self, Debug};
6use std::ops::Deref;
7use std::sync::Arc;
8
9use rayon::iter::{
10 IntoParallelRefIterator,
11 ParallelIterator,
12};
13
14use crate::{Archetype, ComponentTypeID};
15use crate::archetype::{ComponentSetExt, ComponentVecSet};
16use crate::chunk::{Chunk, ChunkAction, ChunkEdit, EntityEntry};
17use crate::chunk_set::ChunkSet;
18use crate::component_data::{ComponentDataVecWriter, ComponentValueRef};
19use crate::entity::EntityID;
20use crate::universe::Universe;
21
22#[derive(Clone, Debug)]
26pub enum EditAction<'a> {
27 SetComponent(ComponentValueRef<'a>),
28 RemoveComponent(ComponentTypeID),
29}
30
31#[derive(Clone, Debug)]
33pub struct Edit<'a>(pub EntityID, pub EditAction<'a>);
34
35#[derive(Clone)]
37pub struct Snapshot {
38 universe: Arc<Universe>,
39 chunk_sets: Vec<ChunkSet>,
40 entities: BTreeMap<EntityID, usize>,
41}
42
43impl Snapshot {
44 pub fn empty(universe: Arc<Universe>) -> Snapshot {
46 Snapshot {
47 universe,
48 chunk_sets: Vec::new(),
49 entities: BTreeMap::new(),
50 }
51 }
52
53 pub fn universe(&self) -> &Arc<Universe> {
55 &self.universe
56 }
57
58 pub fn chunk_sets(&self) -> &[ChunkSet] {
60 &self.chunk_sets
61 }
62
63 pub fn par_iter_chunk_sets(&self) -> impl ParallelIterator<Item=&ChunkSet> {
65 self.chunk_sets.par_iter()
66 }
67
68 pub fn iter_chunk_sets(&self) -> impl Iterator<Item=&ChunkSet> {
70 self.chunk_sets.iter()
71 }
72
73 pub fn par_iter_chunks(&self) -> impl ParallelIterator<Item=&Arc<Chunk>> {
75 self.chunk_sets.par_iter().flat_map(|chunks| chunks.par_iter())
76 }
77
78 pub fn iter_chunks(&self) -> impl Iterator<Item=&Arc<Chunk>> {
80 self.chunk_sets.iter().flat_map(|chunks| chunks.iter())
81 }
82
83 pub fn chunk_set(&self, a: &Arc<Archetype>) -> Option<&ChunkSet> {
85 self.chunk_sets.get(a.id())
86 }
87
88 pub fn entity(&self, id: EntityID) -> Option<EntityEntry> {
92 self.entities.get(&id)
93 .cloned()
94 .and_then(|arch_idx| self.chunk_sets.get(arch_idx))
95 .and_then(|chunks| chunks.chunk_for_entity(id))
96 .and_then(|chunk| {
97 let ids = chunk.components::<EntityID>().unwrap();
98 ids.binary_search(&id).ok()
99 .and_then(|idx| chunk.entity_by_index(idx))
100 })
101 }
102
103 pub fn modify<'a, E>(self: &mut Arc<Self>, edits: E)
108 where E: Iterator<Item=Edit<'a>>
109 {
110 let edit_list = SnapshotEditList::from_edits(self, edits);
111 if !edit_list.is_empty() {
112 let archetype_edits = edit_list.chunk_set_edits;
113
114 let universe = self.universe.clone();
115 let edit_snap = Arc::make_mut(self);
116 if edit_snap.chunk_sets.len() < archetype_edits.len() {
117 edit_snap.chunk_sets.resize(archetype_edits.len(), ChunkSet::new());
118 }
119
120 let chunk_sets = edit_snap.chunk_sets.iter_mut();
121 let arch_edits = archetype_edits.into_iter();
122 let arch_edit_sets = arch_edits.zip(chunk_sets)
123 .enumerate()
124 .map(|(id, (edits, chunk_set))|
125 (universe.archetype_by_id(id).unwrap(), edits, chunk_set));
126
127 for (arch, edits, chunk_set) in arch_edit_sets {
129 chunk_set.modify(arch, edits, &edit_list.component_data);
130 }
131 }
132 }
133}
134
135impl Debug for Snapshot {
136 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137 write!(f, "Snapshot {{\n")?;
138
139 write!(f, " Chunk Sets:\n")?;
140 for (arch_id, chunk_set) in self.chunk_sets.iter().enumerate() {
141 if chunk_set.is_empty() {
142 continue
143 }
144
145 let archetype = self.universe.archetype_by_id(arch_id).unwrap();
146
147 write!(f, " #{} - ", arch_id)?;
148 for ty in archetype.component_types().as_slice() {
149 write!(f, "{:?}, ", ty)?;
150 }
151 write!(f, "\n")?;
152
153 write!(f, " Chunks:\n")?;
154 for chunk in chunk_set.iter() {
155 write!(f, " {:?} - {} entities\n", chunk.deref() as *const _, chunk.len())?;
156
157 let ids = chunk.components::<EntityID>().unwrap();
158
159 for entity_id in ids {
160 write!(f, " Entity {:?} - Chunk {:?}\n", entity_id, chunk.deref() as *const _)?;
161 }
162 }
163 }
164
165 write!(f, "}}\n")
166 }
167}
168
169struct SnapshotEditList<'a> {
170 component_data: Vec<ComponentValueRef<'a>>,
171 chunk_set_edits: Vec<Vec<ChunkEdit>>,
172}
173
174impl<'a> SnapshotEditList<'a> {
175 fn get_chunk_set_edits<'b: 'c, 'c>(edits: &'b mut Vec<Vec<ChunkEdit>>, arch: &Arc<Archetype>) -> &'c mut Vec<ChunkEdit> {
176 let id = arch.id();
177 if edits.len() <= id {
178 edits.resize(id + 1, Vec::new());
179 }
180 &mut edits[id]
181 }
182
183 pub fn from_edits<E>(snap: &mut Arc<Snapshot>, edits: E) -> SnapshotEditList<'a>
184 where E: Iterator<Item=Edit<'a>>
185 {
186 let mut component_data = Vec::new();
187 let mut chunk_set_edits = Vec::new();
188 let mut edits = edits.peekable();
189
190 while let Some(Edit(id, _)) = edits.peek().cloned() {
191 let old_archetype = snap.entities.get(&id)
192 .copied()
193 .and_then(|idx| snap.universe.archetype_by_id(idx));
194 let mut component_types = match old_archetype {
195 Some(ref a) => Cow::Borrowed(a.component_types()),
196 None => Cow::Owned(ComponentVecSet::new(Vec::new())),
197 };
198
199 let mut component_data = ComponentDataVecWriter::new(&mut component_data);
200
201 while edits.peek().map_or(false, |e| e.0 == id) {
202 match edits.next().unwrap().1 {
203 EditAction::SetComponent(value) => {
204 if !component_types.includes(&value.type_id()) {
205 component_types.to_mut().insert(value.type_id());
206 }
207 component_data.set_component(value);
208 }
209 EditAction::RemoveComponent(type_id) => {
210 if component_types.includes(&type_id) {
211 component_types.to_mut().remove(type_id);
212 }
213 component_data.remove_component(type_id);
214 }
215 }
216 }
217
218 let new_archetype = match component_types {
219 Cow::Borrowed(_) => Some(old_archetype.clone().unwrap()),
220 Cow::Owned(component_set) => {
221 if component_set.len() > 1 {
222 Some(snap.universe.ensure_archetype(component_set))
223 } else {
224 None
225 }
226 }
227 };
228
229 let is_empty = old_archetype.is_none() && new_archetype.is_none();
230 let is_move = old_archetype
231 .clone()
232 .and_then(|old| new_archetype.clone().map(|new| (old, new)))
233 .map_or(true, |(a, b)| !Arc::ptr_eq(&a, &b));
234 let is_noop = !is_empty && !is_move && component_data.len() == 0;
235 if is_noop {
236 continue;
237 }
238
239 if let Some(arch) = old_archetype.filter(|_| is_move) {
241 SnapshotEditList::get_chunk_set_edits(&mut chunk_set_edits, &arch)
242 .push(ChunkEdit(id, ChunkAction::Remove));
243
244 let edit_snap = Arc::make_mut(snap);
245 edit_snap.entities.remove(&id);
246 }
247
248 if let Some(arch) = new_archetype {
250 let (start, end) = component_data.range();
251 SnapshotEditList::get_chunk_set_edits(&mut chunk_set_edits, &arch)
252 .push(ChunkEdit(id, ChunkAction::Upsert(start, end)));
253
254 if is_move {
255 let edit_snap = Arc::make_mut(snap);
256 edit_snap.entities.insert(id, arch.id());
257 }
258 }
259 }
260
261 SnapshotEditList {
262 component_data,
263 chunk_set_edits,
264 }
265 }
266
267 pub fn is_empty(&self) -> bool {
269 self.chunk_set_edits.is_empty()
270 }
271}
272