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