1use std::mem;
2
3use bevy_ecs::prelude::DetectChanges;
4use bevy_ecs::query::QueryData;
5use bevy_ecs::world::Ref;
6use valence_math::DVec3;
7use valence_protocol::encode::WritePacket;
8use valence_protocol::packets::play::{
9 EntityAnimationS2c, EntityAttributesS2c, EntityPositionS2c, EntitySetHeadYawS2c,
10 EntitySpawnS2c, EntityStatusS2c, EntityTrackerUpdateS2c, EntityVelocityUpdateS2c,
11 ExperienceOrbSpawnS2c, MoveRelativeS2c, PlayerSpawnS2c, RotateAndMoveRelativeS2c, RotateS2c,
12};
13use valence_protocol::var_int::VarInt;
14use valence_protocol::ByteAngle;
15use valence_server_common::UniqueId;
16
17use crate::attributes::TrackedEntityAttributes;
18use crate::tracked_data::TrackedData;
19use crate::{
20 EntityAnimations, EntityId, EntityKind, EntityLayerId, EntityStatuses, HeadYaw, Look,
21 ObjectData, OldEntityLayerId, OldPosition, OnGround, Position, Velocity,
22};
23
24#[derive(QueryData)]
25pub struct EntityInitQuery {
26 pub entity_id: &'static EntityId,
27 pub uuid: &'static UniqueId,
28 pub kind: &'static EntityKind,
29 pub look: &'static Look,
30 pub head_yaw: &'static HeadYaw,
31 pub on_ground: &'static OnGround,
32 pub object_data: &'static ObjectData,
33 pub velocity: &'static Velocity,
34 pub tracked_data: &'static TrackedData,
35}
36
37impl EntityInitQueryItem<'_> {
38 pub fn write_init_packets<W: WritePacket>(&self, pos: DVec3, mut writer: W) {
42 match *self.kind {
43 EntityKind::MARKER => {}
44 EntityKind::EXPERIENCE_ORB => {
45 writer.write_packet(&ExperienceOrbSpawnS2c {
46 entity_id: self.entity_id.get().into(),
47 position: pos,
48 count: self.object_data.0 as i16,
49 });
50 }
51 EntityKind::PLAYER => {
52 writer.write_packet(&PlayerSpawnS2c {
53 entity_id: self.entity_id.get().into(),
54 player_uuid: self.uuid.0,
55 position: pos,
56 yaw: ByteAngle::from_degrees(self.look.yaw),
57 pitch: ByteAngle::from_degrees(self.look.pitch),
58 });
59
60 writer.write_packet(&EntitySetHeadYawS2c {
62 entity_id: self.entity_id.get().into(),
63 head_yaw: ByteAngle::from_degrees(self.head_yaw.0),
64 });
65 }
66 _ => writer.write_packet(&EntitySpawnS2c {
67 entity_id: self.entity_id.get().into(),
68 object_uuid: self.uuid.0,
69 kind: self.kind.get().into(),
70 position: pos,
71 pitch: ByteAngle::from_degrees(self.look.pitch),
72 yaw: ByteAngle::from_degrees(self.look.yaw),
73 head_yaw: ByteAngle::from_degrees(self.head_yaw.0),
74 data: self.object_data.0.into(),
75 velocity: self.velocity.to_packet_units(),
76 }),
77 }
78
79 if let Some(init_data) = self.tracked_data.init_data() {
80 writer.write_packet(&EntityTrackerUpdateS2c {
81 entity_id: self.entity_id.get().into(),
82 tracked_values: init_data.into(),
83 });
84 }
85 }
86}
87
88#[derive(QueryData)]
89pub struct UpdateEntityQuery {
90 pub id: &'static EntityId,
91 pub pos: &'static Position,
92 pub old_pos: &'static OldPosition,
93 pub loc: &'static EntityLayerId,
94 pub old_loc: &'static OldEntityLayerId,
95 pub look: Ref<'static, Look>,
96 pub head_yaw: Ref<'static, HeadYaw>,
97 pub on_ground: &'static OnGround,
98 pub velocity: Ref<'static, Velocity>,
99 pub tracked_data: &'static TrackedData,
100 pub statuses: &'static EntityStatuses,
101 pub animations: &'static EntityAnimations,
102 pub tracked_attributes: Option<&'static TrackedEntityAttributes>,
104}
105
106impl UpdateEntityQueryItem<'_> {
107 pub fn write_update_packets<W: WritePacket>(&self, mut writer: W) {
108 let entity_id = VarInt(self.id.get());
111
112 let position_delta = self.pos.0 - self.old_pos.get();
113 let needs_teleport = position_delta.abs().max_element() >= 8.0;
114 let changed_position = self.pos.0 != self.old_pos.get();
115
116 if changed_position && !needs_teleport && self.look.is_changed() {
117 writer.write_packet(&RotateAndMoveRelativeS2c {
118 entity_id,
119 delta: (position_delta * 4096.0).to_array().map(|v| v as i16),
120 yaw: ByteAngle::from_degrees(self.look.yaw),
121 pitch: ByteAngle::from_degrees(self.look.pitch),
122 on_ground: self.on_ground.0,
123 });
124 } else {
125 if changed_position && !needs_teleport {
126 writer.write_packet(&MoveRelativeS2c {
127 entity_id,
128 delta: (position_delta * 4096.0).to_array().map(|v| v as i16),
129 on_ground: self.on_ground.0,
130 });
131 }
132
133 if self.look.is_changed() {
134 writer.write_packet(&RotateS2c {
135 entity_id,
136 yaw: ByteAngle::from_degrees(self.look.yaw),
137 pitch: ByteAngle::from_degrees(self.look.pitch),
138 on_ground: self.on_ground.0,
139 });
140 }
141 }
142
143 if needs_teleport {
144 writer.write_packet(&EntityPositionS2c {
145 entity_id,
146 position: self.pos.0,
147 yaw: ByteAngle::from_degrees(self.look.yaw),
148 pitch: ByteAngle::from_degrees(self.look.pitch),
149 on_ground: self.on_ground.0,
150 });
151 }
152
153 if self.velocity.is_changed() {
154 writer.write_packet(&EntityVelocityUpdateS2c {
155 entity_id,
156 velocity: self.velocity.to_packet_units(),
157 });
158 }
159
160 if self.head_yaw.is_changed() {
161 writer.write_packet(&EntitySetHeadYawS2c {
162 entity_id,
163 head_yaw: ByteAngle::from_degrees(self.head_yaw.0),
164 });
165 }
166
167 if let Some(update_data) = self.tracked_data.update_data() {
168 writer.write_packet(&EntityTrackerUpdateS2c {
169 entity_id,
170 tracked_values: update_data.into(),
171 });
172 }
173
174 if self.statuses.0 != 0 {
175 for i in 0..mem::size_of_val(self.statuses) {
176 if (self.statuses.0 >> i) & 1 == 1 {
177 writer.write_packet(&EntityStatusS2c {
178 entity_id: entity_id.0,
179 entity_status: i as u8,
180 });
181 }
182 }
183 }
184
185 if self.animations.0 != 0 {
186 for i in 0..mem::size_of_val(self.animations) {
187 if (self.animations.0 >> i) & 1 == 1 {
188 writer.write_packet(&EntityAnimationS2c {
189 entity_id,
190 animation: i as u8,
191 });
192 }
193 }
194 }
195
196 if let Some(attributes) = self.tracked_attributes {
197 let properties = attributes.get_properties();
198
199 if !properties.is_empty() {
200 writer.write_packet(&EntityAttributesS2c {
201 entity_id,
202 properties,
203 });
204 }
205 }
206 }
207}