1#![doc = include_str!("../README.md")]
2
3use bevy_app::prelude::*;
4use bevy_ecs::prelude::*;
5mod interaction_broadcast;
6pub use interaction_broadcast::EquipmentInteractionBroadcast;
7mod inventory_sync;
8use chunkedge_server::client::{Client, FlushPacketsSet, LoadEntityForClientEvent};
9use chunkedge_server::entity::living::LivingEntity;
10use chunkedge_server::entity::{EntityId, EntityLayerId, Position};
11use chunkedge_server::protocol::packets::play::set_equipment_s2c::{EquipmentEntry, EquipmentSlot};
12use chunkedge_server::protocol::packets::play::SetEquipmentS2c;
13use chunkedge_server::protocol::WritePacket;
14use chunkedge_server::{EntityLayer, ItemStack, Layer};
15pub use inventory_sync::EquipmentInventorySync;
16
17pub struct EquipmentPlugin;
18
19impl Plugin for EquipmentPlugin {
20 fn build(&self, app: &mut App) {
21 app.add_systems(
22 PreUpdate,
23 (
24 on_entity_init,
25 interaction_broadcast::start_interaction,
26 interaction_broadcast::stop_interaction,
27 inventory_sync::on_attach_inventory_sync,
28 inventory_sync::equipment_inventory_sync,
29 inventory_sync::equipment_held_item_sync_from_client,
30 ),
31 )
32 .add_systems(
33 PostUpdate,
34 (
35 update_equipment.before(FlushPacketsSet),
36 on_entity_load.before(FlushPacketsSet),
37 ),
38 )
39 .add_event::<EquipmentChangeEvent>();
40 }
41}
42
43#[derive(Debug, Default, Clone, Component)]
51pub struct Equipment {
52 equipment: [ItemStack; Self::SLOT_COUNT],
53 #[doc(hidden)]
55 pub(crate) changed: u8,
56}
57
58#[allow(clippy::large_stack_arrays)] impl Equipment {
60 pub const SLOT_COUNT: usize = EquipmentSlot::number_of_members();
61
62 #[allow(clippy::too_many_arguments)]
63 pub fn new(
64 main_hand: ItemStack,
65 off_hand: ItemStack,
66 boots: ItemStack,
67 leggings: ItemStack,
68 chestplate: ItemStack,
69 helmet: ItemStack,
70 body: ItemStack,
71 saddle: ItemStack,
72 ) -> Self {
73 let equipment = [
74 main_hand, off_hand, boots, leggings, chestplate, helmet, body, saddle,
75 ];
76 Self {
77 equipment,
78 changed: 0,
79 }
80 }
81
82 pub fn new_empty() -> Self {
83 let equipment = [ItemStack::EMPTY; 8];
84 Self {
85 equipment,
86 changed: 0,
87 }
88 }
89
90 pub fn slot(&self, idx: EquipmentSlot) -> &ItemStack {
91 &self.equipment[idx as usize]
92 }
93
94 pub fn set_slot(&mut self, idx: EquipmentSlot, item: ItemStack) {
95 if self.equipment[idx as usize] != item {
96 self.equipment[idx as usize] = item;
97 self.changed |= 1 << idx as u8;
98 }
99 }
100
101 pub fn main_hand(&self) -> &ItemStack {
102 self.slot(EquipmentSlot::MainHand)
103 }
104
105 pub fn off_hand(&self) -> &ItemStack {
106 self.slot(EquipmentSlot::OffHand)
107 }
108
109 pub fn feet(&self) -> &ItemStack {
110 self.slot(EquipmentSlot::Boots)
111 }
112
113 pub fn legs(&self) -> &ItemStack {
114 self.slot(EquipmentSlot::Leggings)
115 }
116
117 pub fn chest(&self) -> &ItemStack {
118 self.slot(EquipmentSlot::Chestplate)
119 }
120
121 pub fn head(&self) -> &ItemStack {
122 self.slot(EquipmentSlot::Helmet)
123 }
124
125 pub fn body(&self) -> &ItemStack {
126 self.slot(EquipmentSlot::Body)
127 }
128
129 pub fn saddle(&self) -> &ItemStack {
130 self.slot(EquipmentSlot::Saddle)
131 }
132
133 pub fn set_main_hand(&mut self, item: ItemStack) {
134 self.set_slot(EquipmentSlot::MainHand, item);
135 }
136
137 pub fn set_off_hand(&mut self, item: ItemStack) {
138 self.set_slot(EquipmentSlot::OffHand, item);
139 }
140
141 pub fn set_feet(&mut self, item: ItemStack) {
142 self.set_slot(EquipmentSlot::Boots, item);
143 }
144
145 pub fn set_legs(&mut self, item: ItemStack) {
146 self.set_slot(EquipmentSlot::Leggings, item);
147 }
148
149 pub fn set_chest(&mut self, item: ItemStack) {
150 self.set_slot(EquipmentSlot::Chestplate, item);
151 }
152
153 pub fn set_head(&mut self, item: ItemStack) {
154 self.set_slot(EquipmentSlot::Helmet, item);
155 }
156
157 pub fn set_body(&mut self, item: ItemStack) {
158 self.set_slot(EquipmentSlot::Body, item);
159 }
160
161 pub fn set_saddle(&mut self, item: ItemStack) {
162 self.set_slot(EquipmentSlot::Saddle, item);
163 }
164
165 pub fn clear(&mut self) {
166 self.equipment
167 .iter_mut()
168 .for_each(|itm| *itm = ItemStack::EMPTY);
169 self.changed = u8::MAX
170 }
171
172 pub fn is_default(&self) -> bool {
173 self.equipment.iter().all(|item| item.is_empty())
174 }
175}
176
177#[derive(Debug, Clone)]
178pub struct EquipmentSlotChange {
179 idx: u8,
180 stack: ItemStack,
181}
182
183#[derive(Debug, Clone, Event)]
184pub struct EquipmentChangeEvent {
185 pub client: Entity,
186 pub changed: Vec<EquipmentSlotChange>,
187}
188
189fn update_equipment(
190 mut clients: Query<
191 (Entity, &EntityId, &EntityLayerId, &Position, &mut Equipment),
192 Changed<Equipment>,
193 >,
194 mut event_writer: EventWriter<EquipmentChangeEvent>,
195 mut entity_layer: Query<&mut EntityLayer>,
196) {
197 for (entity, entity_id, entity_layer_id, position, mut equipment) in &mut clients {
198 let Ok(mut entity_layer) = entity_layer.get_mut(entity_layer_id.0) else {
199 continue;
200 };
201
202 if equipment.changed != 0 {
203 let mut slots_changed: Vec<EquipmentSlotChange> =
204 Vec::with_capacity(Equipment::SLOT_COUNT);
205
206 for slot in 0..Equipment::SLOT_COUNT {
207 if equipment.changed & (1 << slot) != 0 {
208 slots_changed.push(EquipmentSlotChange {
209 idx: slot as u8,
210 stack: equipment.equipment[slot].clone(),
211 });
212 }
213 }
214
215 entity_layer
216 .view_except_writer(position.0, entity)
217 .write_packet(&SetEquipmentS2c {
218 entity_id: entity_id.get().into(),
219 equipment: slots_changed
220 .iter()
221 .map(|change| EquipmentEntry {
222 slot: change.idx.into(),
223 item: change.stack.clone(),
224 })
225 .collect(),
226 });
227
228 event_writer.send(EquipmentChangeEvent {
229 client: entity,
230 changed: slots_changed,
231 });
232
233 equipment.changed = 0;
234 }
235 }
236}
237
238fn on_entity_load(
241 mut clients: Query<&mut Client>,
242 entities: Query<(&EntityId, &Equipment)>,
243 mut events: EventReader<LoadEntityForClientEvent>,
244) {
245 for event in events.read() {
246 let Ok(mut client) = clients.get_mut(event.client) else {
247 continue;
248 };
249
250 let Ok((entity_id, equipment)) = entities.get(event.entity_loaded) else {
251 continue;
252 };
253
254 if equipment.is_default() {
255 continue;
256 }
257
258 let mut entries: Vec<EquipmentEntry> = Vec::with_capacity(Equipment::SLOT_COUNT);
259 for (idx, stack) in equipment.equipment.iter().enumerate() {
260 entries.push(EquipmentEntry {
261 slot: (idx as u8).into(),
262 item: stack.clone(),
263 });
264 }
265
266 client.write_packet(&SetEquipmentS2c {
267 entity_id: entity_id.get().into(),
268 equipment: entries,
269 });
270 }
271}
272
273fn on_entity_init(
276 mut commands: Commands,
277 mut entities: Query<Entity, (Added<LivingEntity>, Without<Equipment>)>,
278) {
279 for entity in &mut entities {
280 commands.entity(entity).insert(Equipment::default());
281 }
282}