steel_core/player/game_mode/
item_interaction.rs1use super::{
2 Arc, BLOCK_BEHAVIORS, BlockHitResult, Entity, GameType, ITEM_BEHAVIORS, InteractionHand,
3 InteractionResult, InventoryAccess, Player, REGISTRY, SUseItem, UseOnContext, World,
4 wrap_degrees,
5};
6
7pub fn use_item_on(
19 player: &Player,
20 world: &Arc<World>,
21 hand: InteractionHand,
22 hit_result: &BlockHitResult,
23) -> InteractionResult {
24 let pos = hit_result.block_pos;
25 let state = world.get_block_state(pos);
26
27 if player.game_mode() == GameType::Spectator {
30 return InteractionResult::Pass;
31 }
32
33 let have_something = {
35 let inv = player.inventory.lock();
36 !inv.get_item_in_hand(InteractionHand::MainHand).is_empty()
37 || !inv.get_item_in_hand(InteractionHand::OffHand).is_empty()
38 };
39
40 let suppress_block_use = player.is_secondary_use_active() && have_something;
41
42 let block_behaviors = &*BLOCK_BEHAVIORS;
44 let item_behaviors = &*ITEM_BEHAVIORS;
45
46 if !suppress_block_use {
49 let Some(block) = REGISTRY.blocks.by_state_id(state) else {
50 return InteractionResult::Pass;
51 };
52 let behavior = block_behaviors.get_behavior(block);
53
54 let mut inventory_access = InventoryAccess::new(player.inventory.clone(), hand);
55
56 let block_result = behavior.use_item_on(
57 state,
58 world,
59 pos,
60 player,
61 hand,
62 hit_result,
63 &mut inventory_access,
64 );
65
66 if block_result.consumes_action() {
67 return block_result;
68 }
69
70 if matches!(block_result, InteractionResult::TryEmptyHandInteraction)
71 && hand == InteractionHand::MainHand
72 {
73 let empty_result = behavior.use_without_item(
74 state,
75 world,
76 pos,
77 player,
78 hit_result,
79 &mut inventory_access,
80 );
81
82 if empty_result.consumes_action() {
83 return empty_result;
84 }
85 }
86 }
87
88 let inventory_access = InventoryAccess::new(player.inventory.clone(), hand);
89 let (is_empty, original_count, item_ref, stack_before_use) =
90 inventory_access.with_item(|item| (item.is_empty(), item.count, item.item, item.clone()));
91
92 if !is_empty {
93 if player.is_item_on_cooldown(&stack_before_use) {
94 return InteractionResult::Pass;
95 }
96
97 let mut context = UseOnContext::new(
98 player,
99 hand,
100 hit_result.clone(),
101 world,
102 player.inventory.clone(),
103 );
104 let item_behavior = item_behaviors.get_behavior(item_ref);
105 let result = item_behavior.use_on(&mut context);
106
107 if player.has_infinite_materials() {
109 context.inv.with_item(|item| {
110 if item.count < original_count {
111 item.count = original_count;
112 }
113 });
114 }
115
116 return result;
117 }
118
119 InteractionResult::Pass
120}
121
122pub fn use_item(player: &Player, world: &Arc<World>, hand: InteractionHand) -> InteractionResult {
126 if player.game_mode() == GameType::Spectator {
128 return InteractionResult::Pass;
129 }
130
131 let inventory_access = InventoryAccess::new(player.inventory.clone(), hand);
132 let (is_empty, original_count, item_ref, stack_before_use) =
133 inventory_access.with_item(|item| (item.is_empty(), item.count, item.item, item.clone()));
134
135 if !is_empty {
136 if player.is_item_on_cooldown(&stack_before_use) {
137 return InteractionResult::Pass;
138 }
139
140 let mut context =
141 crate::behavior::UseItemContext::new(player, hand, world, player.inventory.clone());
142
143 let item_behaviors = &*ITEM_BEHAVIORS;
145 let item_behavior = item_behaviors.get_behavior(item_ref);
146
147 let result = item_behavior.use_item(&mut context);
148
149 if player.has_infinite_materials() {
151 context.inv.with_item(|item| {
152 if item.count < original_count {
153 item.count = original_count;
154 }
155 });
156 }
157
158 if result.should_apply_item_use_side_effects() {
159 player.apply_item_use_cooldown(&stack_before_use);
160 }
161
162 return result;
163 }
164
165 InteractionResult::Pass
166}
167
168impl Player {
169 pub fn handle_use_item(&self, packet: SUseItem) {
171 if !self.has_client_loaded() {
172 return;
173 }
174
175 log::debug!(
176 "Player {} used {:?} (sequence: {}, yaw: {}, pitch: {})",
177 self.gameprofile.name,
178 packet.hand,
179 packet.sequence,
180 packet.y_rot,
181 packet.x_rot
182 );
183
184 self.ack_block_changes_up_to(packet.sequence);
185
186 let item_stack_is_empty = {
187 let inventory = self.inventory.lock();
188 inventory.get_item_in_hand(packet.hand).is_empty()
189 };
190 if item_stack_is_empty {
191 return;
192 }
193
194 let target_yaw = wrap_degrees(packet.y_rot);
195 let target_pitch = wrap_degrees(packet.x_rot);
196 if self.rotation() != (target_yaw, target_pitch) {
197 self.set_rotation((target_yaw, target_pitch));
198 }
199
200 let world = self.get_world();
201 let result = use_item(self, &world, packet.hand);
202
203 if result.should_swing_server() {
204 self.swing(packet.hand, true);
205 }
206
207 self.broadcast_inventory_changes();
208 }
209}