404 lines
15 KiB
Java
404 lines
15 KiB
Java
/*
|
|
* PlotSquared, a land and world management plugin for Minecraft.
|
|
* Copyright (C) IntellectualSites <https://intellectualsites.com>
|
|
* Copyright (C) IntellectualSites team and contributors
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
package com.plotsquared.bukkit.listener;
|
|
|
|
import com.destroystokyo.paper.event.entity.EntityPathfindEvent;
|
|
import com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent;
|
|
import com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent;
|
|
import com.destroystokyo.paper.event.entity.PreSpawnerSpawnEvent;
|
|
import com.destroystokyo.paper.event.entity.SlimePathfindEvent;
|
|
import com.destroystokyo.paper.event.player.PlayerLaunchProjectileEvent;
|
|
import com.destroystokyo.paper.event.server.AsyncTabCompleteEvent;
|
|
import com.google.inject.Inject;
|
|
import com.plotsquared.bukkit.util.BukkitUtil;
|
|
import com.plotsquared.core.command.Command;
|
|
import com.plotsquared.core.command.MainCommand;
|
|
import com.plotsquared.core.configuration.Settings;
|
|
import com.plotsquared.core.configuration.caption.TranslatableCaption;
|
|
import com.plotsquared.core.location.Location;
|
|
import com.plotsquared.core.permissions.Permission;
|
|
import com.plotsquared.core.player.PlotPlayer;
|
|
import com.plotsquared.core.plot.Plot;
|
|
import com.plotsquared.core.plot.PlotArea;
|
|
import com.plotsquared.core.plot.flag.implementations.DoneFlag;
|
|
import com.plotsquared.core.plot.flag.implementations.ProjectilesFlag;
|
|
import com.plotsquared.core.plot.world.PlotAreaManager;
|
|
import com.plotsquared.core.util.Permissions;
|
|
import net.kyori.adventure.text.minimessage.Template;
|
|
import org.bukkit.Chunk;
|
|
import org.bukkit.block.Block;
|
|
import org.bukkit.block.TileState;
|
|
import org.bukkit.entity.Entity;
|
|
import org.bukkit.entity.EntityType;
|
|
import org.bukkit.entity.Player;
|
|
import org.bukkit.entity.Projectile;
|
|
import org.bukkit.entity.Slime;
|
|
import org.bukkit.event.EventHandler;
|
|
import org.bukkit.event.EventPriority;
|
|
import org.bukkit.event.Listener;
|
|
import org.bukkit.event.block.BlockPlaceEvent;
|
|
import org.bukkit.event.entity.CreatureSpawnEvent;
|
|
import org.bukkit.projectiles.ProjectileSource;
|
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.regex.Pattern;
|
|
|
|
/**
|
|
* Events specific to Paper. Some toit nups here
|
|
*/
|
|
@SuppressWarnings("unused")
|
|
public class PaperListener implements Listener {
|
|
|
|
private final PlotAreaManager plotAreaManager;
|
|
private Chunk lastChunk;
|
|
|
|
@Inject
|
|
public PaperListener(final @NonNull PlotAreaManager plotAreaManager) {
|
|
this.plotAreaManager = plotAreaManager;
|
|
}
|
|
|
|
@EventHandler
|
|
public void onEntityPathfind(EntityPathfindEvent event) {
|
|
if (!Settings.Paper_Components.ENTITY_PATHING) {
|
|
return;
|
|
}
|
|
Location toLoc = BukkitUtil.adapt(event.getLoc());
|
|
Location fromLoc = BukkitUtil.adapt(event.getEntity().getLocation());
|
|
PlotArea tarea = toLoc.getPlotArea();
|
|
if (tarea == null) {
|
|
return;
|
|
}
|
|
PlotArea farea = fromLoc.getPlotArea();
|
|
if (farea == null) {
|
|
return;
|
|
}
|
|
if (tarea != farea) {
|
|
event.setCancelled(true);
|
|
return;
|
|
}
|
|
Plot tplot = toLoc.getPlot();
|
|
Plot fplot = fromLoc.getPlot();
|
|
if (tplot == null ^ fplot == null) {
|
|
event.setCancelled(true);
|
|
return;
|
|
}
|
|
if (tplot == null || tplot.getId().hashCode() == fplot.getId().hashCode()) {
|
|
return;
|
|
}
|
|
if (fplot.isMerged() && fplot.getConnectedPlots().contains(fplot)) {
|
|
return;
|
|
}
|
|
event.setCancelled(true);
|
|
}
|
|
|
|
@EventHandler
|
|
public void onEntityPathfind(SlimePathfindEvent event) {
|
|
if (!Settings.Paper_Components.ENTITY_PATHING) {
|
|
return;
|
|
}
|
|
Slime slime = event.getEntity();
|
|
|
|
Block b = slime.getTargetBlock(4);
|
|
if (b == null) {
|
|
return;
|
|
}
|
|
|
|
Location toLoc = BukkitUtil.adapt(b.getLocation());
|
|
Location fromLoc = BukkitUtil.adapt(event.getEntity().getLocation());
|
|
PlotArea tarea = toLoc.getPlotArea();
|
|
if (tarea == null) {
|
|
return;
|
|
}
|
|
PlotArea farea = fromLoc.getPlotArea();
|
|
if (farea == null) {
|
|
return;
|
|
}
|
|
|
|
if (tarea != farea) {
|
|
event.setCancelled(true);
|
|
return;
|
|
}
|
|
Plot tplot = toLoc.getPlot();
|
|
Plot fplot = fromLoc.getPlot();
|
|
if (tplot == null ^ fplot == null) {
|
|
event.setCancelled(true);
|
|
return;
|
|
}
|
|
if (tplot == null || tplot.getId().hashCode() == fplot.getId().hashCode()) {
|
|
return;
|
|
}
|
|
if (fplot.isMerged() && fplot.getConnectedPlots().contains(fplot)) {
|
|
return;
|
|
}
|
|
event.setCancelled(true);
|
|
}
|
|
|
|
@EventHandler
|
|
public void onPreCreatureSpawnEvent(PreCreatureSpawnEvent event) {
|
|
if (!Settings.Paper_Components.CREATURE_SPAWN) {
|
|
return;
|
|
}
|
|
Location location = BukkitUtil.adapt(event.getSpawnLocation());
|
|
PlotArea area = location.getPlotArea();
|
|
if (!location.isPlotArea()) {
|
|
return;
|
|
}
|
|
//If entities are spawning... the chunk should be loaded?
|
|
Entity[] entities = event.getSpawnLocation().getChunk().getEntities();
|
|
if (entities.length > Settings.Chunk_Processor.MAX_ENTITIES) {
|
|
event.setShouldAbortSpawn(true);
|
|
event.setCancelled(true);
|
|
return;
|
|
}
|
|
CreatureSpawnEvent.SpawnReason reason = event.getReason();
|
|
switch (reason.toString()) {
|
|
case "DISPENSE_EGG":
|
|
case "EGG":
|
|
case "OCELOT_BABY":
|
|
case "SPAWNER_EGG":
|
|
if (!area.isSpawnEggs()) {
|
|
event.setShouldAbortSpawn(true);
|
|
event.setCancelled(true);
|
|
return;
|
|
}
|
|
break;
|
|
case "REINFORCEMENTS":
|
|
case "NATURAL":
|
|
case "MOUNT":
|
|
case "PATROL":
|
|
case "RAID":
|
|
case "SHEARED":
|
|
case "SILVERFISH_BLOCK":
|
|
case "ENDER_PEARL":
|
|
case "TRAP":
|
|
case "VILLAGE_DEFENSE":
|
|
case "VILLAGE_INVASION":
|
|
case "BEEHIVE":
|
|
case "CHUNK_GEN":
|
|
if (!area.isMobSpawning()) {
|
|
event.setShouldAbortSpawn(true);
|
|
event.setCancelled(true);
|
|
return;
|
|
}
|
|
break;
|
|
case "BREEDING":
|
|
if (!area.isSpawnBreeding()) {
|
|
event.setShouldAbortSpawn(true);
|
|
event.setCancelled(true);
|
|
return;
|
|
}
|
|
break;
|
|
case "BUILD_IRONGOLEM":
|
|
case "BUILD_SNOWMAN":
|
|
case "BUILD_WITHER":
|
|
case "CUSTOM":
|
|
if (!area.isSpawnCustom() && event.getType() != EntityType.ARMOR_STAND) {
|
|
event.setShouldAbortSpawn(true);
|
|
event.setCancelled(true);
|
|
return;
|
|
}
|
|
break;
|
|
case "SPAWNER":
|
|
if (!area.isMobSpawnerSpawning()) {
|
|
event.setShouldAbortSpawn(true);
|
|
event.setCancelled(true);
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
Plot plot = location.getOwnedPlotAbs();
|
|
if (plot == null) {
|
|
EntityType type = event.getType();
|
|
if (!area.isMobSpawning()) {
|
|
switch (type) {
|
|
case DROPPED_ITEM:
|
|
if (Settings.Enabled_Components.KILL_ROAD_ITEMS) {
|
|
event.setShouldAbortSpawn(true);
|
|
event.setCancelled(true);
|
|
return;
|
|
}
|
|
case PLAYER:
|
|
return;
|
|
}
|
|
if (type.isAlive()) {
|
|
event.setShouldAbortSpawn(true);
|
|
event.setCancelled(true);
|
|
}
|
|
}
|
|
if (!area.isMiscSpawnUnowned() && !type.isAlive()) {
|
|
event.setShouldAbortSpawn(true);
|
|
event.setCancelled(true);
|
|
}
|
|
return;
|
|
}
|
|
if (Settings.Done.RESTRICT_BUILDING && DoneFlag.isDone(plot)) {
|
|
event.setShouldAbortSpawn(true);
|
|
event.setCancelled(true);
|
|
}
|
|
}
|
|
|
|
@EventHandler
|
|
public void onPlayerNaturallySpawnCreaturesEvent(PlayerNaturallySpawnCreaturesEvent event) {
|
|
if (Settings.Paper_Components.CANCEL_CHUNK_SPAWN) {
|
|
Location location = BukkitUtil.adapt(event.getPlayer().getLocation());
|
|
PlotArea area = location.getPlotArea();
|
|
if (area != null && !area.isMobSpawning()) {
|
|
event.setCancelled(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
@EventHandler
|
|
public void onPreSpawnerSpawnEvent(PreSpawnerSpawnEvent event) {
|
|
if (Settings.Paper_Components.SPAWNER_SPAWN) {
|
|
Location location = BukkitUtil.adapt(event.getSpawnerLocation());
|
|
PlotArea area = location.getPlotArea();
|
|
if (area != null && !area.isMobSpawnerSpawning()) {
|
|
event.setCancelled(true);
|
|
event.setShouldAbortSpawn(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
@EventHandler(priority = EventPriority.HIGHEST)
|
|
public void onBlockPlace(BlockPlaceEvent event) {
|
|
if (!Settings.Paper_Components.TILE_ENTITY_CHECK || !Settings.Enabled_Components.CHUNK_PROCESSOR) {
|
|
return;
|
|
}
|
|
if (!(event.getBlock().getState(false) instanceof TileState)) {
|
|
return;
|
|
}
|
|
final Location location = BukkitUtil.adapt(event.getBlock().getLocation());
|
|
final PlotArea plotArea = location.getPlotArea();
|
|
if (plotArea == null) {
|
|
return;
|
|
}
|
|
final int tileEntityCount = event.getBlock().getChunk().getTileEntities(false).length;
|
|
if (tileEntityCount >= Settings.Chunk_Processor.MAX_TILES) {
|
|
final PlotPlayer<?> plotPlayer = BukkitUtil.adapt(event.getPlayer());
|
|
plotPlayer.sendMessage(
|
|
TranslatableCaption.of("errors.tile_entity_cap_reached"),
|
|
Template.of("amount", String.valueOf(Settings.Chunk_Processor.MAX_TILES))
|
|
);
|
|
event.setCancelled(true);
|
|
event.setBuild(false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Unsure if this will be any performance improvement over the spigot version,
|
|
* but here it is anyway :)
|
|
*
|
|
* @param event Paper's PlayerLaunchProjectileEvent
|
|
*/
|
|
@EventHandler
|
|
public void onProjectileLaunch(PlayerLaunchProjectileEvent event) {
|
|
if (!Settings.Paper_Components.PLAYER_PROJECTILE) {
|
|
return;
|
|
}
|
|
Projectile entity = event.getProjectile();
|
|
ProjectileSource shooter = entity.getShooter();
|
|
if (!(shooter instanceof Player)) {
|
|
return;
|
|
}
|
|
Location location = BukkitUtil.adapt(entity.getLocation());
|
|
if (!this.plotAreaManager.hasPlotArea(location.getWorldName())) {
|
|
return;
|
|
}
|
|
PlotPlayer<Player> pp = BukkitUtil.adapt((Player) shooter);
|
|
Plot plot = location.getOwnedPlot();
|
|
|
|
if (plot == null) {
|
|
if (!Permissions.hasPermission(pp, Permission.PERMISSION_ADMIN_PROJECTILE_ROAD)) {
|
|
pp.sendMessage(
|
|
TranslatableCaption.of("permission.no_permission_event"),
|
|
Template.of("node", String.valueOf(Permission.PERMISSION_ADMIN_PROJECTILE_ROAD))
|
|
);
|
|
entity.remove();
|
|
event.setCancelled(true);
|
|
}
|
|
} else if (!plot.hasOwner()) {
|
|
if (!Permissions.hasPermission(pp, Permission.PERMISSION_ADMIN_PROJECTILE_UNOWNED)) {
|
|
pp.sendMessage(
|
|
TranslatableCaption.of("permission.no_permission_event"),
|
|
Template.of("node", String.valueOf(Permission.PERMISSION_ADMIN_PROJECTILE_UNOWNED))
|
|
);
|
|
entity.remove();
|
|
event.setCancelled(true);
|
|
}
|
|
} else if (!plot.isAdded(pp.getUUID())) {
|
|
if (!plot.getFlag(ProjectilesFlag.class)) {
|
|
if (!Permissions.hasPermission(pp, Permission.PERMISSION_ADMIN_PROJECTILE_OTHER)) {
|
|
pp.sendMessage(
|
|
TranslatableCaption.of("permission.no_permission_event"),
|
|
Template.of("node", String.valueOf(Permission.PERMISSION_ADMIN_PROJECTILE_OTHER))
|
|
);
|
|
entity.remove();
|
|
event.setCancelled(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@EventHandler
|
|
public void onAsyncTabCompletion(final AsyncTabCompleteEvent event) {
|
|
if (!Settings.Paper_Components.ASYNC_TAB_COMPLETION) {
|
|
return;
|
|
}
|
|
String buffer = event.getBuffer();
|
|
if (!(event.getSender() instanceof Player)) {
|
|
return;
|
|
}
|
|
if ((!event.isCommand() && !buffer.startsWith("/")) || buffer.indexOf(' ') == -1) {
|
|
return;
|
|
}
|
|
if (buffer.startsWith("/")) {
|
|
buffer = buffer.substring(1);
|
|
}
|
|
final String[] unprocessedArgs = buffer.split(Pattern.quote(" "));
|
|
if (unprocessedArgs.length == 1) {
|
|
return; // We don't do anything in this case
|
|
} else if (!Settings.Enabled_Components.TAB_COMPLETED_ALIASES
|
|
.contains(unprocessedArgs[0].toLowerCase(Locale.ENGLISH))) {
|
|
return;
|
|
}
|
|
final String[] args = new String[unprocessedArgs.length - 1];
|
|
System.arraycopy(unprocessedArgs, 1, args, 0, args.length);
|
|
try {
|
|
final PlotPlayer<?> player = BukkitUtil.adapt((Player) event.getSender());
|
|
final Collection<Command> objects = MainCommand.getInstance().tab(player, args, buffer.endsWith(" "));
|
|
if (objects == null) {
|
|
return;
|
|
}
|
|
final List<String> result = new ArrayList<>();
|
|
for (final com.plotsquared.core.command.Command o : objects) {
|
|
result.add(o.toString());
|
|
}
|
|
event.setCompletions(result);
|
|
event.setHandled(true);
|
|
} catch (final Exception ignored) {
|
|
}
|
|
}
|
|
|
|
}
|