11package com .landclaims .listeners ;
22
3- import com .hypixel .hytale .component .Ref ;
4- import com .hypixel .hytale .component .Store ;
53import com .hypixel .hytale .event .EventRegistry ;
64import com .hypixel .hytale .math .vector .Vector3i ;
7- import com .hypixel .hytale .server .core .entity .InteractionContext ;
85import com .hypixel .hytale .server .core .entity .entities .Player ;
9- import com .hypixel .hytale .server .core .event .events .ecs .BreakBlockEvent ;
10- import com .hypixel .hytale .server .core .event .events .ecs .DamageBlockEvent ;
11- import com .hypixel .hytale .server .core .event .events .ecs .PlaceBlockEvent ;
12- import com .hypixel .hytale .server .core .event .events .ecs .UseBlockEvent ;
136import com .hypixel .hytale .server .core .event .events .player .PlayerConnectEvent ;
147import com .hypixel .hytale .server .core .event .events .player .PlayerDisconnectEvent ;
158import com .hypixel .hytale .server .core .event .events .player .PlayerInteractEvent ;
169import com .hypixel .hytale .server .core .universe .PlayerRef ;
17- import com .hypixel .hytale .server .core .universe .world .storage .EntityStore ;
1810import com .hypixel .hytale .protocol .InteractionType ;
1911import com .landclaims .LandClaims ;
2012import com .landclaims .managers .ClaimManager ;
2416import java .util .concurrent .ConcurrentHashMap ;
2517
2618/**
27- * Listens for block and interaction events to protect claimed areas.
28- *
29- * Protection strategy:
30- * 1. PlayerInteractEvent - Track which player is interacting with which block, cancel if protected
31- * 2. UseBlockEvent.Pre - Prevent using blocks (chests, doors, etc.) in protected areas
32- * 3. DamageBlockEvent - Prevent block damage in protected areas (blocks mining)
33- * 4. BreakBlockEvent - Final safety net to prevent block breaking
34- * 5. PlaceBlockEvent - Prevent block placing in protected areas
19+ * Listens for player interaction events to protect claimed areas.
20+ * Tracks player interactions to correlate with ECS block events handled by BlockProtectionSystems.
3521 */
3622public class ClaimProtectionListener {
3723 private final LandClaims plugin ;
3824 private final ClaimManager claimManager ;
3925
40- // Track player interactions to correlate with ECS events
26+ // Track player interactions - shared with BlockProtectionSystems
4127 // Key: "x,y,z" block position, Value: PlayerInteraction data
42- private final Map <String , PlayerInteraction > pendingInteractions = new ConcurrentHashMap <>();
28+ private static final Map <String , PlayerInteraction > pendingInteractions = new ConcurrentHashMap <>();
29+ private static final Map <UUID , PlayerInteraction > playerLastInteraction = new ConcurrentHashMap <>();
4330
44- // Also track by player UUID for cases where block position differs (e.g., placing)
45- // Key: player UUID, Value: PlayerInteraction with last interacted position
46- private final Map <UUID , PlayerInteraction > playerLastInteraction = new ConcurrentHashMap <>();
47-
48- // How long to keep interaction data (ms)
4931 private static final long INTERACTION_TIMEOUT_MS = 5000 ;
5032
5133 public ClaimProtectionListener (LandClaims plugin ) {
@@ -54,26 +36,19 @@ public ClaimProtectionListener(LandClaims plugin) {
5436 }
5537
5638 /**
57- * Register all protection event handlers.
39+ * Register player event handlers.
5840 */
5941 public void register (EventRegistry eventRegistry ) {
6042 // Player interaction - track who is interacting with what and cancel if protected
6143 eventRegistry .registerGlobal (PlayerInteractEvent .class , this ::onPlayerInteract );
6244
63- // ECS block events - these fire when blocks are actually modified
64- eventRegistry .registerGlobal (UseBlockEvent .Pre .class , this ::onUseBlock );
65- eventRegistry .registerGlobal (DamageBlockEvent .class , this ::onDamageBlock );
66- eventRegistry .registerGlobal (BreakBlockEvent .class , this ::onBreakBlock );
67- eventRegistry .registerGlobal (PlaceBlockEvent .class , this ::onPlaceBlock );
68-
6945 // Player join/leave for playtime tracking
7046 eventRegistry .register (PlayerConnectEvent .class , this ::onPlayerConnect );
7147 eventRegistry .registerGlobal (PlayerDisconnectEvent .class , this ::onPlayerDisconnect );
7248 }
7349
7450 /**
7551 * Track player interactions and cancel if in protected area.
76- * This fires BEFORE ECS block events.
7752 */
7853 private void onPlayerInteract (PlayerInteractEvent event ) {
7954 Player player = event .getPlayer ();
@@ -84,196 +59,40 @@ private void onPlayerInteract(PlayerInteractEvent event) {
8459
8560 UUID playerId = player .getUuid ();
8661 String worldName = "default" ;
87- InteractionType actionType = event .getActionType ();
8862
8963 // Track this interaction for ECS event correlation
9064 String blockKey = getBlockKey (targetBlock );
9165 PlayerInteraction interaction = new PlayerInteraction (playerId , worldName , targetBlock , System .currentTimeMillis ());
9266 pendingInteractions .put (blockKey , interaction );
9367 playerLastInteraction .put (playerId , interaction );
9468
95- // Clean up old interactions periodically
69+ // Clean up old interactions
9670 cleanupOldInteractions ();
9771
98- // Check if this location is protected
72+ // Check if this location is protected - cancel ALL interactions in protected areas
9973 if (!claimManager .canInteract (playerId , worldName , targetBlock .getX (), targetBlock .getZ ())) {
10074 event .setCancelled (true );
10175 }
10276 }
10377
104- /**
105- * Prevent using blocks (chests, doors, buttons, etc.) in protected areas.
106- */
107- private void onUseBlock (UseBlockEvent .Pre event ) {
108- Vector3i targetBlock = event .getTargetBlock ();
109- if (targetBlock == null ) return ;
110-
111- // Try to get player from interaction context
112- InteractionContext context = event .getContext ();
113- UUID playerId = getPlayerFromContext (context );
114-
115- if (playerId != null ) {
116- String worldName = "default" ;
117- if (!claimManager .canInteract (playerId , worldName , targetBlock .getX (), targetBlock .getZ ())) {
118- event .setCancelled (true );
119- }
120- } else {
121- // Fallback: check tracked interactions
122- String blockKey = getBlockKey (targetBlock );
123- PlayerInteraction interaction = pendingInteractions .get (blockKey );
124-
125- if (interaction != null && !interaction .isExpired ()) {
126- if (!claimManager .canInteract (interaction .playerId , interaction .worldName ,
127- targetBlock .getX (), targetBlock .getZ ())) {
128- event .setCancelled (true );
129- }
130- } else {
131- // No player info - block if claimed (conservative)
132- String worldName = "default" ;
133- if (isProtectedWithoutAccess (worldName , targetBlock )) {
134- event .setCancelled (true );
135- }
136- }
137- }
138- }
139-
140- /**
141- * Prevent block damage in protected areas.
142- */
143- private void onDamageBlock (DamageBlockEvent event ) {
144- Vector3i targetBlock = event .getTargetBlock ();
145- if (targetBlock == null ) return ;
146-
147- String blockKey = getBlockKey (targetBlock );
148- PlayerInteraction interaction = pendingInteractions .get (blockKey );
149-
150- if (interaction != null && !interaction .isExpired ()) {
151- if (!claimManager .canInteract (interaction .playerId , interaction .worldName ,
152- targetBlock .getX (), targetBlock .getZ ())) {
153- event .setCancelled (true );
154- }
155- } else {
156- // Try to find any recent interaction near this block
157- interaction = findNearbyInteraction (targetBlock );
158- if (interaction != null ) {
159- if (!claimManager .canInteract (interaction .playerId , interaction .worldName ,
160- targetBlock .getX (), targetBlock .getZ ())) {
161- event .setCancelled (true );
162- }
163- } else {
164- // No player info - block if claimed
165- String worldName = "default" ;
166- if (isProtectedWithoutAccess (worldName , targetBlock )) {
167- event .setCancelled (true );
168- }
169- }
170- }
171- }
172-
173- /**
174- * Final safety net - prevent block breaking in protected areas.
175- */
176- private void onBreakBlock (BreakBlockEvent event ) {
177- Vector3i targetBlock = event .getTargetBlock ();
178- if (targetBlock == null ) return ;
179-
180- String blockKey = getBlockKey (targetBlock );
181- PlayerInteraction interaction = pendingInteractions .get (blockKey );
182-
183- if (interaction != null && !interaction .isExpired ()) {
184- if (!claimManager .canInteract (interaction .playerId , interaction .worldName ,
185- targetBlock .getX (), targetBlock .getZ ())) {
186- event .setCancelled (true );
187- }
188- pendingInteractions .remove (blockKey );
189- } else {
190- interaction = findNearbyInteraction (targetBlock );
191- if (interaction != null ) {
192- if (!claimManager .canInteract (interaction .playerId , interaction .worldName ,
193- targetBlock .getX (), targetBlock .getZ ())) {
194- event .setCancelled (true );
195- }
196- } else {
197- String worldName = "default" ;
198- if (isProtectedWithoutAccess (worldName , targetBlock )) {
199- event .setCancelled (true );
200- }
201- }
202- }
203- }
204-
205- /**
206- * Prevent block placing in protected areas.
207- */
208- private void onPlaceBlock (PlaceBlockEvent event ) {
209- Vector3i targetBlock = event .getTargetBlock ();
210- if (targetBlock == null ) return ;
211-
212- String blockKey = getBlockKey (targetBlock );
78+ // Static accessors for BlockProtectionSystems
79+ public static PlayerInteraction getInteraction (String blockKey ) {
21380 PlayerInteraction interaction = pendingInteractions .get (blockKey );
214-
215- if (interaction == null || interaction .isExpired ()) {
216- interaction = findNearbyInteraction (targetBlock );
217- }
218-
21981 if (interaction != null && !interaction .isExpired ()) {
220- if (!claimManager .canInteract (interaction .playerId , interaction .worldName ,
221- targetBlock .getX (), targetBlock .getZ ())) {
222- event .setCancelled (true );
223- }
224- } else {
225- String worldName = "default" ;
226- if (isProtectedWithoutAccess (worldName , targetBlock )) {
227- event .setCancelled (true );
228- }
229- }
230- }
231-
232- /**
233- * Try to extract player UUID from an InteractionContext.
234- */
235- private UUID getPlayerFromContext (InteractionContext context ) {
236- if (context == null ) return null ;
237-
238- try {
239- Ref <EntityStore > entityRef = context .getEntity ();
240- if (entityRef == null ) return null ;
241-
242- // The entity ref might give us access to player data
243- // This is a best-effort attempt
244- Ref <EntityStore > owningRef = context .getOwningEntity ();
245- if (owningRef != null ) {
246- // Try to get player component - this may not work directly
247- // but we store the ref for potential future use
248- }
249- } catch (Exception e ) {
250- // Context access failed, fall back to tracking
82+ return interaction ;
25183 }
252-
25384 return null ;
25485 }
25586
256- /**
257- * Check if a block is in a claimed area.
258- * Returns true if it should be blocked (claimed and no known accessor).
259- */
260- private boolean isProtectedWithoutAccess (String worldName , Vector3i block ) {
261- UUID owner = claimManager .getOwnerAt (worldName , block .getX (), block .getZ ());
262- return owner != null ;
263- }
264-
265- /**
266- * Find a player interaction that was on or near a block.
267- */
268- private PlayerInteraction findNearbyInteraction (Vector3i targetBlock ) {
269- // First check exact match
87+ public static PlayerInteraction findNearbyInteraction (Vector3i targetBlock ) {
88+ // Check exact match first
27089 String blockKey = getBlockKey (targetBlock );
27190 PlayerInteraction exact = pendingInteractions .get (blockKey );
27291 if (exact != null && !exact .isExpired ()) {
27392 return exact ;
27493 }
27594
276- // Check all recent player interactions to find one adjacent to this block
95+ // Check adjacent blocks (for placing)
27796 for (PlayerInteraction interaction : playerLastInteraction .values ()) {
27897 if (interaction .isExpired ()) continue ;
27998 if (interaction .blockPos == null ) continue ;
@@ -289,7 +108,11 @@ private PlayerInteraction findNearbyInteraction(Vector3i targetBlock) {
289108 return null ;
290109 }
291110
292- private String getBlockKey (Vector3i pos ) {
111+ public static void removeInteraction (String blockKey ) {
112+ pendingInteractions .remove (blockKey );
113+ }
114+
115+ public static String getBlockKey (Vector3i pos ) {
293116 return pos .getX () + "," + pos .getY () + "," + pos .getZ ();
294117 }
295118
@@ -315,20 +138,23 @@ private void onPlayerDisconnect(PlayerDisconnectEvent event) {
315138 }
316139 }
317140
318- private static class PlayerInteraction {
319- final UUID playerId ;
320- final String worldName ;
321- final Vector3i blockPos ;
322- final long timestamp ;
141+ /**
142+ * Tracks a player's interaction with a block.
143+ */
144+ public static class PlayerInteraction {
145+ public final UUID playerId ;
146+ public final String worldName ;
147+ public final Vector3i blockPos ;
148+ public final long timestamp ;
323149
324- PlayerInteraction (UUID playerId , String worldName , Vector3i blockPos , long timestamp ) {
150+ public PlayerInteraction (UUID playerId , String worldName , Vector3i blockPos , long timestamp ) {
325151 this .playerId = playerId ;
326152 this .worldName = worldName ;
327153 this .blockPos = blockPos ;
328154 this .timestamp = timestamp ;
329155 }
330156
331- boolean isExpired () {
157+ public boolean isExpired () {
332158 return System .currentTimeMillis () - timestamp > INTERACTION_TIMEOUT_MS ;
333159 }
334160 }
0 commit comments