1.15.2 PlayerInteractEvent overrides BlockPlace/Break

Discussion in 'Spigot Plugin Development' started by Potters, Mar 7, 2020.

  1. I'm developing a custom Towny Plugin for someone, and have run into issues with the Permissions System.

    Please note, I'm using the Helper API, however, this isn't affecting the issue, just syntax.

    Essentially, PlayerInteractEvent is overriding BlockBreakEvent and BlockPlaceEvent (this is logical, but I need to override it). I need to find a way to detect if it's one of these two events that subsequently will be called, and simply return, if that makes sense. This is cancelling the two latter events I've mentioned. I'm aware that the event.setCancelled(true); is what's causing the issue, but I'm not sure how to resolve it. I can't simply check the Action (e.g LEFT_CLICK_BLOCK and RIGHT_CLICK_BLOCK) and then check the item in their hand, as this has meant that they can break with no item, or a pickaxe - they can also open a chest with a block (for example.)

    Any help would be appreciated - I'd strongly prefer not to have to go down a Packet route, as someone else has suggested, for obvious reasons.

    Code (Java):
    final Map<Predicate<BlockState>, Permission> interactPerms = ImmutableMap.<Predicate<BlockState>, Permission>builder()
                    .put((state -> state instanceof Sign), Permission.SIGN)
                    .put((state -> state instanceof Lectern), Permission.LECTERN)
                    .put((state -> (state instanceof Door) || (state instanceof Gate)), Permission.DOOR)
                    .put((state -> state instanceof Container && !(state instanceof BrewingStand)), Permission.CONTAINER)
                    .put((state -> state instanceof Redstone), Permission.REDSTONE)
                    .put((state -> state instanceof Beacon), Permission.BEACON)
                    .put((state -> state instanceof Bed), Permission.BED)
                    .put((state -> state instanceof BrewingStand), Permission.BREWING)
                    .build();

            Events.subscribe(PlayerInteractEvent.class)
                    .filter(EventFilters.ignoreCancelled())
                    .handler(event -> {
                                boolean hasBlock = event.hasBlock();
                                Block block = event.getClickedBlock();
                                Player player = event.getPlayer();

                                if (hasBlock) {
                                    boolean hasPermission = interactPerms.entrySet().stream()
                                            .filter(perm -> perm.getKey().test(block.getState()))
                                            .anyMatch(perm -> canInteract(player, block.getLocation(), perm.getValue()));

                                    if (hasPermission) {
                                        return;
                                    }

                                }

    //                    if (canInteract(event.getPlayer(), (hasBlock) ? block.getLocation() : event.getPlayer().getLocation(), Permission.INTERACT)) {
    //                        return;
    //                    }

                                event.setCancelled(true);
                            }
                    );
    The Physical System works completely as intended, and if I remove event.setCancelled(true) it has unintended consequences, and will allow general interaction that should always be denied (e.g setting fire).
     
  2. Tried changing the eventpriority for them?
     
    • Friendly Friendly x 1
  3. EventPriorities are called on a per-event basis, so it wouldn't really help.
     
  4. Not exactly sure what the issue is here...
    You are seeing if the player has permission to interact with a block, if they do you just continue on as usual. If they do not, you cancel the interact event.
    How does the block break event interfere with it? You are not showing code where you subscribe to that event, and if you cancel the interact event, the block will not get broken, and the event will not be invoked.
    If the general actions like setting the block on fire are not allowed and not being detected by your event listener you need to add conditions to detect and cancel the event in there.
    I don't think you can get around not canceling the event, because to me that seems to be what you want to be doing?
     
  5. The issue is significantly more complex than this. After speaking to various Spigot Support Staff, it appears that the only real valid solution is to invoke the BlockBreakEvent/BlockPlaceEvent on EventPriority.LOWEST (as to be called first), see if either is cancelled, and proceed from there. After introducing this solution, it appears to work fine.
     
  6. Glad you learned something.
    = )