11use crate :: disconnect_player;
22use crate :: entity:: PlayerComponent ;
3- use crate :: network:: PacketQueue ;
4- use feather_core:: inventory:: { Inventory , InventoryType } ;
3+ use crate :: network:: { send_packet_to_all_players, NetworkComponent , PacketQueue } ;
4+ use feather_core:: inventory:: {
5+ Inventory , InventoryType , SlotIndex , HOTBAR_SIZE , SLOT_ENTITY_EQUIPMENT_MAIN_HAND ,
6+ SLOT_HOTBAR_OFFSET ,
7+ } ;
58use feather_core:: network:: cast_packet;
6- use feather_core:: network:: packet:: implementation:: CreativeInventoryAction ;
9+ use feather_core:: network:: packet:: implementation:: {
10+ CreativeInventoryAction , EntityEquipment , HeldItemChangeServerbound ,
11+ } ;
712use feather_core:: network:: packet:: PacketType ;
813use feather_core:: Gamemode ;
14+ use shrev:: EventChannel ;
915use specs:: storage:: BTreeStorage ;
10- use specs:: System ;
11- use specs:: { Component , LazyUpdate , Read , ReadStorage , WriteStorage } ;
16+ use specs:: SystemData ;
17+ use specs:: { Component , Entities , LazyUpdate , Read , ReadStorage , ReaderId , World , WriteStorage } ;
18+ use specs:: { Entity , System , Write } ;
1219use std:: ops:: { Deref , DerefMut } ;
1320
1421/// Component for storing a player's inventory.
1522#[ derive( Clone , Debug ) ]
1623pub struct InventoryComponent {
1724 inventory : Inventory ,
25+ pub held_item : SlotIndex ,
1826}
1927
2028impl InventoryComponent {
2129 pub fn new ( ) -> Self {
2230 Self {
2331 inventory : Inventory :: new ( InventoryType :: Player , 46 ) ,
32+ held_item : 0 ,
2433 }
2534 }
2635}
@@ -49,19 +58,26 @@ impl Component for InventoryComponent {
4958 type Storage = BTreeStorage < Self > ;
5059}
5160
61+ /// Event which is triggered when a player updates
62+ /// their held item.
63+ pub struct HeldItemUpdateEvent {
64+ pub player : Entity ,
65+ }
66+
5267/// System for handling Creative Inventory Action packets.
5368pub struct CreativeInventorySystem ;
5469
5570impl < ' a > System < ' a > for CreativeInventorySystem {
5671 type SystemData = (
5772 WriteStorage < ' a , InventoryComponent > ,
5873 ReadStorage < ' a , PlayerComponent > ,
74+ Write < ' a , EventChannel < HeldItemUpdateEvent > > ,
5975 Read < ' a , PacketQueue > ,
6076 Read < ' a , LazyUpdate > ,
6177 ) ;
6278
6379 fn run ( & mut self , data : Self :: SystemData ) {
64- let ( mut inventories, players, packet_queue, lazy) = data;
80+ let ( mut inventories, players, mut events , packet_queue, lazy) = data;
6581
6682 let packets = packet_queue. for_packet ( PacketType :: CreativeInventoryAction ) ;
6783
@@ -96,8 +112,91 @@ impl<'a> System<'a> for CreativeInventorySystem {
96112 inventory. clear_item_at ( packet. slot as usize ) ;
97113 }
98114 }
115+
116+ // If the updates slot was the player's held item,
117+ // we need to broadcast the equipment update
118+ if inventory. held_item == packet. slot as usize {
119+ let event = HeldItemUpdateEvent { player } ;
120+ events. single_write ( event) ;
121+ }
122+ }
123+ }
124+ }
125+
126+ /// System for handling Held Item Change packets.
127+ pub struct HeldItemChangeSystem ;
128+
129+ impl < ' a > System < ' a > for HeldItemChangeSystem {
130+ type SystemData = (
131+ WriteStorage < ' a , InventoryComponent > ,
132+ Write < ' a , EventChannel < HeldItemUpdateEvent > > ,
133+ Read < ' a , PacketQueue > ,
134+ Read < ' a , LazyUpdate > ,
135+ ) ;
136+
137+ fn run ( & mut self , data : Self :: SystemData ) {
138+ let ( mut inventories, mut events, packet_queue, lazy) = data;
139+
140+ let packets = packet_queue. for_packet ( PacketType :: HeldItemChangeServerbound ) ;
141+
142+ for ( player, packet) in packets {
143+ let packet = cast_packet :: < HeldItemChangeServerbound > ( & * packet) ;
144+
145+ if packet. slot as usize >= HOTBAR_SIZE {
146+ disconnect_player ( player, "Hotbar index out of bounds" . to_string ( ) , & lazy) ;
147+ continue ;
148+ }
149+
150+ let inventory = inventories. get_mut ( player) . unwrap ( ) ;
151+ inventory. held_item = packet. slot as usize ;
152+
153+ // Trigger event
154+ let event = HeldItemUpdateEvent { player } ;
155+ events. single_write ( event) ;
156+ }
157+ }
158+ }
159+
160+ /// System for broadcasting held item updates.
161+ #[ derive( Default ) ]
162+ pub struct HeldItemBroadcastSystem {
163+ reader : Option < ReaderId < HeldItemUpdateEvent > > ,
164+ }
165+
166+ impl < ' a > System < ' a > for HeldItemBroadcastSystem {
167+ type SystemData = (
168+ ReadStorage < ' a , NetworkComponent > ,
169+ ReadStorage < ' a , InventoryComponent > ,
170+ Read < ' a , EventChannel < HeldItemUpdateEvent > > ,
171+ Entities < ' a > ,
172+ ) ;
173+
174+ fn run ( & mut self , data : Self :: SystemData ) {
175+ let ( networks, inventories, events, entities) = data;
176+
177+ for event in events. read ( & mut self . reader . as_mut ( ) . unwrap ( ) ) {
178+ let inv = inventories. get ( event. player ) . unwrap ( ) ;
179+ let item = inv. item_at ( SLOT_HOTBAR_OFFSET + inv. held_item ) . cloned ( ) ;
180+
181+ let packet = EntityEquipment :: new (
182+ event. player . id ( ) as i32 ,
183+ SLOT_ENTITY_EQUIPMENT_MAIN_HAND as i32 ,
184+ item,
185+ ) ;
186+
187+ send_packet_to_all_players ( & networks, & entities, packet, Some ( event. player ) ) ;
99188 }
100189 }
190+
191+ fn setup ( & mut self , world : & mut World ) {
192+ Self :: SystemData :: setup ( world) ;
193+
194+ self . reader = Some (
195+ world
196+ . fetch_mut :: < EventChannel < HeldItemUpdateEvent > > ( )
197+ . register_reader ( ) ,
198+ ) ;
199+ }
101200}
102201
103202#[ cfg( test) ]
@@ -118,6 +217,8 @@ mod tests {
118217
119218 t:: receive_packet ( & player, & w, packet) ;
120219
220+ let mut event_reader = t:: reader ( & w) ;
221+
121222 d. dispatch ( & w) ;
122223 w. maintain ( ) ;
123224
@@ -127,6 +228,15 @@ mod tests {
127228 let inv = inv_storage. get ( player. entity ) . unwrap ( ) ;
128229 assert_eq ! ( inv. item_at( 0 ) . unwrap( ) , & ItemStack :: new( Item :: IronSword , 1 ) ) ;
129230
231+ // Confirm that event was triggered
232+ {
233+ let channel = w. fetch :: < EventChannel < HeldItemUpdateEvent > > ( ) ;
234+ let events = channel. read ( & mut event_reader) . collect :: < Vec < _ > > ( ) ;
235+ assert_eq ! ( events. len( ) , 1 ) ;
236+ let first = events. first ( ) . unwrap ( ) ;
237+ assert_eq ! ( first. player, player. entity) ;
238+ }
239+
130240 drop ( inv_storage) ;
131241
132242 let packet = CreativeInventoryAction :: new ( 0 , None ) ;
@@ -172,4 +282,72 @@ mod tests {
172282
173283 t:: assert_disconnected ( & player) ;
174284 }
285+
286+ #[ test]
287+ fn test_held_item_change_system ( ) {
288+ let ( mut w, mut d) = t:: init_world ( ) ;
289+
290+ let player = t:: add_player ( & mut w) ;
291+
292+ let slot = 4 ;
293+
294+ let mut event_reader = t:: reader ( & w) ;
295+
296+ let packet = HeldItemChangeServerbound :: new ( slot) ;
297+ t:: receive_packet ( & player, & w, packet) ;
298+
299+ d. dispatch ( & w) ;
300+ w. maintain ( ) ;
301+
302+ let channel = w. fetch :: < EventChannel < HeldItemUpdateEvent > > ( ) ;
303+ let events = channel. read ( & mut event_reader) . collect :: < Vec < _ > > ( ) ;
304+
305+ assert_eq ! ( events. len( ) , 1 ) ;
306+
307+ let first = events. first ( ) . unwrap ( ) ;
308+ assert_eq ! ( first. player, player. entity) ;
309+
310+ let inventories = w. read_component :: < InventoryComponent > ( ) ;
311+ let inv = inventories. get ( player. entity ) . unwrap ( ) ;
312+
313+ assert_eq ! ( inv. held_item, slot as usize ) ;
314+ }
315+
316+ #[ test]
317+ fn test_held_item_change_system_out_of_bounds ( ) {
318+ let ( mut w, mut d) = t:: init_world ( ) ;
319+
320+ let player = t:: add_player ( & mut w) ;
321+
322+ let slot = 9 ;
323+
324+ let packet = HeldItemChangeServerbound :: new ( slot) ;
325+ t:: receive_packet ( & player, & w, packet) ;
326+
327+ d. dispatch ( & w) ;
328+ w. maintain ( ) ;
329+
330+ t:: assert_disconnected ( & player) ; // Slot out of bounds
331+ }
332+
333+ #[ test]
334+ fn test_held_item_broadcast_system ( ) {
335+ let ( mut w, mut d) = t:: init_world ( ) ;
336+
337+ let player = t:: add_player ( & mut w) ;
338+ let player2 = t:: add_player ( & mut w) ;
339+
340+ let event = HeldItemUpdateEvent {
341+ player : player. entity ,
342+ } ;
343+
344+ w. fetch_mut :: < EventChannel < HeldItemUpdateEvent > > ( )
345+ . single_write ( event) ;
346+
347+ d. dispatch ( & w) ;
348+ w. maintain ( ) ;
349+
350+ t:: assert_packet_received ( & player2, PacketType :: EntityEquipment ) ;
351+ t:: assert_packet_not_received ( & player, PacketType :: EntityEquipment ) ;
352+ }
175353}
0 commit comments