11use bevy_ecs:: prelude:: * ;
2+ use std:: collections:: HashMap ;
3+ use std:: time:: { Duration , Instant } ;
24use temper_codec:: net_types:: network_position:: NetworkPosition ;
35use temper_codec:: net_types:: var_int:: VarInt ;
6+ use temper_components:: interaction:: InteractionCooldown ;
47use temper_components:: player:: position:: Position ;
58use temper_config:: server_config:: get_global_config;
9+ use temper_core:: pos:: BlockPos ;
610use temper_messages:: { BlockInteractMessage , BlockToggledEvent , DoorToggledEvent } ;
711use temper_net_runtime:: connection:: StreamWriter ;
812use temper_protocol:: outgoing:: block_change_ack:: BlockChangeAck ;
@@ -20,10 +24,30 @@ pub fn handle_block_interact(
2024 query : Query < ( Entity , & StreamWriter , & Position ) > ,
2125 mut toggled_writer : MessageWriter < BlockToggledEvent > ,
2226 mut door_toggled_writer : MessageWriter < DoorToggledEvent > ,
27+ mut cooldowns : Local < HashMap < BlockPos , Instant > > ,
2328) {
29+ let cooldown_duration = Duration :: from_millis ( InteractionCooldown :: default ( ) . cooldown_ms ) ;
30+
2431 for event in events. read ( ) {
2532 let pos = event. position ;
2633
34+ // Ignore rapid repeated interactions on the same block
35+ if cooldowns
36+ . get ( & pos)
37+ . is_some_and ( |t| t. elapsed ( ) < cooldown_duration)
38+ {
39+ if let Ok ( ( _, conn, _) ) = query. get ( event. player ) {
40+ let ack = BlockChangeAck {
41+ sequence : event. sequence ,
42+ } ;
43+ if let Err ( e) = conn. send_packet_ref ( & ack) {
44+ error ! ( "Failed to send BlockChangeAck (cooldown): {:?}" , e) ;
45+ }
46+ }
47+ continue ;
48+ }
49+ cooldowns. insert ( pos, Instant :: now ( ) ) ;
50+
2751 // Load the chunk and get current block state
2852 let mut chunk = match temper_world:: World :: get_or_generate_mut (
2953 & state. 0 . world ,
0 commit comments