1use borger_plugin_sdk::diff_ser::DiffSerializer;
2use borger_plugin_sdk::multiplayer_tradeoff::{AnyTradeOff, DiffSerializerToImpl};
3use borger_plugin_sdk::primitive::{DeserializeOopsy, PrimitiveSerDes, usize32};
4use borger_plugin_sdk::traits::{
5 ConstructPlugin, CustomStruct, DiffDeserializeCustomStruct, DiffDeserializePlugin, SnapshotState,
6 UntrackedState,
7};
8use borger_plugin_sdk::{ClientKind, diff_operation_enum};
9use std::collections::HashMap;
10use std::iter::FusedIterator;
11use std::mem;
12use std::ops::Deref;
13use std::rc::Rc;
14
15#[cfg(feature = "server")]
16use borger_plugin_sdk::NetVisibility;
17
18#[cfg(feature = "client")]
19use {
20 crate::simulation::Client,
21 borger_plugin_sdk::TickID,
22 borger_plugin_sdk::multiplayer_tradeoff::Impl,
23 borger_plugin_sdk::traits::{PresentationCollect, PresentationOutput},
24 std::any::TypeId,
25 std::mem::MaybeUninit,
26 std::ptr,
27 std::vec,
28};
29
30diff_operation_enum!("SlotMap");
31
32#[derive(Debug, Clone, PartialEq, Eq)]
44pub struct RawSlotMap<V> {
45 slots: Vec<(usize32, V)>,
46 random_access: HashMap<usize32, usize32>, next_id: usize32,
48}
49
50impl<V> Default for RawSlotMap<V> {
51 fn default() -> Self {
52 Self::new()
53 }
54}
55
56impl<V> RawSlotMap<V> {
57 pub fn new() -> Self {
58 Self {
59 slots: Vec::new(),
60 random_access: HashMap::new(),
61 next_id: 0,
62 }
63 }
64
65 pub fn add(&mut self, value: V) -> (usize32, &mut V) {
66 self.add_with_id(|_| value)
67 }
68
69 pub fn add_with_id(&mut self, value: impl FnOnce(usize32) -> V) -> (usize32, &mut V) {
70 let start_id = self.next_id;
71 let id = loop {
72 let new_id = self.next_id;
73
74 self.next_id = self.next_id.wrapping_add(1);
75 if self.next_id == start_id {
76 panic!("SlotMap's belly is too full to eat no more");
77 }
78
79 if !self.random_access.contains_key(&new_id) {
80 break new_id;
81 }
82 };
83
84 let physical_index = self.len();
85
86 self.slots.push((id, value(id)));
87 self.random_access.insert(id, physical_index);
88
89 let slot = self.slots.last_mut().unwrap();
90 (slot.0, &mut slot.1)
91 }
92
93 pub fn remove(&mut self, id: usize32) -> Option<V> {
94 let physical_index_u32 = self.random_access.remove(&id)?;
95 let physical_index_usize = physical_index_u32 as usize;
96 let (_, removed_slot) = self.slots.swap_remove(physical_index_usize);
97
98 if physical_index_u32 < self.len() {
99 *self
102 .random_access
103 .get_mut(&self.slots[physical_index_usize].0)
104 .unwrap() = physical_index_u32;
105 }
106
107 Some(removed_slot)
108 }
109
110 pub fn clear(&mut self) -> usize32 {
111 let len = self.len();
112
113 self.slots.clear();
114 self.random_access.clear();
115 self.next_id = 0;
116
117 len
118 }
119
120 pub fn has(&self, id: usize32) -> bool {
121 self.random_access.contains_key(&id)
122 }
123
124 pub fn get(&self, id: usize32) -> Option<&V> {
125 self.random_access
126 .get(&id)
127 .map(|&physical_index| &self.slots.get(physical_index as usize).unwrap().1)
128 }
129
130 pub fn get_mut(&mut self, id: usize32) -> Option<&mut V> {
131 self.random_access
132 .get(&id)
133 .map(|&physical_index| &mut self.slots.get_mut(physical_index as usize).unwrap().1)
134 }
135
136 pub fn len(&self) -> usize32 {
137 self.slots.len() as usize32
138 }
139
140 pub fn is_empty(&self) -> bool {
141 self.slots.is_empty()
142 }
143
144 pub fn iter(
145 &self,
146 ) -> impl ExactSizeIterator<Item = (usize32, &V)> + DoubleEndedIterator + FusedIterator + Clone {
147 self.slots.iter().map(|slot| (slot.0, &slot.1))
148 }
149
150 pub fn iter_mut(
151 &mut self,
152 ) -> impl ExactSizeIterator<Item = (usize32, &mut V)> + DoubleEndedIterator + FusedIterator {
153 self.slots.iter_mut().map(|slot| (slot.0, &mut slot.1))
154 }
155
156 pub fn ids(
157 &self,
158 ) -> impl ExactSizeIterator<Item = usize32> + DoubleEndedIterator + FusedIterator + Clone {
159 self.slots.iter().map(|slot| slot.0)
160 }
161
162 pub fn values(&self) -> impl ExactSizeIterator<Item = &V> + DoubleEndedIterator + FusedIterator + Clone {
163 self.slots.iter().map(|slot| &slot.1)
164 }
165
166 pub fn values_mut(
167 &mut self,
168 ) -> impl ExactSizeIterator<Item = &mut V> + DoubleEndedIterator + FusedIterator {
169 self.slots.iter_mut().map(|slot| &mut slot.1)
170 }
171}
172
173#[derive(Debug)]
176pub struct SlotMap<V: CustomStruct> {
177 diff_path: Rc<[usize32]>,
178 field_id: usize32,
179
180 #[cfg(feature = "server")]
181 visibility: NetVisibility,
182
183 data: RawSlotMap<V>,
184}
185
186impl<V: CustomStruct> ConstructPlugin for SlotMap<V> {
189 fn construct(
190 path: &Rc<[usize32]>,
191 field_id: usize32,
192
193 #[cfg(feature = "server")] visibility: NetVisibility,
194 ) -> Self {
195 Self {
196 diff_path: path.clone(),
197 field_id,
198
199 #[cfg(feature = "server")]
200 visibility,
201
202 data: RawSlotMap::new(),
203 }
204 }
205}
206
207impl<V: CustomStruct> Deref for SlotMap<V> {
210 type Target = RawSlotMap<V>;
211
212 fn deref(&self) -> &Self::Target {
213 &self.data
214 }
215}
216
217impl<V: CustomStruct> SlotMap<V> {
218 pub fn add(&mut self, diff: &mut DiffSerializer<impl AnyTradeOff>) -> (usize32, &mut V) {
219 self.add_with_client_owned(ClientKind::NA, diff)
220 }
221
222 pub(crate) fn add_with_client_owned(
223 &mut self,
224 client_kind: ClientKind,
225 diff: &mut DiffSerializer<impl AnyTradeOff>,
226 ) -> (usize32, &mut V) {
227 let op = DiffOperation::SlotMapAdd;
228 let diff = diff.to_impl();
229
230 if let Some(buffer) = diff.ser_rollback_begin(&self.diff_path) {
231 self.data.next_id.ser_rollback(buffer);
232 self.field_id.ser_rollback(buffer);
233 op.ser_rollback(buffer);
234 }
235
236 #[cfg(feature = "server")]
237 for buffer in diff.ser_tx_begin(&self.diff_path, self.visibility) {
238 op.ser_tx(buffer);
239 self.field_id.ser_tx(buffer);
240 }
241
242 self.data
243 .add_with_id(|id| V::construct(&build_slot_path(id, &self.diff_path, self.field_id), client_kind))
244 }
245
246 #[must_use]
251 pub fn remove(&mut self, id: usize32, diff: &mut DiffSerializer<impl AnyTradeOff>) -> Option<()> {
252 let physical_index = *self.data.random_access.get(&id)?;
253 let removed_slot = self.data.remove(id).unwrap();
254
255 let op = DiffOperation::SlotMapRemove;
256 let diff = diff.to_impl();
257
258 if let Some(buffer) = diff.ser_rollback_begin(&self.diff_path) {
259 removed_slot.ser_rollback_predict_remove(buffer);
260 physical_index.ser_rollback(buffer);
261 id.ser_rollback(buffer);
262 self.field_id.ser_rollback(buffer);
263 op.ser_rollback(buffer);
264 }
265
266 #[cfg(feature = "server")]
267 for buffer in diff.ser_tx_begin(&self.diff_path, self.visibility) {
268 op.ser_tx(buffer);
269 self.field_id.ser_tx(buffer);
270 id.ser_tx(buffer);
271 }
272
273 Some(())
274 }
275
276 pub fn clear(&mut self, diff: &mut DiffSerializer<impl AnyTradeOff>) -> usize32 {
279 let len = self.data.len();
280
281 if len == 0 && self.data.next_id == 0 {
283 return 0;
284 }
285
286 let op = DiffOperation::SlotMapClear;
287 let diff = diff.to_impl();
288
289 if let Some(buffer) = diff.ser_rollback_begin(&self.diff_path) {
290 self.ser_rollback_predict_remove(buffer);
291 self.field_id.ser_rollback(buffer);
292 op.ser_rollback(buffer);
293 }
294
295 #[cfg(feature = "server")]
296 for buffer in diff.ser_tx_begin(&self.diff_path, self.visibility) {
297 op.ser_tx(buffer);
298 self.field_id.ser_tx(buffer);
299 }
300
301 self.data.clear();
302 len
303 }
304
305 pub fn get_mut(&mut self, id: usize32) -> Option<&mut V> {
306 self.data.get_mut(id)
307 }
308
309 pub fn iter_mut(
310 &mut self,
311 ) -> impl ExactSizeIterator<Item = (usize32, &mut V)> + DoubleEndedIterator + FusedIterator {
312 self.data.iter_mut()
313 }
314
315 pub fn values_mut(
316 &mut self,
317 ) -> impl ExactSizeIterator<Item = &mut V> + DoubleEndedIterator + FusedIterator {
318 self.data.values_mut()
319 }
320}
321
322impl<V: CustomStruct> DiffDeserializePlugin for SlotMap<V> {
325 fn navigate_down(&mut self, element_id: usize32) -> Option<&mut dyn DiffDeserializeCustomStruct> {
326 self.get_mut(element_id)
327 .map(|v| v as &mut dyn DiffDeserializeCustomStruct)
328 }
329
330 fn des_rollback(&mut self, diff_op: u8, buffer: &mut Vec<u8>) -> Result<(), DeserializeOopsy> {
331 match DiffOperation::try_from(diff_op).map_err(|_| DeserializeOopsy)? {
332 DiffOperation::SlotMapAdd => {
333 self.data.next_id = usize32::des_rollback(buffer)?;
334 let id = self.data.slots.pop().unwrap().0;
335 self.data.random_access.remove(&id).unwrap();
336 }
337 DiffOperation::SlotMapRemove => {
338 let id = usize32::des_rollback(buffer)?;
339 let physical_index = usize32::des_rollback(buffer)?;
340
341 let mut value = V::construct(
345 &build_slot_path(id, &self.diff_path, self.field_id),
346 ClientKind::NA,
347 );
348 value.des_rollback_predict_remove(buffer)?;
349
350 let reincarnated_slot = (id, value);
351 if physical_index == self.data.len() {
352 self.data.slots.push(reincarnated_slot);
354 } else {
355 let end_slot =
358 mem::replace(&mut self.data.slots[physical_index as usize], reincarnated_slot);
359 *self.data.random_access.get_mut(&end_slot.0).unwrap() = self.data.len();
360 self.data.slots.push(end_slot);
361 }
362
363 self.data.random_access.insert(id, physical_index);
364 }
365 DiffOperation::SlotMapClear => {
366 self.des_rollback_predict_remove(buffer)?;
367 }
368 };
369
370 Ok(())
371 }
372
373 #[cfg(feature = "client")]
374 fn des_rx(
375 &mut self,
376 diff_op: u8,
377 buffer: &mut vec::IntoIter<u8>,
378 diff: &mut DiffSerializer<Impl>,
379 ) -> Result<(), DeserializeOopsy> {
380 match DiffOperation::try_from(diff_op).map_err(|_| DeserializeOopsy)? {
381 DiffOperation::SlotMapAdd => {
382 self.add(diff);
383 }
384 DiffOperation::SlotMapRemove => {
385 let id = usize32::des_rx(buffer)?;
386 self.remove(id, diff).ok_or(DeserializeOopsy)?;
387 }
388 DiffOperation::SlotMapClear => {
389 self.clear(diff);
390 }
391 };
392
393 Ok(())
394 }
395}
396
397impl<V: CustomStruct> SnapshotState for SlotMap<V> {
400 #[cfg(feature = "server")]
401 fn ser_tx_new_client(&self, client_id: usize32, buffer: &mut Vec<u8>) {
402 self.data.next_id.ser_tx(buffer);
403 self.len().ser_tx(buffer);
404
405 for slot in self.data.slots.iter() {
406 slot.0.ser_tx(buffer);
407 slot.1.ser_tx_new_client(client_id, buffer);
408 }
409 }
410
411 #[cfg(feature = "client")]
412 fn des_rx_new_client(
413 &mut self,
414 client_id: usize32,
415 buffer: &mut impl Iterator<Item = u8>,
416 ) -> Result<(), DeserializeOopsy> {
417 self.data.next_id = usize32::des_rx(buffer)?;
418 let len = usize32::des_rx(buffer)?;
419
420 for physical_index in 0..len {
421 let id = usize32::des_rx(buffer)?;
422
423 let client_kind = if id == client_id && TypeId::of::<V>() == TypeId::of::<Client>() {
427 ClientKind::Owned
428 } else {
429 ClientKind::Remote
430 };
431
432 let mut slot = V::construct(&build_slot_path(id, &self.diff_path, self.field_id), client_kind);
433 slot.des_rx_new_client(client_id, buffer)?;
434
435 self.data.slots.push((id, slot));
436 self.data.random_access.insert(id, physical_index);
437 }
438
439 Ok(())
440 }
441
442 fn ser_rollback_predict_remove(&self, buffer: &mut Vec<u8>) {
449 for slot in self.data.slots.iter().rev() {
450 slot.1.ser_rollback_predict_remove(buffer);
451 slot.0.ser_rollback(buffer);
452 }
453
454 self.data.len().ser_rollback(buffer);
455 self.data.next_id.ser_rollback(buffer);
456 }
457
458 fn des_rollback_predict_remove(&mut self, buffer: &mut Vec<u8>) -> Result<(), DeserializeOopsy> {
459 self.data.next_id = usize32::des_rollback(buffer)?;
460 let slots_len = usize32::des_rollback(buffer)?;
461
462 for physical_index in 0..slots_len {
463 let id = usize32::des_rollback(buffer)?;
464 let mut slot = V::construct(
465 &build_slot_path(id, &self.diff_path, self.field_id),
466 ClientKind::NA,
467 );
468 slot.des_rollback_predict_remove(buffer)?;
469
470 self.data.slots.push((id, slot));
471 self.data.random_access.insert(id, physical_index);
472 }
473
474 Ok(())
475 }
476}
477
478impl<V: CustomStruct> UntrackedState for SlotMap<V> {
480 fn reset_untracked(&mut self) {
481 for slot in self.values_mut() {
482 slot.reset_untracked();
483 }
484 }
485}
486
487#[cfg(feature = "client")]
490impl<V> PresentationCollect for SlotMap<V>
491where
492 V: CustomStruct + PresentationCollect,
493{
494 type PresentationCollect = RawSlotMap<V::PresentationCollect>;
495 fn presentation_collect(&self, tick: TickID) -> Self::PresentationCollect {
496 RawSlotMap {
497 slots: self
498 .data
499 .slots
500 .iter()
501 .map(|slot| (slot.0, slot.1.presentation_collect(tick)))
502 .collect(),
503
504 random_access: self.data.random_access.clone(),
505 next_id: self.data.next_id,
506 }
507 }
508}
509
510#[cfg(feature = "client")]
513pub struct PresentationOutputSlotMap<V> {
514 pub data: RawSlotMap<V>,
515 pub slots_ptr: *const (usize32, V),
516 pub slots_len: usize32,
517 pub removed: Vec<usize32>,
518 pub removed_ptr: *const usize32,
519 pub removed_len: usize32,
520 pub added: Vec<usize32>,
521 pub added_ptr: *const usize32,
522 pub added_len: usize32,
523}
524
525#[cfg(feature = "client")]
526impl<V: PresentationOutput<Prv>, Prv> PresentationOutput<RawSlotMap<Prv>> for RawSlotMap<V> {
527 type PresentationOutput = PresentationOutputSlotMap<V::PresentationOutput>;
528 fn presentation_output(
529 prv: Option<&RawSlotMap<Prv>>,
530 cur: &Self,
531 interpolation_alpha: f32,
532 received_new_tick: bool,
533 ) -> Self::PresentationOutput {
534 let Some(prv) = prv else {
535 let slots: Vec<(u32, V::PresentationOutput)> = cur
536 .slots
537 .iter()
538 .map(|slot| {
539 (
540 slot.0,
541 V::presentation_output(None, &slot.1, interpolation_alpha, received_new_tick),
542 )
543 })
544 .collect();
545
546 let slots_ptr = slots.as_ptr();
547 let added: Vec<_> = if received_new_tick {
548 cur.ids().collect()
549 } else {
550 Vec::new()
551 };
552
553 let added_ptr = added.as_ptr();
554 let added_len = added.len() as usize32;
555
556 return PresentationOutputSlotMap {
557 data: RawSlotMap {
558 slots,
559 random_access: cur.random_access.clone(),
560 next_id: cur.next_id,
561 },
562
563 slots_ptr,
564 slots_len: cur.len(),
565 removed: Vec::default(),
566 removed_ptr: ptr::null(),
567 removed_len: 0,
568 added,
569 added_ptr,
570 added_len,
571 };
572 };
573
574 let mut slots: Vec<MaybeUninit<(u32, V::PresentationOutput)>> =
575 (0..cur.slots.len()).map(|_a| MaybeUninit::uninit()).collect();
576
577 let mut removed = Vec::new();
578 let mut added = Vec::new();
579
580 let max_len = prv.slots.len().max(cur.slots.len());
581 for slot_idx in 0..max_len {
582 let prv_slot = prv.slots.get(slot_idx);
590 let cur_slot = cur.slots.get(slot_idx);
591 if let (Some(prv_slot), Some(cur_slot)) = (prv_slot, cur_slot)
592 && prv_slot.0 == cur_slot.0
593 {
594 slots[slot_idx].write((
597 cur_slot.0,
598 V::presentation_output(
599 Some(&prv_slot.1),
600 &cur_slot.1,
601 interpolation_alpha,
602 received_new_tick,
603 ),
604 ));
605 } else {
606 if let Some(cur_slot) = cur_slot {
607 let prv_moved_slot = prv.get(cur_slot.0);
608 slots[slot_idx].write((
609 cur_slot.0,
610 V::presentation_output(
611 prv_moved_slot,
612 &cur_slot.1,
613 interpolation_alpha,
614 received_new_tick,
615 ),
616 ));
617
618 if received_new_tick && prv_moved_slot.is_none() {
619 added.push(cur_slot.0);
620 }
621 }
622
623 if received_new_tick && let Some(prv_slot) = prv_slot {
624 if !cur.has(prv_slot.0) {
625 removed.push(prv_slot.0);
626 }
627 }
628 }
629 }
630
631 let slots: Vec<(u32, V::PresentationOutput)> = unsafe { mem::transmute(slots) };
633 let slots_ptr = slots.as_ptr();
634 let removed_ptr = removed.as_ptr();
635 let removed_len = removed.len() as usize32;
636 let added_ptr = added.as_ptr();
637 let added_len = added.len() as usize32;
638
639 PresentationOutputSlotMap {
640 data: RawSlotMap {
641 slots,
642 random_access: cur.random_access.clone(),
643 next_id: cur.next_id,
644 },
645
646 slots_ptr,
647 slots_len: cur.len(),
648 removed,
649 removed_ptr,
650 removed_len,
651 added,
652 added_ptr,
653 added_len,
654 }
655 }
656}
657
658fn build_slot_path(id: usize32, diff_path: &Rc<[usize32]>, field_id: usize32) -> Rc<[usize32]> {
661 diff_path.iter().copied().chain([field_id, id]).collect()
662}