chunkedge_entity/
query.rs

1use std::mem;
2
3use bevy_ecs::prelude::DetectChanges;
4use bevy_ecs::query::QueryData;
5use bevy_ecs::world::Ref;
6use chunkedge_math::DVec3;
7use chunkedge_protocol::encode::WritePacket;
8use chunkedge_protocol::packets::play::{
9    AddEntityS2c, AnimateS2c, EntityEventS2c, EntityPositionSyncS2c, MoveEntityPosRotS2c,
10    MoveEntityPosS2c, MoveEntityRotS2c, RotateHeadS2c, SetEntityDataS2c, SetEntityMotionS2c,
11    UpdateAttributesS2c,
12};
13use chunkedge_protocol::{ByteAngle, VarInt};
14use chunkedge_server_common::UniqueId;
15
16use crate::attributes::TrackedEntityAttributes;
17use crate::tracked_data::TrackedData;
18use crate::{
19    EntityAnimations, EntityId, EntityKind, EntityLayerId, EntityStatuses, HeadYaw, Look,
20    ObjectData, OldEntityLayerId, OldPosition, OnGround, Position, Velocity,
21};
22
23#[derive(QueryData)]
24pub struct EntityInitQuery {
25    pub entity_id: &'static EntityId,
26    pub uuid: &'static UniqueId,
27    pub kind: &'static EntityKind,
28    pub look: &'static Look,
29    pub head_yaw: &'static HeadYaw,
30    pub on_ground: &'static OnGround,
31    pub object_data: &'static ObjectData,
32    pub velocity: &'static Velocity,
33    pub tracked_data: &'static TrackedData,
34}
35
36impl EntityInitQueryItem<'_> {
37    /// Writes the appropriate packets to initialize an entity. This will spawn
38    /// the entity and initialize tracked data. `pos` is the initial position of
39    /// the entity.
40    pub fn write_init_packets<W: WritePacket>(&self, pos: DVec3, mut writer: W) {
41        match *self.kind {
42            EntityKind::MARKER => {}
43            _ => writer.write_packet(&AddEntityS2c {
44                entity_id: self.entity_id.get().into(),
45                object_uuid: self.uuid.0,
46                kind: self.kind.get().into(),
47                position: pos,
48                pitch: ByteAngle::from_degrees(self.look.pitch),
49                yaw: ByteAngle::from_degrees(self.look.yaw),
50                head_yaw: ByteAngle::from_degrees(self.head_yaw.0),
51                data: self.object_data.0.into(),
52                velocity: self.velocity.to_packet_units(),
53            }),
54        }
55
56        if let Some(init_data) = self.tracked_data.init_data() {
57            writer.write_packet(&SetEntityDataS2c {
58                entity_id: self.entity_id.get().into(),
59                tracked_values: init_data.into(),
60            });
61        }
62    }
63}
64
65#[derive(QueryData)]
66pub struct UpdateEntityQuery {
67    pub id: &'static EntityId,
68    pub pos: &'static Position,
69    pub old_pos: &'static OldPosition,
70    pub loc: &'static EntityLayerId,
71    pub old_loc: &'static OldEntityLayerId,
72    pub look: Ref<'static, Look>,
73    pub head_yaw: Ref<'static, HeadYaw>,
74    pub on_ground: &'static OnGround,
75    pub velocity: Ref<'static, Velocity>,
76    pub tracked_data: &'static TrackedData,
77    pub statuses: &'static EntityStatuses,
78    pub animations: &'static EntityAnimations,
79    // Option because not all entities have attributes, only LivingEntity.
80    pub tracked_attributes: Option<&'static TrackedEntityAttributes>,
81}
82
83impl UpdateEntityQueryItem<'_> {
84    pub fn write_update_packets<W: WritePacket>(&self, mut writer: W) {
85        // 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
86
87        let entity_id = VarInt(self.id.get());
88
89        let position_delta = self.pos.0 - self.old_pos.get();
90        let needs_teleport = position_delta.abs().max_element() >= 8.0;
91        let changed_position = self.pos.0 != self.old_pos.get();
92
93        if changed_position && !needs_teleport && self.look.is_changed() {
94            writer.write_packet(&MoveEntityPosRotS2c {
95                entity_id,
96                delta: (position_delta * 4096.0).to_array().map(|v| v as i16),
97                yaw: ByteAngle::from_degrees(self.look.yaw),
98                pitch: ByteAngle::from_degrees(self.look.pitch),
99                on_ground: self.on_ground.0,
100            });
101        } else {
102            if changed_position && !needs_teleport {
103                writer.write_packet(&MoveEntityPosS2c {
104                    entity_id,
105                    delta: (position_delta * 4096.0).to_array().map(|v| v as i16),
106                    on_ground: self.on_ground.0,
107                });
108            }
109
110            if self.look.is_changed() {
111                writer.write_packet(&MoveEntityRotS2c {
112                    entity_id,
113                    yaw: ByteAngle::from_degrees(self.look.yaw),
114                    pitch: ByteAngle::from_degrees(self.look.pitch),
115                    on_ground: self.on_ground.0,
116                });
117            }
118        }
119
120        if needs_teleport {
121            writer.write_packet(&EntityPositionSyncS2c {
122                entity_id,
123                position: self.pos.0,
124                velocity: self.velocity.0,
125                yaw: self.look.yaw,
126                pitch: self.look.pitch,
127                on_ground: self.on_ground.0,
128            });
129        }
130
131        if self.velocity.is_changed() {
132            writer.write_packet(&SetEntityMotionS2c {
133                entity_id,
134                velocity: self.velocity.to_packet_units(),
135            });
136        }
137
138        if self.head_yaw.is_changed() {
139            writer.write_packet(&RotateHeadS2c {
140                entity_id,
141                head_yaw: ByteAngle::from_degrees(self.head_yaw.0),
142            });
143        }
144
145        if let Some(update_data) = self.tracked_data.update_data() {
146            writer.write_packet(&SetEntityDataS2c {
147                entity_id,
148                tracked_values: update_data.into(),
149            });
150        }
151
152        if self.statuses.0 != 0 {
153            for i in 0..mem::size_of_val(self.statuses) {
154                if (self.statuses.0 >> i) & 1 == 1 {
155                    writer.write_packet(&EntityEventS2c {
156                        entity_id: entity_id.0,
157                        entity_status: i as u8,
158                    });
159                }
160            }
161        }
162
163        if self.animations.0 != 0 {
164            for i in 0..mem::size_of_val(self.animations) {
165                if (self.animations.0 >> i) & 1 == 1 {
166                    writer.write_packet(&AnimateS2c {
167                        entity_id,
168                        animation: i as u8,
169                    });
170                }
171            }
172        }
173
174        if let Some(attributes) = self.tracked_attributes {
175            let properties = attributes.get_properties();
176
177            if !properties.is_empty() {
178                writer.write_packet(&UpdateAttributesS2c {
179                    entity_id,
180                    properties,
181                });
182            }
183        }
184    }
185}