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