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