001/*
002 * PlotSquared, a land and world management plugin for Minecraft.
003 * Copyright (C) IntellectualSites <https://intellectualsites.com>
004 * Copyright (C) IntellectualSites team and contributors
005 *
006 * This program is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 *
011 * This program is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014 * GNU General Public License for more details.
015 *
016 * You should have received a copy of the GNU General Public License
017 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
018 */
019package com.plotsquared.bukkit.listener;
020
021import com.destroystokyo.paper.MaterialTags;
022import com.google.common.base.Charsets;
023import com.google.inject.Inject;
024import com.plotsquared.bukkit.player.BukkitPlayer;
025import com.plotsquared.bukkit.util.BukkitEntityUtil;
026import com.plotsquared.bukkit.util.BukkitUtil;
027import com.plotsquared.bukkit.util.UpdateUtility;
028import com.plotsquared.core.PlotSquared;
029import com.plotsquared.core.configuration.Settings;
030import com.plotsquared.core.configuration.caption.Caption;
031import com.plotsquared.core.configuration.caption.TranslatableCaption;
032import com.plotsquared.core.listener.PlayerBlockEventType;
033import com.plotsquared.core.listener.PlotListener;
034import com.plotsquared.core.location.Location;
035import com.plotsquared.core.permissions.Permission;
036import com.plotsquared.core.player.ConsolePlayer;
037import com.plotsquared.core.player.MetaDataAccess;
038import com.plotsquared.core.player.PlayerMetaDataKeys;
039import com.plotsquared.core.player.PlotPlayer;
040import com.plotsquared.core.plot.Plot;
041import com.plotsquared.core.plot.PlotArea;
042import com.plotsquared.core.plot.PlotId;
043import com.plotsquared.core.plot.PlotInventory;
044import com.plotsquared.core.plot.flag.FlagContainer;
045import com.plotsquared.core.plot.flag.implementations.AnimalInteractFlag;
046import com.plotsquared.core.plot.flag.implementations.BlockedCmdsFlag;
047import com.plotsquared.core.plot.flag.implementations.ChatFlag;
048import com.plotsquared.core.plot.flag.implementations.DenyPortalTravelFlag;
049import com.plotsquared.core.plot.flag.implementations.DenyPortalsFlag;
050import com.plotsquared.core.plot.flag.implementations.DenyTeleportFlag;
051import com.plotsquared.core.plot.flag.implementations.DoneFlag;
052import com.plotsquared.core.plot.flag.implementations.DropProtectionFlag;
053import com.plotsquared.core.plot.flag.implementations.EditSignFlag;
054import com.plotsquared.core.plot.flag.implementations.HangingBreakFlag;
055import com.plotsquared.core.plot.flag.implementations.HangingPlaceFlag;
056import com.plotsquared.core.plot.flag.implementations.HostileInteractFlag;
057import com.plotsquared.core.plot.flag.implementations.ItemDropFlag;
058import com.plotsquared.core.plot.flag.implementations.KeepInventoryFlag;
059import com.plotsquared.core.plot.flag.implementations.LecternReadBookFlag;
060import com.plotsquared.core.plot.flag.implementations.MiscInteractFlag;
061import com.plotsquared.core.plot.flag.implementations.PlayerInteractFlag;
062import com.plotsquared.core.plot.flag.implementations.PreventCreativeCopyFlag;
063import com.plotsquared.core.plot.flag.implementations.TamedInteractFlag;
064import com.plotsquared.core.plot.flag.implementations.TileDropFlag;
065import com.plotsquared.core.plot.flag.implementations.UntrustedVisitFlag;
066import com.plotsquared.core.plot.flag.implementations.VehicleBreakFlag;
067import com.plotsquared.core.plot.flag.implementations.VehicleUseFlag;
068import com.plotsquared.core.plot.flag.implementations.VillagerInteractFlag;
069import com.plotsquared.core.plot.world.PlotAreaManager;
070import com.plotsquared.core.util.EventDispatcher;
071import com.plotsquared.core.util.MathMan;
072import com.plotsquared.core.util.PlotFlagUtil;
073import com.plotsquared.core.util.PremiumVerification;
074import com.plotsquared.core.util.entity.EntityCategories;
075import com.plotsquared.core.util.task.TaskManager;
076import com.plotsquared.core.util.task.TaskTime;
077import com.sk89q.worldedit.WorldEdit;
078import com.sk89q.worldedit.bukkit.BukkitAdapter;
079import com.sk89q.worldedit.world.block.BlockType;
080import io.papermc.lib.PaperLib;
081import net.kyori.adventure.text.Component;
082import net.kyori.adventure.text.minimessage.MiniMessage;
083import net.kyori.adventure.text.minimessage.tag.Tag;
084import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
085import net.kyori.adventure.text.minimessage.tag.standard.StandardTags;
086import org.bukkit.Bukkit;
087import org.bukkit.FluidCollisionMode;
088import org.bukkit.Material;
089import org.bukkit.block.Block;
090import org.bukkit.block.BlockFace;
091import org.bukkit.block.BlockState;
092import org.bukkit.block.Sign;
093import org.bukkit.block.data.Waterlogged;
094import org.bukkit.command.PluginCommand;
095import org.bukkit.entity.ArmorStand;
096import org.bukkit.entity.Boat;
097import org.bukkit.entity.Entity;
098import org.bukkit.entity.EntityType;
099import org.bukkit.entity.HumanEntity;
100import org.bukkit.entity.ItemFrame;
101import org.bukkit.entity.LivingEntity;
102import org.bukkit.entity.Player;
103import org.bukkit.entity.Projectile;
104import org.bukkit.entity.Tameable;
105import org.bukkit.entity.Vehicle;
106import org.bukkit.event.Event;
107import org.bukkit.event.EventHandler;
108import org.bukkit.event.EventPriority;
109import org.bukkit.event.Listener;
110import org.bukkit.event.block.Action;
111import org.bukkit.event.block.BlockBreakEvent;
112import org.bukkit.event.entity.EntityPickupItemEvent;
113import org.bukkit.event.entity.EntityPlaceEvent;
114import org.bukkit.event.entity.EntityPotionEffectEvent;
115import org.bukkit.event.entity.PlayerDeathEvent;
116import org.bukkit.event.hanging.HangingBreakByEntityEvent;
117import org.bukkit.event.hanging.HangingPlaceEvent;
118import org.bukkit.event.inventory.ClickType;
119import org.bukkit.event.inventory.InventoryClickEvent;
120import org.bukkit.event.inventory.InventoryCloseEvent;
121import org.bukkit.event.player.AsyncPlayerChatEvent;
122import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
123import org.bukkit.event.player.PlayerBucketEmptyEvent;
124import org.bukkit.event.player.PlayerBucketFillEvent;
125import org.bukkit.event.player.PlayerChangedWorldEvent;
126import org.bukkit.event.player.PlayerCommandPreprocessEvent;
127import org.bukkit.event.player.PlayerDropItemEvent;
128import org.bukkit.event.player.PlayerEvent;
129import org.bukkit.event.player.PlayerInteractAtEntityEvent;
130import org.bukkit.event.player.PlayerInteractEntityEvent;
131import org.bukkit.event.player.PlayerInteractEvent;
132import org.bukkit.event.player.PlayerJoinEvent;
133import org.bukkit.event.player.PlayerLocaleChangeEvent;
134import org.bukkit.event.player.PlayerMoveEvent;
135import org.bukkit.event.player.PlayerPortalEvent;
136import org.bukkit.event.player.PlayerQuitEvent;
137import org.bukkit.event.player.PlayerRespawnEvent;
138import org.bukkit.event.player.PlayerTakeLecternBookEvent;
139import org.bukkit.event.player.PlayerTeleportEvent;
140import org.bukkit.event.vehicle.VehicleDestroyEvent;
141import org.bukkit.event.vehicle.VehicleEntityCollisionEvent;
142import org.bukkit.event.vehicle.VehicleMoveEvent;
143import org.bukkit.event.world.PortalCreateEvent;
144import org.bukkit.help.HelpTopic;
145import org.bukkit.inventory.ItemStack;
146import org.bukkit.inventory.PlayerInventory;
147import org.bukkit.inventory.meta.ItemMeta;
148import org.bukkit.metadata.FixedMetadataValue;
149import org.bukkit.metadata.MetadataValue;
150import org.bukkit.plugin.Plugin;
151import org.bukkit.potion.PotionEffect;
152import org.bukkit.util.Vector;
153import org.checkerframework.checker.nullness.qual.NonNull;
154
155import java.lang.reflect.Field;
156import java.util.HashSet;
157import java.util.List;
158import java.util.Locale;
159import java.util.Set;
160import java.util.UUID;
161
162/**
163 * Player Events involving plots.
164 */
165@SuppressWarnings("unused")
166public class PlayerEventListener implements Listener {
167
168    private static final Set<Material> MINECARTS = Set.of(
169            Material.MINECART,
170            Material.TNT_MINECART,
171            Material.CHEST_MINECART,
172            Material.COMMAND_BLOCK_MINECART,
173            Material.FURNACE_MINECART,
174            Material.HOPPER_MINECART
175    );
176    private static final Set<Material> BOOKS = Set.of(
177            Material.BOOK,
178            Material.KNOWLEDGE_BOOK,
179            Material.WRITABLE_BOOK,
180            Material.WRITTEN_BOOK
181    );
182    private static final Set<String> DYES;
183    static {
184        Set<String> mutableDyes = new HashSet<>(Set.of(
185                "WHITE_DYE",
186                "LIGHT_GRAY_DYE",
187                "GRAY_DYE",
188                "BLACK_DYE",
189                "BROWN_DYE",
190                "RED_DYE",
191                "ORANGE_DYE",
192                "YELLOW_DYE",
193                "LIME_DYE",
194                "GREEN_DYE",
195                "CYAN_DYE",
196                "LIGHT_BLUE_DYE",
197                "BLUE_DYE",
198                "PURPLE_DYE",
199                "MAGENTA_DYE",
200                "PINK_DYE",
201                "GLOW_INK_SAC"
202        ));
203        int[] version = PlotSquared.platform().serverVersion();
204        if (version[1] >= 20 && version[2] >= 1) {
205            mutableDyes.add("HONEYCOMB");
206        }
207        DYES = Set.copyOf(mutableDyes);
208    }
209    private final EventDispatcher eventDispatcher;
210    private final WorldEdit worldEdit;
211    private final PlotAreaManager plotAreaManager;
212    private final PlotListener plotListener;
213    // To prevent recursion
214    private boolean tmpTeleport = true;
215    private Field fieldPlayer;
216    private PlayerMoveEvent moveTmp;
217    private String internalVersion;
218
219    {
220        try {
221            fieldPlayer = PlayerEvent.class.getDeclaredField("player");
222            fieldPlayer.setAccessible(true);
223        } catch (NoSuchFieldException e) {
224            e.printStackTrace();
225        }
226    }
227
228    @Inject
229    public PlayerEventListener(
230            final @NonNull PlotAreaManager plotAreaManager,
231            final @NonNull EventDispatcher eventDispatcher,
232            final @NonNull WorldEdit worldEdit,
233            final @NonNull PlotListener plotListener
234    ) {
235        this.eventDispatcher = eventDispatcher;
236        this.worldEdit = worldEdit;
237        this.plotAreaManager = plotAreaManager;
238        this.plotListener = plotListener;
239    }
240
241    @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
242    public void onBlockBreak(final BlockBreakEvent event) {
243        Location location = BukkitUtil.adapt(event.getBlock().getLocation());
244        PlotArea area = location.getPlotArea();
245        if (area == null) {
246            return;
247        }
248        Plot plot = area.getPlot(location);
249        if (plot != null) {
250            event.setDropItems(plot.getFlag(TileDropFlag.class));
251        }
252    }
253
254    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
255    public void onPlayerDyeSign(PlayerInteractEvent event) {
256        ItemStack itemStack = event.getItem();
257        if (itemStack == null) {
258            return;
259        }
260        Block block = event.getClickedBlock();
261        if (block != null && block.getState() instanceof Sign) {
262            if (DYES.contains(itemStack.getType().toString())) {
263                Location location = BukkitUtil.adapt(block.getLocation());
264                PlotArea area = location.getPlotArea();
265                if (area == null) {
266                    return;
267                }
268                Plot plot = location.getOwnedPlot();
269                if (plot == null) {
270                    if (PlotFlagUtil.isAreaRoadFlagsAndFlagEquals(area, EditSignFlag.class, false)
271                            && !event.getPlayer().hasPermission(Permission.PERMISSION_ADMIN_INTERACT_ROAD.toString())) {
272                        event.setCancelled(true);
273                    }
274                    return;
275                }
276                if (plot.isAdded(event.getPlayer().getUniqueId())) {
277                    return; // allow for added players
278                }
279                if (!plot.getFlag(EditSignFlag.class)
280                        && !event.getPlayer().hasPermission(Permission.PERMISSION_ADMIN_INTERACT_OTHER.toString())) {
281                    plot.debug(event.getPlayer().getName() + " could not color the sign because of edit-sign = false");
282                    event.setCancelled(true);
283                }
284            }
285        }
286    }
287
288    @EventHandler(ignoreCancelled = true)
289    public void onEffect(@NonNull EntityPotionEffectEvent event) {
290        if (Settings.Enabled_Components.DISABLE_BEACON_EFFECT_OVERFLOW ||
291                event.getCause() != EntityPotionEffectEvent.Cause.BEACON ||
292                !(event.getEntity() instanceof Player player)) {
293            return;
294        }
295
296        UUID uuid = player.getUniqueId();
297        PotionEffect effect = event.getNewEffect();
298        if (effect == null) {
299            PotionEffect oldEffect = event.getOldEffect();
300            if (oldEffect != null) {
301                String name = oldEffect.getType().getName();
302                plotListener.addEffect(uuid, name, -1);
303            }
304        } else {
305            long expiresAt = System.currentTimeMillis() + effect.getDuration() * 50L; //Convert ticks to milliseconds
306            String name = effect.getType().getName();
307            plotListener.addEffect(uuid, name, expiresAt);
308        }
309    }
310
311    @EventHandler
312    public void onVehicleEntityCollision(VehicleEntityCollisionEvent e) {
313        if (e.getVehicle().getType() == EntityType.BOAT) {
314            Location location = BukkitUtil.adapt(e.getEntity().getLocation());
315            if (location.isPlotArea()) {
316                if (e.getEntity() instanceof Player) {
317                    PlotPlayer<Player> player = BukkitUtil.adapt((Player) e.getEntity());
318                    Plot plot = player.getCurrentPlot();
319                    if (plot != null) {
320                        if (!plot.isAdded(player.getUUID())) {
321                            //Here the event is only canceled if the player is not the owner
322                            //of the property on which he is located.
323                            e.setCancelled(true);
324                        }
325                    } else {
326                        e.setCancelled(true);
327                    }
328                } else {
329                    //Here the event is cancelled too, otherwise you can move the
330                    //boat with EchoPets or other mobs running around on the plot.
331                    e.setCancelled(true);
332                }
333            }
334        }
335    }
336
337    @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
338    public void playerCommand(PlayerCommandPreprocessEvent event) {
339        String msg = event.getMessage().replace("/", "").toLowerCase(Locale.ROOT).trim();
340        if (msg.isEmpty()) {
341            return;
342        }
343        Player player = event.getPlayer();
344        PlotPlayer<Player> plotPlayer = BukkitUtil.adapt(player);
345        Location location = plotPlayer.getLocation();
346        PlotArea area = location.getPlotArea();
347        if (area == null) {
348            return;
349        }
350        String[] parts = msg.split(" ");
351        Plot plot = plotPlayer.getCurrentPlot();
352        // Check WorldEdit
353        switch (parts[0]) {
354            case "up", "worldedit:up" -> {
355                if (plot == null || (!plot.isAdded(plotPlayer.getUUID()) && !plotPlayer.hasPermission(
356                        Permission.PERMISSION_ADMIN_BUILD_OTHER,
357                        true
358                ))) {
359                    event.setCancelled(true);
360                    return;
361                }
362            }
363        }
364        if (plot == null && !area.isRoadFlags()) {
365            return;
366        }
367
368        List<String> blockedCommands = plot != null ?
369                plot.getFlag(BlockedCmdsFlag.class) :
370                area.getFlag(BlockedCmdsFlag.class);
371        if (blockedCommands.isEmpty()) {
372            return;
373        }
374        if (plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_INTERACT_BLOCKED_CMDS)) {
375            return;
376        }
377        // When using namespaced commands, we're not interested in the namespace
378        String part = parts[0];
379        if (part.contains(":")) {
380            String[] namespaced = part.split(":");
381            part = namespaced[1];
382            msg = msg.substring(namespaced[0].length() + 1);
383        }
384        msg = replaceAliases(msg, part);
385        for (String blocked : blockedCommands) {
386            if (blocked.equalsIgnoreCase(msg)) {
387                String perm;
388                if (plot != null && plot.isAdded(plotPlayer.getUUID())) {
389                    perm = "plots.admin.command.blocked-cmds.shared";
390                } else {
391                    perm = "plots.admin.command.blocked-cmds.road";
392                }
393                if (!plotPlayer.hasPermission(perm)) {
394                    plotPlayer.sendMessage(TranslatableCaption.of("blockedcmds.command_blocked"));
395                    event.setCancelled(true);
396                }
397                return;
398            }
399        }
400    }
401
402    private String replaceAliases(String msg, String part) {
403        String s1 = part;
404        Set<String> aliases = new HashSet<>();
405        for (HelpTopic cmdLabel : Bukkit.getServer().getHelpMap().getHelpTopics()) {
406            if (part.equals(cmdLabel.getName())) {
407                break;
408            }
409            String label = cmdLabel.getName().replaceFirst("/", "");
410            if (aliases.contains(label)) {
411                continue;
412            }
413            PluginCommand p = Bukkit.getPluginCommand(label);
414            if (p != null) {
415                for (String a : p.getAliases()) {
416                    if (aliases.contains(a)) {
417                        continue;
418                    }
419                    aliases.add(a);
420                    a = a.replaceFirst("/", "");
421                    if (!a.equals(label) && a.equals(part)) {
422                        part = label;
423                        break;
424                    }
425                }
426            }
427        }
428        if (!s1.equals(part)) {
429            msg = msg.replace(s1, part);
430        }
431        return msg;
432    }
433
434    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
435    public void onPreLogin(final AsyncPlayerPreLoginEvent event) {
436        final UUID uuid;
437        if (Settings.UUID.OFFLINE) {
438            if (Settings.UUID.FORCE_LOWERCASE) {
439                uuid = UUID.nameUUIDFromBytes(("OfflinePlayer:" + event.getName().toLowerCase()).getBytes(Charsets.UTF_8));
440            } else {
441                uuid = UUID.nameUUIDFromBytes(("OfflinePlayer:" + event.getName()).getBytes(Charsets.UTF_8));
442            }
443        } else {
444            uuid = event.getUniqueId();
445        }
446        PlotSquared.get().getImpromptuUUIDPipeline().storeImmediately(event.getName(), uuid);
447    }
448
449    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
450    @SuppressWarnings("deprecation") // Paper deprecation
451    public void onConnect(PlayerJoinEvent event) {
452        final Player player = event.getPlayer();
453        PlotSquared.platform().playerManager().removePlayer(player.getUniqueId());
454        final PlotPlayer<Player> pp = BukkitUtil.adapt(player);
455
456        // we're stripping the country code as we don't want to differ between countries
457        pp.setLocale(Locale.forLanguageTag(player.getLocale().substring(0, 2)));
458
459        Location location = pp.getLocation();
460        PlotArea area = location.getPlotArea();
461        if (area != null) {
462            Plot plot = area.getPlot(location);
463            if (plot != null) {
464                plotListener.plotEntry(pp, plot);
465            }
466        }
467        // Delayed
468
469        // Async
470        TaskManager.runTaskLaterAsync(() -> {
471            if (!player.hasPlayedBefore() && player.isOnline()) {
472                player.saveData();
473            }
474            this.eventDispatcher.doJoinTask(pp);
475        }, TaskTime.seconds(1L));
476
477        if (pp.hasPermission(Permission.PERMISSION_ADMIN_UPDATE_NOTIFICATION.toString()) && Settings.Enabled_Components.UPDATE_NOTIFICATIONS
478                && PremiumVerification.isPremium() && UpdateUtility.hasUpdate) {
479            Caption boundary = TranslatableCaption.of("update.update_boundary");
480            Caption updateNotification = TranslatableCaption.of("update.update_notification");
481            pp.sendMessage(boundary);
482            pp.sendMessage(
483                    updateNotification,
484                    TagResolver.builder()
485                            .tag("p2version", Tag.inserting(Component.text(UpdateUtility.internalVersion.versionString())))
486                            .tag("spigotversion", Tag.inserting(Component.text(UpdateUtility.spigotVersion)))
487                            .tag("downloadurl", Tag.preProcessParsed("https://www.spigotmc.org/resources/77506/updates"))
488                            .build()
489            );
490            pp.sendMessage(boundary);
491        }
492    }
493
494    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
495    public void playerRespawn(PlayerRespawnEvent event) {
496        Player player = event.getPlayer();
497        PlotPlayer<Player> pp = BukkitUtil.adapt(player);
498        this.eventDispatcher.doRespawnTask(pp);
499    }
500
501    @SuppressWarnings("deprecation") // We explicitly want #getHomeSynchronous here
502    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
503    public void onTeleport(PlayerTeleportEvent event) {
504        Player player = event.getPlayer();
505        //We need to account for bad plugins like NoCheatPlus that teleports player on/before login -_-
506        if (!player.isOnline()) {
507            return;
508        }
509        BukkitPlayer pp = BukkitUtil.adapt(player);
510        try (final MetaDataAccess<Plot> lastPlotAccess =
511                     pp.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LAST_PLOT)) {
512            Plot lastPlot = lastPlotAccess.get().orElse(null);
513            org.bukkit.Location to = event.getTo();
514            //noinspection ConstantConditions
515            if (to != null) {
516                Location location = BukkitUtil.adapt(to);
517                PlotArea area = location.getPlotArea();
518                if (area == null) {
519                    if (lastPlot != null) {
520                        plotListener.plotExit(pp, lastPlot);
521                        lastPlotAccess.remove();
522                    }
523                    try (final MetaDataAccess<Location> lastLocationAccess =
524                                 pp.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LOCATION)) {
525                        lastLocationAccess.remove();
526                    }
527                    return;
528                }
529                Plot plot = area.getPlot(location);
530                if (plot != null) {
531                    final boolean result = DenyTeleportFlag.allowsTeleport(pp, plot);
532                    // there is one possibility to still allow teleportation:
533                    // to is identical to the plot's home location, and untrusted-visit is true
534                    // i.e. untrusted-visit can override deny-teleport
535                    // this is acceptable, because otherwise it wouldn't make sense to have both flags set
536                    if (!result && !(plot.getFlag(UntrustedVisitFlag.class) && plot
537                            .getHomeSynchronous()
538                            .equals(BukkitUtil.adaptComplete(to)))) {
539                        pp.sendMessage(
540                                TranslatableCaption.of("deny.no_enter"),
541                                TagResolver.resolver("plot", Tag.inserting(Component.text(plot.toString())))
542                        );
543                        event.setCancelled(true);
544                    }
545                }
546            }
547        }
548        playerMove(event);
549    }
550
551    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
552    public void vehicleMove(VehicleMoveEvent event)
553            throws IllegalAccessException {
554        final org.bukkit.Location from = event.getFrom();
555        final org.bukkit.Location to = event.getTo();
556
557        int toX, toZ;
558        if ((toX = MathMan.roundInt(to.getX())) != MathMan.roundInt(from.getX()) | (toZ = MathMan.roundInt(to.getZ())) != MathMan
559                .roundInt(from.getZ())) {
560            Vehicle vehicle = event.getVehicle();
561
562            // Check allowed
563            if (!vehicle.getPassengers().isEmpty()) {
564                Entity passenger = vehicle.getPassengers().get(0);
565
566                if (passenger instanceof final Player player) {
567                    // reset
568                    if (moveTmp == null) {
569                        moveTmp = new PlayerMoveEvent(null, from, to);
570                    }
571                    moveTmp.setFrom(from);
572                    moveTmp.setTo(to);
573                    moveTmp.setCancelled(false);
574                    fieldPlayer.set(moveTmp, player);
575
576                    List<Entity> passengers = vehicle.getPassengers();
577
578                    this.playerMove(moveTmp);
579                    org.bukkit.Location dest;
580                    if (moveTmp.isCancelled()) {
581                        dest = from;
582                    } else if (MathMan.roundInt(moveTmp.getTo().getX()) != toX || MathMan.roundInt(moveTmp
583                            .getTo()
584                            .getZ()) != toZ) {
585                        dest = to;
586                    } else {
587                        dest = null;
588                    }
589                    if (dest != null) {
590                        vehicle.eject();
591                        vehicle.setVelocity(new Vector(0d, 0d, 0d));
592                        PaperLib.teleportAsync(vehicle, dest);
593                        passengers.forEach(vehicle::addPassenger);
594                        return;
595                    }
596                }
597                if (Settings.Enabled_Components.KILL_ROAD_VEHICLES) {
598                    final com.sk89q.worldedit.world.entity.EntityType entityType = BukkitAdapter.adapt(vehicle.getType());
599                    // Horses etc are vehicles, but they're also animals
600                    // so this filters out all living entities
601                    if (EntityCategories.VEHICLE.contains(entityType) && !EntityCategories.ANIMAL.contains(entityType)) {
602                        List<MetadataValue> meta = vehicle.getMetadata("plot");
603                        Plot toPlot = BukkitUtil.adapt(to).getPlot();
604                        if (!meta.isEmpty()) {
605                            Plot origin = (Plot) meta.get(0).value();
606                            if (origin != null && !origin.getBasePlot(false).equals(toPlot)) {
607                                vehicle.remove();
608                            }
609                        } else if (toPlot != null) {
610                            vehicle.setMetadata("plot", new FixedMetadataValue((Plugin) PlotSquared.platform(), toPlot));
611                        }
612                    }
613                }
614            }
615
616        }
617    }
618
619    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
620    public void playerMove(PlayerMoveEvent event) {
621        org.bukkit.Location from = event.getFrom();
622        org.bukkit.Location to = event.getTo();
623        int x2;
624        if (MathMan.roundInt(from.getX()) != (x2 = MathMan.roundInt(to.getX()))) {
625            Player player = event.getPlayer();
626            BukkitPlayer pp = BukkitUtil.adapt(player);
627            // Cancel teleport
628            if (TaskManager.removeFromTeleportQueue(pp.getName())) {
629                pp.sendMessage(TranslatableCaption.of("teleport.teleport_failed"));
630            }
631            // Set last location
632            Location location = BukkitUtil.adapt(to);
633            try (final MetaDataAccess<Location> lastLocationAccess =
634                         pp.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LOCATION)) {
635                lastLocationAccess.remove();
636            }
637            PlotArea area = location.getPlotArea();
638            if (area == null) {
639                try (final MetaDataAccess<Plot> lastPlotAccess =
640                             pp.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LAST_PLOT)) {
641                    lastPlotAccess.remove();
642                }
643                return;
644            }
645            Plot now = area.getPlot(location);
646            Plot lastPlot;
647            try (final MetaDataAccess<Plot> lastPlotAccess =
648                         pp.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LAST_PLOT)) {
649                lastPlot = lastPlotAccess.get().orElse(null);
650            }
651            if (now == null) {
652                try (final MetaDataAccess<Boolean> kickAccess =
653                             pp.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_KICK)) {
654                    if (lastPlot != null && !plotListener.plotExit(pp, lastPlot) && this.tmpTeleport && !kickAccess.get().orElse(
655                            false)) {
656                        pp.sendMessage(
657                                TranslatableCaption.of("permission.no_permission_event"),
658                                TagResolver.resolver(
659                                        "node",
660                                        Tag.inserting(Permission.PERMISSION_ADMIN_EXIT_DENIED)
661                                )
662                        );
663                        this.tmpTeleport = false;
664                        if (lastPlot.equals(BukkitUtil.adapt(from).getPlot())) {
665                            player.teleport(from);
666                        } else {
667                            player.teleport(player.getWorld().getSpawnLocation());
668                        }
669                        this.tmpTeleport = true;
670                        event.setCancelled(true);
671                        return;
672                    }
673                }
674            } else if (now.equals(lastPlot)) {
675                ForceFieldListener.handleForcefield(player, pp, now);
676            } else if (!plotListener.plotEntry(pp, now) && this.tmpTeleport) {
677                pp.sendMessage(
678                        TranslatableCaption.of("deny.no_enter"),
679                        TagResolver.resolver("plot", Tag.inserting(Component.text(now.toString())))
680                );
681                this.tmpTeleport = false;
682                to.setX(from.getBlockX());
683                to.setY(from.getBlockY());
684                to.setZ(from.getBlockZ());
685                player.teleport(event.getTo());
686                this.tmpTeleport = true;
687                return;
688            }
689            int border = area.getBorder(true);
690            int x1;
691            if (x2 > border && this.tmpTeleport) {
692                if (!pp.hasPermission(Permission.PERMISSION_ADMIN_BYPASS_BORDER)) {
693                    to.setX(border - 1);
694                    this.tmpTeleport = false;
695                    player.teleport(event.getTo());
696                    this.tmpTeleport = true;
697                    pp.sendMessage(TranslatableCaption.of("border.denied"));
698                } else if (MathMan.roundInt(from.getX()) <= border) { // Only send if they just moved out of the border
699                    pp.sendMessage(TranslatableCaption.of("border.bypass.exited"));
700                }
701            } else if (x2 < -border && this.tmpTeleport) {
702                if (!pp.hasPermission(Permission.PERMISSION_ADMIN_BYPASS_BORDER)) {
703                    to.setX(-border + 1);
704                    this.tmpTeleport = false;
705                    player.teleport(event.getTo());
706                    this.tmpTeleport = true;
707                    pp.sendMessage(TranslatableCaption.of("border.denied"));
708                } else if (MathMan.roundInt(from.getX()) >= -border) { // Only send if they just moved out of the border
709                    pp.sendMessage(TranslatableCaption.of("border.bypass.exited"));
710                }
711            } else if (((x1 = MathMan.roundInt(from.getX())) >= border && x2 <= border) || (x1 <= -border && x2 >= -border)) {
712                if (pp.hasPermission(Permission.PERMISSION_ADMIN_BYPASS_BORDER)) {
713                    pp.sendMessage(TranslatableCaption.of("border.bypass.entered"));
714                }
715            }
716        }
717        int z2;
718        if (MathMan.roundInt(from.getZ()) != (z2 = MathMan.roundInt(to.getZ()))) {
719            Player player = event.getPlayer();
720            BukkitPlayer pp = BukkitUtil.adapt(player);
721            // Cancel teleport
722            if (TaskManager.removeFromTeleportQueue(pp.getName())) {
723                pp.sendMessage(TranslatableCaption.of("teleport.teleport_failed"));
724            }
725            // Set last location
726            Location location = BukkitUtil.adapt(to);
727            try (final MetaDataAccess<Location> lastLocationAccess =
728                         pp.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LOCATION)) {
729                lastLocationAccess.set(location);
730            }
731            PlotArea area = location.getPlotArea();
732            if (area == null) {
733                try (final MetaDataAccess<Plot> lastPlotAccess =
734                             pp.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LAST_PLOT)) {
735                    lastPlotAccess.remove();
736                }
737                return;
738            }
739            Plot plot = area.getPlot(location);
740            Plot lastPlot;
741            try (final MetaDataAccess<Plot> lastPlotAccess =
742                         pp.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LAST_PLOT)) {
743                lastPlot = lastPlotAccess.get().orElse(null);
744            }
745            if (plot == null) {
746                try (final MetaDataAccess<Boolean> kickAccess =
747                             pp.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_KICK)) {
748                    if (lastPlot != null && !plotListener.plotExit(pp, lastPlot) && this.tmpTeleport && !kickAccess.get().orElse(
749                            false)) {
750                        pp.sendMessage(
751                                TranslatableCaption.of("permission.no_permission_event"),
752                                TagResolver.resolver(
753                                        "node",
754                                        Tag.inserting(Permission.PERMISSION_ADMIN_EXIT_DENIED)
755                                )
756                        );
757                        this.tmpTeleport = false;
758                        if (lastPlot.equals(BukkitUtil.adapt(from).getPlot())) {
759                            player.teleport(from);
760                        } else {
761                            player.teleport(player.getWorld().getSpawnLocation());
762                        }
763                        this.tmpTeleport = true;
764                        event.setCancelled(true);
765                        return;
766                    }
767                }
768            } else if (plot.equals(lastPlot)) {
769                ForceFieldListener.handleForcefield(player, pp, plot);
770            } else if (!plotListener.plotEntry(pp, plot) && this.tmpTeleport) {
771                pp.sendMessage(
772                        TranslatableCaption.of("deny.no_enter"),
773                        TagResolver.resolver("plot", Tag.inserting(Component.text(plot.toString())))
774                );
775                this.tmpTeleport = false;
776                player.teleport(from);
777                to.setX(from.getBlockX());
778                to.setY(from.getBlockY());
779                to.setZ(from.getBlockZ());
780                player.teleport(event.getTo());
781                this.tmpTeleport = true;
782                return;
783            }
784            int border = area.getBorder(true);
785            int z1;
786            if (z2 > border && this.tmpTeleport) {
787                if (!pp.hasPermission(Permission.PERMISSION_ADMIN_BYPASS_BORDER)) {
788                    to.setZ(border - 1);
789                    this.tmpTeleport = false;
790                    player.teleport(event.getTo());
791                    this.tmpTeleport = true;
792                    pp.sendMessage(TranslatableCaption.of("border.denied"));
793                } else if (MathMan.roundInt(from.getZ()) <= border) { // Only send if they just moved out of the border
794                    pp.sendMessage(TranslatableCaption.of("border.bypass.exited"));
795                }
796            } else if (z2 < -border && this.tmpTeleport) {
797                if (!pp.hasPermission(Permission.PERMISSION_ADMIN_BYPASS_BORDER)) {
798                    to.setZ(-border + 1);
799                    this.tmpTeleport = false;
800                    player.teleport(event.getTo());
801                    this.tmpTeleport = true;
802                    pp.sendMessage(TranslatableCaption.of("border.denied"));
803                } else if (MathMan.roundInt(from.getZ()) >= -border) { // Only send if they just moved out of the border
804                    pp.sendMessage(TranslatableCaption.of("border.bypass.exited"));
805                }
806            } else if (((z1 = MathMan.roundInt(from.getZ())) >= border && z2 <= border) || (z1 <= -border && z2 >= -border)) {
807                if (pp.hasPermission(Permission.PERMISSION_ADMIN_BYPASS_BORDER)) {
808                    pp.sendMessage(TranslatableCaption.of("border.bypass.entered"));
809                }
810            }
811        }
812    }
813
814    @EventHandler(priority = EventPriority.LOW)
815    @SuppressWarnings("deprecation") // Paper deprecation
816    public void onChat(AsyncPlayerChatEvent event) {
817        if (event.isCancelled()) {
818            return;
819        }
820
821        BukkitPlayer plotPlayer = BukkitUtil.adapt(event.getPlayer());
822        Location location = plotPlayer.getLocation();
823        PlotArea area = location.getPlotArea();
824        if (area == null) {
825            return;
826        }
827        Plot plot = area.getPlot(location);
828        if (plot == null) {
829            return;
830        }
831        if (!((plot.getFlag(ChatFlag.class) && area.isPlotChat() && plotPlayer.getAttribute("chat"))
832                || area.isForcingPlotChat())) {
833            return;
834        }
835        if (plot.isDenied(plotPlayer.getUUID()) && !plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_CHAT_BYPASS)) {
836            return;
837        }
838        event.setCancelled(true);
839        Set<Player> recipients = event.getRecipients();
840        recipients.clear();
841        Set<PlotPlayer<?>> spies = new HashSet<>();
842        Set<PlotPlayer<?>> plotRecipients = new HashSet<>();
843        for (final PlotPlayer<?> pp : PlotSquared.platform().playerManager().getPlayers()) {
844            if (pp.getAttribute("chatspy")) {
845                spies.add(pp);
846            } else {
847                Plot current = pp.getCurrentPlot();
848                if (current != null && current.getBasePlot(false).equals(plot)) {
849                    plotRecipients.add(pp);
850                }
851            }
852        }
853        String message = event.getMessage();
854        String sender = event.getPlayer().getDisplayName();
855        PlotId id = plot.getId();
856        String worldName = plot.getWorldName();
857        Caption msg = TranslatableCaption.of("chat.plot_chat_format");
858        TagResolver.Builder builder = TagResolver.builder();
859        builder.tag("world", Tag.inserting(Component.text(worldName)));
860        builder.tag("plot_id", Tag.inserting(Component.text(id.toString())));
861        builder.tag("sender", Tag.inserting(Component.text(sender)));
862        if (plotPlayer.hasPermission("plots.chat.color")) {
863            builder.tag("msg", Tag.inserting(MiniMessage.miniMessage().deserialize(
864                    message,
865                    TagResolver.resolver(StandardTags.color(), StandardTags.gradient(),
866                            StandardTags.rainbow(), StandardTags.decorations()
867                    )
868            )));
869        } else {
870            builder.tag("msg", Tag.inserting(Component.text(message)));
871        }
872        for (PlotPlayer<?> receiver : plotRecipients) {
873            receiver.sendMessage(msg, builder.build());
874        }
875        if (!spies.isEmpty()) {
876            Caption spymsg = TranslatableCaption.of("chat.plot_chat_spy_format");
877            for (PlotPlayer<?> player : spies) {
878                player.sendMessage(spymsg, builder.tag("message", Tag.inserting(Component.text(message))).build());
879            }
880        }
881        if (Settings.Chat.LOG_PLOTCHAT_TO_CONSOLE) {
882            Caption spymsg = TranslatableCaption.of("chat.plot_chat_spy_format");
883            ConsolePlayer.getConsole().sendMessage(
884                    spymsg,
885                    builder.tag("message", Tag.inserting(Component.text(message))).build()
886            );
887        }
888    }
889
890    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
891    public void onWorldChanged(PlayerChangedWorldEvent event) {
892        Player player = event.getPlayer();
893        BukkitPlayer pp = BukkitUtil.adapt(player);
894        // Delete last location
895        Plot plot;
896        try (final MetaDataAccess<Plot> lastPlotAccess =
897                     pp.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LAST_PLOT)) {
898            plot = lastPlotAccess.remove();
899        }
900        try (final MetaDataAccess<Location> lastLocationAccess =
901                     pp.accessTemporaryMetaData(PlayerMetaDataKeys.TEMPORARY_LOCATION)) {
902            lastLocationAccess.remove();
903        }
904        if (plot != null) {
905            plotListener.plotExit(pp, plot);
906        }
907        if (this.worldEdit != null) {
908            if (!pp.hasPermission(Permission.PERMISSION_WORLDEDIT_BYPASS)) {
909                if (pp.getAttribute("worldedit")) {
910                    pp.removeAttribute("worldedit");
911                }
912            }
913        }
914        Location location = pp.getLocation();
915        PlotArea area = location.getPlotArea();
916        if (location.isPlotArea()) {
917            plot = location.getPlot();
918            if (plot != null) {
919                plotListener.plotEntry(pp, plot);
920            }
921        }
922    }
923
924    @SuppressWarnings("deprecation")
925    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
926    public void onInventoryClick(InventoryClickEvent event) {
927        /*if (!event.isLeftClick() || (event.getAction() != InventoryAction.PLACE_ALL) || event
928            .isShiftClick()) {
929            return;
930        }*/
931        HumanEntity entity = event.getWhoClicked();
932        if (!(entity instanceof Player) || !this.plotAreaManager
933                .hasPlotArea(entity.getWorld().getName())) {
934            return;
935        }
936
937        HumanEntity clicker = event.getWhoClicked();
938        if (!(clicker instanceof Player player)) {
939            return;
940        }
941        BukkitPlayer pp = BukkitUtil.adapt(player);
942        final PlotInventory inventory = PlotInventory.getOpenPlotInventory(pp);
943        if (inventory != null && event.getRawSlot() == event.getSlot()) {
944            if (!inventory.onClick(event.getSlot())) {
945                event.setResult(Event.Result.DENY);
946                event.setCancelled(true);
947                inventory.close();
948            }
949        }
950        PlayerInventory inv = player.getInventory();
951        int slot = inv.getHeldItemSlot();
952        if ((slot > 8) || !event.getEventName().equals("InventoryCreativeEvent")) {
953            return;
954        }
955        ItemStack oldItem = inv.getItemInHand();
956        ItemMeta oldMeta = oldItem.getItemMeta();
957        ItemStack newItem = event.getCursor();
958        ItemMeta newMeta = newItem.getItemMeta();
959
960        if (event.getClick() == ClickType.CREATIVE) {
961            final Plot plot = pp.getCurrentPlot();
962            if (plot != null) {
963                if (plot.getFlag(PreventCreativeCopyFlag.class) && !plot
964                        .isAdded(player.getUniqueId()) && !pp.hasPermission(Permission.PERMISSION_ADMIN_INTERACT_OTHER)) {
965                    final ItemStack newStack =
966                            new ItemStack(newItem.getType(), newItem.getAmount());
967                    event.setCursor(newStack);
968                    plot.debug(player.getName()
969                            + " could not creative-copy an item because prevent-creative-copy = true");
970                }
971            } else {
972                PlotArea area = pp.getPlotAreaAbs();
973                if (area != null && PlotFlagUtil.isAreaRoadFlagsAndFlagEquals(area, PreventCreativeCopyFlag.class, true)) {
974                    final ItemStack newStack =
975                            new ItemStack(newItem.getType(), newItem.getAmount());
976                    event.setCursor(newStack);
977                }
978            }
979            return;
980        }
981
982        String newLore = "";
983        if (newMeta != null) {
984            List<String> lore = newMeta.getLore();
985            if (lore != null) {
986                newLore = lore.toString();
987            }
988        }
989        String oldLore = "";
990        if (oldMeta != null) {
991            List<String> lore = oldMeta.getLore();
992            if (lore != null) {
993                oldLore = lore.toString();
994            }
995        }
996        Material itemType = newItem.getType();
997        if (!"[(+NBT)]".equals(newLore) || (oldItem.equals(newItem) && newLore.equals(oldLore))) {
998            if (newMeta == null || (itemType != Material.LEGACY_BANNER && itemType != Material.PLAYER_HEAD)) {
999                return;
1000            }
1001        }
1002        Block block = player.getTargetBlock(null, 7);
1003        org.bukkit.block.BlockState state = block.getState();
1004        Material stateType = state.getType();
1005        if (stateType != itemType) {
1006            if (stateType == Material.LEGACY_WALL_BANNER || stateType == Material.LEGACY_STANDING_BANNER) {
1007                if (itemType != Material.LEGACY_BANNER) {
1008                    return;
1009                }
1010            } else if (stateType == Material.LEGACY_SKULL) {
1011                if (itemType != Material.LEGACY_SKULL_ITEM) {
1012                    return;
1013                }
1014            } else {
1015                return;
1016            }
1017        }
1018        Location location = BukkitUtil.adapt(state.getLocation());
1019        PlotArea area = location.getPlotArea();
1020        if (area == null) {
1021            return;
1022        }
1023        Plot plot = area.getPlotAbs(location);
1024        boolean cancelled = false;
1025        if (plot == null) {
1026            if (!pp.hasPermission(Permission.PERMISSION_ADMIN_INTERACT_ROAD)) {
1027                pp.sendMessage(
1028                        TranslatableCaption.of("permission.no_permission_event"),
1029                        TagResolver.resolver(
1030                                "node",
1031                                Tag.inserting(Permission.PERMISSION_ADMIN_INTERACT_ROAD)
1032                        )
1033                );
1034                cancelled = true;
1035            }
1036        } else if (!plot.hasOwner()) {
1037            if (!pp.hasPermission(Permission.PERMISSION_ADMIN_INTERACT_UNOWNED)) {
1038                pp.sendMessage(
1039                        TranslatableCaption.of("permission.no_permission_event"),
1040                        TagResolver.resolver(
1041                                "node",
1042                                Tag.inserting(Permission.PERMISSION_ADMIN_INTERACT_UNOWNED)
1043                        )
1044                );
1045                cancelled = true;
1046            }
1047        } else {
1048            UUID uuid = pp.getUUID();
1049            if (!plot.isAdded(uuid)) {
1050                if (!pp.hasPermission(Permission.PERMISSION_ADMIN_INTERACT_OTHER)) {
1051                    pp.sendMessage(
1052                            TranslatableCaption.of("permission.no_permission_event"),
1053                            TagResolver.resolver(
1054                                    "node",
1055                                    Tag.inserting(Permission.PERMISSION_ADMIN_INTERACT_OTHER)
1056                            )
1057                    );
1058                    cancelled = true;
1059                }
1060            }
1061        }
1062        if (cancelled) {
1063            if ((oldItem.getType() == newItem.getType()) && (oldItem.getDurability() == newItem
1064                    .getDurability())) {
1065                event.setCursor(
1066                        new ItemStack(newItem.getType(), newItem.getAmount(), newItem.getDurability()));
1067                event.setCancelled(true);
1068                return;
1069            }
1070            event.setCursor(
1071                    new ItemStack(newItem.getType(), newItem.getAmount(), newItem.getDurability()));
1072        }
1073    }
1074
1075    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
1076    public void onInteract(PlayerInteractAtEntityEvent e) {
1077        Entity entity = e.getRightClicked();
1078        if (!(entity instanceof ArmorStand) && !(entity instanceof ItemFrame)) {
1079            return;
1080        }
1081        Location location = BukkitUtil.adapt(e.getRightClicked().getLocation());
1082        PlotArea area = location.getPlotArea();
1083        if (area == null) {
1084            return;
1085        }
1086        EntitySpawnListener.testNether(entity);
1087        Plot plot = location.getPlotAbs();
1088        BukkitPlayer pp = BukkitUtil.adapt(e.getPlayer());
1089        if (plot == null) {
1090            if (!PlotFlagUtil.isAreaRoadFlagsAndFlagEquals(area, MiscInteractFlag.class, true) && !pp.hasPermission(
1091                    Permission.PERMISSION_ADMIN_INTERACT_ROAD
1092            )) {
1093                pp.sendMessage(
1094                        TranslatableCaption.of("permission.no_permission_event"),
1095                        TagResolver.resolver(
1096                                "node",
1097                                Tag.inserting(Permission.PERMISSION_ADMIN_INTERACT_ROAD)
1098                        )
1099                );
1100                e.setCancelled(true);
1101            }
1102        } else {
1103            if (Settings.Done.RESTRICT_BUILDING && DoneFlag.isDone(plot)) {
1104                if (!pp.hasPermission(Permission.PERMISSION_ADMIN_BUILD_OTHER)) {
1105                    pp.sendMessage(TranslatableCaption.of("done.building_restricted"));
1106                    e.setCancelled(true);
1107                    return;
1108                }
1109            }
1110            if (!plot.hasOwner()) {
1111                if (!pp.hasPermission("plots.admin.interact.unowned")) {
1112                    pp.sendMessage(
1113                            TranslatableCaption.of("permission.no_permission_event"),
1114                            TagResolver.resolver(
1115                                    "node",
1116                                    Tag.inserting(Permission.PERMISSION_ADMIN_INTERACT_UNOWNED)
1117                            )
1118                    );
1119                    e.setCancelled(true);
1120                }
1121            } else {
1122                UUID uuid = pp.getUUID();
1123                if (plot.isAdded(uuid)) {
1124                    return;
1125                }
1126                if (plot.getFlag(MiscInteractFlag.class)) {
1127                    return;
1128                }
1129                if (!pp.hasPermission(Permission.PERMISSION_ADMIN_INTERACT_OTHER)) {
1130                    pp.sendMessage(
1131                            TranslatableCaption.of("permission.no_permission_event"),
1132                            TagResolver.resolver(
1133                                    "node",
1134                                    Tag.inserting(Permission.PERMISSION_ADMIN_INTERACT_OTHER)
1135                            )
1136                    );
1137                    e.setCancelled(true);
1138                    plot.debug(pp.getName() + " could not interact with " + entity.getType()
1139                            + " because misc-interact = false");
1140                }
1141            }
1142        }
1143    }
1144
1145    @EventHandler(priority = EventPriority.LOW)
1146    @SuppressWarnings("deprecation") // Paper deprecation
1147    public void onCancelledInteract(PlayerInteractEvent event) {
1148        if (event.isCancelled() && event.getAction() == Action.RIGHT_CLICK_AIR) {
1149            Player player = event.getPlayer();
1150            BukkitPlayer pp = BukkitUtil.adapt(player);
1151            PlotArea area = pp.getPlotAreaAbs();
1152            if (area == null) {
1153                return;
1154            }
1155            if (event.getAction() == Action.RIGHT_CLICK_AIR) {
1156                Material item = event.getMaterial();
1157                if (item.toString().toLowerCase().endsWith("_egg")) {
1158                    event.setCancelled(true);
1159                    event.setUseItemInHand(Event.Result.DENY);
1160                }
1161            }
1162            ItemStack hand = player.getInventory().getItemInMainHand();
1163            ItemStack offHand = player.getInventory().getItemInOffHand();
1164            Material type = hand.getType();
1165            Material offType = offHand.getType();
1166            if (type == Material.AIR) {
1167                type = offType;
1168            }
1169            if (type.toString().toLowerCase().endsWith("_egg")) {
1170                Block block = player.getTargetBlockExact(5, FluidCollisionMode.SOURCE_ONLY);
1171                if (block != null && block.getType() != Material.AIR) {
1172                    Location location = BukkitUtil.adapt(block.getLocation());
1173                    if (!this.eventDispatcher.checkPlayerBlockEvent(pp, PlayerBlockEventType.SPAWN_MOB, location, null, true)) {
1174                        event.setCancelled(true);
1175                        event.setUseItemInHand(Event.Result.DENY);
1176                    }
1177                }
1178            }
1179        }
1180    }
1181
1182    @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
1183    public void onInteract(PlayerInteractEvent event) {
1184        Player player = event.getPlayer();
1185        BukkitPlayer pp = BukkitUtil.adapt(player);
1186        PlotArea area = pp.getPlotAreaAbs();
1187        if (area == null) {
1188            return;
1189        }
1190        PlayerBlockEventType eventType;
1191        BlockType blocktype1;
1192        Block block = event.getClickedBlock();
1193        if (block == null) {
1194            // We do not care in this case, the player is likely interacting with air ("nothing").
1195            return;
1196        }
1197        Location location = BukkitUtil.adapt(block.getLocation());
1198        Action action = event.getAction();
1199        switch (action) {
1200            case PHYSICAL -> {
1201                eventType = PlayerBlockEventType.TRIGGER_PHYSICAL;
1202                blocktype1 = BukkitAdapter.asBlockType(block.getType());
1203            }
1204
1205            //todo rearrange the right click code. it is all over the place.
1206            case RIGHT_CLICK_BLOCK -> {
1207                Material blockType = block.getType();
1208                eventType = PlayerBlockEventType.INTERACT_BLOCK;
1209                blocktype1 = BukkitAdapter.asBlockType(block.getType());
1210
1211                if (blockType.isInteractable()) {
1212                    if (!player.isSneaking()) {
1213                        break;
1214                    }
1215                    ItemStack hand = player.getInventory().getItemInMainHand();
1216                    ItemStack offHand = player.getInventory().getItemInOffHand();
1217
1218                    // sneaking players interact with blocks if both hands are empty
1219                    if (hand.getType() == Material.AIR && offHand.getType() == Material.AIR) {
1220                        break;
1221                    }
1222                }
1223
1224                Material type = event.getMaterial();
1225
1226                // in the following, lb needs to have the material of the item in hand i.e. type
1227                switch (type.toString()) {
1228                    case "REDSTONE", "STRING", "PUMPKIN_SEEDS", "MELON_SEEDS", "COCOA_BEANS", "WHEAT_SEEDS", "BEETROOT_SEEDS",
1229                            "SWEET_BERRIES", "GLOW_BERRIES" -> {
1230                        return;
1231                    }
1232                    default -> {
1233                        //eventType = PlayerBlockEventType.PLACE_BLOCK;
1234                        if (type.isBlock()) {
1235                            return;
1236                        }
1237                    }
1238                }
1239                if (PaperLib.isPaper()) {
1240                    if (MaterialTags.SPAWN_EGGS.isTagged(type) || Material.EGG.equals(type)) {
1241                        eventType = PlayerBlockEventType.SPAWN_MOB;
1242                        break;
1243                    }
1244                } else {
1245                    if (type.toString().toLowerCase().endsWith("egg")) {
1246                        eventType = PlayerBlockEventType.SPAWN_MOB;
1247                        break;
1248                    }
1249                }
1250                if (type.isEdible()) {
1251                    //Allow all players to eat while also allowing the block place event to be fired
1252                    return;
1253                }
1254                if (type == Material.ARMOR_STAND) {
1255                    location = BukkitUtil.adapt(block.getRelative(event.getBlockFace()).getLocation());
1256                    eventType = PlayerBlockEventType.PLACE_MISC;
1257                }
1258                if (org.bukkit.Tag.ITEMS_BOATS.isTagged(type) || MINECARTS.contains(type)) {
1259                    eventType = PlayerBlockEventType.PLACE_VEHICLE;
1260                    break;
1261                }
1262                if (type == Material.FIREWORK_ROCKET || type == Material.FIREWORK_STAR) {
1263                    eventType = PlayerBlockEventType.SPAWN_MOB;
1264                    break;
1265                }
1266                if (BOOKS.contains(type)) {
1267                    eventType = PlayerBlockEventType.READ;
1268                    break;
1269                }
1270            }
1271            case LEFT_CLICK_BLOCK -> {
1272                Material blockType = block.getType();
1273
1274                // todo: when the code above is rearranged, it would be great to beautify this as well.
1275                // will code this as a temporary, specific bug fix (for dragon eggs)
1276                if (blockType != Material.DRAGON_EGG) {
1277                    return;
1278                }
1279
1280                eventType = PlayerBlockEventType.INTERACT_BLOCK;
1281                blocktype1 = BukkitAdapter.asBlockType(block.getType());
1282            }
1283            default -> {
1284                return;
1285            }
1286        }
1287        if (this.worldEdit != null && pp.getAttribute("worldedit")) {
1288            if (event.getMaterial() == Material.getMaterial(this.worldEdit.getConfiguration().wandItem)) {
1289                return;
1290            }
1291        }
1292        if (!this.eventDispatcher.checkPlayerBlockEvent(pp, eventType, location, blocktype1, true)) {
1293            event.setCancelled(true);
1294            event.setUseInteractedBlock(Event.Result.DENY);
1295        }
1296    }
1297
1298    // Boats can sometimes be placed on interactable blocks such as levers,
1299    // see PS-175. Armor stands, minecarts and end crystals (the other entities
1300    // supported by this event) don't have this issue.
1301    @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
1302    public void onBoatPlace(EntityPlaceEvent event) {
1303        Player player = event.getPlayer();
1304        if (player == null) {
1305            return;
1306        }
1307        Entity placed = event.getEntity();
1308        if (!(placed instanceof Boat)) {
1309            return;
1310        }
1311        BukkitPlayer pp = BukkitUtil.adapt(event.getPlayer());
1312        PlotArea area = pp.getPlotAreaAbs();
1313        if (area == null) {
1314            return;
1315        }
1316        PlayerBlockEventType eventType = PlayerBlockEventType.PLACE_VEHICLE;
1317        Block block = event.getBlock();
1318        BlockType blockType = BukkitAdapter.asBlockType(block.getType());
1319        Location location = BukkitUtil.adapt(block.getLocation());
1320        if (!PlotSquared.get().getEventDispatcher()
1321                .checkPlayerBlockEvent(pp, eventType, location, blockType, true)) {
1322            event.setCancelled(true);
1323        }
1324    }
1325
1326    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
1327    public void onBucketEmpty(PlayerBucketEmptyEvent event) {
1328        BlockFace bf = event.getBlockFace();
1329        // Note: a month after Bukkit 1.14.4 released, they added the API method
1330        // PlayerBucketEmptyEvent#getBlock(), which returns the block the
1331        // bucket contents is going to be placed at. Currently we determine this
1332        // block ourselves to retain compatibility with 1.13.
1333        final Block block;
1334        // if the block can be waterlogged, the event might waterlog the block
1335        // sometimes
1336        if (event.getBlockClicked().getBlockData() instanceof Waterlogged waterlogged
1337                && !waterlogged.isWaterlogged() && event.getBucket() != Material.LAVA_BUCKET) {
1338            block = event.getBlockClicked();
1339        } else {
1340            block = event.getBlockClicked().getLocation()
1341                    .add(bf.getModX(), bf.getModY(), bf.getModZ())
1342                    .getBlock();
1343        }
1344        Location location = BukkitUtil.adapt(block.getLocation());
1345        PlotArea area = location.getPlotArea();
1346        if (area == null) {
1347            return;
1348        }
1349        BukkitPlayer pp = BukkitUtil.adapt(event.getPlayer());
1350        Plot plot = area.getPlot(location);
1351        if (plot == null) {
1352            if (pp.hasPermission(Permission.PERMISSION_ADMIN_BUILD_ROAD)) {
1353                return;
1354            }
1355            pp.sendMessage(
1356                    TranslatableCaption.of("permission.no_permission_event"),
1357                    TagResolver.resolver("node", Tag.inserting(Permission.PERMISSION_ADMIN_BUILD_ROAD))
1358            );
1359            event.setCancelled(true);
1360        } else if (!plot.hasOwner()) {
1361            if (pp.hasPermission(Permission.PERMISSION_ADMIN_BUILD_UNOWNED)) {
1362                return;
1363            }
1364            pp.sendMessage(
1365                    TranslatableCaption.of("permission.no_permission_event"),
1366                    TagResolver.resolver(
1367                            "node",
1368                            Tag.inserting(Permission.PERMISSION_ADMIN_BUILD_UNOWNED)
1369                    )
1370            );
1371            event.setCancelled(true);
1372        } else if (!plot.isAdded(pp.getUUID())) {
1373            if (pp.hasPermission(Permission.PERMISSION_ADMIN_BUILD_OTHER)) {
1374                return;
1375            }
1376            pp.sendMessage(
1377                    TranslatableCaption.of("permission.no_permission_event"),
1378                    TagResolver.resolver(
1379                            "node",
1380                            Tag.inserting(Permission.PERMISSION_ADMIN_BUILD_OTHER)
1381                    )
1382            );
1383            event.setCancelled(true);
1384        } else if (Settings.Done.RESTRICT_BUILDING && DoneFlag.isDone(plot)) {
1385            if (!pp.hasPermission(Permission.PERMISSION_ADMIN_BUILD_OTHER)) {
1386                pp.sendMessage(
1387                        TranslatableCaption.of("done.building_restricted")
1388                );
1389                event.setCancelled(true);
1390            }
1391        }
1392    }
1393
1394    @EventHandler(priority = EventPriority.HIGHEST)
1395    public void onInventoryClose(InventoryCloseEvent event) {
1396        HumanEntity closer = event.getPlayer();
1397        if (!(closer instanceof Player player)) {
1398            return;
1399        }
1400        PlotInventory.removePlotInventoryOpen(BukkitUtil.adapt(player));
1401    }
1402
1403    @EventHandler(priority = EventPriority.MONITOR)
1404    public void onLeave(PlayerQuitEvent event) {
1405        TaskManager.removeFromTeleportQueue(event.getPlayer().getName());
1406        BukkitPlayer pp = BukkitUtil.adapt(event.getPlayer());
1407        pp.unregister();
1408        plotListener.logout(pp.getUUID());
1409    }
1410
1411    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
1412    public void onBucketFill(PlayerBucketFillEvent event) {
1413        Block blockClicked = event.getBlockClicked();
1414        Location location = BukkitUtil.adapt(blockClicked.getLocation());
1415        PlotArea area = location.getPlotArea();
1416        if (area == null) {
1417            return;
1418        }
1419        Player player = event.getPlayer();
1420        BukkitPlayer plotPlayer = BukkitUtil.adapt(player);
1421        Plot plot = area.getPlot(location);
1422        if (plot == null) {
1423            if (plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_BUILD_ROAD)) {
1424                return;
1425            }
1426            plotPlayer.sendMessage(
1427                    TranslatableCaption.of("permission.no_permission_event"),
1428                    TagResolver.resolver("node", Tag.inserting(Permission.PERMISSION_ADMIN_BUILD_ROAD))
1429            );
1430            event.setCancelled(true);
1431        } else if (!plot.hasOwner()) {
1432            if (plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_BUILD_UNOWNED)) {
1433                return;
1434            }
1435            plotPlayer.sendMessage(
1436                    TranslatableCaption.of("permission.no_permission_event"),
1437                    TagResolver.resolver(
1438                            "node",
1439                            Tag.inserting(Permission.PERMISSION_ADMIN_BUILD_UNOWNED)
1440                    )
1441            );
1442            event.setCancelled(true);
1443        } else if (!plot.isAdded(plotPlayer.getUUID())) {
1444            if (plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_BUILD_OTHER)) {
1445                return;
1446            }
1447            plotPlayer.sendMessage(
1448                    TranslatableCaption.of("permission.no_permission_event"),
1449                    TagResolver.resolver(
1450                            "node",
1451                            Tag.inserting(Permission.PERMISSION_ADMIN_BUILD_OTHER)
1452                    )
1453            );
1454            event.setCancelled(true);
1455        } else if (Settings.Done.RESTRICT_BUILDING && DoneFlag.isDone(plot)) {
1456            if (!plotPlayer.hasPermission(Permission.PERMISSION_ADMIN_BUILD_OTHER)) {
1457                plotPlayer.sendMessage(
1458                        TranslatableCaption.of("done.building_restricted")
1459                );
1460                event.setCancelled(true);
1461            }
1462        }
1463    }
1464
1465    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
1466    public void onHangingPlace(HangingPlaceEvent event) {
1467        Block block = event.getBlock().getRelative(event.getBlockFace());
1468        Location location = BukkitUtil.adapt(block.getLocation());
1469        PlotArea area = location.getPlotArea();
1470        if (area == null) {
1471            return;
1472        }
1473        Player p = event.getPlayer();
1474        if (p == null) {
1475            event.setCancelled(true);
1476            return;
1477        }
1478        BukkitPlayer pp = BukkitUtil.adapt(p);
1479        Plot plot = area.getPlot(location);
1480        if (plot == null) {
1481            if (!pp.hasPermission(Permission.PERMISSION_ADMIN_BUILD_ROAD)) {
1482                pp.sendMessage(
1483                        TranslatableCaption.of("permission.no_permission_event"),
1484                        TagResolver.resolver(
1485                                "node",
1486                                Tag.inserting(Permission.PERMISSION_ADMIN_BUILD_ROAD)
1487                        )
1488                );
1489                event.setCancelled(true);
1490            }
1491        } else {
1492            if (!plot.hasOwner()) {
1493                if (!pp.hasPermission(Permission.PERMISSION_ADMIN_BUILD_UNOWNED)) {
1494                    pp.sendMessage(
1495                            TranslatableCaption.of("permission.no_permission_event"),
1496                            TagResolver.resolver(
1497                                    "node",
1498                                    Tag.inserting(Permission.PERMISSION_ADMIN_BUILD_UNOWNED)
1499                            )
1500                    );
1501                    event.setCancelled(true);
1502                }
1503                return;
1504            }
1505            if (!plot.isAdded(pp.getUUID())) {
1506                if (!plot.getFlag(HangingPlaceFlag.class)) {
1507                    if (!pp.hasPermission(Permission.PERMISSION_ADMIN_BUILD_OTHER)) {
1508                        pp.sendMessage(
1509                                TranslatableCaption.of("permission.no_permission_event"),
1510                                TagResolver.resolver(
1511                                        "node",
1512                                        Tag.inserting(Permission.PERMISSION_ADMIN_BUILD_OTHER)
1513                                )
1514                        );
1515                        event.setCancelled(true);
1516                    }
1517                    return;
1518                }
1519            }
1520            if (BukkitEntityUtil.checkEntity(event.getEntity(), plot)) {
1521                event.setCancelled(true);
1522            }
1523
1524        }
1525    }
1526
1527    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
1528    public void onHangingBreakByEntity(HangingBreakByEntityEvent event) {
1529        Entity remover = event.getRemover();
1530        if (remover instanceof Player p) {
1531            Location location = BukkitUtil.adapt(event.getEntity().getLocation());
1532            PlotArea area = location.getPlotArea();
1533            if (area == null) {
1534                return;
1535            }
1536            BukkitPlayer pp = BukkitUtil.adapt(p);
1537            Plot plot = area.getPlot(location);
1538            if (plot == null) {
1539                if (!pp.hasPermission(Permission.PERMISSION_ADMIN_DESTROY_ROAD)) {
1540                    pp.sendMessage(
1541                            TranslatableCaption.of("permission.no_permission_event"),
1542                            TagResolver.resolver(
1543                                    "node",
1544                                    Tag.inserting(Permission.PERMISSION_ADMIN_DESTROY_ROAD)
1545                            )
1546                    );
1547                    event.setCancelled(true);
1548                }
1549            } else if (!plot.hasOwner()) {
1550                if (!pp.hasPermission(Permission.PERMISSION_ADMIN_DESTROY_UNOWNED)) {
1551                    pp.sendMessage(
1552                            TranslatableCaption.of("permission.no_permission_event"),
1553                            TagResolver.resolver(
1554                                    "node",
1555                                    Tag.inserting(Permission.PERMISSION_ADMIN_DESTROY_UNOWNED)
1556                            )
1557                    );
1558                    event.setCancelled(true);
1559                }
1560            } else if (!plot.isAdded(pp.getUUID())) {
1561                if (plot.getFlag(HangingBreakFlag.class)) {
1562                    return;
1563                }
1564                if (!pp.hasPermission(Permission.PERMISSION_ADMIN_DESTROY_OTHER)) {
1565                    pp.sendMessage(
1566                            TranslatableCaption.of("permission.no_permission_event"),
1567                            TagResolver.resolver(
1568                                    "node",
1569                                    Tag.inserting(Permission.PERMISSION_ADMIN_DESTROY_OTHER)
1570                            )
1571                    );
1572                    event.setCancelled(true);
1573                    plot.debug(p.getName()
1574                            + " could not break hanging entity because hanging-break = false");
1575                }
1576            }
1577        } else if (remover instanceof Projectile p) {
1578            if (p.getShooter() instanceof Player shooter) {
1579                Location location = BukkitUtil.adapt(event.getEntity().getLocation());
1580                PlotArea area = location.getPlotArea();
1581                if (area == null) {
1582                    return;
1583                }
1584                BukkitPlayer player = BukkitUtil.adapt(shooter);
1585                Plot plot = area.getPlot(BukkitUtil.adapt(event.getEntity().getLocation()));
1586                if (plot != null) {
1587                    if (!plot.hasOwner()) {
1588                        if (!player.hasPermission(Permission.PERMISSION_ADMIN_DESTROY_UNOWNED)) {
1589                            player.sendMessage(
1590                                    TranslatableCaption.of("permission.no_permission_event"),
1591                                    TagResolver.resolver(
1592                                            "node",
1593                                            Tag.inserting(Permission.PERMISSION_ADMIN_DESTROY_UNOWNED)
1594                                    )
1595                            );
1596                            event.setCancelled(true);
1597                        }
1598                    } else if (!plot.isAdded(player.getUUID())) {
1599                        if (!plot.getFlag(HangingBreakFlag.class)) {
1600                            if (!player.hasPermission(Permission.PERMISSION_ADMIN_DESTROY_OTHER)) {
1601                                player.sendMessage(
1602                                        TranslatableCaption.of("permission.no_permission_event"),
1603                                        TagResolver.resolver(
1604                                                "node",
1605                                                Tag.inserting(Permission.PERMISSION_ADMIN_DESTROY_OTHER)
1606                                        )
1607                                );
1608                                event.setCancelled(true);
1609                                plot.debug(player.getName()
1610                                        + " could not break hanging entity because hanging-break = false");
1611                            }
1612                        }
1613                    }
1614                }
1615            }
1616        } else {
1617            event.setCancelled(true);
1618        }
1619    }
1620
1621    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
1622    public void onPlayerInteractEntity(PlayerInteractEntityEvent event) {
1623        if (event.getRightClicked().getType() == EntityType.UNKNOWN) {
1624            return;
1625        }
1626        Location location = BukkitUtil.adapt(event.getRightClicked().getLocation());
1627        PlotArea area = location.getPlotArea();
1628        if (area == null) {
1629            return;
1630        }
1631        Player p = event.getPlayer();
1632        BukkitPlayer pp = BukkitUtil.adapt(p);
1633        Plot plot = area.getPlot(location);
1634        if (plot == null && !area.isRoadFlags()) {
1635            if (!pp.hasPermission(Permission.PERMISSION_ADMIN_INTERACT_ROAD)) {
1636                pp.sendMessage(
1637                        TranslatableCaption.of("permission.no_permission_event"),
1638                        TagResolver.resolver(
1639                                "node",
1640                                Tag.inserting(Permission.PERMISSION_ADMIN_INTERACT_ROAD)
1641                        )
1642                );
1643                event.setCancelled(true);
1644            }
1645        } else if (plot != null && !plot.hasOwner()) {
1646            if (!pp.hasPermission(Permission.PERMISSION_ADMIN_INTERACT_UNOWNED)) {
1647                pp.sendMessage(
1648                        TranslatableCaption.of("permission.no_permission_event"),
1649                        TagResolver.resolver(
1650                                "node",
1651                                Tag.inserting(Permission.PERMISSION_ADMIN_INTERACT_UNOWNED)
1652                        )
1653                );
1654                event.setCancelled(true);
1655            }
1656        } else if ((plot != null && !plot.isAdded(pp.getUUID())) || (plot == null && area
1657                .isRoadFlags())) {
1658            final Entity entity = event.getRightClicked();
1659            final com.sk89q.worldedit.world.entity.EntityType entityType =
1660                    BukkitAdapter.adapt(entity.getType());
1661
1662            FlagContainer flagContainer;
1663            if (plot == null) {
1664                flagContainer = area.getRoadFlagContainer();
1665            } else {
1666                flagContainer = plot.getFlagContainer();
1667            }
1668
1669            if (EntityCategories.HOSTILE.contains(entityType) && flagContainer
1670                    .getFlag(HostileInteractFlag.class).getValue()) {
1671                return;
1672            }
1673
1674            if (EntityCategories.ANIMAL.contains(entityType) && flagContainer
1675                    .getFlag(AnimalInteractFlag.class).getValue()) {
1676                return;
1677            }
1678
1679            // This actually makes use of the interface, so we don't use the
1680            // category
1681            if (entity instanceof Tameable && ((Tameable) entity).isTamed() && flagContainer
1682                    .getFlag(TamedInteractFlag.class).getValue()) {
1683                return;
1684            }
1685
1686            if (EntityCategories.VEHICLE.contains(entityType) && flagContainer
1687                    .getFlag(VehicleUseFlag.class).getValue()) {
1688                return;
1689            }
1690
1691            if (EntityCategories.PLAYER.contains(entityType) && flagContainer
1692                    .getFlag(PlayerInteractFlag.class).getValue()) {
1693                return;
1694            }
1695
1696            if (EntityCategories.VILLAGER.contains(entityType) && flagContainer
1697                    .getFlag(VillagerInteractFlag.class).getValue()) {
1698                return;
1699            }
1700
1701            if ((EntityCategories.HANGING.contains(entityType) || EntityCategories.OTHER
1702                    .contains(entityType)) && flagContainer.getFlag(MiscInteractFlag.class)
1703                    .getValue()) {
1704                return;
1705            }
1706
1707            if (!pp.hasPermission(Permission.PERMISSION_ADMIN_INTERACT_OTHER)) {
1708                pp.sendMessage(
1709                        TranslatableCaption.of("permission.no_permission_event"),
1710                        TagResolver.resolver(
1711                                "node",
1712                                Tag.inserting(Permission.PERMISSION_ADMIN_INTERACT_OTHER)
1713                        )
1714                );
1715                event.setCancelled(true);
1716            }
1717        }
1718    }
1719
1720    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
1721    public void onVehicleDestroy(VehicleDestroyEvent event) {
1722        Location location = BukkitUtil.adapt(event.getVehicle().getLocation());
1723        PlotArea area = location.getPlotArea();
1724        if (area == null) {
1725            return;
1726        }
1727        Entity attacker = event.getAttacker();
1728        if (attacker instanceof Player p) {
1729            BukkitPlayer pp = BukkitUtil.adapt(p);
1730            Plot plot = area.getPlot(location);
1731            if (plot == null) {
1732                if (!PlotFlagUtil.isAreaRoadFlagsAndFlagEquals(area, VehicleBreakFlag.class, true) && !pp.hasPermission(
1733                        Permission.PERMISSION_ADMIN_DESTROY_VEHICLE_ROAD
1734                )) {
1735                    pp.sendMessage(
1736                            TranslatableCaption.of("permission.no_permission_event"),
1737                            TagResolver.resolver(
1738                                    "node",
1739                                    Tag.inserting(Permission.PERMISSION_ADMIN_DESTROY_VEHICLE_ROAD)
1740                            )
1741                    );
1742                    event.setCancelled(true);
1743                }
1744            } else {
1745                if (!plot.hasOwner()) {
1746                    if (!pp.hasPermission(Permission.PERMISSION_ADMIN_DESTROY_VEHICLE_UNOWNED)) {
1747                        pp.sendMessage(
1748                                TranslatableCaption.of("permission.no_permission_event"),
1749                                TagResolver.resolver(
1750                                        "node",
1751                                        Tag.inserting(Permission.PERMISSION_ADMIN_DESTROY_VEHICLE_UNOWNED)
1752                                )
1753                        );
1754                        event.setCancelled(true);
1755                        return;
1756                    }
1757                    return;
1758                }
1759                if (!plot.isAdded(pp.getUUID())) {
1760                    if (plot.getFlag(VehicleBreakFlag.class)) {
1761                        return;
1762                    }
1763                    if (!pp.hasPermission(Permission.PERMISSION_ADMIN_DESTROY_VEHICLE_OTHER)) {
1764                        pp.sendMessage(
1765                                TranslatableCaption.of("permission.no_permission_event"),
1766                                TagResolver.resolver(
1767                                        "node",
1768                                        Tag.inserting(Permission.PERMISSION_ADMIN_DESTROY_VEHICLE_OTHER)
1769                                )
1770                        );
1771                        event.setCancelled(true);
1772                        plot.debug(pp.getName()
1773                                + " could not break vehicle because vehicle-break = false");
1774                    }
1775                }
1776            }
1777        }
1778    }
1779
1780    @EventHandler
1781    public void onItemDrop(PlayerDropItemEvent event) {
1782        Player player = event.getPlayer();
1783        BukkitPlayer pp = BukkitUtil.adapt(player);
1784        Location location = pp.getLocation();
1785        PlotArea area = location.getPlotArea();
1786        if (area == null) {
1787            return;
1788        }
1789        Plot plot = location.getOwnedPlot();
1790        if (plot == null) {
1791            if (PlotFlagUtil.isAreaRoadFlagsAndFlagEquals(area, ItemDropFlag.class, false)) {
1792                event.setCancelled(true);
1793            }
1794            return;
1795        }
1796        UUID uuid = pp.getUUID();
1797        if (!plot.isAdded(uuid)) {
1798            if (!plot.getFlag(ItemDropFlag.class)) {
1799                plot.debug(player.getName() + " could not drop item because of item-drop = false");
1800                event.setCancelled(true);
1801            }
1802        }
1803    }
1804
1805    @EventHandler
1806    public void onItemPickup(EntityPickupItemEvent event) {
1807        LivingEntity ent = event.getEntity();
1808        if (ent instanceof Player player) {
1809            BukkitPlayer pp = BukkitUtil.adapt(player);
1810            Location location = pp.getLocation();
1811            PlotArea area = location.getPlotArea();
1812            if (area == null) {
1813                return;
1814            }
1815            Plot plot = location.getOwnedPlot();
1816            if (plot == null) {
1817                if (PlotFlagUtil.isAreaRoadFlagsAndFlagEquals(area, DropProtectionFlag.class, true)) {
1818                    event.setCancelled(true);
1819                }
1820                return;
1821            }
1822            UUID uuid = pp.getUUID();
1823            if (!plot.isAdded(uuid) && plot.getFlag(DropProtectionFlag.class)) {
1824                plot.debug(player.getName() + " could not pick up item because of drop-protection = true");
1825                event.setCancelled(true);
1826            }
1827        }
1828    }
1829
1830    @EventHandler
1831    public void onDeath(final PlayerDeathEvent event) {
1832        Location location = BukkitUtil.adapt(event.getEntity().getLocation());
1833        PlotArea area = location.getPlotArea();
1834        if (area == null) {
1835            return;
1836        }
1837        Plot plot = location.getOwnedPlot();
1838        if (plot == null) {
1839            if (PlotFlagUtil.isAreaRoadFlagsAndFlagEquals(area, KeepInventoryFlag.class, true)) {
1840                event.setCancelled(true);
1841            }
1842            return;
1843        }
1844        if (plot.getFlag(KeepInventoryFlag.class)) {
1845            plot.debug(event.getEntity().getName() + " kept their inventory because of keep-inventory = true");
1846            event.getDrops().clear();
1847            event.setKeepInventory(true);
1848        }
1849    }
1850
1851    @SuppressWarnings("deprecation") // #getLocate is needed for Spigot compatibility
1852    @EventHandler
1853    public void onLocaleChange(final PlayerLocaleChangeEvent event) {
1854        // The event is fired before the player is deemed online upon login
1855        if (!event.getPlayer().isOnline()) {
1856            return;
1857        }
1858        BukkitPlayer player = BukkitUtil.adapt(event.getPlayer());
1859        // we're stripping the country code as we don't want to differ between countries
1860        player.setLocale(Locale.forLanguageTag(event.getLocale().substring(0, 2)));
1861    }
1862
1863    @EventHandler
1864    public void onPortalEnter(PlayerPortalEvent event) {
1865        Location location = BukkitUtil.adapt(event.getPlayer().getLocation());
1866        PlotArea area = location.getPlotArea();
1867        if (area == null) {
1868            return;
1869        }
1870        Plot plot = location.getOwnedPlot();
1871        if (plot == null) {
1872            if (PlotFlagUtil.isAreaRoadFlagsAndFlagEquals(area, DenyPortalTravelFlag.class, true)) {
1873                event.setCancelled(true);
1874            }
1875            return;
1876        }
1877        if (plot.getFlag(DenyPortalTravelFlag.class)) {
1878            plot.debug(event.getPlayer().getName() + " did not travel thru a portal because of deny-portal-travel = true");
1879            event.setCancelled(true);
1880        }
1881    }
1882
1883    @EventHandler
1884    public void onPortalCreation(PortalCreateEvent event) {
1885        String world = event.getWorld().getName();
1886        if (PlotSquared.get().getPlotAreaManager().getPlotAreasSet(world).size() == 0) {
1887            return;
1888        }
1889        BukkitPlayer pp = (event.getEntity() instanceof Player player) ? BukkitUtil.adapt(player) : null;
1890        int minX = Integer.MAX_VALUE;
1891        int maxX = Integer.MIN_VALUE;
1892        int minZ = Integer.MAX_VALUE;
1893        int maxZ = Integer.MIN_VALUE;
1894        for (BlockState state : event.getBlocks()) {
1895            minX = Math.min(state.getX(), minX);
1896            maxX = Math.max(state.getX(), maxX);
1897            minZ = Math.min(state.getZ(), minZ);
1898            maxZ = Math.max(state.getZ(), maxZ);
1899        }
1900        int y = event.getBlocks().get(0).getY(); // Don't need to worry about this too much
1901        for (Location location : List.of( // We don't care about duplicate locations
1902                Location.at(world, minX, y, minZ),
1903                Location.at(world, minX, y, maxZ),
1904                Location.at(world, maxX, y, minZ),
1905                Location.at(world, maxX, y, maxZ)
1906        )) {
1907            PlotArea area = location.getPlotArea();
1908            if (area == null) {
1909                continue;
1910            }
1911            if (area.notifyIfOutsideBuildArea(pp, location.getY())) {
1912                event.setCancelled(true);
1913                return;
1914            }
1915            Plot plot = location.getOwnedPlot();
1916            if (plot == null) {
1917                if (PlotFlagUtil.isAreaRoadFlagsAndFlagEquals(area, DenyPortalsFlag.class, true)) {
1918                    event.setCancelled(true);
1919                    return;
1920                }
1921                continue;
1922            }
1923            if (plot.getFlag(DenyPortalsFlag.class)) {
1924                StringBuilder builder = new StringBuilder();
1925                if (event.getEntity() != null) {
1926                    builder.append(event.getEntity().getName()).append(" did not create a portal");
1927                } else {
1928                    builder.append("Portal creation cancelled");
1929                }
1930                plot.debug(builder.append(" because of deny-portals = true").toString());
1931                event.setCancelled(true);
1932                return;
1933            }
1934        }
1935    }
1936
1937    @EventHandler
1938    public void onPlayerTakeLecternBook(PlayerTakeLecternBookEvent event) {
1939        Player player = event.getPlayer();
1940        BukkitPlayer pp = BukkitUtil.adapt(player);
1941        Location location = pp.getLocation();
1942        PlotArea area = location.getPlotArea();
1943        if (area == null) {
1944            return;
1945        }
1946        Plot plot = location.getOwnedPlot();
1947        if (plot == null) {
1948            if (PlotFlagUtil.isAreaRoadFlagsAndFlagEquals(area, LecternReadBookFlag.class, true)) {
1949                event.setCancelled(true);
1950            }
1951            return;
1952        }
1953        if (!plot.isAdded(pp.getUUID())) {
1954            if (plot.getFlag(LecternReadBookFlag.class)) {
1955                plot.debug(event.getPlayer().getName() + " could not take the book because of lectern-read-book = true");
1956                event.setCancelled(true);
1957            }
1958        }
1959    }
1960
1961}