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