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