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.bukkit.listener;
020
021import com.plotsquared.bukkit.util.BukkitEntityUtil;
022import com.plotsquared.bukkit.util.BukkitUtil;
023import com.plotsquared.core.PlotSquared;
024import com.plotsquared.core.configuration.Settings;
025import com.plotsquared.core.location.Location;
026import com.plotsquared.core.plot.Plot;
027import com.plotsquared.core.plot.PlotArea;
028import com.plotsquared.core.plot.flag.implementations.DoneFlag;
029import io.papermc.lib.PaperLib;
030import org.bukkit.Chunk;
031import org.bukkit.World;
032import org.bukkit.block.Block;
033import org.bukkit.entity.ArmorStand;
034import org.bukkit.entity.Entity;
035import org.bukkit.entity.EntityType;
036import org.bukkit.entity.Vehicle;
037import org.bukkit.event.EventHandler;
038import org.bukkit.event.EventPriority;
039import org.bukkit.event.Listener;
040import org.bukkit.event.entity.CreatureSpawnEvent;
041import org.bukkit.event.entity.EntitySpawnEvent;
042import org.bukkit.event.entity.EntityTeleportEvent;
043import org.bukkit.event.vehicle.VehicleBlockCollisionEvent;
044import org.bukkit.event.vehicle.VehicleCreateEvent;
045import org.bukkit.event.vehicle.VehicleMoveEvent;
046import org.bukkit.event.vehicle.VehicleUpdateEvent;
047import org.bukkit.event.world.ChunkLoadEvent;
048import org.bukkit.metadata.FixedMetadataValue;
049import org.bukkit.metadata.MetadataValue;
050import org.bukkit.plugin.Plugin;
051import org.checkerframework.checker.nullness.qual.NonNull;
052
053import java.util.List;
054
055public class EntitySpawnListener implements Listener {
056
057    private static final String KEY = "P2";
058    private static boolean ignoreTP = false;
059    private static boolean hasPlotArea = false;
060    private static String areaName = null;
061
062    public static void testNether(final Entity entity) {
063        @NonNull World world = entity.getWorld();
064        if (world.getEnvironment() != World.Environment.NETHER && world.getEnvironment() != World.Environment.THE_END) {
065            return;
066        }
067        test(entity);
068    }
069
070    public static void testCreate(final Entity entity) {
071        @NonNull World world = entity.getWorld();
072        if (!world.getName().equals(areaName)) {
073            areaName = world.getName();
074            hasPlotArea = PlotSquared.get().getPlotAreaManager().hasPlotArea(areaName);
075        }
076        if (!hasPlotArea) {
077            return;
078        }
079        test(entity);
080    }
081
082    public static void test(Entity entity) {
083        @NonNull World world = entity.getWorld();
084        List<MetadataValue> meta = entity.getMetadata(KEY);
085        if (meta.isEmpty()) {
086            if (PlotSquared.get().getPlotAreaManager().hasPlotArea(world.getName())) {
087                entity.setMetadata(KEY, new FixedMetadataValue((Plugin) PlotSquared.platform(), entity.getLocation()));
088            }
089        } else {
090            org.bukkit.Location origin = (org.bukkit.Location) meta.get(0).value();
091            World originWorld = origin.getWorld();
092            if (!originWorld.equals(world)) {
093                if (!ignoreTP) {
094                    if (!world.getName().equalsIgnoreCase(originWorld + "_the_end")) {
095                        if (entity.getType() == EntityType.PLAYER) {
096                            return;
097                        }
098                        try {
099                            ignoreTP = true;
100                            PaperLib.teleportAsync(entity, origin);
101                        } finally {
102                            ignoreTP = false;
103                        }
104                        if (entity.getLocation().getWorld().equals(world)) {
105                            entity.remove();
106                        }
107                    }
108                } else {
109                    if (entity.getType() == EntityType.PLAYER) {
110                        return;
111                    }
112                    entity.remove();
113                }
114            }
115        }
116    }
117
118    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
119    public void creatureSpawnEvent(EntitySpawnEvent event) {
120        Entity entity = event.getEntity();
121        Location location = BukkitUtil.adapt(entity.getLocation());
122        PlotArea area = location.getPlotArea();
123        if (!location.isPlotArea() || area == null) {
124            return;
125        }
126        if (PaperLib.isPaper()) {
127            //noinspection ConstantValue - getEntitySpawnReason annotated as NotNull, but is not NotNull. lol.
128            if (area.isSpawnCustom() && entity.getEntitySpawnReason() != null && "CUSTOM".equals(entity.getEntitySpawnReason().name())) {
129                return;
130            }
131        }
132        Plot plot = location.getOwnedPlotAbs();
133        EntityType type = entity.getType();
134        if (plot == null) {
135            if (type == EntityType.DROPPED_ITEM) {
136                if (Settings.Enabled_Components.KILL_ROAD_ITEMS) {
137                    event.setCancelled(true);
138                }
139                return;
140            }
141            if (!area.isMobSpawning()) {
142                if (type == EntityType.PLAYER) {
143                    return;
144                }
145                if (type.isAlive()) {
146                    event.setCancelled(true);
147                }
148            }
149            if (!area.isMiscSpawnUnowned() && !type.isAlive()) {
150                event.setCancelled(true);
151            }
152            return;
153        }
154        if (Settings.Done.RESTRICT_BUILDING && DoneFlag.isDone(plot)) {
155            event.setCancelled(true);
156        }
157        if (type == EntityType.ENDER_CRYSTAL) {
158            if (BukkitEntityUtil.checkEntity(entity, plot)) {
159                event.setCancelled(true);
160            }
161            return;
162        }
163        if (type == EntityType.SHULKER) {
164            if (!entity.hasMetadata("shulkerPlot")) {
165                entity.setMetadata("shulkerPlot", new FixedMetadataValue((Plugin) PlotSquared.platform(), plot.getId()));
166            }
167        }
168    }
169
170    @EventHandler
171    public void onChunkLoad(ChunkLoadEvent event) {
172        @NonNull Chunk chunk = event.getChunk();
173        for (final Entity entity : chunk.getEntities()) {
174            testCreate(entity);
175        }
176    }
177
178    @EventHandler
179    public void onVehicle(VehicleUpdateEvent event) {
180        testNether(event.getVehicle());
181    }
182
183    @EventHandler
184    public void onVehicle(VehicleCreateEvent event) {
185        testCreate(event.getVehicle());
186    }
187
188    @EventHandler
189    public void onVehicle(VehicleBlockCollisionEvent event) {
190        testNether(event.getVehicle());
191    }
192
193    @EventHandler
194    public void onTeleport(EntityTeleportEvent event) {
195        Entity entity = event.getEntity();
196        Entity fromLocation = event.getEntity();
197        Block toLocation = event.getTo().getBlock();
198        final Location fromLocLocation = BukkitUtil.adapt(fromLocation.getLocation());
199        final PlotArea fromArea = fromLocLocation.getPlotArea();
200        Location toLocLocation = BukkitUtil.adapt(toLocation.getLocation());
201        PlotArea toArea = toLocLocation.getPlotArea();
202
203        if (toArea == null) {
204            if (fromLocation.getType() == EntityType.SHULKER && fromArea != null) {
205                event.setCancelled(true);
206            }
207            return;
208        }
209        Plot toPlot = toArea.getOwnedPlot(toLocLocation);
210        if (fromLocation.getType() == EntityType.SHULKER && fromArea != null) {
211            final Plot fromPlot = fromArea.getOwnedPlot(fromLocLocation);
212
213            if (fromPlot != null || toPlot != null) {
214                if ((fromPlot == null || !fromPlot.equals(toPlot)) && (toPlot == null || !toPlot.equals(fromPlot))) {
215                    event.setCancelled(true);
216                    return;
217                }
218            }
219        }
220        if (entity instanceof Vehicle || entity instanceof ArmorStand) {
221            testNether(event.getEntity());
222        }
223    }
224
225    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
226    public void vehicleMove(VehicleMoveEvent event) {
227        testNether(event.getVehicle());
228    }
229
230    @EventHandler
231    public void spawn(CreatureSpawnEvent event) {
232        if (event.getEntityType() == EntityType.ARMOR_STAND) {
233            testCreate(event.getEntity());
234        }
235    }
236
237}