libtw2_demo/ddnet/
writer.rs1use crate::format;
2use libtw2_common::digest::Sha256;
3use libtw2_common::num::Cast;
4use libtw2_gamenet_common::snap_obj;
5use libtw2_gamenet_common::traits::MessageExt as _;
6use libtw2_gamenet_common::traits::Protocol;
7use libtw2_gamenet_common::traits::SnapObj as _;
8use libtw2_packer::with_packer;
9use libtw2_snapshot::snap;
10use libtw2_snapshot::Delta;
11use libtw2_snapshot::Snap;
12use std::convert::TryInto;
13use std::io;
14use std::marker::PhantomData;
15use std::mem;
16use thiserror::Error;
17
18#[derive(Debug, Error)]
19pub enum WriteError {
20 #[error(transparent)]
21 Inner(crate::WriteError),
22 #[error("Snap creation - {0:?}")]
23 SnapBuilder(snap::BuilderError),
24 #[error("Tick decreased or is negative")]
25 TooLowTickNumber,
26 #[error("Snap data does not fit into buffer")]
27 TooLargeSnap,
28 #[error("Net message data does not fit into buffer")]
29 TooLongNetMsg,
30}
31
32impl From<crate::WriteError> for WriteError {
33 fn from(value: crate::WriteError) -> Self {
34 Self::Inner(value)
35 }
36}
37
38impl From<snap::BuilderError> for WriteError {
39 fn from(value: snap::BuilderError) -> Self {
40 Self::SnapBuilder(value)
41 }
42}
43
44pub struct DemoWriter<P: for<'a> Protocol<'a>> {
48 inner: crate::Writer,
49 last_tick: i32,
51 last_keyframe: Option<i32>,
53 uuid_index: UuidIndex,
54 snap: Snap,
55 builder: snap::Builder,
56 delta: Delta,
57 buf: arrayvec::ArrayVec<[u8; format::MAX_SNAPSHOT_SIZE]>,
58 i32_buf: Vec<i32>,
59 protocol: PhantomData<P>,
60}
61
62#[derive(Default)]
63struct UuidIndex(Vec<[u8; 16]>);
64
65impl UuidIndex {
66 fn index_to_type_id(index: u16) -> u16 {
67 0x7fff - index
69 }
70
71 fn write_to_snap(&mut self, builder: &mut snap::Builder) -> Result<(), snap::BuilderError> {
75 for (index, uuid) in self.0.iter().enumerate() {
76 let type_id = Self::index_to_type_id(index.assert_u16());
77 let mut uuid_item_ints = [0; 4];
78 for (uuid_int, uuid_bytes) in uuid_item_ints.iter_mut().zip(uuid.chunks(4)) {
79 *uuid_int = i32::from_be_bytes(uuid_bytes.try_into().unwrap());
80 }
81 builder.add_item(0, type_id, &uuid_item_ints)?;
82 }
83 Ok(())
84 }
85
86 fn get_type_id(&mut self, uuid: &[u8; 16]) -> u16 {
87 if let Some(index) = self.0.iter().position(|e| e == uuid) {
88 Self::index_to_type_id(index.assert_u16())
89 } else {
90 let new_index = self.0.len().assert_u16();
91 self.0.push(*uuid);
92 Self::index_to_type_id(new_index)
93 }
94 }
95}
96
97impl<P: for<'a> Protocol<'a>> DemoWriter<P> {
98 pub fn new<T: io::Write + io::Seek + 'static>(
99 file: T,
100 net_version: &[u8],
101 map_name: &[u8],
102 map_sha256: Option<Sha256>,
103 map_crc: u32,
104 kind: crate::DemoKind,
105 length: i32,
106 timestamp: &[u8],
107 map: &[u8],
108 ) -> Result<Self, WriteError> {
109 let raw = crate::Writer::new(
110 file,
111 net_version,
112 map_name,
113 map_sha256,
114 map_crc,
115 kind,
116 length,
117 timestamp,
118 map,
119 )?;
120
121 Ok(Self {
122 inner: raw,
123 last_tick: -1,
124 last_keyframe: None,
125 uuid_index: UuidIndex::default(),
126 snap: Snap::default(),
127 delta: Delta::default(),
128 builder: snap::Builder::default(),
129 buf: arrayvec::ArrayVec::new(),
130 i32_buf: Vec::new(),
131 protocol: PhantomData,
132 })
133 }
134
135 pub fn write_snap<'a, T: Iterator<Item = (&'a P::SnapObj, u16)>>(
136 &mut self,
137 tick: i32,
138 items: T,
139 ) -> Result<(), WriteError> {
140 if tick < self.last_tick {
142 return Err(WriteError::TooLowTickNumber);
143 }
144 let is_keyframe = match self.last_keyframe {
147 None => true,
148 Some(last_keyframe) => tick - last_keyframe > 250,
149 };
150
151 for (item, id) in items {
153 let type_id = match item.obj_type_id() {
154 snap_obj::TypeId::Ordinal(type_id) => type_id,
155 snap_obj::TypeId::Uuid(uuid) => self.uuid_index.get_type_id(uuid.as_bytes()),
156 };
157 self.builder.add_item(type_id, id, item.encode())?;
158 }
159 self.uuid_index.write_to_snap(&mut self.builder)?;
160
161 let old_snap = mem::take(&mut self.snap);
162 let new_snap = mem::take(&mut self.builder).finish();
163
164 self.inner.write_tick(is_keyframe, tick)?;
165 if is_keyframe {
166 let keys = &mut self.i32_buf;
167 with_packer(&mut self.buf, |p| new_snap.write(keys, p))
168 .map_err(|_| WriteError::TooLargeSnap)?;
169 self.inner.write_snapshot(&self.buf)?;
170 } else {
171 self.delta.create(&old_snap, &new_snap);
172 let delta = &self.delta;
173 with_packer(&mut self.buf, |p| delta.write(P::obj_size, p))
174 .map_err(|_| WriteError::TooLargeSnap)?;
175 self.inner.write_snapshot_delta(&self.buf)?;
176 }
177
178 self.snap = new_snap;
182 self.builder = old_snap.recycle();
183 self.buf.clear();
184 self.last_tick = tick;
185 if is_keyframe {
186 self.last_keyframe = Some(tick);
187 }
188 Ok(())
189 }
190 pub fn write_msg(&mut self, msg: &<P as Protocol<'_>>::Game) -> Result<(), WriteError> {
191 with_packer(&mut self.buf, |p| msg.encode(p)).map_err(|_| WriteError::TooLongNetMsg)?;
192 self.inner.write_message(self.buf.as_slice())?;
193 self.buf.clear();
194 Ok(())
195 }
196}