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 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 pub tracked_attributes: Option<&'static TrackedEntityAttributes>,
81}
82
83impl UpdateEntityQueryItem<'_> {
84 pub fn write_update_packets<W: WritePacket>(&self, mut writer: W) {
85 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}