borger/handwritten/
diff_ser.rs1use crate::DiffOperation;
2use crate::multiplayer_tradeoff::{AnyTradeOff, Impl};
3use crate::networked_types::primitive::{PrimitiveSerDes, SliceSerDes, usize32};
4use crate::tick::TickType;
5use std::marker::PhantomData;
6use std::mem;
7use std::rc::Rc;
8
9#[cfg(feature = "server")]
10use {crate::NetVisibility, std::collections::HashMap};
11
12#[derive(Default)]
26pub struct DiffSerializer<TradeOff: AnyTradeOff> {
27 pub(crate) rollback_buffer: Vec<u8>, rollback_enabled: bool, rollback_prv_path: Option<Rc<Vec<usize32>>>,
38
39 #[cfg(feature = "server")]
44 tx: HashMap<usize32, TxData>,
45 #[cfg(feature = "client")]
46 tx: TxData,
47
48 phantom_menace: PhantomData<TradeOff>,
53}
54
55struct TxData {
56 buffer: Vec<u8>,
57
58 #[cfg(feature = "server")]
59 enabled: bool,
60 #[cfg(feature = "server")]
61 cur_path: Rc<Vec<usize32>>,
62}
63
64impl Default for TxData {
65 fn default() -> Self {
66 Self {
67 buffer: Vec::default(),
68
69 #[cfg(feature = "server")]
70 enabled: true, #[cfg(feature = "server")]
72 cur_path: Rc::default(),
73 }
74 }
75}
76
77impl DiffSerializer<Impl> {
78 pub(crate) fn ser_rollback_begin(&mut self, path: &Rc<Vec<usize32>>) -> Option<&mut Vec<u8>> {
84 if self.rollback_enabled {
85 self.ser_rollback_navigate_to(path);
86 Some(&mut self.rollback_buffer)
87 } else {
88 None
89 }
90 }
91
92 #[cfg(feature = "server")]
94 pub(crate) fn ser_tx_begin(
95 &mut self,
96 path: &Rc<Vec<usize32>>,
97 visibility: NetVisibility,
98 ) -> impl Iterator<Item = &mut Vec<u8>> {
99 self.tx
100 .iter_mut()
101 .filter(move |&(&tx_client_id, ref tx)| {
102 tx.enabled
105 && match visibility {
106 NetVisibility::Public => true,
107 NetVisibility::Private => false,
108 NetVisibility::Owner => {
109 let modified_client_id = path[1];
110 tx_client_id == modified_client_id
111 }
112 }
113 })
114 .map(|(_, tx)| {
115 Self::ser_tx_navigate_to(tx, path);
116 &mut tx.buffer
117 })
118 }
119
120 #[cfg(feature = "client")]
122 pub(crate) fn ser_tx_begin(&mut self) -> &mut Vec<u8> {
123 &mut self.tx.buffer
124 }
125
126 pub(crate) fn rollback_begin_tick(&mut self, tick_type: TickType) {
129 self.rollback_enabled = tick_type == TickType::Predicted;
130 self.rollback_prv_path = None;
131
132 if self.rollback_enabled {
133 DiffOperation::RollbackTickSeparator.ser_rollback(&mut self.rollback_buffer);
134 }
135 }
136
137 pub(crate) fn rollback_end_tick(&mut self) {
138 if self.rollback_enabled {
139 self.ser_rollback_navigate_to(&Rc::default());
140 }
141 }
142
143 #[cfg(feature = "server")]
144 pub(crate) fn tx_begin_tick(&mut self, id: usize32, enable: bool) -> Option<&mut Vec<u8>> {
145 let client = self.tx.get_mut(&id).unwrap();
146 client.enabled = enable;
147 enable.then(|| &mut client.buffer)
148 }
149
150 pub(crate) fn tx_end_tick(&mut self, #[cfg(feature = "server")] client_id: usize32) -> Option<Vec<u8>> {
153 #[cfg(feature = "server")]
155 let (tx, enabled) = {
156 let tx = self.tx.get_mut(&client_id).unwrap();
157 let enabled = tx.enabled;
158 (tx, enabled)
159 };
160
161 #[cfg(feature = "client")]
163 let (tx, enabled) = (&mut self.tx, true);
164
165 if enabled {
166 #[cfg(feature = "server")]
171 {
172 debug_assert!(!tx.buffer.is_empty());
173
174 tx.cur_path = Rc::default();
175 }
176
177 let ret = mem::take(&mut tx.buffer);
178 Some(ret)
179 } else {
180 debug_assert!(tx.buffer.is_empty());
181 None
182 }
183 }
184
185 #[cfg(feature = "server")]
186 pub(crate) fn on_connect(&mut self, client_id: usize32) -> &mut Vec<u8> {
187 self.tx.insert(client_id, TxData::default()); &mut self.tx.get_mut(&client_id).unwrap().buffer
189 }
190
191 #[cfg(feature = "server")]
192 pub(crate) fn on_disconnect(&mut self, client_id: usize32) {
193 self.tx.remove(&client_id).unwrap();
194 }
195
196 fn ser_rollback_navigate_to(&mut self, new_path: &Rc<Vec<usize32>>) {
206 if let Some(prv_path) = &self.rollback_prv_path {
215 if let Some(shared_len) = find_first_mismatch(&new_path, &prv_path) {
216 let new_len = new_path.len();
217 let prv_len = prv_path.len();
218 let buffer = &mut self.rollback_buffer;
219
220 if prv_len > shared_len {
221 prv_path[shared_len as usize..prv_len as usize].ser_rollback(buffer);
222 (((prv_len - shared_len) / 2) as u8).ser_rollback(buffer);
223 DiffOperation::NavigateDown.ser_rollback(buffer);
224 }
225
226 if new_len > shared_len {
227 if shared_len == 0 {
228 DiffOperation::NavigateReset.ser_rollback(buffer);
229 } else {
230 (((new_len - shared_len) / 2) as u8).ser_rollback(buffer);
231 DiffOperation::NavigateUp.ser_rollback(buffer);
232 }
233 }
234
235 self.rollback_prv_path = Some(new_path.clone());
236 }
237 } else {
238 self.rollback_prv_path = Some(new_path.clone());
239 }
240 }
241
242 #[cfg(feature = "server")]
243 fn ser_tx_navigate_to(tx_data: &mut TxData, new_path: &Rc<Vec<usize32>>) {
244 if let Some(shared_len) = find_first_mismatch(&tx_data.cur_path, &new_path) {
245 let cur_len = tx_data.cur_path.len();
246 let new_len = new_path.len();
247 let buffer = &mut tx_data.buffer;
248
249 if cur_len > shared_len {
250 if shared_len == 0 {
251 DiffOperation::NavigateReset.ser_tx(buffer);
252 } else {
253 DiffOperation::NavigateUp.ser_tx(buffer);
254 (((cur_len - shared_len) / 2) as u8).ser_rollback(buffer);
255 }
256 }
257
258 if new_len > shared_len {
259 DiffOperation::NavigateDown.ser_tx(buffer);
260 (((new_len - shared_len) / 2) as u8).ser_tx(buffer);
261 new_path[shared_len as usize..new_len as usize].ser_tx(buffer);
262 }
263
264 tx_data.cur_path = new_path.clone();
265 }
266 }
267}
268
269fn find_first_mismatch<T: PartialEq>(vec1: &[T], vec2: &[T]) -> Option<usize> {
270 for (i, (level1, level2)) in vec1.chunks(2).zip(vec2.chunks(2)).enumerate() {
273 if level1[0] != level2[0] || level1[1] != level2[1] {
274 return Some(i * 2);
275 }
276 }
277
278 if vec1.len() != vec2.len() {
279 return Some(vec1.len().min(vec2.len()));
280 }
281
282 None
283}