@@ -337,15 +337,9 @@ pub struct ClientInventoryState {
337337 /// on the `CursorItem` component to make maintaining accurate change
338338 /// detection for end users easier.
339339 client_updated_cursor_item : bool ,
340- // TODO: make this a separate modifiable component.
341- held_item_slot : u16 ,
342340}
343341
344342impl ClientInventoryState {
345- pub fn held_item_slot ( & self ) -> u16 {
346- self . held_item_slot
347- }
348-
349343 #[ doc( hidden) ]
350344 pub fn window_id ( & self ) -> u8 {
351345 self . window_id
@@ -357,6 +351,20 @@ impl ClientInventoryState {
357351 }
358352}
359353
354+ /// Indicates which hotbar slot the player is currently holding.
355+ #[ derive( Debug , Clone , Copy , PartialEq , Eq , Component ) ]
356+ pub struct HeldItem {
357+ held_item_slot : u16 ,
358+ }
359+
360+ impl HeldItem {
361+ /// The slot ID of the currently held item, in the range 36-44 inclusive.
362+ /// This value is safe to use on the player's inventory directly.
363+ pub fn slot ( & self ) -> u16 {
364+ self . held_item_slot
365+ }
366+ }
367+
360368/// The item stack that the client thinks it's holding under the mouse
361369/// cursor.
362370#[ derive( Component , Clone , PartialEq , Default , Debug ) ]
@@ -533,6 +541,8 @@ fn init_new_client_inventories(clients: Query<Entity, Added<Client>>, mut comman
533541 state_id : Wrapping ( 0 ) ,
534542 slots_changed : 0 ,
535543 client_updated_cursor_item : false ,
544+ } ,
545+ HeldItem {
536546 // First slot of the hotbar.
537547 held_item_slot : 36 ,
538548 } ,
@@ -1039,43 +1049,42 @@ fn handle_click_slot(
10391049
10401050fn handle_player_actions (
10411051 mut packets : EventReader < PacketEvent > ,
1042- mut clients : Query < ( & mut Inventory , & mut ClientInventoryState ) > ,
1052+ mut clients : Query < ( & mut Inventory , & mut ClientInventoryState , & HeldItem ) > ,
10431053 mut drop_item_stack_events : EventWriter < DropItemStack > ,
10441054) {
10451055 for packet in packets. iter ( ) {
10461056 if let Some ( pkt) = packet. decode :: < PlayerActionC2s > ( ) {
10471057 match pkt. action {
10481058 PlayerAction :: DropAllItems => {
1049- if let Ok ( ( mut inv, mut inv_state) ) = clients. get_mut ( packet. client ) {
1050- if let Some ( stack) = inv. replace_slot ( inv_state . held_item_slot , None ) {
1051- inv_state. slots_changed |= 1 << inv_state . held_item_slot ;
1059+ if let Ok ( ( mut inv, mut inv_state, & held ) ) = clients. get_mut ( packet. client ) {
1060+ if let Some ( stack) = inv. replace_slot ( held . slot ( ) , None ) {
1061+ inv_state. slots_changed |= 1 << held . slot ( ) ;
10521062
10531063 drop_item_stack_events. send ( DropItemStack {
10541064 client : packet. client ,
1055- from_slot : Some ( inv_state . held_item_slot ) ,
1065+ from_slot : Some ( held . slot ( ) ) ,
10561066 stack,
10571067 } ) ;
10581068 }
10591069 }
10601070 }
10611071 PlayerAction :: DropItem => {
1062- if let Ok ( ( mut inv, mut inv_state) ) = clients. get_mut ( packet. client ) {
1063- if let Some ( mut stack) = inv. replace_slot ( inv_state. held_item_slot ( ) , None )
1064- {
1072+ if let Ok ( ( mut inv, mut inv_state, held) ) = clients. get_mut ( packet. client ) {
1073+ if let Some ( mut stack) = inv. replace_slot ( held. slot ( ) , None ) {
10651074 if stack. count ( ) > 1 {
10661075 inv. set_slot (
1067- inv_state . held_item_slot ( ) ,
1076+ held . slot ( ) ,
10681077 stack. clone ( ) . with_count ( stack. count ( ) - 1 ) ,
10691078 ) ;
10701079
10711080 stack. set_count ( 1 ) ;
10721081 }
10731082
1074- inv_state. slots_changed |= 1 << inv_state . held_item_slot ( ) ;
1083+ inv_state. slots_changed |= 1 << held . slot ( ) ;
10751084
10761085 drop_item_stack_events. send ( DropItemStack {
10771086 client : packet. client ,
1078- from_slot : Some ( inv_state . held_item_slot ( ) ) ,
1087+ from_slot : Some ( held . slot ( ) ) ,
10791088 stack,
10801089 } )
10811090 }
@@ -1169,14 +1178,17 @@ pub struct UpdateSelectedSlot {
11691178
11701179fn handle_update_selected_slot (
11711180 mut packets : EventReader < PacketEvent > ,
1172- mut clients : Query < & mut ClientInventoryState > ,
1181+ mut clients : Query < & mut HeldItem > ,
11731182 mut events : EventWriter < UpdateSelectedSlot > ,
11741183) {
11751184 for packet in packets. iter ( ) {
11761185 if let Some ( pkt) = packet. decode :: < UpdateSelectedSlotC2s > ( ) {
1177- if let Ok ( mut inv_state) = clients. get_mut ( packet. client ) {
1178- // TODO: validate this.
1179- inv_state. held_item_slot = convert_hotbar_slot_id ( pkt. slot as u16 ) ;
1186+ if let Ok ( mut held) = clients. get_mut ( packet. client ) {
1187+ if pkt. slot < 0 || pkt. slot > 8 {
1188+ // The client is trying to interact with a slot that does not exist, ignore.
1189+ continue ;
1190+ }
1191+ held. held_item_slot = convert_hotbar_slot_id ( pkt. slot as u16 ) ;
11801192
11811193 events. send ( UpdateSelectedSlot {
11821194 client : packet. client ,
0 commit comments