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