1use valence_inventory::player_inventory::PlayerInventory;
2use valence_inventory::{HeldItem, Inventory, UpdateSelectedSlotEvent};
3use valence_server::entity::player::PlayerEntity;
45use super::*;
67/// This component will sync a player's [`Equipment`], which is visible to other
8/// players, with the player [`Inventory`].
9#[derive(Debug, Default, Clone, Component)]
10pub struct EquipmentInventorySync;
1112/// Syncs the player [`Equipment`] with the [`Inventory`].
13/// If a change in the player's inventory and in the equipment occurs in the
14/// same tick, the equipment change has priority.
15/// Note: This system only handles direct changes to the held item (not actual
16/// changes from the client) see [`equipment_held_item_sync_from_client`]
17pub(crate) fn equipment_inventory_sync(
18mut clients: Query<
19 (&mut Equipment, &mut Inventory, &mut HeldItem),
20 (
21 Or<(Changed<Equipment>, Changed<Inventory>, Changed<HeldItem>)>,
22 With<EquipmentInventorySync>,
23 With<PlayerEntity>,
24 ),
25 >,
26) {
27for (mut equipment, mut inventory, held_item) in &mut clients {
28// Equipment change has priority over held item changes
29if equipment.changed & (1 << Equipment::MAIN_HAND_IDX) != 0 {
30let item = equipment.main_hand().clone();
31 inventory.set_slot(held_item.slot(), item);
32 } else {
33// If we change the inventory (e.g by pickung up an item)
34 // then the HeldItem slot wont be changed
3536 // This will only be called if we change the held item from valence,
37 // the client change is handled in `equipment_held_item_sync_from_client`
38let item = inventory.slot(held_item.slot()).clone();
39 equipment.set_main_hand(item);
40 }
4142let slots = [
43 (Equipment::OFF_HAND_IDX, PlayerInventory::SLOT_OFFHAND),
44 (Equipment::HEAD_IDX, PlayerInventory::SLOT_HEAD),
45 (Equipment::CHEST_IDX, PlayerInventory::SLOT_CHEST),
46 (Equipment::LEGS_IDX, PlayerInventory::SLOT_LEGS),
47 (Equipment::FEET_IDX, PlayerInventory::SLOT_FEET),
48 ];
4950// We cant rely on the changed bitfield of inventory here
51 // because that gets reset when the client changes the inventory
5253for (equipment_slot, inventory_slot) in slots {
54// Equipment has priority over inventory changes
55if equipment.changed & (1 << equipment_slot) != 0 {
56let item = equipment.slot(equipment_slot).clone();
57 inventory.set_slot(inventory_slot, item);
58 } else if inventory.is_changed() {
59let item = inventory.slot(inventory_slot).clone();
60 equipment.set_slot(equipment_slot, item);
61 }
62 }
63 }
64}
6566/// Handles the case where the client changes the slot (the bevy change is
67/// suppressed for this)
68pub(crate) fn equipment_held_item_sync_from_client(
69mut clients: Query<(&HeldItem, &Inventory, &mut Equipment), With<EquipmentInventorySync>>,
70mut events: EventReader<UpdateSelectedSlotEvent>,
71) {
72for event in events.read() {
73let Ok((held_item, inventory, mut equipment)) = clients.get_mut(event.client) else {
74continue;
75 };
7677let item = inventory.slot(held_item.slot()).clone();
78 equipment.set_main_hand(item);
79 }
80}
8182pub(crate) fn on_attach_inventory_sync(
83 entities: Query<Option<&PlayerEntity>, (Added<EquipmentInventorySync>, With<Inventory>)>,
84) {
85for entity in &entities {
86if entity.is_none() {
87tracing::warn!(
88"EquipmentInventorySync attached to non-player entity, this will have no effect"
89);
90 }
91 }
92}