diff options
Diffstat (limited to 'snapshot/src/receiver.rs')
-rw-r--r-- | snapshot/src/receiver.rs | 245 |
1 files changed, 245 insertions, 0 deletions
diff --git a/snapshot/src/receiver.rs b/snapshot/src/receiver.rs new file mode 100644 index 0000000..49833d1 --- /dev/null +++ b/snapshot/src/receiver.rs @@ -0,0 +1,245 @@ +use common::num::Cast; +use gamenet::msg::system; +use std::ops; +use to_usize; +use vec_map::VecMap; +use warn::Warn; + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum Warning { + DuplicateSnap, + DifferingAttributes, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum Error { + OldDelta, + InvalidNumParts, + InvalidPart, + DuplicatePart, +} + +// TODO: How to handle `tick` overflowing? +#[derive(Clone, Debug)] +struct CurrentDelta { + tick: i32, + delta_tick: i32, + num_parts: i32, + crc: i32, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct ReceivedDelta<'a> { + pub delta_tick: i32, + pub tick: i32, + pub data_and_crc: Option<(&'a [u8], i32)>, +} + +#[derive(Clone, Default)] +pub struct DeltaReceiver { + previous_tick: Option<i32>, + current: Option<CurrentDelta>, + // `parts` points into `receive_buf`. + parts: VecMap<ops::Range<u32>>, + receive_buf: Vec<u8>, + result: Vec<u8>, +} + +impl DeltaReceiver { + pub fn new() -> DeltaReceiver { + Default::default() + } + pub fn reset(&mut self) { + self.previous_tick = None; + self.current = None; + } + fn can_receive(&self, tick: i32) -> bool { + self.current.as_ref().map(|c| c.tick <= tick) + .or(self.previous_tick.map(|t| t < tick)) + .unwrap_or(true) + } + fn init_delta(&mut self) { + self.parts.clear(); + self.receive_buf.clear(); + self.result.clear(); + } + fn finish_delta(&mut self, tick: i32) { + self.current = None; + self.previous_tick = Some(tick); + } + pub fn snap_empty<W>(&mut self, warn: &mut W, snap: system::SnapEmpty) + -> Result<Option<ReceivedDelta>, Error> + where W: Warn<Warning>, + { + if !self.can_receive(snap.tick) { + return Err(Error::OldDelta); + } + if self.current.as_ref().map(|c| c.tick == snap.tick).unwrap_or(false) { + warn.warn(Warning::DuplicateSnap); + } + self.init_delta(); + self.finish_delta(snap.tick); + Ok(Some(ReceivedDelta { + delta_tick: snap.tick.wrapping_sub(snap.delta_tick), + tick: snap.tick, + data_and_crc: None, + })) + } + pub fn snap_single<W>(&mut self, warn: &mut W, snap: system::SnapSingle) + -> Result<Option<ReceivedDelta>, Error> + where W: Warn<Warning>, + { + if !self.can_receive(snap.tick) { + return Err(Error::OldDelta); + } + if self.current.as_ref().map(|c| c.tick == snap.tick).unwrap_or(false) { + warn.warn(Warning::DuplicateSnap); + } + self.init_delta(); + self.finish_delta(snap.tick); + self.result.extend(snap.data); + Ok(Some(ReceivedDelta { + delta_tick: snap.tick.wrapping_sub(snap.delta_tick), + tick: snap.tick, + data_and_crc: Some((&self.result, snap.crc)), + })) + } + pub fn snap<W>(&mut self, warn: &mut W, snap: system::Snap) + -> Result<Option<ReceivedDelta>, Error> + where W: Warn<Warning>, + { + if !self.can_receive(snap.tick) { + return Err(Error::OldDelta); + } + if !(0 <= snap.num_parts && snap.num_parts <= 32) { + return Err(Error::InvalidNumParts); + } + if !(0 <= snap.part && snap.part < snap.num_parts) { + return Err(Error::InvalidPart); + } + if self.current.as_ref().map(|c| c.tick != snap.tick).unwrap_or(false) { + self.current = None; + } + if let None = self.current { + self.init_delta(); + self.current = Some(CurrentDelta { + tick: snap.tick, + delta_tick: snap.tick.wrapping_sub(snap.delta_tick), + num_parts: snap.num_parts, + crc: snap.crc, + }); + + // Checked above. + let num_parts = snap.num_parts.assert_usize(); + self.parts.reserve_len(num_parts); + } + let delta_tick; + let tick; + let crc; + let num_parts; + { + let current: &mut CurrentDelta = self.current.as_mut().unwrap(); + if snap.delta_tick != current.delta_tick + || snap.num_parts != current.num_parts + || snap.crc != current.crc + { + warn.warn(Warning::DifferingAttributes); + } + delta_tick = current.delta_tick; + tick = current.tick; + crc = current.crc; + num_parts = current.num_parts; + } + let part = snap.part.assert_usize(); + if self.parts.contains_key(part) { + return Err(Error::DuplicatePart); + } + let start = self.receive_buf.len().assert_u32(); + let end = (self.receive_buf.len() + snap.data.len()).assert_u32(); + self.receive_buf.extend(snap.data); + assert!(self.parts.insert(part, start..end).is_none()); + + if self.parts.len().assert_i32() != num_parts { + return Ok(None); + } + + self.finish_delta(tick); + self.result.reserve(self.receive_buf.len()); + for range in self.parts.values() { + self.result.extend(&self.receive_buf[to_usize(range.clone())]); + } + + Ok(Some(ReceivedDelta { + delta_tick: delta_tick, + tick: tick, + data_and_crc: Some((&self.result, crc)), + })) + } +} + +#[cfg(test)] +mod test { + use common::num::Cast; + use gamenet::msg::system::Snap; + use gamenet::msg::system::SnapEmpty; + use gamenet::msg::system::SnapSingle; + use super::DeltaReceiver; + use super::Error; + use super::ReceivedDelta; + use warn::Panic; + + #[test] + fn old() { + let mut receiver = DeltaReceiver::new(); + { + let result = receiver.snap_empty(&mut Panic, SnapEmpty { + tick: 1, + delta_tick: 2, + }).unwrap(); + + assert_eq!(result, Some(ReceivedDelta { + delta_tick: -1, + tick: 1, + data_and_crc: None, + })); + } + + assert_eq!(receiver.snap_single(&mut Panic, SnapSingle { + tick: 0, + delta_tick: 0, + data: b"123", + crc: 0, + }).unwrap_err(), Error::OldDelta); + } + + #[test] + fn reorder() { + let mut receiver = DeltaReceiver::new(); + let chunks: &[(i32, &[u8])] = &[ + (3, b"3"), + (2, b"2"), + (4, b"4_"), + (1, b"1__"), + (0, b"0"), + ]; + for &(i, c) in chunks { + let result = receiver.snap(&mut Panic, Snap { + tick: 2, + delta_tick: 1, + num_parts: chunks.len().assert_i32(), + part: i, + crc: 3, + data: c, + }).unwrap(); + if i != 0 { + assert_eq!(result, None); + } else { + assert_eq!(result, Some(ReceivedDelta { + delta_tick: 1, + tick: 2, + data_and_crc: Some((b"01__234_", 3)), + })); + } + } + } +} |