1use bevy_ecs::prelude::*;
2use tracing::warn;
3use valence_protocol::Encode;
45/// Cache for all the tracked data of an entity. Used for the
6/// [`EntityTrackerUpdateS2c`][packet] packet.
7///
8/// [packet]: valence_protocol::packets::play::EntityTrackerUpdateS2c
9#[derive(Component, Default, Debug)]
10pub struct TrackedData {
11 init_data: Vec<u8>,
12/// A map of tracked data indices to the byte length of the entry in
13 /// `init_data`.
14init_entries: Vec<(u8, u32)>,
15 update_data: Vec<u8>,
16}
1718impl TrackedData {
19/// Returns initial tracked data for the entity, ready to be sent in the
20 /// [`EntityTrackerUpdateS2c`][packet] packet. This is used when the entity
21 /// enters the view of a client.
22 ///
23 /// [packet]: valence_protocol::packets::play::EntityTrackerUpdateS2c
24pub fn init_data(&self) -> Option<&[u8]> {
25 (self.init_data.len() > 1).then_some(&self.init_data)
26 }
2728/// Contains updated tracked data for the entity, ready to be sent in the
29 /// [`EntityTrackerUpdateS2c`][packet] packet. This is used when tracked
30 /// data is changed and the client is already in view of the entity.
31 ///
32 /// [packet]: valence_protocol::packets::play::EntityTrackerUpdateS2c
33pub fn update_data(&self) -> Option<&[u8]> {
34 (self.update_data.len() > 1).then_some(&self.update_data)
35 }
3637pub fn insert_init_value<V: Encode>(&mut self, index: u8, type_id: u8, value: V) {
38debug_assert!(
39 index != 0xff,
40"index of 0xff is reserved for the terminator"
41);
4243self.remove_init_value(index);
4445self.init_data.pop(); // Remove terminator.
4647 // Append the new value to the end.
48let len_before = self.init_data.len();
4950self.init_data.extend_from_slice(&[index, type_id]);
51if let Err(e) = value.encode(&mut self.init_data) {
52warn!("failed to encode initial tracked data: {e:#}");
53 }
5455let len = self.init_data.len() - len_before;
5657self.init_entries.push((index, len as u32));
5859self.init_data.push(0xff); // Add terminator.
60}
6162pub fn remove_init_value(&mut self, index: u8) -> bool {
63let mut start = 0;
6465for (pos, &(idx, len)) in self.init_entries.iter().enumerate() {
66if idx == index {
67let end = start + len as usize;
6869self.init_data.drain(start..end);
70self.init_entries.remove(pos);
7172return true;
73 }
7475 start += len as usize;
76 }
7778false
79}
8081pub fn append_update_value<V: Encode>(&mut self, index: u8, type_id: u8, value: V) {
82debug_assert!(
83 index != 0xff,
84"index of 0xff is reserved for the terminator"
85);
8687self.update_data.pop(); // Remove terminator.
8889self.update_data.extend_from_slice(&[index, type_id]);
90if let Err(e) = value.encode(&mut self.update_data) {
91warn!("failed to encode updated tracked data: {e:#}");
92 }
9394self.update_data.push(0xff); // Add terminator.
95}
9697pub fn clear_update_values(&mut self) {
98self.update_data.clear();
99 }
100}
101102#[cfg(test)]
103mod tests {
104use super::*;
105106#[test]
107fn insert_remove_init_tracked_data() {
108let mut td = TrackedData::default();
109110 td.insert_init_value(0, 3, "foo");
111 td.insert_init_value(10, 6, "bar");
112 td.insert_init_value(5, 9, "baz");
113114assert!(td.remove_init_value(10));
115assert!(!td.remove_init_value(10));
116117// Insertion overwrites value at index 0.
118td.insert_init_value(0, 64, "quux");
119120assert!(td.remove_init_value(0));
121assert!(td.remove_init_value(5));
122123assert!(td.init_data.as_slice().is_empty() || td.init_data.as_slice() == [0xff]);
124assert!(td.init_data().is_none());
125126assert!(td.update_data.is_empty());
127 }
128}