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