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