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