valence_entity/
tracked_data.rs1use bevy_ecs::prelude::*;
2use tracing::warn;
3use valence_protocol::Encode;
4
5#[derive(Component, Default, Debug)]
10pub struct TrackedData {
11 init_data: Vec<u8>,
12 init_entries: Vec<(u8, u32)>,
15 update_data: Vec<u8>,
16}
17
18impl TrackedData {
19 pub fn init_data(&self) -> Option<&[u8]> {
25 (self.init_data.len() > 1).then_some(&self.init_data)
26 }
27
28 pub fn update_data(&self) -> Option<&[u8]> {
34 (self.update_data.len() > 1).then_some(&self.update_data)
35 }
36
37 pub fn insert_init_value<V: Encode>(&mut self, index: u8, type_id: u8, value: V) {
38 debug_assert!(
39 index != 0xff,
40 "index of 0xff is reserved for the terminator"
41 );
42
43 self.remove_init_value(index);
44
45 self.init_data.pop(); let len_before = self.init_data.len();
49
50 self.init_data.extend_from_slice(&[index, type_id]);
51 if let Err(e) = value.encode(&mut self.init_data) {
52 warn!("failed to encode initial tracked data: {e:#}");
53 }
54
55 let len = self.init_data.len() - len_before;
56
57 self.init_entries.push((index, len as u32));
58
59 self.init_data.push(0xff); }
61
62 pub fn remove_init_value(&mut self, index: u8) -> bool {
63 let mut start = 0;
64
65 for (pos, &(idx, len)) in self.init_entries.iter().enumerate() {
66 if idx == index {
67 let end = start + len as usize;
68
69 self.init_data.drain(start..end);
70 self.init_entries.remove(pos);
71
72 return true;
73 }
74
75 start += len as usize;
76 }
77
78 false
79 }
80
81 pub fn append_update_value<V: Encode>(&mut self, index: u8, type_id: u8, value: V) {
82 debug_assert!(
83 index != 0xff,
84 "index of 0xff is reserved for the terminator"
85 );
86
87 self.update_data.pop(); self.update_data.extend_from_slice(&[index, type_id]);
90 if let Err(e) = value.encode(&mut self.update_data) {
91 warn!("failed to encode updated tracked data: {e:#}");
92 }
93
94 self.update_data.push(0xff); }
96
97 pub fn clear_update_values(&mut self) {
98 self.update_data.clear();
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[test]
107 fn insert_remove_init_tracked_data() {
108 let mut td = TrackedData::default();
109
110 td.insert_init_value(0, 3, "foo");
111 td.insert_init_value(10, 6, "bar");
112 td.insert_init_value(5, 9, "baz");
113
114 assert!(td.remove_init_value(10));
115 assert!(!td.remove_init_value(10));
116
117 td.insert_init_value(0, 64, "quux");
119
120 assert!(td.remove_init_value(0));
121 assert!(td.remove_init_value(5));
122
123 assert!(td.init_data.as_slice().is_empty() || td.init_data.as_slice() == [0xff]);
124 assert!(td.init_data().is_none());
125
126 assert!(td.update_data.is_empty());
127 }
128}