refactor held item into a new component (#356)

## Description

This moves the `held_item_slot` field in `ClientInventoryState` to a new
component: `HeldItem`

related: pr #355
This commit is contained in:
Carson McManus 2023-06-07 19:31:49 -04:00 committed by GitHub
parent 6338fc6300
commit e76e913b3c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 45 additions and 33 deletions

View file

@ -1,6 +1,6 @@
#![allow(clippy::type_complexity)] #![allow(clippy::type_complexity)]
use valence::inventory::ClientInventoryState; use valence::inventory::HeldItem;
use valence::prelude::*; use valence::prelude::*;
use valence_client::interact_block::InteractBlockEvent; use valence_client::interact_block::InteractBlockEvent;
@ -109,14 +109,14 @@ fn digging_survival_mode(
} }
fn place_blocks( fn place_blocks(
mut clients: Query<(&mut Inventory, &GameMode, &ClientInventoryState)>, mut clients: Query<(&mut Inventory, &GameMode, &HeldItem)>,
mut instances: Query<&mut Instance>, mut instances: Query<&mut Instance>,
mut events: EventReader<InteractBlockEvent>, mut events: EventReader<InteractBlockEvent>,
) { ) {
let mut instance = instances.single_mut(); let mut instance = instances.single_mut();
for event in events.iter() { for event in events.iter() {
let Ok((mut inventory, game_mode, inv_state)) = clients.get_mut(event.client) else { let Ok((mut inventory, game_mode, held)) = clients.get_mut(event.client) else {
continue; continue;
}; };
if event.hand != Hand::Main { if event.hand != Hand::Main {
@ -124,7 +124,7 @@ fn place_blocks(
} }
// get the held item // get the held item
let slot_id = inv_state.held_item_slot(); let slot_id = held.slot();
let Some(stack) = inventory.slot(slot_id) else { let Some(stack) = inventory.slot(slot_id) else {
// no item in the slot // no item in the slot
continue; continue;

View file

@ -6,8 +6,8 @@ use valence_inventory::packet::{
OpenScreenS2c, ScreenHandlerSlotUpdateS2c, SlotChange, UpdateSelectedSlotC2s, OpenScreenS2c, ScreenHandlerSlotUpdateS2c, SlotChange, UpdateSelectedSlotC2s,
}; };
use valence_inventory::{ use valence_inventory::{
convert_to_player_slot_id, ClientInventoryState, CursorItem, DropItemStack, Inventory, convert_to_player_slot_id, ClientInventoryState, CursorItem, DropItemStack, HeldItem,
InventoryKind, OpenInventory, Inventory, InventoryKind, OpenInventory,
}; };
use super::*; use super::*;
@ -474,12 +474,12 @@ fn test_should_handle_set_held_item() {
app.update(); app.update();
// Make assertions // Make assertions
let inv_state = app let held = app
.world .world
.get::<ClientInventoryState>(client_ent) .get::<HeldItem>(client_ent)
.expect("could not find client"); .expect("could not find client");
assert_eq!(inv_state.held_item_slot(), 40); assert_eq!(held.slot(), 40);
} }
#[test] #[test]
@ -602,11 +602,11 @@ mod dropping_items {
app.update(); app.update();
// Make assertions // Make assertions
let inv_state = app let held = app
.world .world
.get::<ClientInventoryState>(client_ent) .get::<HeldItem>(client_ent)
.expect("could not find client"); .expect("could not find client");
assert_eq!(inv_state.held_item_slot(), 36); assert_eq!(held.slot(), 36);
let inventory = app let inventory = app
.world .world
.get::<Inventory>(client_ent) .get::<Inventory>(client_ent)

View file

@ -337,15 +337,9 @@ pub struct ClientInventoryState {
/// on the `CursorItem` component to make maintaining accurate change /// on the `CursorItem` component to make maintaining accurate change
/// detection for end users easier. /// detection for end users easier.
client_updated_cursor_item: bool, client_updated_cursor_item: bool,
// TODO: make this a separate modifiable component.
held_item_slot: u16,
} }
impl ClientInventoryState { impl ClientInventoryState {
pub fn held_item_slot(&self) -> u16 {
self.held_item_slot
}
#[doc(hidden)] #[doc(hidden)]
pub fn window_id(&self) -> u8 { pub fn window_id(&self) -> u8 {
self.window_id self.window_id
@ -357,6 +351,20 @@ impl ClientInventoryState {
} }
} }
/// Indicates which hotbar slot the player is currently holding.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Component)]
pub struct HeldItem {
held_item_slot: u16,
}
impl HeldItem {
/// The slot ID of the currently held item, in the range 36-44 inclusive.
/// This value is safe to use on the player's inventory directly.
pub fn slot(&self) -> u16 {
self.held_item_slot
}
}
/// The item stack that the client thinks it's holding under the mouse /// The item stack that the client thinks it's holding under the mouse
/// cursor. /// cursor.
#[derive(Component, Clone, PartialEq, Default, Debug)] #[derive(Component, Clone, PartialEq, Default, Debug)]
@ -533,6 +541,8 @@ fn init_new_client_inventories(clients: Query<Entity, Added<Client>>, mut comman
state_id: Wrapping(0), state_id: Wrapping(0),
slots_changed: 0, slots_changed: 0,
client_updated_cursor_item: false, client_updated_cursor_item: false,
},
HeldItem {
// First slot of the hotbar. // First slot of the hotbar.
held_item_slot: 36, held_item_slot: 36,
}, },
@ -1039,43 +1049,42 @@ fn handle_click_slot(
fn handle_player_actions( fn handle_player_actions(
mut packets: EventReader<PacketEvent>, mut packets: EventReader<PacketEvent>,
mut clients: Query<(&mut Inventory, &mut ClientInventoryState)>, mut clients: Query<(&mut Inventory, &mut ClientInventoryState, &HeldItem)>,
mut drop_item_stack_events: EventWriter<DropItemStack>, mut drop_item_stack_events: EventWriter<DropItemStack>,
) { ) {
for packet in packets.iter() { for packet in packets.iter() {
if let Some(pkt) = packet.decode::<PlayerActionC2s>() { if let Some(pkt) = packet.decode::<PlayerActionC2s>() {
match pkt.action { match pkt.action {
PlayerAction::DropAllItems => { PlayerAction::DropAllItems => {
if let Ok((mut inv, mut inv_state)) = clients.get_mut(packet.client) { if let Ok((mut inv, mut inv_state, &held)) = clients.get_mut(packet.client) {
if let Some(stack) = inv.replace_slot(inv_state.held_item_slot, None) { if let Some(stack) = inv.replace_slot(held.slot(), None) {
inv_state.slots_changed |= 1 << inv_state.held_item_slot; inv_state.slots_changed |= 1 << held.slot();
drop_item_stack_events.send(DropItemStack { drop_item_stack_events.send(DropItemStack {
client: packet.client, client: packet.client,
from_slot: Some(inv_state.held_item_slot), from_slot: Some(held.slot()),
stack, stack,
}); });
} }
} }
} }
PlayerAction::DropItem => { PlayerAction::DropItem => {
if let Ok((mut inv, mut inv_state)) = clients.get_mut(packet.client) { if let Ok((mut inv, mut inv_state, held)) = clients.get_mut(packet.client) {
if let Some(mut stack) = inv.replace_slot(inv_state.held_item_slot(), None) if let Some(mut stack) = inv.replace_slot(held.slot(), None) {
{
if stack.count() > 1 { if stack.count() > 1 {
inv.set_slot( inv.set_slot(
inv_state.held_item_slot(), held.slot(),
stack.clone().with_count(stack.count() - 1), stack.clone().with_count(stack.count() - 1),
); );
stack.set_count(1); stack.set_count(1);
} }
inv_state.slots_changed |= 1 << inv_state.held_item_slot(); inv_state.slots_changed |= 1 << held.slot();
drop_item_stack_events.send(DropItemStack { drop_item_stack_events.send(DropItemStack {
client: packet.client, client: packet.client,
from_slot: Some(inv_state.held_item_slot()), from_slot: Some(held.slot()),
stack, stack,
}) })
} }
@ -1169,14 +1178,17 @@ pub struct UpdateSelectedSlot {
fn handle_update_selected_slot( fn handle_update_selected_slot(
mut packets: EventReader<PacketEvent>, mut packets: EventReader<PacketEvent>,
mut clients: Query<&mut ClientInventoryState>, mut clients: Query<&mut HeldItem>,
mut events: EventWriter<UpdateSelectedSlot>, mut events: EventWriter<UpdateSelectedSlot>,
) { ) {
for packet in packets.iter() { for packet in packets.iter() {
if let Some(pkt) = packet.decode::<UpdateSelectedSlotC2s>() { if let Some(pkt) = packet.decode::<UpdateSelectedSlotC2s>() {
if let Ok(mut inv_state) = clients.get_mut(packet.client) { if let Ok(mut held) = clients.get_mut(packet.client) {
// TODO: validate this. if pkt.slot < 0 || pkt.slot > 8 {
inv_state.held_item_slot = convert_hotbar_slot_id(pkt.slot as u16); // The client is trying to interact with a slot that does not exist, ignore.
continue;
}
held.held_item_slot = convert_hotbar_slot_id(pkt.slot as u16);
events.send(UpdateSelectedSlot { events.send(UpdateSelectedSlot {
client: packet.client, client: packet.client,