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.core.util;
020
021import com.google.common.eventbus.EventBus;
022import com.intellectualsites.annotations.DoNotUse;
023import com.plotsquared.core.PlotSquared;
024import com.plotsquared.core.configuration.Settings;
025import com.plotsquared.core.configuration.caption.TranslatableCaption;
026import com.plotsquared.core.events.PlayerAutoPlotEvent;
027import com.plotsquared.core.events.PlayerAutoPlotsChosenEvent;
028import com.plotsquared.core.events.PlayerBuyPlotEvent;
029import com.plotsquared.core.events.PlayerClaimPlotEvent;
030import com.plotsquared.core.events.PlayerEnterPlotEvent;
031import com.plotsquared.core.events.PlayerLeavePlotEvent;
032import com.plotsquared.core.events.PlayerPlotAddEvent;
033import com.plotsquared.core.events.PlayerPlotAddRemoveEvent;
034import com.plotsquared.core.events.PlayerPlotDeniedEvent;
035import com.plotsquared.core.events.PlayerPlotDenyEvent;
036import com.plotsquared.core.events.PlayerPlotHelperEvent;
037import com.plotsquared.core.events.PlayerPlotLimitEvent;
038import com.plotsquared.core.events.PlayerPlotRemoveEvent;
039import com.plotsquared.core.events.PlayerPlotTrustEvent;
040import com.plotsquared.core.events.PlayerPlotTrustedEvent;
041import com.plotsquared.core.events.PlayerTeleportToPlotEvent;
042import com.plotsquared.core.events.PlotAutoMergeEvent;
043import com.plotsquared.core.events.PlotChangeOwnerEvent;
044import com.plotsquared.core.events.PlotClaimedNotifyEvent;
045import com.plotsquared.core.events.PlotClearEvent;
046import com.plotsquared.core.events.PlotComponentSetEvent;
047import com.plotsquared.core.events.PlotDeleteEvent;
048import com.plotsquared.core.events.PlotDoneEvent;
049import com.plotsquared.core.events.PlotEvent;
050import com.plotsquared.core.events.PlotFlagAddEvent;
051import com.plotsquared.core.events.PlotFlagRemoveEvent;
052import com.plotsquared.core.events.PlotMergeEvent;
053import com.plotsquared.core.events.PlotRateEvent;
054import com.plotsquared.core.events.PlotUnlinkEvent;
055import com.plotsquared.core.events.RemoveRoadEntityEvent;
056import com.plotsquared.core.events.TeleportCause;
057import com.plotsquared.core.events.post.PostPlayerAutoPlotEvent;
058import com.plotsquared.core.events.post.PostPlayerBuyPlotEvent;
059import com.plotsquared.core.events.post.PostPlayerPlotAddedEvent;
060import com.plotsquared.core.events.post.PostPlayerPlotDeniedEvent;
061import com.plotsquared.core.events.post.PostPlayerPlotTrustedEvent;
062import com.plotsquared.core.events.post.PostPlotChangeOwnerEvent;
063import com.plotsquared.core.events.post.PostPlotClearEvent;
064import com.plotsquared.core.events.post.PostPlotDeleteEvent;
065import com.plotsquared.core.events.post.PostPlotMergeEvent;
066import com.plotsquared.core.events.post.PostPlotUnlinkEvent;
067import com.plotsquared.core.listener.PlayerBlockEventType;
068import com.plotsquared.core.location.Direction;
069import com.plotsquared.core.location.Location;
070import com.plotsquared.core.permissions.Permission;
071import com.plotsquared.core.player.OfflinePlotPlayer;
072import com.plotsquared.core.player.PlotPlayer;
073import com.plotsquared.core.plot.Plot;
074import com.plotsquared.core.plot.PlotArea;
075import com.plotsquared.core.plot.PlotId;
076import com.plotsquared.core.plot.Rating;
077import com.plotsquared.core.plot.flag.PlotFlag;
078import com.plotsquared.core.plot.flag.implementations.DeviceInteractFlag;
079import com.plotsquared.core.plot.flag.implementations.EditSignFlag;
080import com.plotsquared.core.plot.flag.implementations.MiscPlaceFlag;
081import com.plotsquared.core.plot.flag.implementations.MobPlaceFlag;
082import com.plotsquared.core.plot.flag.implementations.PlaceFlag;
083import com.plotsquared.core.plot.flag.implementations.UseFlag;
084import com.plotsquared.core.plot.flag.implementations.VehiclePlaceFlag;
085import com.plotsquared.core.plot.flag.types.BlockTypeWrapper;
086import com.plotsquared.core.plot.world.SinglePlotArea;
087import com.plotsquared.core.util.task.TaskManager;
088import com.sk89q.worldedit.WorldEdit;
089import com.sk89q.worldedit.entity.Entity;
090import com.sk89q.worldedit.function.pattern.Pattern;
091import com.sk89q.worldedit.world.block.BlockCategories;
092import com.sk89q.worldedit.world.block.BlockType;
093import com.sk89q.worldedit.world.block.BlockTypes;
094import net.kyori.adventure.text.Component;
095import net.kyori.adventure.text.minimessage.tag.Tag;
096import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
097import org.checkerframework.checker.nullness.qual.NonNull;
098import org.checkerframework.checker.nullness.qual.Nullable;
099
100import java.util.ArrayList;
101import java.util.List;
102import java.util.UUID;
103
104@DoNotUse
105public class EventDispatcher {
106
107    private final EventBus eventBus = new EventBus("PlotSquaredEvents");
108    private final List<Object> listeners = new ArrayList<>();
109    private final WorldEdit worldEdit;
110
111    public EventDispatcher(final @Nullable WorldEdit worldEdit) {
112        this.worldEdit = worldEdit;
113    }
114
115    public void registerListener(Object listener) {
116        eventBus.register(listener);
117        listeners.add(listener);
118    }
119
120    public void unregisterListener(Object listener) {
121        eventBus.unregister(listener);
122        listeners.remove(listener);
123    }
124
125    public void unregisterAll() {
126        for (Object listener : listeners) {
127            eventBus.unregister(listener);
128        }
129    }
130
131    public void callGenericEvent(final @NonNull Object event) {
132        eventBus.post(event);
133    }
134
135    public void callEvent(final @NonNull PlotEvent event) {
136        eventBus.post(event);
137    }
138
139    public PlayerClaimPlotEvent callClaim(PlotPlayer<?> player, Plot plot, String schematic) {
140        PlayerClaimPlotEvent event = new PlayerClaimPlotEvent(player, plot, schematic);
141        callEvent(event);
142        return event;
143    }
144
145    public PlayerAutoPlotEvent callAuto(
146            PlotPlayer<?> player, PlotArea area, String schematic,
147            int size_x, int size_z
148    ) {
149        PlayerAutoPlotEvent event =
150                new PlayerAutoPlotEvent(player, area, schematic, size_x, size_z);
151        callEvent(event);
152        return event;
153    }
154
155    public PostPlayerAutoPlotEvent callPostAuto(PlotPlayer<?> player, Plot plot) {
156        PostPlayerAutoPlotEvent event = new PostPlayerAutoPlotEvent(player, plot);
157        callEvent(event);
158        return event;
159    }
160
161    public PlayerAutoPlotsChosenEvent callAutoPlotsChosen(
162            PlotPlayer<?> player, List<Plot> plots
163    ) {
164        PlayerAutoPlotsChosenEvent event =
165                new PlayerAutoPlotsChosenEvent(player, plots);
166        callEvent(event);
167        return event;
168    }
169
170    public PlotClaimedNotifyEvent callPlotClaimedNotify(Plot plot, boolean auto) {
171        PlotClaimedNotifyEvent event = new PlotClaimedNotifyEvent(plot, auto);
172        callEvent(event);
173        return event;
174    }
175
176    public PlayerTeleportToPlotEvent callTeleport(PlotPlayer<?> player, Location from, Plot plot, TeleportCause cause) {
177        PlayerTeleportToPlotEvent event = new PlayerTeleportToPlotEvent(player, from, plot, cause);
178        callEvent(event);
179        return event;
180    }
181
182    public PlotComponentSetEvent callComponentSet(Plot plot, String component, Pattern pattern) {
183        PlotComponentSetEvent event = new PlotComponentSetEvent(plot, component, pattern);
184        callEvent(event);
185        return event;
186    }
187
188    public PlotClearEvent callClear(Plot plot) {
189        PlotClearEvent event = new PlotClearEvent(plot);
190        callEvent(event);
191        return event;
192    }
193
194    public PostPlotClearEvent callPostPlotClear(PlotPlayer<?> player, Plot plot) {
195        PostPlotClearEvent event = new PostPlotClearEvent(player, plot);
196        callEvent(event);
197        return event;
198    }
199
200    public PlotDeleteEvent callDelete(Plot plot) {
201        PlotDeleteEvent event = new PlotDeleteEvent(plot);
202        callEvent(event);
203        return event;
204    }
205
206    public PostPlotDeleteEvent callPostDelete(Plot plot) {
207        PostPlotDeleteEvent event = new PostPlotDeleteEvent(plot);
208        callEvent(event);
209        return event;
210    }
211
212    public PlotFlagAddEvent callFlagAdd(PlotFlag<?, ?> flag, Plot plot) {
213        PlotFlagAddEvent event = new PlotFlagAddEvent(flag, plot);
214        callEvent(event);
215        return event;
216    }
217
218    public PlotFlagRemoveEvent callFlagRemove(PlotFlag<?, ?> flag, Plot plot) {
219        PlotFlagRemoveEvent event = new PlotFlagRemoveEvent(flag, plot);
220        callEvent(event);
221        return event;
222    }
223
224    public PlotMergeEvent callMerge(Plot plot, Direction dir, int max, PlotPlayer<?> player) {
225        PlotMergeEvent event = new PlotMergeEvent(plot.getWorldName(), plot, dir, max, player);
226        callEvent(event);
227        return event;
228    }
229
230    public PostPlotMergeEvent callPostMerge(PlotPlayer<?> player, Plot plot) {
231        PostPlotMergeEvent event = new PostPlotMergeEvent(player, plot);
232        callEvent(event);
233        return event;
234    }
235
236    public PlotAutoMergeEvent callAutoMerge(Plot plot, List<PlotId> plots) {
237        PlotAutoMergeEvent event = new PlotAutoMergeEvent(plot.getWorldName(), plot, plots);
238        callEvent(event);
239        return event;
240    }
241
242    public PlotUnlinkEvent callUnlink(
243            PlotArea area, Plot plot, boolean createRoad,
244            boolean createSign, PlotUnlinkEvent.REASON reason
245    ) {
246        PlotUnlinkEvent event = new PlotUnlinkEvent(area, plot, createRoad, createSign, reason);
247        callEvent(event);
248        return event;
249    }
250
251    public PostPlotUnlinkEvent callPostUnlink(Plot plot, PlotUnlinkEvent.REASON reason) {
252        PostPlotUnlinkEvent event = new PostPlotUnlinkEvent(plot, reason);
253        callEvent(event);
254        return event;
255    }
256
257    public PlayerEnterPlotEvent callEntry(PlotPlayer<?> player, Plot plot) {
258        PlayerEnterPlotEvent event = new PlayerEnterPlotEvent(player, plot);
259        callEvent(event);
260        return event;
261    }
262
263    public PlayerLeavePlotEvent callLeave(PlotPlayer<?> player, Plot plot) {
264        PlayerLeavePlotEvent event = new PlayerLeavePlotEvent(player, plot);
265        callEvent(event);
266        return event;
267    }
268
269    @Deprecated(forRemoval = true, since = "7.6.0")
270    public PlayerPlotDeniedEvent callDenied(
271            PlotPlayer<?> initiator, Plot plot, UUID player,
272            boolean added
273    ) {
274        PlayerPlotDeniedEvent event = new PlayerPlotDeniedEvent(initiator, plot, player, added);
275        callEvent(event);
276        return event;
277    }
278
279    @Deprecated(forRemoval = true, since = "7.6.0")
280    public PlayerPlotTrustedEvent callTrusted(
281            PlotPlayer<?> initiator, Plot plot, UUID player,
282            boolean added
283    ) {
284        PlayerPlotTrustedEvent event = new PlayerPlotTrustedEvent(initiator, plot, player, added);
285        callEvent(event);
286        return event;
287    }
288
289    @Deprecated(forRemoval = true, since = "7.6.0")
290    public PlayerPlotHelperEvent callMember(
291            PlotPlayer<?> initiator, Plot plot, UUID player,
292            boolean added
293    ) {
294        PlayerPlotHelperEvent event = new PlayerPlotHelperEvent(initiator, plot, player, added);
295        callEvent(event);
296        return event;
297    }
298
299    public PostPlayerPlotDeniedEvent callPostDenied(
300            PlotPlayer<?> initiator, Plot plot, UUID player, boolean added, PlayerPlotAddRemoveEvent.Reason reason
301    ) {
302        PostPlayerPlotDeniedEvent event = new PostPlayerPlotDeniedEvent(initiator, plot, player, added, reason);
303        callEvent(event);
304        return event;
305    }
306
307    public PostPlayerPlotTrustedEvent callPostTrusted(
308            PlotPlayer<?> initiator, Plot plot, UUID player, boolean added, PlayerPlotAddRemoveEvent.Reason reason
309    ) {
310        PostPlayerPlotTrustedEvent event = new PostPlayerPlotTrustedEvent(initiator, plot, player, added, reason);
311        callEvent(event);
312        return event;
313    }
314
315    public PostPlayerPlotAddedEvent callPostAdded(
316            PlotPlayer<?> initiator, Plot plot, UUID player, boolean added, PlayerPlotAddRemoveEvent.Reason reason
317    ) {
318        PostPlayerPlotAddedEvent event = new PostPlayerPlotAddedEvent(initiator, plot, player, added, reason);
319        callEvent(event);
320        return event;
321    }
322
323    public PlayerPlotDenyEvent callPlayerDeny(
324            PlotPlayer<?> initiator, Plot plot, UUID player, PlayerPlotAddRemoveEvent.Reason reason
325    ) {
326        PlayerPlotDenyEvent event = new PlayerPlotDenyEvent(initiator, plot, player, reason);
327        callEvent(event);
328        return event;
329    }
330
331    public PlayerPlotTrustEvent callPlayerTrust(
332            PlotPlayer<?> initiator, Plot plot, UUID player, PlayerPlotAddRemoveEvent.Reason reason
333    ) {
334        PlayerPlotTrustEvent event = new PlayerPlotTrustEvent(initiator, plot, player, reason);
335        callEvent(event);
336        return event;
337    }
338
339    public PlayerPlotAddEvent callPlayerAdd(
340            PlotPlayer<?> initiator, Plot plot, UUID player, PlayerPlotAddRemoveEvent.Reason reason
341    ) {
342        PlayerPlotAddEvent event = new PlayerPlotAddEvent(initiator, plot, player, reason);
343        callEvent(event);
344        return event;
345    }
346
347    public PlayerPlotRemoveEvent callPlayerRemove(
348            PlotPlayer<?> initiator, Plot plot, UUID player, PlayerPlotAddRemoveEvent.Reason reason
349    ) {
350        PlayerPlotRemoveEvent event = new PlayerPlotRemoveEvent(initiator, plot, player, reason);
351        callEvent(event);
352        return event;
353    }
354
355    public PlotChangeOwnerEvent callOwnerChange(
356            PlotPlayer<?> initiator, Plot plot, UUID oldOwner,
357            UUID newOwner, boolean hasOldOwner
358    ) {
359        PlotChangeOwnerEvent event =
360                new PlotChangeOwnerEvent(initiator, plot, oldOwner, newOwner, hasOldOwner);
361        callEvent(event);
362        return event;
363    }
364
365    public PostPlotChangeOwnerEvent callPostOwnerChange(PlotPlayer<?> player, Plot plot, @Nullable UUID oldOwner) {
366        PostPlotChangeOwnerEvent event = new PostPlotChangeOwnerEvent(player, plot, oldOwner);
367        callEvent(event);
368        return event;
369    }
370
371    public PlotRateEvent callRating(PlotPlayer<?> player, Plot plot, Rating rating) {
372        PlotRateEvent event = new PlotRateEvent(player, rating, plot);
373        eventBus.post(event);
374        return event;
375    }
376
377    public PlotDoneEvent callDone(Plot plot) {
378        PlotDoneEvent event = new PlotDoneEvent(plot);
379        callEvent(event);
380        return event;
381    }
382
383    public RemoveRoadEntityEvent callRemoveRoadEntity(Entity entity) {
384        RemoveRoadEntityEvent event = new RemoveRoadEntityEvent(entity);
385        eventBus.post(event);
386        return event;
387    }
388
389    public PlayerPlotLimitEvent callPlayerPlotLimit(PlotPlayer<?> player, int calculatedLimit) {
390        PlayerPlotLimitEvent event = new PlayerPlotLimitEvent(player, calculatedLimit);
391        eventBus.post(event);
392        return event;
393    }
394
395    public PlayerBuyPlotEvent callPlayerBuyPlot(PlotPlayer<?> player, Plot plot, double price) {
396        PlayerBuyPlotEvent event = new PlayerBuyPlotEvent(player, plot, price);
397        eventBus.post(event);
398        return event;
399    }
400
401    public void callPostPlayerBuyPlot(PlotPlayer<?> player, OfflinePlotPlayer previousOwner, Plot plot,
402                                      double price) {
403        eventBus.post(new PostPlayerBuyPlotEvent(player, previousOwner, plot, price));
404    }
405
406    public void doJoinTask(final PlotPlayer<?> player) {
407        if (player == null) {
408            return; //possible future warning message to figure out where we are retrieving null
409        }
410        if (PlotSquared.platform().expireManager() != null) {
411            PlotSquared.platform().expireManager().handleJoin(player);
412        }
413        if (this.worldEdit != null) {
414            if (player.getAttribute("worldedit")) {
415                player.sendMessage(TranslatableCaption.of("worldedit.worldedit_bypassed"));
416            }
417        }
418        final Plot plot = player.getCurrentPlot();
419        if (Settings.Teleport.ON_LOGIN && plot != null && !(plot
420                .getArea() instanceof SinglePlotArea)) {
421            TaskManager.runTask(() -> plot.teleportPlayer(player, TeleportCause.LOGIN, result -> {
422            }));
423            player.sendMessage(TranslatableCaption.of("teleport.teleported_to_road"));
424        }
425    }
426
427    public void doRespawnTask(final PlotPlayer<?> player) {
428        final Plot plot = player.getCurrentPlot();
429        if (Settings.Teleport.ON_DEATH && plot != null) {
430            TaskManager.runTask(() -> plot.teleportPlayer(player, TeleportCause.DEATH, result -> {
431            }));
432            player.sendMessage(TranslatableCaption.of("teleport.teleported_to_road"));
433        }
434    }
435
436    public boolean checkPlayerBlockEvent(
437            PlotPlayer<?> player, @NonNull PlayerBlockEventType type,
438            Location location, BlockType blockType, boolean notifyPerms
439    ) {
440        PlotArea area = location.getPlotArea();
441        // the interaction target location might be outside a plot area
442        if (area == null) {
443            return true;
444        }
445        if (!area.buildRangeContainsY(location.getY()) && !player.hasPermission(Permission.PERMISSION_ADMIN_BUILD_HEIGHT_LIMIT)) {
446            player.sendMessage(
447                    TranslatableCaption.of("height.height_limit"),
448                    TagResolver.builder()
449                            .tag("minheight", Tag.inserting(Component.text(area.getMinBuildHeight())))
450                            .tag("maxheight", Tag.inserting(Component.text(area.getMaxBuildHeight())))
451                            .build()
452            );
453            return false;
454        }
455        Plot plot = area.getPlot(location);
456        if (plot != null) {
457            if (plot.isAdded(player.getUUID())) {
458                return true;
459            }
460        }
461        switch (type) {
462            case TELEPORT_OBJECT -> {
463                return false;
464            }
465            case READ -> {
466                return true;
467            }
468            case INTERACT_BLOCK -> {
469                if (plot == null) {
470                    final List<BlockTypeWrapper> use = area.getRoadFlag(UseFlag.class);
471                    for (final BlockTypeWrapper blockTypeWrapper : use) {
472                        if (blockTypeWrapper.accepts(BlockTypes.AIR) || blockTypeWrapper.accepts(blockType)) {
473                            return true;
474                        }
475                    }
476                    return player.hasPermission(Permission.PERMISSION_ADMIN_INTERACT_ROAD, notifyPerms);
477                }
478                if (!plot.hasOwner()) {
479                    return player.hasPermission(Permission.PERMISSION_ADMIN_INTERACT_UNOWNED, notifyPerms);
480                }
481                final List<BlockTypeWrapper> use = plot.getFlag(UseFlag.class);
482                for (final BlockTypeWrapper blockTypeWrapper : use) {
483                    if (blockTypeWrapper.accepts(BlockTypes.AIR) || blockTypeWrapper
484                            .accepts(blockType)) {
485                        return true;
486                    }
487                }
488                if (player.hasPermission(Permission.PERMISSION_ADMIN_INTERACT_OTHER, false)) {
489                    return true;
490                }
491                // we check for the EditSignFlag in the PlayerSignOpenEvent again, but we must not cancel the interact event
492                // or send a message if the flag is true
493                if (BlockCategories.ALL_SIGNS != null && BlockCategories.ALL_SIGNS.contains(blockType)
494                        && plot.getFlag(EditSignFlag.class)) {
495                    return true;
496                }
497                if (notifyPerms) {
498                    player.sendMessage(
499                            TranslatableCaption.of("commandconfig.flag_tutorial_usage"),
500                            TagResolver.resolver("flag", Tag.inserting(PlaceFlag.getFlagNameComponent(UseFlag.class)))
501                    );
502                }
503                return false;
504            }
505            case TRIGGER_PHYSICAL -> {
506                if (plot == null) {
507                    final List<BlockTypeWrapper> use = area.getRoadFlag(UseFlag.class);
508                    for (final BlockTypeWrapper blockTypeWrapper : use) {
509                        if (blockTypeWrapper.accepts(BlockTypes.AIR) || blockTypeWrapper.accepts(blockType)) {
510                            return true;
511                        }
512                    }
513                    return player.hasPermission(Permission.PERMISSION_ADMIN_INTERACT_ROAD, false);
514                }
515                if (!plot.hasOwner()) {
516                    return player.hasPermission(Permission.PERMISSION_ADMIN_INTERACT_UNOWNED, false);
517                }
518                if (plot.getFlag(DeviceInteractFlag.class)) {
519                    return true;
520                }
521                List<BlockTypeWrapper> use = plot.getFlag(UseFlag.class);
522                for (final BlockTypeWrapper blockTypeWrapper : use) {
523                    if (blockTypeWrapper.accepts(BlockTypes.AIR) || blockTypeWrapper
524                            .accepts(blockType)) {
525                        return true;
526                    }
527                }
528                return player.hasPermission(Permission.PERMISSION_ADMIN_INTERACT_OTHER, false);
529            }
530            case SPAWN_MOB -> {
531                if (plot == null) {
532                    return player.hasPermission(Permission.PERMISSION_ADMIN_INTERACT_ROAD, notifyPerms);
533                }
534                if (!plot.hasOwner()) {
535                    return player.hasPermission(Permission.PERMISSION_ADMIN_INTERACT_UNOWNED, notifyPerms);
536                }
537                if (plot.getFlag(MobPlaceFlag.class)) {
538                    return true;
539                }
540                List<BlockTypeWrapper> place = plot.getFlag(PlaceFlag.class);
541                for (final BlockTypeWrapper blockTypeWrapper : place) {
542                    if (blockTypeWrapper.accepts(BlockTypes.AIR) || blockTypeWrapper
543                            .accepts(blockType)) {
544                        return true;
545                    }
546                }
547                if (player.hasPermission(Permission.PERMISSION_ADMIN_INTERACT_OTHER, false)) {
548                    return true;
549                }
550                if (notifyPerms) {
551                    player.sendMessage(
552                            TranslatableCaption.of("commandconfig.flag_tutorial_usage"),
553                            TagResolver.resolver(
554                                    "flag",
555                                    Tag.inserting(
556                                            PlotFlag.getFlagNameComponent(MobPlaceFlag.class)
557                                                    .append(Component.text("/"))
558                                                    .append(PlotFlag.getFlagNameComponent(PlaceFlag.class))
559                                    )
560                            )
561                    );
562                }
563                return false;
564            }
565            case PLACE_MISC -> {
566                if (plot == null) {
567                    return player.hasPermission(Permission.PERMISSION_ADMIN_INTERACT_ROAD, notifyPerms);
568                }
569                if (!plot.hasOwner()) {
570                    return player.hasPermission(Permission.PERMISSION_ADMIN_INTERACT_UNOWNED, notifyPerms);
571                }
572                if (plot.getFlag(MiscPlaceFlag.class)) {
573                    return true;
574                }
575                List<BlockTypeWrapper> place = plot.getFlag(PlaceFlag.class);
576                for (final BlockTypeWrapper blockTypeWrapper : place) {
577                    if (blockTypeWrapper.accepts(BlockTypes.AIR) || blockTypeWrapper
578                            .accepts(blockType)) {
579                        return true;
580                    }
581                }
582                if (player.hasPermission(Permission.PERMISSION_ADMIN_INTERACT_OTHER, false)) {
583                    return true;
584                }
585                if (notifyPerms) {
586                    player.sendMessage(
587                            TranslatableCaption.of("commandconfig.flag_tutorial_usage"),
588                            TagResolver.resolver(
589                                    "flag",
590                                    Tag.inserting(
591                                            PlotFlag.getFlagNameComponent(MiscPlaceFlag.class)
592                                                    .append(Component.text("/"))
593                                                    .append(PlotFlag.getFlagNameComponent(PlaceFlag.class))
594                                    )
595                            )
596                    );
597                }
598                return false;
599            }
600            case PLACE_VEHICLE -> {
601                if (plot == null) {
602                    return player.hasPermission(Permission.PERMISSION_ADMIN_PLACE_VEHICLE_ROAD, notifyPerms);
603                }
604                if (!plot.hasOwner()) {
605                    return player.hasPermission(Permission.PERMISSION_ADMIN_PLACE_VEHICLE_UNOWNED, notifyPerms);
606                }
607                if (plot.getFlag(VehiclePlaceFlag.class)) {
608                    return true;
609                }
610                if (player.hasPermission(Permission.PERMISSION_ADMIN_PLACE_VEHICLE_OTHER, false)) {
611                    return true;
612                }
613                if (notifyPerms) {
614                    player.sendMessage(
615                            TranslatableCaption.of("commandconfig.flag_tutorial_usage"),
616                            TagResolver.resolver(
617                                    "flag",
618                                    Tag.inserting(
619                                            PlotFlag.getFlagNameComponent(VehiclePlaceFlag.class)
620                                    )
621                            )
622                    );
623                }
624                return false;
625            }
626            default -> {
627            }
628        }
629        return true;
630    }
631
632}