valence_entity/
query.rs

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    /// Writes the appropriate packets to initialize an entity. This will spawn
39    /// the entity and initialize tracked data. `pos` is the initial position of
40    /// the entity.
41    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                // Player spawn packet doesn't include head yaw for some reason.
61                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    // Option because not all entities have attributes, only LivingEntity.
103    pub tracked_attributes: Option<&'static TrackedEntityAttributes>,
104}
105
106impl UpdateEntityQueryItem<'_> {
107    pub fn write_update_packets<W: WritePacket>(&self, mut writer: W) {
108        // TODO: @RJ I saw you're using UpdateEntityPosition and UpdateEntityRotation sometimes. These two packets are actually broken on the client and will erase previous position/rotation https://bugs.mojang.com/browse/MC-255263 -Moulberry
109
110        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}